active_remote 1.3.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -38,10 +38,10 @@ module ActiveRemote
38
38
  # end
39
39
  #
40
40
  def belongs_to(belongs_to_klass, options={})
41
- perform_association( belongs_to_klass, options ) do |klass, obj|
41
+ perform_association(belongs_to_klass, options) do |klass, object|
42
42
  foreign_key = options.fetch(:foreign_key) { :"#{belongs_to_klass}_guid" }
43
- association_guid = obj.read_attribute(foreign_key)
44
- klass.search(:guid => association_guid).first
43
+ association_guid = object.read_attribute(foreign_key)
44
+ klass.search(:guid => association_guid).first if association_guid
45
45
  end
46
46
  end
47
47
 
@@ -76,9 +76,9 @@ module ActiveRemote
76
76
  # end
77
77
  #
78
78
  def has_many(has_many_class, options={})
79
- perform_association( has_many_class, options ) do |klass, obj|
80
- foreign_key = options.fetch(:foreign_key) { :"#{obj.class.name.demodulize.underscore}_guid" }
81
- klass.search(foreign_key => obj.guid)
79
+ perform_association( has_many_class, options ) do |klass, object|
80
+ foreign_key = options.fetch(:foreign_key) { :"#{object.class.name.demodulize.underscore}_guid" }
81
+ object.guid ? klass.search(foreign_key => object.guid) : []
82
82
  end
83
83
  end
84
84
 
@@ -112,9 +112,9 @@ module ActiveRemote
112
112
  # end
113
113
  #
114
114
  def has_one(has_one_klass, options={})
115
- perform_association( has_one_klass, options ) do |klass, obj|
116
- foreign_key = options.fetch(:foreign_key) { :"#{obj.class.name.demodulize.underscore}_guid" }
117
- klass.search(foreign_key => obj.guid).first
115
+ perform_association(has_one_klass, options) do |klass, object|
116
+ foreign_key = options.fetch(:foreign_key) { :"#{object.class.name.demodulize.underscore}_guid" }
117
+ klass.search(foreign_key => object.guid).first if object.guid
118
118
  end
119
119
  end
120
120
 
@@ -51,6 +51,15 @@ module ActiveRemote
51
51
  end
52
52
  end
53
53
 
54
+ # Override #write_attribute (along with #[]=) so we can provide support for
55
+ # ActiveModel::Dirty.
56
+ #
57
+ def write_attribute(name, value)
58
+ __send__("#{name}_will_change!") unless value == self[name]
59
+ super
60
+ end
61
+ alias_method :[]=, :write_attribute
62
+
54
63
  private
55
64
 
56
65
  # Override ActiveAttr's attribute= method so we can provide support for
@@ -1,3 +1,3 @@
1
1
  module ActiveRemote
2
- VERSION = "1.3.3"
2
+ VERSION = "1.4.0"
3
3
  end
@@ -1,144 +1,168 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe ActiveRemote::Association do
4
4
  let(:record) { double(:record) }
5
5
  let(:records) { [ record ] }
6
6
 
7
- describe '.belongs_to' do
8
-
9
- context 'simple association' do
10
- let(:author_guid) { 'AUT-123' }
7
+ describe ".belongs_to" do
8
+ context "simple association" do
9
+ let(:author_guid) { "AUT-123" }
11
10
 
12
11
  subject { Post.new(:author_guid => author_guid) }
12
+
13
13
  it { should respond_to(:author) }
14
14
 
15
- it 'searches the associated model for a single record' do
15
+ it "searches the associated model for a single record" do
16
16
  Author.should_receive(:search).with(:guid => subject.author_guid).and_return(records)
17
17
  subject.author.should eq record
18
18
  end
19
19
 
20
- it 'memoizes the result record' do
20
+ it "memoizes the result record" do
21
21
  Author.should_receive(:search).once.with(:guid => subject.author_guid).and_return(records)
22
22
  3.times { subject.author.should eq record }
23
23
  end
24
24
 
25
- context 'when the search is empty' do
26
- it 'returns a nil' do
25
+ context "when guid is nil" do
26
+ subject { Post.new }
27
+
28
+ it "returns nil" do
29
+ subject.author.should be_nil
30
+ end
31
+ end
32
+
33
+ context "when the search is empty" do
34
+ it "returns a nil" do
27
35
  Author.should_receive(:search).with(:guid => subject.author_guid).and_return([])
28
36
  subject.author.should be_nil
29
37
  end
30
38
  end
31
39
  end
32
40
 
33
- context 'specific association with class name' do
34
- let(:author_guid) { 'AUT-456' }
41
+ context "specific association with class name" do
42
+ let(:author_guid) { "AUT-456" }
35
43
 
36
44
  subject { Post.new(:author_guid => author_guid) }
37
45
  it { should respond_to(:coauthor) }
38
46
 
39
- it 'searches the associated model for a single record' do
47
+ it "searches the associated model for a single record" do
40
48
  Author.should_receive(:search).with(:guid => subject.author_guid).and_return(records)
41
49
  subject.author.should eq record
42
50
  end
43
51
  end
44
52
 
45
- context 'specific association with class name and foreign_key' do
46
- let(:author_guid) { 'AUT-456' }
53
+ context "specific association with class name and foreign_key" do
54
+ let(:author_guid) { "AUT-456" }
47
55
 
48
56
  subject { Post.new(:author_guid => author_guid) }
49
57
  it { should respond_to(:bestseller) }
50
58
 
51
- it 'searches the associated model for a single record' do
59
+ it "searches the associated model for a single record" do
52
60
  Author.should_receive(:search).with(:guid => subject.bestseller_guid).and_return(records)
53
61
  subject.author.should eq record
54
62
  end
55
63
  end
56
-
57
64
  end
58
65
 
59
- describe '.has_many' do
66
+ describe ".has_many" do
60
67
  let(:records) { [ record, record, record ] }
61
- let(:guid) { 'AUT-123' }
68
+ let(:guid) { "AUT-123" }
62
69
 
63
70
  subject { Author.new(:guid => guid) }
71
+
64
72
  it { should respond_to(:posts) }
65
73
 
66
- it 'searches the associated model for all associated records' do
74
+ it "searches the associated model for all associated records" do
67
75
  Post.should_receive(:search).with(:author_guid => subject.guid).and_return(records)
68
76
  subject.posts.should eq records
69
77
  end
70
78
 
71
- it 'memoizes the result record' do
79
+ it "memoizes the result record" do
72
80
  Post.should_receive(:search).once.with(:author_guid => subject.guid).and_return(records)
73
81
  3.times { subject.posts.should eq records }
74
82
  end
75
83
 
76
- context 'when the search is empty' do
77
- it 'returns the empty set' do
84
+ context "when guid is nil" do
85
+ subject { Author.new }
86
+
87
+ it "returns []" do
88
+ subject.posts.should eq []
89
+ end
90
+ end
91
+
92
+ context "when the search is empty" do
93
+ it "returns the empty set" do
78
94
  Post.should_receive(:search).with(:author_guid => subject.guid).and_return([])
79
95
  subject.posts.should be_empty
80
96
  end
81
97
  end
82
98
 
83
- context 'specific association with class name' do
99
+ context "specific association with class name" do
84
100
  it { should respond_to(:flagged_posts) }
85
101
 
86
- it 'searches the associated model for a single record' do
102
+ it "searches the associated model for a single record" do
87
103
  Post.should_receive(:search).with(:author_guid => subject.guid).and_return([])
88
104
  subject.flagged_posts.should be_empty
89
105
  end
90
106
  end
91
107
 
92
- context 'specific association with class name and foreign_key' do
108
+ context "specific association with class name and foreign_key" do
93
109
  it { should respond_to(:bestseller_posts) }
94
110
 
95
- it 'searches the associated model for multiple record' do
111
+ it "searches the associated model for multiple record" do
96
112
  Post.should_receive(:search).with(:bestseller_guid => subject.guid).and_return(records)
97
113
  subject.bestseller_posts.should eq(records)
98
114
  end
99
115
  end
100
116
  end
101
117
 
102
- describe '.has_one' do
103
- let(:guid) { 'PST-123' }
118
+ describe ".has_one" do
119
+ let(:guid) { "PST-123" }
104
120
 
105
121
  subject { Post.new(:guid => guid) }
122
+
106
123
  it { should respond_to(:category) }
107
124
 
108
- it 'searches the associated model for all associated records' do
125
+ it "searches the associated model for all associated records" do
109
126
  Category.should_receive(:search).with(:post_guid => subject.guid).and_return(records)
110
127
  subject.category.should eq record
111
128
  end
112
129
 
113
- it 'memoizes the result record' do
130
+ it "memoizes the result record" do
114
131
  Category.should_receive(:search).once.with(:post_guid => subject.guid).and_return(records)
115
132
  3.times { subject.category.should eq record }
116
133
  end
117
134
 
118
- context 'when the search is empty' do
119
- it 'returns a nil value' do
135
+ context "when guid is nil" do
136
+ subject { Post.new }
137
+
138
+ it "returns nil" do
139
+ subject.category.should be_nil
140
+ end
141
+ end
142
+
143
+ context "when the search is empty" do
144
+ it "returns a nil value" do
120
145
  Category.should_receive(:search).with(:post_guid => subject.guid).and_return([])
121
146
  subject.category.should be_nil
122
147
  end
123
148
  end
124
149
 
125
- context 'specific association with class name' do
150
+ context "specific association with class name" do
126
151
  it { should respond_to(:main_category) }
127
152
 
128
- it 'searches the associated model for a single record' do
153
+ it "searches the associated model for a single record" do
129
154
  Category.should_receive(:search).with(:post_guid => subject.guid).and_return(records)
130
155
  subject.main_category.should eq record
131
156
  end
132
157
  end
133
158
 
134
- context 'specific association with class name and foreign_key' do
159
+ context "specific association with class name and foreign_key" do
135
160
  it { should respond_to(:default_category) }
136
161
 
137
- it 'searches the associated model for a single record' do
162
+ it "searches the associated model for a single record" do
138
163
  Category.should_receive(:search).with(:template_post_guid => subject.guid).and_return(records)
139
164
  subject.default_category.should eq record
140
165
  end
141
166
  end
142
167
  end
143
-
144
168
  end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRemote::Dirty do
4
+ context "when writing attributes through the setter" do
5
+ subject { Post.new(:name => 'foo') }
6
+
7
+ before {
8
+ subject.previous_changes.try(:clear)
9
+ subject.changed_attributes.try(:clear)
10
+ }
11
+
12
+ context "when the value changes" do
13
+ before { subject.name = 'bar' }
14
+
15
+ its(:name_changed?) { should be_true }
16
+ end
17
+
18
+ context "when the value doesn't change" do
19
+ before { subject.name = 'foo' }
20
+
21
+ its(:name_changed?) { should be_false }
22
+ end
23
+ end
24
+
25
+ context "when writing attributes directly" do
26
+ subject { Post.new(:name => 'foo') }
27
+
28
+ before {
29
+ subject.previous_changes.try(:clear)
30
+ subject.changed_attributes.try(:clear)
31
+ }
32
+
33
+ context "when the value changes" do
34
+ before { subject[:name] = 'bar' }
35
+
36
+ its(:name_changed?) { should be_true }
37
+ end
38
+
39
+ context "when the value doesn't change" do
40
+ before { subject[:name] = 'foo' }
41
+
42
+ its(:name_changed?) { should be_false }
43
+ end
44
+ end
45
+
46
+ describe "#reload" do
47
+ subject { Post.new(:name => 'foo') }
48
+
49
+ before {
50
+ subject.stub(:last_response).and_return({})
51
+ subject.stub(:execute)
52
+ subject.reload
53
+ }
54
+
55
+ its(:changes) { should be_empty }
56
+ end
57
+
58
+ describe "#save" do
59
+ let!(:changes) { subject.changes }
60
+
61
+ subject { Post.new(:name => 'foo') }
62
+
63
+ before {
64
+ subject.stub(:create_or_update).and_return(true)
65
+ subject.save
66
+ }
67
+
68
+ its(:previous_changes) { should eq changes }
69
+ its(:changes) { should be_empty }
70
+ end
71
+
72
+ describe "#save!" do
73
+ let!(:changes) { subject.changes }
74
+
75
+ subject { Post.new(:name => 'foo') }
76
+
77
+ before {
78
+ subject.stub(:save).and_return(true)
79
+ subject.save!
80
+ }
81
+
82
+ its(:previous_changes) { should eq changes }
83
+ its(:changes) { should be_empty }
84
+ end
85
+
86
+ describe "#serialize_records" do
87
+ let(:post) { Post.new }
88
+ let(:record) { ::Generic::Remote::Post.new(:name => 'foo') }
89
+ let(:response) { double(:response, :records => [ record ]) }
90
+
91
+ before {
92
+ post.stub(:last_response).and_return(response)
93
+ }
94
+
95
+ it "clears previous changes" do
96
+ records = post.serialize_records
97
+ records.first.previous_changes.should be_nil
98
+ end
99
+
100
+ it "clears changes" do
101
+ records = post.serialize_records
102
+ records.first.changes.should be_empty
103
+ end
104
+ end
105
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_remote
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.3
4
+ version: 1.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-27 00:00:00.000000000 Z
12
+ date: 2013-02-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: active_attr
@@ -178,6 +178,7 @@ files:
178
178
  - spec/lib/active_remote/association_spec.rb
179
179
  - spec/lib/active_remote/base_spec.rb
180
180
  - spec/lib/active_remote/bulk_spec.rb
181
+ - spec/lib/active_remote/dirty_spec.rb
181
182
  - spec/lib/active_remote/dsl_spec.rb
182
183
  - spec/lib/active_remote/persistence_spec.rb
183
184
  - spec/lib/active_remote/rpc_spec.rb
@@ -218,7 +219,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
218
219
  version: '0'
219
220
  segments:
220
221
  - 0
221
- hash: 432433655534443133
222
+ hash: -2137387569376097013
222
223
  required_rubygems_version: !ruby/object:Gem::Requirement
223
224
  none: false
224
225
  requirements:
@@ -227,7 +228,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
227
228
  version: '0'
228
229
  segments:
229
230
  - 0
230
- hash: 432433655534443133
231
+ hash: -2137387569376097013
231
232
  requirements: []
232
233
  rubyforge_project:
233
234
  rubygems_version: 1.8.24
@@ -239,6 +240,7 @@ test_files:
239
240
  - spec/lib/active_remote/association_spec.rb
240
241
  - spec/lib/active_remote/base_spec.rb
241
242
  - spec/lib/active_remote/bulk_spec.rb
243
+ - spec/lib/active_remote/dirty_spec.rb
242
244
  - spec/lib/active_remote/dsl_spec.rb
243
245
  - spec/lib/active_remote/persistence_spec.rb
244
246
  - spec/lib/active_remote/rpc_spec.rb