active_remote 5.1.1 → 5.2.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -48,7 +48,7 @@ module ActiveRemote
48
48
  # Instantiate a record with the given remote attributes. Generally used
49
49
  # when retrieving records that already exist, so @new_record is set to false.
50
50
  #
51
- def instantiate(new_attributes, options = {})
51
+ def instantiate(new_attributes = {}, options = {})
52
52
  attributes = self.build_from_rpc(new_attributes)
53
53
  new_object = self.allocate.init_with(attributes)
54
54
  new_object.readonly! if options[:readonly]
@@ -32,5 +32,23 @@ module ActiveRemote
32
32
  def primary_key
33
33
  self.class.primary_key
34
34
  end
35
+
36
+ # Returns an Array of all key attributes if any of the attributes is set, whether or not
37
+ # the object is persisted. Returns +nil+ if there are no key attributes.
38
+ #
39
+ # class Person
40
+ # include ActiveModel::Conversion
41
+ # attr_accessor :id
42
+ #
43
+ # def initialize(id)
44
+ # @id = id
45
+ # end
46
+ # end
47
+ #
48
+ # person = Person.new(1)
49
+ # person.to_key # => [1]
50
+ def to_key
51
+ send(primary_key) ? [send(primary_key)] : nil
52
+ end
35
53
  end
36
54
  end
@@ -15,8 +15,8 @@ module ActiveRemote
15
15
  def build_from_rpc(values)
16
16
  values = values.stringify_keys
17
17
 
18
- attributes.inject({}) do |attributes, (name, definition)|
19
- attributes[name] = definition.from_rpc(values[name])
18
+ attribute_names.inject(_default_attributes.deep_dup) do |attributes, name|
19
+ attributes.write_from_database(name, values[name])
20
20
  attributes
21
21
  end
22
22
  end
@@ -53,8 +53,7 @@ module ActiveRemote
53
53
  end
54
54
 
55
55
  def assign_attributes_from_rpc(response)
56
- new_attributes = self.class.build_from_rpc(response.to_hash)
57
- @attributes.update(new_attributes)
56
+ @attributes = self.class.build_from_rpc(response.to_hash)
58
57
  add_errors(response.errors) if response.respond_to?(:errors)
59
58
  end
60
59
 
@@ -117,7 +117,8 @@ module ActiveRemote
117
117
  #
118
118
  def reload
119
119
  fresh_object = self.class.find(scope_key_hash)
120
- @attributes.update(fresh_object.instance_variable_get("@attributes"))
120
+ @attributes = fresh_object.instance_variable_get("@attributes")
121
+ self
121
122
  end
122
123
  end
123
124
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveRemote
2
- VERSION = "5.1.1"
2
+ VERSION = "5.2.0.alpha"
3
3
  end
@@ -155,14 +155,6 @@ describe ActiveRemote::Association do
155
155
  expect(subject.user_posts).to eq(records)
156
156
  end
157
157
 
158
- context "when user_guid doesnt exist on model " do
159
- before { allow(subject).to receive(:respond_to?).with("user_guid").and_return(false) }
160
-
161
- it "raises an error" do
162
- expect { subject.user_posts }.to raise_error(::ActiveRemote::UnknownAttributeError)
163
- end
164
- end
165
-
166
158
  context "when user_guid doesnt exist on associated model " do
167
159
  before { allow(Post).to receive_message_chain(:public_instance_methods, :include?).with(:user_guid).and_return(false) }
168
160
 
@@ -251,14 +243,6 @@ describe ActiveRemote::Association do
251
243
  expect(subject.chief_editor).to eq(record)
252
244
  end
253
245
 
254
- context "when user_guid doesnt exist on model " do
255
- before { allow(subject).to receive(:respond_to?).with("user_guid").and_return(false) }
256
-
257
- it "raises an error" do
258
- expect { subject.chief_editor }.to raise_error(::ActiveRemote::UnknownAttributeError)
259
- end
260
- end
261
-
262
246
  context "when user_guid doesnt exist on associated model " do
263
247
  before { allow(Author).to receive_message_chain(:public_instance_methods, :include?).with(:user_guid).and_return(false) }
264
248
 
@@ -4,10 +4,10 @@ describe ActiveRemote::Dirty do
4
4
  context "when writing attributes through the setter" do
5
5
  subject { Post.new(:name => "foo") }
6
6
 
7
- before {
8
- subject.previous_changes.try(:clear)
9
- subject.changed_attributes.try(:clear)
10
- }
7
+ before do
8
+ subject.changes_applied
9
+ subject.clear_changes_information
10
+ end
11
11
 
12
12
  context "when the value changes" do
13
13
  before { subject.name = "bar" }
@@ -25,10 +25,10 @@ describe ActiveRemote::Dirty do
25
25
  context "when writing attributes directly" do
26
26
  subject { Post.new(:name => "foo") }
27
27
 
28
- before {
29
- subject.previous_changes.try(:clear)
30
- subject.changed_attributes.try(:clear)
31
- }
28
+ before do
29
+ subject.changes_applied
30
+ subject.clear_changes_information
31
+ end
32
32
 
33
33
  context "when the value changes" do
34
34
  before { subject[:name] = "bar" }
@@ -46,10 +46,10 @@ describe ActiveRemote::Dirty do
46
46
  describe "#reload" do
47
47
  subject { Post.new(:name => "foo") }
48
48
 
49
- before {
49
+ before do
50
50
  allow(Post).to receive(:find).and_return(Post.new(:name => "foo"))
51
51
  subject.reload
52
- }
52
+ end
53
53
 
54
54
  its(:changes) { should be_empty }
55
55
  end
@@ -68,10 +68,10 @@ describe ActiveRemote::Dirty do
68
68
 
69
69
  subject { Post.new(:name => "foo") }
70
70
 
71
- before {
71
+ before do
72
72
  allow(subject).to receive(:create_or_update).and_return(true)
73
73
  subject.save
74
- }
74
+ end
75
75
 
76
76
  its(:previous_changes) { should eq changes }
77
77
  its(:changes) { should be_empty }
@@ -82,27 +82,12 @@ describe ActiveRemote::Dirty do
82
82
 
83
83
  subject { Post.new(:name => "foo") }
84
84
 
85
- before {
85
+ before do
86
86
  allow(subject).to receive(:save).and_return(true)
87
87
  subject.save!
88
- }
88
+ end
89
89
 
90
90
  its(:previous_changes) { should eq changes }
91
91
  its(:changes) { should be_empty }
92
92
  end
93
-
94
- describe "#instantiate" do
95
- let(:post) { Post.new }
96
- let(:record) { ::Generic::Remote::Post.new(:name => "foo") }
97
-
98
- it "clears previous changes" do
99
- new_record = post.instantiate(record.to_hash)
100
- expect(new_record.previous_changes).to eq({})
101
- end
102
-
103
- it "clears changes" do
104
- new_record = post.instantiate(record.to_hash)
105
- expect(new_record.changes).to be_empty
106
- end
107
- end
108
93
  end
@@ -2,37 +2,41 @@ require "spec_helper"
2
2
 
3
3
  describe ::ActiveRemote::Integration do
4
4
  let(:guid) { "GUID-derp" }
5
- subject { Tag.new(:guid => guid) }
5
+ let(:tag) { ::Tag.new(:guid => guid) }
6
+
7
+ subject { tag }
8
+
9
+ context ".to_param" do
10
+ specify { expect(::Tag).to respond_to(:to_param) }
11
+ end
6
12
 
7
13
  context "#to_param" do
8
- # API
9
14
  specify { expect(subject).to respond_to(:to_param) }
10
15
 
11
16
  it "returns the guid if the guid is present (by default)" do
12
- expect(subject.to_param).to eq(guid)
17
+ expect(tag.to_param).to eq(guid)
13
18
  end
14
19
  end
15
20
 
16
21
  context "#cache_key" do
17
- # API
18
22
  specify { expect(subject).to respond_to(:cache_key) }
19
23
 
20
24
  it "sets 'new' as the identifier when the record has not been persisted" do
21
- expect(subject).to receive(:new_record?).and_return(true)
22
- expect(subject.cache_key).to match(/tag\/new/)
25
+ expect(tag).to receive(:new_record?).and_return(true)
26
+ expect(tag.cache_key).to match(/tags\/new/)
23
27
  end
24
28
 
25
29
  it "sets the cache_key to the class/guid as a default" do
26
- expect(subject).to receive(:new_record?).and_return(false)
27
- expect(subject.cache_key).to eq("tag/#{guid}")
30
+ expect(tag).to receive(:new_record?).and_return(false)
31
+ expect(tag.cache_key).to eq("tags/#{guid}")
28
32
  end
29
33
 
30
34
  it "adds the 'updated_at' attribute to the cache_key if updated_at is present" do
31
35
  ::ActiveRemote.config.default_cache_key_updated_at = true
32
- twenty_o_one_one = subject[:updated_at] = DateTime.new(2001, 0o1, 0o1)
33
- expect(subject).to receive(:new_record?).and_return(false)
34
- expect(subject.cache_key).to eq("tag/#{guid}-#{twenty_o_one_one.to_s(:number)}")
35
- subject[:updated_at] = nil
36
+ twenty_o_one_one = tag.updated_at = DateTime.new(2001, 0o1, 0o1)
37
+ expect(tag).to receive(:new_record?).and_return(false)
38
+ expect(tag.cache_key).to eq("tags/#{guid}-#{twenty_o_one_one.to_s(:usec)}")
39
+ tag.updated_at = nil
36
40
  ::ActiveRemote.config.default_cache_key_updated_at = false
37
41
  end
38
42
 
@@ -40,4 +44,68 @@ describe ::ActiveRemote::Integration do
40
44
  expect(::ActiveRemote.config.default_cache_key_updated_at?).to be_falsey
41
45
  end
42
46
  end
47
+
48
+ describe "#cache_key_with_version" do
49
+ let(:tag) { ::Tag.new(:guid => guid, :updated_at => ::DateTime.current) }
50
+
51
+ specify { expect(subject).to respond_to(:cache_key_with_version) }
52
+
53
+ context "when cache versioning is enabled" do
54
+ around do |example|
55
+ cache_versioning_value = ::Tag.cache_versioning
56
+ ::Tag.cache_versioning = true
57
+ example.run
58
+ ::Tag.cache_versioning = cache_versioning_value
59
+ end
60
+
61
+ it "returns a cache key with the version" do
62
+ expect(tag.cache_version).to be_present
63
+ expect(tag.cache_key_with_version).to eq("#{tag.cache_key}-#{tag.cache_version}")
64
+ end
65
+ end
66
+
67
+ context "when cache versioning is not enabled" do
68
+ around do |example|
69
+ cache_versioning_value = ::Tag.cache_versioning
70
+ ::Tag.cache_versioning = false
71
+ example.run
72
+ ::Tag.cache_versioning = cache_versioning_value
73
+ end
74
+
75
+ it "returns a cache key without the version" do
76
+ expect(tag.cache_key_with_version).to eq(tag.cache_key)
77
+ end
78
+ end
79
+ end
80
+
81
+ describe "#cache_version" do
82
+ specify { expect(subject).to respond_to(:cache_version) }
83
+
84
+ context "when cache versioning is enabled" do
85
+ around do |example|
86
+ cache_versioning_value = ::Tag.cache_versioning
87
+ ::Tag.cache_versioning = true
88
+ example.run
89
+ ::Tag.cache_versioning = cache_versioning_value
90
+ end
91
+
92
+ it "returns a cache version" do
93
+ tag.updated_at = DateTime.new(2001, 0o1, 0o1)
94
+ expect(tag.cache_version).to eq(tag.updated_at.utc.to_s(:usec))
95
+ end
96
+ end
97
+
98
+ context "when cache versioning is not enabled" do
99
+ around do |example|
100
+ cache_versioning_value = ::Tag.cache_versioning
101
+ ::Tag.cache_versioning = false
102
+ example.run
103
+ ::Tag.cache_versioning = cache_versioning_value
104
+ end
105
+
106
+ it "returns nil" do
107
+ expect(tag.cache_version).to be_nil
108
+ end
109
+ end
110
+ end
43
111
  end
@@ -4,18 +4,14 @@ describe ::ActiveRemote::QueryAttributes do
4
4
  subject { ::Author.new }
5
5
 
6
6
  describe "#query_attribute" do
7
- it "raises when getting an undefined attribute" do
8
- expect { subject.query_attribute(:foobar) }.to raise_error(::ActiveRemote::UnknownAttributeError)
9
- end
10
-
11
7
  it "is false when the attribute is false" do
12
- subject.name = false
13
- expect(subject.name?).to eq false
8
+ subject.writes_fiction = false
9
+ expect(subject.writes_fiction?).to eq false
14
10
  end
15
11
 
16
12
  it "is true when the attribute is true" do
17
- subject.name = true
18
- expect(subject.name?).to eq true
13
+ subject.writes_fiction = true
14
+ expect(subject.writes_fiction?).to eq true
19
15
  end
20
16
 
21
17
  it "is false when the attribute is nil" do
@@ -23,11 +19,6 @@ describe ::ActiveRemote::QueryAttributes do
23
19
  expect(subject.name?).to eq false
24
20
  end
25
21
 
26
- it "is true when the attribute is an Object" do
27
- subject.name = Object.new
28
- expect(subject.name?).to eq true
29
- end
30
-
31
22
  it "is false when the attribute is an empty string" do
32
23
  subject.name = ""
33
24
  expect(subject.name?).to eq false
@@ -40,13 +31,13 @@ describe ::ActiveRemote::QueryAttributes do
40
31
 
41
32
  # This behavior varies from ActiveRecord, so we test it explicitly
42
33
  it "is true when the attribute is 0" do
43
- subject.name = 0
44
- expect(subject.name?).to eq true
34
+ subject.age = 0
35
+ expect(subject.age?).to eq true
45
36
  end
46
37
 
47
38
  it "is true when the attribute is 1" do
48
- subject.name = 1
49
- expect(subject.name?).to eq true
39
+ subject.age = 1
40
+ expect(subject.age?).to eq true
50
41
  end
51
42
  end
52
43
  end
@@ -6,6 +6,10 @@ describe ::ActiveRemote::RPC do
6
6
  describe ".build_from_rpc" do
7
7
  let(:new_attributes) { { :name => "test" } }
8
8
 
9
+ it "dups the default attributes" do
10
+ expect { ::Tag.build_from_rpc(new_attributes) }.to_not change { ::Tag._default_attributes["name"] }
11
+ end
12
+
9
13
  context "missing attributes from rpc" do
10
14
  it "initializes to nil" do
11
15
  expect(::Tag.build_from_rpc(new_attributes)).to include("guid" => nil)
@@ -25,7 +29,7 @@ describe ::ActiveRemote::RPC do
25
29
 
26
30
  it "calls the typecasters" do
27
31
  expect(
28
- ::TypecastedAuthor.build_from_rpc(new_attributes)
32
+ ::Author.build_from_rpc(new_attributes)
29
33
  ).to include("birthday" => "2017-01-01".to_datetime)
30
34
  end
31
35
  end
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe ActiveRemote::Search do
4
- let(:records) { [Generic::Remote::Tag.new] }
4
+ let(:records) { [Generic::Remote::Tag.new(:guid => "123")] }
5
5
  let(:response) { Generic::Remote::Tags.new(:records => records) }
6
6
  let(:rpc) { double(:rpc) }
7
7
 
@@ -37,7 +37,7 @@ describe ActiveRemote::Search do
37
37
  end
38
38
 
39
39
  describe ".search" do
40
- let(:serialized_records) { [Tag.new] }
40
+ let(:serialized_records) { [Tag.instantiate(:guid => "123")] }
41
41
 
42
42
  context "given args that respond to :to_hash" do
43
43
  let(:args) { {} }
@@ -52,7 +52,8 @@ describe ActiveRemote::Search do
52
52
  end
53
53
 
54
54
  it "returns records" do
55
- expect(Tag.search(args)).to eq serialized_records
55
+ records = Tag.search(args)
56
+ expect(records).to eq serialized_records
56
57
  end
57
58
  end
58
59
 
@@ -5,4 +5,3 @@ require "support/models/category"
5
5
  require "support/models/no_attributes"
6
6
  require "support/models/post"
7
7
  require "support/models/tag"
8
- require "support/models/typecasted_author"
@@ -6,12 +6,16 @@ require "support/protobuf/author.pb"
6
6
  class Author < ::ActiveRemote::Base
7
7
  service_class ::Generic::Remote::AuthorService
8
8
 
9
- attribute :guid
10
- attribute :name
11
- attribute :user_guid
12
- attribute :chief_editor_guid
13
- attribute :editor_guid
14
- attribute :category_guid
9
+ attribute :guid, :string
10
+ attribute :name, :string
11
+ attribute :user_guid, :string
12
+ attribute :chief_editor_guid, :string
13
+ attribute :editor_guid, :string
14
+ attribute :category_guid, :string
15
+ attribute :age, :integer
16
+ attribute :birthday, :datetime
17
+ attribute :writes_fiction, :boolean
18
+ attribute :net_sales, :float
15
19
 
16
20
  has_many :posts
17
21
  has_many :user_posts, :class_name => "::Post", :scope => :user_guid
@@ -6,9 +6,9 @@ require "support/protobuf/category.pb"
6
6
  class Category < ::ActiveRemote::Base
7
7
  service_class ::Generic::Remote::CategoryService
8
8
 
9
- attribute :guid
10
- attribute :user_guid
11
- attribute :chief_editor_guid
9
+ attribute :guid, :string
10
+ attribute :user_guid, :string
11
+ attribute :chief_editor_guid, :string
12
12
 
13
13
  has_many :posts
14
14