diff options
-rw-r--r-- | History.txt | 26 | ||||
-rw-r--r-- | arel.gemspec | 6 | ||||
-rw-r--r-- | lib/arel.rb | 2 | ||||
-rw-r--r-- | lib/arel/attributes/attribute.rb | 2 | ||||
-rw-r--r-- | lib/arel/crud.rb | 45 | ||||
-rw-r--r-- | lib/arel/nodes/node.rb | 2 | ||||
-rw-r--r-- | lib/arel/nodes/unqualified_column.rb | 4 | ||||
-rw-r--r-- | lib/arel/select_manager.rb | 6 | ||||
-rw-r--r-- | lib/arel/table.rb | 34 | ||||
-rw-r--r-- | lib/arel/visitors/dot.rb | 12 | ||||
-rw-r--r-- | lib/arel/visitors/join_sql.rb | 2 | ||||
-rw-r--r-- | lib/arel/visitors/to_sql.rb | 37 | ||||
-rw-r--r-- | lib/arel/visitors/visitor.rb | 3 | ||||
-rw-r--r-- | test/attributes/test_attribute.rb | 6 | ||||
-rw-r--r-- | test/helper.rb | 2 | ||||
-rw-r--r-- | test/test_crud.rb | 12 | ||||
-rw-r--r-- | test/test_select_manager.rb | 42 | ||||
-rw-r--r-- | test/test_table.rb | 25 | ||||
-rw-r--r-- | test/visitors/test_join_sql.rb | 3 | ||||
-rw-r--r-- | test/visitors/test_to_sql.rb | 10 |
20 files changed, 187 insertions, 94 deletions
diff --git a/History.txt b/History.txt index fa08015903..d3172f4162 100644 --- a/History.txt +++ b/History.txt @@ -1,3 +1,29 @@ +== 2.1.0 (unreleased) + +* Enhancements + + * AST is now Enumerable + +* Deprecations + + * Calls to `insert` are deprecated. Please use `compile_insert` then call + `to_sql` on the resulting object and execute that SQL. + + * Calls to `update` are deprecated. Please use `compile_update` then call + `to_sql` on the resulting object and execute that SQL. + + * Calls to `delete` are deprecated. Please use `compile_delete` then call + `to_sql` on the resulting object and execute that SQL. + + * Arel::Table#joins is deprecated and will be removed in 3.0.0 with no + replacement. + + * Arel::Table#columns is deprecated and will be removed in 3.0.0 with no + replacement. + + * Arel::Table.table_cache is deprecated and will be removed in 3.0.0 with no + replacement. + == 2.0.6 12/01/2010 * Bug Fixes diff --git a/arel.gemspec b/arel.gemspec index 2e52ae4a19..c7ec979042 100644 --- a/arel.gemspec +++ b/arel.gemspec @@ -2,11 +2,11 @@ Gem::Specification.new do |s| s.name = %q{arel} - s.version = "2.0.5.20101130111154" + s.version = "2.0.7.beta.20101201093009" - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= s.authors = ["Aaron Patterson", "Bryan Halmkamp", "Emilio Tagua", "Nick Kallen"] - s.date = %q{2010-11-30} + s.date = %q{2010-12-01} s.description = %q{Arel is a Relational Algebra for Ruby. It 1) simplifies the generation complex of SQL queries and it 2) adapts to various RDBMS systems. It is intended to be a framework framework; that is, you can build your own ORM with it, focusing on innovative object and collection modeling as opposed to database compatibility and query generation.} s.email = ["aaron@tenderlovemaking.com", "bryan@brynary.com", "miloops@gmail.com", "nick@example.org"] s.extra_rdoc_files = ["History.txt", "MIT-LICENSE.txt", "Manifest.txt", "README.markdown"] diff --git a/lib/arel.rb b/lib/arel.rb index 6c9f51d03a..6a8d9571d1 100644 --- a/lib/arel.rb +++ b/lib/arel.rb @@ -29,7 +29,7 @@ require 'arel/sql_literal' #### module Arel - VERSION = '2.0.6' + VERSION = '2.0.7.beta' def self.sql raw_sql Arel::Nodes::SqlLiteral.new raw_sql diff --git a/lib/arel/attributes/attribute.rb b/lib/arel/attributes/attribute.rb index 5cbe194b41..9a42e5a4da 100644 --- a/lib/arel/attributes/attribute.rb +++ b/lib/arel/attributes/attribute.rb @@ -1,6 +1,6 @@ module Arel module Attributes - class Attribute < Struct.new :relation, :name, :column + class Attribute < Struct.new :relation, :name include Arel::Expressions include Arel::Predications end diff --git a/lib/arel/crud.rb b/lib/arel/crud.rb index b3199e6d38..ec58734456 100644 --- a/lib/arel/crud.rb +++ b/lib/arel/crud.rb @@ -2,8 +2,7 @@ module Arel ### # FIXME hopefully we can remove this module Crud - # FIXME: this method should go away - def update values + def compile_update values um = UpdateManager.new @engine if Nodes::SqlLiteral === values @@ -16,22 +15,54 @@ module Arel um.take @ast.limit um.order(*@ast.orders) um.wheres = @ctx.wheres + um + end + + # FIXME: this method should go away + def update values + if $VERBOSE + warn <<-eowarn +update (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please +switch to `compile_update` + eowarn + end + um = compile_update values @engine.connection.update um.to_sql, 'AREL' end - # FIXME: this method should go away - def insert values + def compile_insert values im = InsertManager.new @engine im.insert values - @engine.connection.insert im.to_sql + im end - def delete + # FIXME: this method should go away + def insert values + if $VERBOSE + warn <<-eowarn +insert (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please +switch to `compile_insert` + eowarn + end + @engine.connection.insert compile_insert(values).to_sql + end + + def compile_delete dm = DeleteManager.new @engine dm.wheres = @ctx.wheres dm.from @ctx.froms - @engine.connection.delete dm.to_sql, 'AREL' + dm + end + + def delete + if $VERBOSE + warn <<-eowarn +delete (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please +switch to `compile_delete` + eowarn + end + @engine.connection.delete compile_delete.to_sql, 'AREL' end end end diff --git a/lib/arel/nodes/node.rb b/lib/arel/nodes/node.rb index 404ad22ece..567221aab2 100644 --- a/lib/arel/nodes/node.rb +++ b/lib/arel/nodes/node.rb @@ -3,6 +3,8 @@ module Arel ### # Abstract base class for all AST nodes class Node + include Enumerable + ### # Factory method to create a Nodes::Not node that has the recipient of # the caller as a child. diff --git a/lib/arel/nodes/unqualified_column.rb b/lib/arel/nodes/unqualified_column.rb index f7ba653c11..2820dba9d2 100644 --- a/lib/arel/nodes/unqualified_column.rb +++ b/lib/arel/nodes/unqualified_column.rb @@ -4,6 +4,10 @@ module Arel alias :attribute :expr alias :attribute= :expr= + def relation + @expr.relation + end + def column @expr.column end diff --git a/lib/arel/select_manager.rb b/lib/arel/select_manager.rb index 08cfd41e7f..de12402e9f 100644 --- a/lib/arel/select_manager.rb +++ b/lib/arel/select_manager.rb @@ -143,8 +143,8 @@ module Arel def join_sql return nil unless @ctx.froms - viz = Visitors::JoinSql.new @engine - Nodes::SqlLiteral.new viz.accept @ctx + sql = @visitor.dup.extend(Visitors::JoinSql).accept @ctx + Nodes::SqlLiteral.new sql end def order_clauses @@ -155,7 +155,7 @@ module Arel def joins manager if $VERBOSE - warn "joins is deprecated and will be removed in 2.2" + warn "joins is deprecated and will be removed in 3.0.0" warn "please remove your call to joins from #{caller.first}" end manager.join_sql diff --git a/lib/arel/table.rb b/lib/arel/table.rb index aa23a7d601..e7a626a4c6 100644 --- a/lib/arel/table.rb +++ b/lib/arel/table.rb @@ -17,7 +17,6 @@ module Arel if Hash === engine @engine = engine[:engine] || Table.engine - @columns = attributes_for engine[:columns] # Sometime AR sends an :as parameter to table, to let the table know # that it is an Alias. We may want to override new, and return a @@ -46,7 +45,7 @@ module Arel def joins manager if $VERBOSE - warn "joins is deprecated and will be removed in 2.2" + warn "joins is deprecated and will be removed in 3.0.0" warn "please remove your call to joins from #{caller.first}" end nil @@ -93,41 +92,46 @@ module Arel end def columns + if $VERBOSE + warn <<-eowarn +(#{caller.first}) Arel::Table#columns is deprecated and will be removed in +Arel 3.0.0 with no replacement. PEW PEW PEW!!! + eowarn + end @columns ||= attributes_for @engine.connection.columns(@name, "#{@name} Columns") end def [] name - return nil unless table_exists? - - name = name.to_sym - columns.find { |column| column.name == name } + ::Arel::Attribute.new self, name.to_sym end def select_manager SelectManager.new(@engine) end + def insert_manager + InsertManager.new(@engine) + end + private def attributes_for columns return nil unless columns columns.map do |column| - Attributes.for(column).new self, column.name.to_sym, column + Attributes.for(column).new self, column.name.to_sym end end - def table_exists? - @table_exists ||= tables.key?(@name) || engine.connection.table_exists?(name) - end - - def tables - self.class.table_cache(@engine) - end - @@table_cache = nil def self.table_cache engine # :nodoc: + if $VERBOSE + warn <<-eowarn +(#{caller.first}) Arel::Table.table_cache is deprecated and will be removed in +Arel 3.0.0 with no replacement. PEW PEW PEW!!! + eowarn + end @@table_cache ||= Hash[engine.connection.tables.map { |x| [x,true] }] end end diff --git a/lib/arel/visitors/dot.rb b/lib/arel/visitors/dot.rb index c515cbe220..eab5e4afdc 100644 --- a/lib/arel/visitors/dot.rb +++ b/lib/arel/visitors/dot.rb @@ -28,10 +28,6 @@ module Arel end private - def visit_Arel_Nodes_Grouping o - visit_edge o, "expr" - end - def visit_Arel_Nodes_Ordering o visit_edge o, "expr" visit_edge o, "direction" @@ -55,10 +51,6 @@ module Arel visit_edge o, "distinct" end - def visit_Arel_Nodes_On o - visit_edge o, "expr" - end - def visit_Arel_Nodes_Values o visit_edge o, "expressions" end @@ -80,10 +72,6 @@ module Arel visit_edge o, "wheres" end - def visit_Arel_Nodes_UnqualifiedColumn o - visit_edge o, "attribute" - end - def unary o visit_edge o, "expr" end diff --git a/lib/arel/visitors/join_sql.rb b/lib/arel/visitors/join_sql.rb index d3fb18d3c6..7ba2bde540 100644 --- a/lib/arel/visitors/join_sql.rb +++ b/lib/arel/visitors/join_sql.rb @@ -8,7 +8,7 @@ module Arel # # This visitor is used in SelectManager#join_sql and is for backwards # compatibility with Arel V1.0 - class JoinSql < Arel::Visitors::ToSql + module JoinSql private def visit_Arel_Nodes_SelectCore o diff --git a/lib/arel/visitors/to_sql.rb b/lib/arel/visitors/to_sql.rb index 2fb464b265..bd0d664b44 100644 --- a/lib/arel/visitors/to_sql.rb +++ b/lib/arel/visitors/to_sql.rb @@ -10,6 +10,8 @@ module Arel @last_column = nil @quoted_tables = {} @quoted_columns = {} + @column_cache = Hash.new { |h,k| h[k] = {} } + @table_exists = {} end def accept object @@ -66,9 +68,36 @@ module Arel o.alias ? " AS #{visit o.alias}" : ''}" end + def table_exists? name + return true if @table_exists.key? name + + @connection.tables.each do |table| + @table_exists[table] = true + end + + @table_exists.key? name + end + + def column_for attr + name = attr.name.to_sym + table = attr.relation.name + + return nil unless table_exists? table + + # If we don't have this column cached, get a list of columns and + # cache them for this table + unless @column_cache.key? table + #$stderr.puts "MISS: #{self.class.name} #{object_id} #{table.inspect} : #{name.inspect}" + columns = @connection.columns(table, "#{table}(#{name}) Columns") + @column_cache[table] = Hash[columns.map { |c| [c.name.to_sym, c] }] + end + + @column_cache[table][name] + end + def visit_Arel_Nodes_Values o - "VALUES (#{o.expressions.zip(o.columns).map { |value, column| - quote(value, column && column.column) + "VALUES (#{o.expressions.zip(o.columns).map { |value, attr| + quote(value, attr && column_for(attr)) }.join ', '})" end @@ -226,7 +255,7 @@ module Arel end def visit_Arel_Nodes_Assignment o - right = quote(o.right, o.left.column) + right = quote(o.right, column_for(o.left)) "#{visit o.left} = #{right}" end @@ -259,7 +288,7 @@ module Arel end def visit_Arel_Attributes_Attribute o - @last_column = o.column + @last_column = column_for o join_name = o.relation.table_alias || o.relation.name "#{quote_table_name join_name}.#{quote_column_name o.name}" end diff --git a/lib/arel/visitors/visitor.rb b/lib/arel/visitors/visitor.rb index 055acf9765..85359f3e67 100644 --- a/lib/arel/visitors/visitor.rb +++ b/lib/arel/visitors/visitor.rb @@ -13,7 +13,8 @@ module Arel def visit object send DISPATCH[object.class], object - rescue NoMethodError + rescue NoMethodError => e + raise e if respond_to?(DISPATCH[object.class], true) warn "visiting #{object.class} via superclass, this will be removed in arel 2.2.0" if $VERBOSE superklass = object.class.ancestors.find { |klass| respond_to?(DISPATCH[klass], true) diff --git a/test/attributes/test_attribute.rb b/test/attributes/test_attribute.rb index 06954c242b..df7dc69621 100644 --- a/test/attributes/test_attribute.rb +++ b/test/attributes/test_attribute.rb @@ -326,7 +326,7 @@ module Arel describe '#eq' do it 'should return an equality node' do - attribute = Attribute.new nil, nil, nil + attribute = Attribute.new nil, nil equality = attribute.eq 1 equality.left.must_equal attribute equality.right.must_equal 1 @@ -485,7 +485,7 @@ module Arel end it 'should return an in node' do - attribute = Attribute.new nil, nil, nil + attribute = Attribute.new nil, nil node = Nodes::In.new attribute, [1,2,3] node.left.must_equal attribute node.right.must_equal [1, 2, 3] @@ -538,7 +538,7 @@ module Arel end it 'should return a NotIn node' do - attribute = Attribute.new nil, nil, nil + attribute = Attribute.new nil, nil node = Nodes::NotIn.new attribute, [1,2,3] node.left.must_equal attribute node.right.must_equal [1, 2, 3] diff --git a/test/helper.rb b/test/helper.rb index 3f9ac22447..f13596f9ec 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -8,6 +8,6 @@ Arel::Table.engine = Arel::Sql::Engine.new(FakeRecord::Base.new) class Object def must_be_like other - self.gsub(/\s+/, ' ').strip.must_equal other.gsub(/\s+/, ' ').strip + gsub(/\s+/, ' ').strip.must_equal other.gsub(/\s+/, ' ').strip end end diff --git a/test/test_crud.rb b/test/test_crud.rb index 582f0ec072..ebe5d68908 100644 --- a/test/test_crud.rb +++ b/test/test_crud.rb @@ -47,10 +47,8 @@ module Arel table = Table.new :users fc = FakeCrudder.new fc.from table - fc.update [[table[:id], 'foo']] - fc.engine.calls.find { |method, _| - method == :update - }.wont_be_nil + stmt = fc.compile_update [[table[:id], 'foo']] + assert_instance_of Arel::UpdateManager, stmt end end @@ -59,10 +57,8 @@ module Arel table = Table.new :users fc = FakeCrudder.new fc.from table - fc.delete - fc.engine.calls.find { |method, _| - method == :delete - }.wont_be_nil + stmt = fc.compile_delete + assert_instance_of Arel::DeleteManager, stmt end end end diff --git a/test/test_select_manager.rb b/test/test_select_manager.rb index d63bec0093..826832c5ab 100644 --- a/test/test_select_manager.rb +++ b/test/test_select_manager.rb @@ -26,6 +26,17 @@ module Arel def quote_table_name thing; @engine.connection.quote_table_name thing end def quote_column_name thing; @engine.connection.quote_column_name thing end def quote thing, column; @engine.connection.quote thing, column end + def columns table, message = nil + @engine.connection.columns table, message + end + + def table_exists? name + @engine.connection.table_exists? name + end + + def tables + @engine.connection.tables + end def execute sql, name = nil, *args @executed << sql @@ -312,7 +323,6 @@ module Arel manager.join_sql.must_be_like %{ INNER JOIN "users" "users_2" "users"."id" = "users_2"."id" } - manager.joins(manager).must_equal manager.join_sql end it 'returns outer join sql' do @@ -323,7 +333,6 @@ module Arel manager.join_sql.must_be_like %{ LEFT OUTER JOIN "users" "users_2" "users"."id" = "users_2"."id" } - manager.joins(manager).must_equal manager.join_sql end it 'returns string join sql' do @@ -331,7 +340,6 @@ module Arel manager = Arel::SelectManager.new Table.engine manager.from Nodes::StringJoin.new(table, 'hello') manager.join_sql.must_be_like %{ 'hello' } - manager.joins(manager).must_equal manager.join_sql end it 'returns nil join sql' do @@ -393,9 +401,9 @@ module Arel table = Table.new :users manager = Arel::SelectManager.new engine manager.from table - manager.delete + stmt = manager.compile_delete - engine.executed.last.must_be_like %{ DELETE FROM "users" } + stmt.to_sql.must_be_like %{ DELETE FROM "users" } end it "copies where" do @@ -404,9 +412,9 @@ module Arel manager = Arel::SelectManager.new engine manager.from table manager.where table[:id].eq 10 - manager.delete + stmt = manager.compile_delete - engine.executed.last.must_be_like %{ + stmt.to_sql.must_be_like %{ DELETE FROM "users" WHERE "users"."id" = 10 } end @@ -436,9 +444,9 @@ module Arel manager = Arel::SelectManager.new engine manager.from table manager.take 1 - manager.update(SqlLiteral.new('foo = bar')) + stmt = manager.compile_update(SqlLiteral.new('foo = bar')) - engine.executed.last.must_be_like %{ + stmt.to_sql.must_be_like %{ UPDATE "users" SET foo = bar WHERE "users"."id" IN (SELECT "users"."id" FROM "users" LIMIT 1) } @@ -450,9 +458,9 @@ module Arel manager = Arel::SelectManager.new engine manager.from table manager.order :foo - manager.update(SqlLiteral.new('foo = bar')) + stmt = manager.compile_update(SqlLiteral.new('foo = bar')) - engine.executed.last.must_be_like %{ + stmt.to_sql.must_be_like %{ UPDATE "users" SET foo = bar WHERE "users"."id" IN (SELECT "users"."id" FROM "users" ORDER BY foo) } @@ -463,9 +471,9 @@ module Arel table = Table.new :users manager = Arel::SelectManager.new engine manager.from table - manager.update(SqlLiteral.new('foo = bar')) + stmt = manager.compile_update(SqlLiteral.new('foo = bar')) - engine.executed.last.must_be_like %{ UPDATE "users" SET foo = bar } + stmt.to_sql.must_be_like %{ UPDATE "users" SET foo = bar } end it 'copies where clauses' do @@ -474,9 +482,9 @@ module Arel manager = Arel::SelectManager.new engine manager.where table[:id].eq 10 manager.from table - manager.update(table[:id] => 1) + stmt = manager.compile_update(table[:id] => 1) - engine.executed.last.must_be_like %{ + stmt.to_sql.must_be_like %{ UPDATE "users" SET "id" = 1 WHERE "users"."id" = 10 } end @@ -486,9 +494,9 @@ module Arel table = Table.new :users manager = Arel::SelectManager.new engine manager.from table - manager.update(table[:id] => 1) + stmt = manager.compile_update(table[:id] => 1) - engine.executed.last.must_be_like %{ + stmt.to_sql.must_be_like %{ UPDATE "users" SET "id" = 1 } end diff --git a/test/test_table.rb b/test/test_table.rb index 8d37a8eaff..bb7bd255fd 100644 --- a/test/test_table.rb +++ b/test/test_table.rb @@ -6,6 +6,18 @@ module Arel @relation = Table.new(:users) end + it 'should return an insert manager' do + im = @relation.compile_insert 'VALUES(NULL)' + assert_kind_of Arel::InsertManager, im + assert_equal 'INSERT INTO NULL VALUES(NULL)', im.to_sql + end + + it 'should return IM from insert_manager' do + im = @relation.insert_manager + assert_kind_of Arel::InsertManager, im + assert_equal im.engine, @relation.engine + end + describe 'skip' do it 'should add an offset' do sm = @relation.skip 2 @@ -36,12 +48,6 @@ module Arel end describe 'backwards compat' do - describe 'joins' do - it 'returns nil' do - @relation.joins(nil).must_equal nil - end - end - describe 'join' do it 'noops on nil' do mgr = @relation.join nil @@ -168,13 +174,6 @@ module Arel it "manufactures an attribute if the symbol names an attribute within the relation" do column = @relation[:id] column.name.must_equal :id - column.must_be_kind_of Attributes::Integer - end - end - - describe 'when table does not exist' do - it 'returns nil' do - @relation[:foooo].must_be_nil end end end diff --git a/test/visitors/test_join_sql.rb b/test/visitors/test_join_sql.rb index 8253fe5ab4..181ef6c570 100644 --- a/test/visitors/test_join_sql.rb +++ b/test/visitors/test_join_sql.rb @@ -4,7 +4,8 @@ module Arel module Visitors describe 'the join_sql visitor' do before do - @visitor = JoinSql.new Table.engine + @visitor = ToSql.new Table.engine + @visitor.extend(JoinSql) end describe 'inner join' do diff --git a/test/visitors/test_to_sql.rb b/test/visitors/test_to_sql.rb index b9d6c78e62..09f037f75f 100644 --- a/test/visitors/test_to_sql.rb +++ b/test/visitors/test_to_sql.rb @@ -96,7 +96,7 @@ module Arel end it "should visit visit_Arel_Attributes_Time" do - attr = Attributes::Time.new(@attr.relation, @attr.name, @attr.column) + attr = Attributes::Time.new(@attr.relation, @attr.name) @visitor.accept attr end @@ -155,7 +155,9 @@ module Arel end in_node = Nodes::In.new @attr, %w{ a b c } visitor = visitor.new(Table.engine) - visitor.expected = @attr.column + visitor.expected = Table.engine.connection.columns(:users).find { |x| + x.name == 'name' + } visitor.accept(in_node).must_equal %("users"."name" IN ('a', 'b', 'c')) end end @@ -201,7 +203,9 @@ module Arel end in_node = Nodes::NotIn.new @attr, %w{ a b c } visitor = visitor.new(Table.engine) - visitor.expected = @attr.column + visitor.expected = Table.engine.connection.columns(:users).find { |x| + x.name == 'name' + } visitor.accept(in_node).must_equal %("users"."name" NOT IN ('a', 'b', 'c')) end end |