active_remote 5.1.1 → 5.2.0.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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