returning 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,7 +6,6 @@ rvm:
6
6
  - rbx
7
7
  - rbx-2.0
8
8
  - ree
9
- - jruby
10
9
  - ruby-head
11
10
  gemfile:
12
11
  - Gemfile
@@ -1,3 +1,6 @@
1
+ 0.0.4 - 2011-08-24
2
+ * Save returning in an instance variable of the model, instead of connection. Previous approach failed when you tried to load an association inside a callback.
3
+
1
4
  0.0.3 - 2011-08-22
2
5
  -----
3
6
  * Pass parameters down on save. This makes update_attribute work.
@@ -2,14 +2,27 @@ require 'active_record'
2
2
  require 'active_record/connection_adapters/postgresql_adapter'
3
3
 
4
4
  require "returning/version"
5
- require 'returning/active_record/returning'
6
- require 'returning/active_record/adapter'
7
-
8
-
9
- module Returning
10
- # Your code goes here...
5
+ if ActiveRecord::VERSION::STRING =~ /^3\.0/
6
+ require 'returning/rails30/returning'
7
+ elsif ActiveRecord::VERSION::STRING =~ /^3\.1/
8
+ require 'returning/rails31/returning'
9
+ else
10
+ raise "ActiveRecord version #{ActiveRecord::VERSION::STRING} is not supported!"
11
11
  end
12
12
 
13
+ require 'returning/active_record/returning'
14
+ require 'returning/arel/tree_manager'
15
+ require 'returning/arel/nodes/select_core'
16
+ require 'returning/arel/nodes/returning'
17
+ require 'returning/arel/delete_update_manager'
18
+ require 'returning/arel/nodes/delete_update_statement'
19
+ require 'returning/arel/visitors/postgresql'
13
20
 
14
21
  ActiveRecord::Base.send(:include, Returning::ActiveRecord::Returning)
15
- ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:include, Returning::ActiveRecord::Adapter)
22
+ Arel::SelectManager.send(:include, Returning::Arel::TreeManager)
23
+ Arel::UpdateManager.send(:include, Returning::Arel::DeleteUpdateManager)
24
+ Arel::DeleteManager.send(:include, Returning::Arel::DeleteUpdateManager)
25
+ Arel::Nodes::SelectCore.send(:include, Returning::Arel::Nodes::SelectCore)
26
+ Arel::Nodes::UpdateStatement.send(:include, Returning::Arel::Nodes::DeleteUpdateStatement)
27
+ Arel::Nodes::DeleteStatement.send(:include, Returning::Arel::Nodes::DeleteUpdateStatement)
28
+ Arel::Visitors::PostgreSQL.send(:include, Returning::Arel::Visitors::PostgreSQL)
@@ -3,22 +3,13 @@ require 'active_support/concern'
3
3
  module Returning
4
4
  module ActiveRecord
5
5
  module Returning
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- if method(:find_by_sql).arity == 1
10
- class_eval do
11
- def self.find_by_sql(sql, binds = [])
12
- connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
13
- end
14
- end
15
- end
16
- end
17
-
18
6
  def save(options = {})
19
7
  if r = options[:returning]
20
- connection.returning(r, self.class) do
8
+ begin
9
+ old_returning, @_returning = @_returning, r
21
10
  super
11
+ ensure
12
+ @_returning = old_returning
22
13
  end
23
14
  else
24
15
  super
@@ -26,10 +17,18 @@ module Returning
26
17
  end
27
18
 
28
19
  def create_or_update
29
- if connection.returning? && !new_record?
30
- fields = update
31
- # if no attributes were changed return self
32
- fields == 0 ? self : fields[0]
20
+ if @_returning
21
+ raise ReadOnlyRecord if readonly?
22
+ if new_record?
23
+ create
24
+ self
25
+ elsif r = update
26
+ r = self.class.send(:instantiate, r[0])
27
+ r.readonly!
28
+ r
29
+ else
30
+ false
31
+ end
33
32
  else
34
33
  super
35
34
  end
@@ -37,8 +36,16 @@ module Returning
37
36
 
38
37
  def destroy(options = {})
39
38
  if r = options[:returning]
40
- connection.returning(r, self.class) do
41
- super()
39
+ begin
40
+ old_returning, @_returning = @_returning, r
41
+ if r = super()
42
+ r = self.class.send(:instantiate, r[0])
43
+ r.readonly!
44
+ end
45
+
46
+ r
47
+ ensure
48
+ @_returning = old_returning
42
49
  end
43
50
  else
44
51
  super()
@@ -0,0 +1,10 @@
1
+ module Returning
2
+ module Arel
3
+ module DeleteUpdateManager
4
+ def returning returnings
5
+ @ast.returnings = Nodes::Returning.new(returnings) if returnings
6
+ self
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module Returning
2
+ module Arel
3
+ module Nodes
4
+ module DeleteUpdateStatement
5
+ attr_accessor :returnings
6
+
7
+ def initialize_copy other
8
+ super
9
+ @returnings = @returnings.clone if @returnings
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ module Returning
2
+ module Arel
3
+ module Nodes
4
+ class Returning < ::Arel::Nodes::Node
5
+ attr_accessor :returnings
6
+
7
+ def initialize(returnings)
8
+ @returnings = returnings
9
+ end
10
+
11
+ def initialize_copy other
12
+ super
13
+ @returnings = @returnings.clone if @returnings
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ module Returning
2
+ module Arel
3
+ module Nodes
4
+ module SelectCore
5
+ attr_accessor :returnings
6
+
7
+ def initialize_copy other
8
+ super
9
+ @returnings = @returnings.clone if @returnings
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module Returning
2
+ module Arel
3
+ module TreeManager
4
+ def returning *returnings
5
+ @ctx.returnings = returnings if !returnings.compact.empty?
6
+ self
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,32 @@
1
+ module Returning
2
+ module Arel
3
+ module Visitors
4
+ module PostgreSQL
5
+ private
6
+ def visit_Returning_Arel_Nodes_Returning o
7
+ if o.returnings.empty?
8
+ ""
9
+ else
10
+ " RETURNING #{o.returnings.join(', ')}"
11
+ end
12
+ end
13
+
14
+ def visit_Arel_Nodes_UpdateStatement o
15
+ if o.returnings
16
+ "#{super}#{visit o.returnings}"
17
+ else
18
+ super
19
+ end
20
+ end
21
+
22
+ def visit_Arel_Nodes_DeleteStatement o
23
+ if o.returnings
24
+ "#{super}#{visit o.returnings}"
25
+ else
26
+ super
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ module Returning
2
+ module Rails30
3
+ module ActiveRecord
4
+ module Persistence
5
+ extend ActiveSupport::Concern
6
+ included do
7
+ def update(attribute_names = @attributes.keys, options = {})
8
+ attributes_with_values = arel_attributes_values(false, false, attribute_names)
9
+ return @_returning ? [@attributes] : 0 if attributes_with_values.empty?
10
+ self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.returning(@_returning).update(attributes_with_values)
11
+ end
12
+
13
+ def destroy
14
+ destroy_associations
15
+
16
+ if persisted?
17
+ result = self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.returning(@_returning).delete
18
+ end
19
+
20
+ @destroyed = true
21
+ freeze
22
+
23
+ result ? result : self
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ module Returning
2
+ module Rails30
3
+ module Arel
4
+ module Crud
5
+ def update values
6
+ um = ::Arel::UpdateManager.new @engine
7
+
8
+ if ::Arel::Nodes::SqlLiteral === values
9
+ relation = @ctx.froms
10
+ else
11
+ relation = values.first.first.relation
12
+ end
13
+ um.table relation
14
+ um.set values
15
+ um.take @ast.limit.expr if @ast.limit
16
+ um.order(*@ast.orders)
17
+ um.wheres = @ctx.wheres
18
+ um.returning @ctx.returnings if @ctx.returnings
19
+
20
+ if @ctx.returnings
21
+ @engine.connection.select_all um.to_sql, 'AREL'
22
+ else
23
+ @engine.connection.update um.to_sql, 'AREL'
24
+ end
25
+ end
26
+
27
+ def delete
28
+ dm = ::Arel::DeleteManager.new @engine
29
+ dm.wheres = @ctx.wheres
30
+ dm.from @ctx.froms
31
+ dm.returning @ctx.returnings if @ctx.returnings
32
+
33
+ if @ctx.returnings
34
+ @engine.connection.select_all dm.to_sql, 'AREL'
35
+ else
36
+ @engine.connection.delete dm.to_sql, 'AREL'
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,6 @@
1
+ require 'returning/rails30/active_record/persistence'
2
+ require 'returning/rails30/arel/crud'
3
+
4
+
5
+ Arel::SelectManager.send(:include, Returning::Rails30::Arel::Crud)
6
+ ActiveRecord::Persistence.send(:include, Returning::Rails30::ActiveRecord::Persistence)
@@ -0,0 +1,51 @@
1
+ module Returning
2
+ module Rails31
3
+ module ActiveRecord
4
+ module Persistence
5
+ extend ActiveSupport::Concern
6
+ included do
7
+ def update(attribute_names = @attributes.keys)
8
+ attributes_with_values = arel_attributes_values(false, false, attribute_names)
9
+ return @_returning ? [@attributes] : 0 if attributes_with_values.empty?
10
+ klass = self.class
11
+ stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.returning(@_returning).compile_update(attributes_with_values)
12
+ if @_returning
13
+ klass.connection.select_all stmt
14
+ else
15
+ klass.connection.update stmt
16
+ end
17
+ end
18
+
19
+ def destroy
20
+ destroy_associations
21
+
22
+ if persisted?
23
+ ::ActiveRecord::IdentityMap.remove(self) if ::ActiveRecord::IdentityMap.enabled?
24
+ pk = self.class.primary_key
25
+ column = self.class.columns_hash[pk]
26
+ substitute = connection.substitute_at(column, 0)
27
+
28
+ relation = self.class.unscoped.where(
29
+ self.class.arel_table[pk].eq(substitute))
30
+
31
+ relation.bind_values = [[column, id]]
32
+ stmt = relation.arel.returning(@_returning).compile_delete
33
+
34
+ klass = self.class
35
+ result = if @_returning
36
+ klass.connection.select_all stmt, 'SQL', relation.bind_values
37
+ else
38
+ klass.connection.delete stmt, 'SQL', relation.bind_values
39
+ end
40
+ end
41
+
42
+ @destroyed = true
43
+ freeze
44
+
45
+ result ? result : self
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,32 @@
1
+ module Returning
2
+ module Rails31
3
+ module Arel
4
+ module Crud
5
+ def compile_update values
6
+ um = ::Arel::UpdateManager.new @engine
7
+
8
+ if ::Arel::Nodes::SqlLiteral === values
9
+ relation = @ctx.from
10
+ else
11
+ relation = values.first.first.relation
12
+ end
13
+ um.table relation
14
+ um.set values
15
+ um.take @ast.limit.expr if @ast.limit
16
+ um.order(*@ast.orders)
17
+ um.wheres = @ctx.wheres
18
+ um.returning @ctx.returnings if @ctx.returnings
19
+ um
20
+ end
21
+
22
+ def compile_delete
23
+ dm = ::Arel::DeleteManager.new @engine
24
+ dm.wheres = @ctx.wheres
25
+ dm.from @ctx.froms
26
+ dm.returning @ctx.returnings if @ctx.returnings
27
+ dm
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,6 @@
1
+ require 'returning/rails31/active_record/persistence'
2
+ require 'returning/rails31/arel/crud'
3
+
4
+
5
+ Arel::SelectManager.send(:include, Returning::Rails31::Arel::Crud)
6
+ ActiveRecord::Persistence.send(:include, Returning::Rails31::ActiveRecord::Persistence)
@@ -1,3 +1,3 @@
1
1
  module Returning
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'logger'
2
3
 
3
4
  describe Returning do
4
5
  before do
@@ -37,13 +38,23 @@ describe Returning do
37
38
  post.save(:returning => "name", :validate => false).should be_true
38
39
  end
39
40
  end
41
+
42
+ context 'works fine with associations' do
43
+ it "does not pollute destroy with RETURNING of an association" do
44
+ post = Post.first
45
+ post.update_attributes :tags_attributes => [{:tag => 'a'}, {:tag => 'b'}]
46
+ post.tags_attributes = Post.find(post.id).tags.map { |t| {:id => t.id, :_destroy => '1'} }
47
+ post.name = 'New name'
48
+ post.save(:returning => 'name')
49
+ end
50
+ end
40
51
  end
41
52
 
42
53
  describe '#destroy' do
43
54
  it 'returns the column passed to returning' do
44
55
  post = Post.first
45
56
  post.name = 'hello world'
46
- post.destroy(:returning => 'name').name.should == 'hello world'
57
+ post.destroy(:returning => 'name').name.should == 'hello'
47
58
  end
48
59
 
49
60
  context 'with query cache' do
@@ -3,7 +3,7 @@ require 'bundler/setup'
3
3
 
4
4
  Bundler.require
5
5
 
6
- Dir['spec/support/**/*.rb'].each { |f| require f }
6
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
7
7
 
8
8
  RSpec.configure do
9
9
  end
@@ -23,16 +23,28 @@ class Migration < ActiveRecord::Migration
23
23
  create_table :posts, :force => true do |t|
24
24
  t.string :name, :author
25
25
  end
26
+ create_table :tags, :force => true do |t|
27
+ t.string :tag
28
+ t.references :post
29
+ end
26
30
  end
27
31
 
28
32
  def self.down
29
33
  drop_table :posts
34
+ drop_table :tags
30
35
  end
31
36
  end
32
37
 
33
38
  Migration.up
34
39
 
35
40
  class Post < ActiveRecord::Base
41
+ has_many :tags
42
+
43
+ accepts_nested_attributes_for :tags, :allow_destroy => true
44
+ end
45
+
46
+ class Tag < ActiveRecord::Base
47
+ belongs_to :post
36
48
  end
37
49
 
38
50
  class PostQueryCache < ActiveRecord::Base
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: returning
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 3
10
- version: 0.0.3
9
+ - 4
10
+ version: 0.0.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Eugene Pimenov
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-22 00:00:00 Z
18
+ date: 2011-08-24 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  requirement: &id001 !ruby/object:Gem::Requirement
@@ -67,6 +67,18 @@ files:
67
67
  - lib/returning.rb
68
68
  - lib/returning/active_record/adapter.rb
69
69
  - lib/returning/active_record/returning.rb
70
+ - lib/returning/arel/delete_update_manager.rb
71
+ - lib/returning/arel/nodes/delete_update_statement.rb
72
+ - lib/returning/arel/nodes/returning.rb
73
+ - lib/returning/arel/nodes/select_core.rb
74
+ - lib/returning/arel/tree_manager.rb
75
+ - lib/returning/arel/visitors/postgresql.rb
76
+ - lib/returning/rails30/active_record/persistence.rb
77
+ - lib/returning/rails30/arel/crud.rb
78
+ - lib/returning/rails30/returning.rb
79
+ - lib/returning/rails31/active_record/persistence.rb
80
+ - lib/returning/rails31/arel/crud.rb
81
+ - lib/returning/rails31/returning.rb
70
82
  - lib/returning/version.rb
71
83
  - returning.gemspec
72
84
  - spec/returning_spec.rb