active_remote 2.4.0 → 3.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -0
  3. data/CHANGES.md +22 -0
  4. data/README.md +2 -0
  5. data/active_remote.gemspec +2 -2
  6. data/bin/console +10 -0
  7. data/lib/active_remote/association.rb +4 -0
  8. data/lib/active_remote/attribute_assignment.rb +53 -0
  9. data/lib/active_remote/attribute_definition.rb +106 -0
  10. data/lib/active_remote/attributes.rb +165 -8
  11. data/lib/active_remote/base.rb +41 -36
  12. data/lib/active_remote/dirty.rb +0 -9
  13. data/lib/active_remote/errors.rb +6 -0
  14. data/lib/active_remote/persistence.rb +10 -19
  15. data/lib/active_remote/query_attributes.rb +45 -0
  16. data/lib/active_remote/rpc.rb +17 -67
  17. data/lib/active_remote/search.rb +0 -20
  18. data/lib/active_remote/serialization.rb +1 -32
  19. data/lib/active_remote/typecasting.rb +3 -12
  20. data/lib/active_remote/version.rb +1 -1
  21. data/lib/active_remote.rb +0 -2
  22. data/spec/lib/active_remote/association_spec.rb +11 -2
  23. data/spec/lib/active_remote/attributes_spec.rb +177 -0
  24. data/spec/lib/active_remote/persistence_spec.rb +7 -16
  25. data/spec/lib/active_remote/query_attribute_spec.rb +171 -0
  26. data/spec/lib/active_remote/rpc_spec.rb +33 -75
  27. data/spec/lib/active_remote/search_spec.rb +0 -21
  28. data/spec/lib/active_remote/serialization_spec.rb +0 -23
  29. data/spec/support/models/no_attributes.rb +2 -0
  30. data/spec/support/models.rb +1 -0
  31. metadata +21 -66
  32. data/lib/active_remote/attribute_defaults.rb +0 -100
  33. data/lib/active_remote/bulk.rb +0 -168
  34. data/lib/active_remote/core_ext/date.rb +0 -7
  35. data/lib/active_remote/core_ext/date_time.rb +0 -7
  36. data/lib/active_remote/core_ext/integer.rb +0 -19
  37. data/lib/active_remote/core_ext.rb +0 -3
  38. data/lib/active_remote/publication.rb +0 -54
  39. data/lib/active_remote/serializers/json.rb +0 -16
  40. data/spec/core_ext/date_time_spec.rb +0 -9
  41. data/spec/lib/active_remote/attribute_defaults_spec.rb +0 -26
  42. data/spec/lib/active_remote/bulk_spec.rb +0 -83
  43. data/spec/lib/active_remote/publication_spec.rb +0 -18
  44. data/spec/lib/active_remote/serializers/json_spec.rb +0 -78
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7f95e9d97b5f04b0f9cfb65f1fa67b1c28610494
4
- data.tar.gz: 20ab489954edb8912d271122b5c153a84029328e
3
+ metadata.gz: a6501c9a34d84239729afd215a01cea574af7a5c
4
+ data.tar.gz: 55a6b558a7fa1fba40a415266743f7e01fc31fc9
5
5
  SHA512:
6
- metadata.gz: 0766023e587197504ce97037fbcb771354ed3b99144707765bb5d924ebc35e96701f008a90278938b91457da216e1da49e13f7d71b76ae51af818e56820d10de
7
- data.tar.gz: 929d191533ec354c95449201ef31f9f5043c603d18e6316c83b09196ef3203f8552597afd8aef354793fdfd25871ddecb63718e475b4da810379e3e3166a09d4
6
+ metadata.gz: 57f951cd6dc0f19d37942880c2da4066d9cd7773c1ee16def3c0068cfb337d68aa4f596b80fe679ef1e8f83bf5685a5f5396b8a9204293ca273f0db24428ef78
7
+ data.tar.gz: f861f24788ce54b8dfaa0693ee236cc5aad887945bba35670687b9a8fb57f56f4eb06ec9e090ce052eef1c8f4242d6c026910d92f607dd79c5f1b07d700e11dc
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ ---
2
+ language: ruby
3
+ rvm:
4
+ - 2.2
5
+ - jruby
data/CHANGES.md ADDED
@@ -0,0 +1,22 @@
1
+ # ActiveRemote Changes
2
+
3
+ 3.0.0
4
+ ----------
5
+
6
+ - Remove dependency on ActiveAttr [#48]
7
+ - Remove core exts [#49]
8
+ - Remove deprecated rpc methods `.request`, `.request_type`, #execute`, `#remote_call`
9
+ These methods are handled by the rpc adater now. [#49]
10
+ - Remove deprecated method `._active_remote_search_args` [#49]
11
+ - Remove deprecated `.parse_records` method [#49]
12
+ - Remove publication, `#publishable_hash` method [#49]
13
+ - Drop support for Rails 3 mass assignment protection. Add support for strong param
14
+ enforcement for Rails 4+. [#50]
15
+ - Improve performance of many methods including `respond_to?` and `new`. [#50]
16
+ - Refactor of attribute storage internals [#50]
17
+ - Remove a method was was doign dirty tracking twice [#52]
18
+ - Extracted bulk methods to active_remote-bulk [#54]
19
+ - Removed search callbacks [#55]
20
+ - Refactor of instantiate from rpc codepath [#56]
21
+ - Change to internals of typecasting. Declaring `attribute :name, :type => Integer`
22
+ will no longer affect performance negatively. [#56]
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status](https://travis-ci.org/liveh2o/active_remote.svg?branch=master)](https://travis-ci.org/liveh2o/active_remote)
2
+
1
3
  # Active Remote
2
4
 
3
5
  Active Remote provides [Active Record](https://github.com/rails/rails/tree/master/activerecord)-like object-relational mapping over RPC. Think of it as Active Record for your platform: within a service, use Active Record to persist objects and between services, use Active Remote.
@@ -19,8 +19,8 @@ Gem::Specification.new do |s|
19
19
  ##
20
20
  # Dependencies
21
21
  #
22
- s.add_dependency "active_attr", ">= 0.8"
23
- s.add_dependency "activesupport", ">= 3.2"
22
+ s.add_dependency "activemodel", ">= 4.0"
23
+ s.add_dependency "activesupport", ">= 4.0"
24
24
  s.add_dependency "protobuf", ">= 3.0"
25
25
 
26
26
  ##
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "active_remote"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require "pry"
10
+ Pry.start
@@ -83,6 +83,9 @@ module ActiveRemote
83
83
 
84
84
  search_hash.values.any?(&:nil?) ? [] : klass.search(search_hash)
85
85
  end
86
+
87
+ options.merge!(:has_many => true)
88
+ create_setter_method(has_many_class, options)
86
89
  end
87
90
 
88
91
  # Create a `has_one` association for a given remote resource.
@@ -135,6 +138,7 @@ module ActiveRemote
135
138
  def create_setter_method(associated_klass, options={})
136
139
  writer_method = "#{associated_klass}=".to_sym
137
140
  define_method(writer_method) do |new_value|
141
+ raise "New value must be an array" if options[:has_many] == true && new_value.class != Array
138
142
  klass_name = options.fetch(:class_name){ associated_klass }
139
143
  klass = klass_name.to_s.classify.constantize
140
144
 
@@ -0,0 +1,53 @@
1
+ require "active_support/core_ext/hash/keys"
2
+
3
+ # Added ActiveModel AttributeAssignment to support Rails 4.
4
+ # In Rails 5 this is built in to ActiveModel, but our behavior
5
+ # in ActiveRemote is different.
6
+ module ActiveRemote
7
+ module AttributeAssignment
8
+ include ::ActiveModel::ForbiddenAttributesProtection
9
+
10
+ # Allows you to set all the attributes by passing in a hash of attributes with
11
+ # keys matching the attribute names.
12
+ #
13
+ # If the passed hash responds to <tt>permitted?</tt> method and the return value
14
+ # of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt>
15
+ # exception is raised.
16
+ #
17
+ # class Cat
18
+ # include ActiveModel::AttributeAssignment
19
+ # attr_accessor :name, :status
20
+ # end
21
+ #
22
+ # cat = Cat.new
23
+ # cat.assign_attributes(name: "Gorby", status: "yawning")
24
+ # cat.name # => 'Gorby'
25
+ # cat.status => 'yawning'
26
+ # cat.assign_attributes(status: "sleeping")
27
+ # cat.name # => 'Gorby'
28
+ # cat.status => 'sleeping'
29
+ def assign_attributes(new_attributes)
30
+ if !new_attributes.respond_to?(:stringify_keys)
31
+ raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
32
+ end
33
+ return if new_attributes.nil? || new_attributes.empty?
34
+
35
+ attributes = new_attributes.stringify_keys
36
+ _assign_attributes(sanitize_for_mass_assignment(attributes))
37
+ end
38
+
39
+ private
40
+
41
+ def _assign_attributes(attributes)
42
+ attributes.each do |name, value|
43
+ _assign_attribute(name, value)
44
+ end
45
+ end
46
+
47
+ # ActiveRemote silently ignores unknown attributes, unlike ActiveModel
48
+ # Consider changing ActiveRemote to behave the same
49
+ def _assign_attribute(name, value)
50
+ public_send("#{name}=", value) if respond_to?("#{name}=")
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,106 @@
1
+ module ActiveRemote
2
+ # Represents an attribute for reflection
3
+ #
4
+ # @example Usage
5
+ # AttributeDefinition.new(:amount)
6
+ #
7
+ # @since 0.2.0
8
+ class AttributeDefinition
9
+ include Comparable
10
+
11
+ # The attribute name
12
+ # @since 0.2.0
13
+ attr_reader :name
14
+
15
+ # Compare attribute definitions
16
+ #
17
+ # @example
18
+ # attribute_definition <=> other
19
+ #
20
+ # @param [ActiveAttr::AttributeDefinition, Object] other The other
21
+ # attribute definition to compare with.
22
+ #
23
+ # @return [-1, 0, 1, nil]
24
+ #
25
+ # @since 0.2.1
26
+ def <=>(other)
27
+ return nil unless other.instance_of? self.class
28
+ return nil if name == other.name && options != other.options
29
+ self.name.to_s <=> other.name.to_s
30
+ end
31
+
32
+ # Read an attribute option
33
+ #
34
+ # @example
35
+ # attribute_definition[:type]
36
+ #
37
+ # @param [Symbol] key The option key
38
+ #
39
+ # @since 0.5.0
40
+ def [](key)
41
+ @options[key]
42
+ end
43
+
44
+ # Creates a new AttributeDefinition
45
+ #
46
+ # @example Create an attribute defintion
47
+ # AttributeDefinition.new(:amount)
48
+ #
49
+ # @param [Symbol, String, #to_sym] name attribute name
50
+ # @param [Hash{Symbol => Object}] options attribute options
51
+ #
52
+ # @return [ActiveAttr::AttributeDefinition]
53
+ #
54
+ # @since 0.2.0
55
+ def initialize(name, options={})
56
+ raise TypeError, "can't convert #{name.class} into Symbol" unless name.respond_to? :to_sym
57
+ @name = name.to_sym
58
+ @options = options
59
+
60
+ if @options[:type]
61
+ typecaster = ::ActiveRemote::Typecasting::TYPECASTER_MAP[@options[:type]]
62
+ fail ::ActiveRemote::UnknownType unless typecaster
63
+ @options[:typecaster] = typecaster
64
+ end
65
+ end
66
+
67
+ # Returns the code that would generate the attribute definition
68
+ #
69
+ # @example Inspect the attribute definition
70
+ # attribute.inspect
71
+ #
72
+ # @return [String] Human-readable presentation of the attribute
73
+ # definition
74
+ #
75
+ # @since 0.6.0
76
+ def inspect
77
+ options_description = options.map { |key, value| "#{key.inspect} => #{value.inspect}" }.sort.join(", ")
78
+ inspected_options = ", #{options_description}" unless options_description.empty?
79
+ "attribute :#{name}#{inspected_options}"
80
+ end
81
+
82
+ # The attribute name
83
+ #
84
+ # @return [String] the attribute name
85
+ #
86
+ # @since 0.2.0
87
+ def to_s
88
+ name.to_s
89
+ end
90
+
91
+ # The attribute name
92
+ #
93
+ # @return [Symbol] the attribute name
94
+ #
95
+ # @since 0.2.1
96
+ def to_sym
97
+ name
98
+ end
99
+
100
+ protected
101
+
102
+ # The attribute options
103
+ # @since 0.5.0
104
+ attr_reader :options
105
+ end
106
+ end
@@ -1,22 +1,47 @@
1
1
  module ActiveRemote
2
2
  module Attributes
3
+ extend ::ActiveSupport::Concern
4
+ include ::ActiveModel::AttributeMethods
5
+
6
+ included do
7
+ attribute_method_suffix "="
8
+ end
9
+
10
+ # Performs equality checking on the result of attributes and its type.
11
+ #
12
+ # @example Compare for equality.
13
+ # model == other
14
+ #
15
+ def ==(other)
16
+ return false unless other.instance_of?(self.class)
17
+ attributes == other.attributes
18
+ end
19
+
20
+ # Returns a copy of our attributes hash
3
21
  def attributes
4
- @attributes ||= begin
5
- attribute_names = self.class.attribute_names
6
- Hash[attribute_names.map { |key| [key, send(key)] }]
7
- end
8
22
  @attributes.dup
9
23
  end
10
24
 
25
+ # Returns the class name plus its attributes
26
+ #
27
+ # @example Inspect the model.
28
+ # person.inspect
29
+ #
30
+ def inspect
31
+ attribute_descriptions = attributes.sort.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
32
+ separator = " " unless attribute_descriptions.empty?
33
+ "#<#{self.class.name}#{separator}#{attribute_descriptions}>"
34
+ end
35
+
11
36
  # Read attribute from the attributes hash
12
37
  #
13
38
  def read_attribute(name)
14
39
  name = name.to_s
15
40
 
16
- if respond_to? name
41
+ if respond_to?(name)
17
42
  attribute(name)
18
43
  else
19
- raise ActiveAttr::UnknownAttributeError, "unknown attribute: #{name}"
44
+ raise ::ActiveRemote::UnknownAttributeError, "unknown attribute: #{name}"
20
45
  end
21
46
  end
22
47
  alias_method :[], :read_attribute
@@ -26,12 +51,144 @@ module ActiveRemote
26
51
  def write_attribute(name, value)
27
52
  name = name.to_s
28
53
 
29
- if respond_to? "#{name}="
54
+ if respond_to?("#{name}=")
30
55
  __send__("attribute=", name, value)
31
56
  else
32
- raise ActiveAttr::UnknownAttributeError, "unknown attribute: #{name}"
57
+ raise ::ActiveRemote::UnknownAttributeError, "unknown attribute: #{name}"
33
58
  end
34
59
  end
35
60
  alias_method :[]=, :write_attribute
61
+
62
+ # Read an attribute from the attributes hash
63
+ #
64
+ def attribute(name)
65
+ @attributes[name]
66
+ end
67
+
68
+ # Write an attribute to the attributes hash
69
+ #
70
+ def attribute=(name, value)
71
+ @attributes[name] = value
72
+ end
73
+
74
+ def attribute_method?(attr_name)
75
+ # Check if @attributes is defined because dangerous_attribute? method
76
+ # can check allocate.respond_to? before actaully calling initialize
77
+ defined?(@attributes) && @attributes.key?(attr_name)
78
+ end
79
+
80
+ module ClassMethods
81
+ # Defines an attribute
82
+ #
83
+ # For each attribute that is defined, a getter and setter will be
84
+ # added as an instance method to the model. An
85
+ # {AttributeDefinition} instance will be added to result of the
86
+ # attributes class method.
87
+ #
88
+ # @example Define an attribute.
89
+ # attribute :name
90
+ #
91
+ def attribute(name, options={})
92
+ if dangerous_attribute_method_name = dangerous_attribute?(name)
93
+ raise ::ActiveRemote::DangerousAttributeError, %{an attribute method named "#{dangerous_attribute_method_name}" would conflict with an existing method}
94
+ else
95
+ attribute!(name, options)
96
+ end
97
+ end
98
+
99
+ # Defines an attribute without checking for conflicts
100
+ #
101
+ # Allows you to define an attribute whose methods will conflict
102
+ # with an existing method. For example, Ruby's Timeout library
103
+ # adds a timeout method to Object. Attempting to define a timeout
104
+ # attribute using .attribute will raise a
105
+ # {DangerousAttributeError}, but .attribute! will not.
106
+ #
107
+ # @example Define a dangerous attribute.
108
+ # attribute! :timeout
109
+ #
110
+ def attribute!(name, options={})
111
+ ::ActiveRemote::AttributeDefinition.new(name, options).tap do |attribute_definition|
112
+ attribute_name = attribute_definition.name.to_s
113
+ # Force active model to generate attribute methods
114
+ remove_instance_variable("@attribute_methods_generated") if instance_variable_defined?("@attribute_methods_generated")
115
+ define_attribute_methods([attribute_definition.name]) unless attribute_names.include?(attribute_name)
116
+ attributes[attribute_name] = attribute_definition
117
+ end
118
+ end
119
+
120
+ # Returns an Array of attribute names as Strings
121
+ #
122
+ # @example Get attribute names
123
+ # Person.attribute_names
124
+ #
125
+ def attribute_names
126
+ attributes.keys
127
+ end
128
+
129
+ # Returns a Hash of AttributeDefinition instances
130
+ #
131
+ # @example Get attribute definitions
132
+ # Person.attributes
133
+ #
134
+ def attributes
135
+ @attributes ||= ::ActiveSupport::HashWithIndifferentAccess.new
136
+ end
137
+
138
+ # Determine if a given attribute name is dangerous
139
+ #
140
+ # Some attribute names can cause conflicts with existing methods
141
+ # on an object. For example, an attribute named "timeout" would
142
+ # conflict with the timeout method that Ruby's Timeout library
143
+ # mixes into Object.
144
+ #
145
+ # @example Testing a harmless attribute
146
+ # Person.dangerous_attribute? :name #=> false
147
+ #
148
+ # @example Testing a dangerous attribute
149
+ # Person.dangerous_attribute? :timeout #=> "timeout"
150
+ #
151
+ def dangerous_attribute?(name)
152
+ return false if attribute_names.include?(name.to_s)
153
+
154
+ attribute_methods(name).detect do |method_name|
155
+ allocate.respond_to?(method_name, true)
156
+ end
157
+ end
158
+
159
+ # Returns the class name plus its attribute names
160
+ #
161
+ # @example Inspect the model's definition.
162
+ # Person.inspect
163
+ #
164
+ def inspect
165
+ inspected_attributes = attribute_names.sort
166
+ attributes_list = "(#{inspected_attributes.join(", ")})" unless inspected_attributes.empty?
167
+ "#{name}#{attributes_list}"
168
+ end
169
+
170
+ protected
171
+
172
+ # Assign a set of attribute definitions, used when subclassing models
173
+ #
174
+ def attributes=(attributes)
175
+ @attributes = attributes
176
+ end
177
+
178
+ private
179
+
180
+ # Expand an attribute name into its generated methods names
181
+ #
182
+ def attribute_methods(name)
183
+ attribute_method_matchers.map { |matcher| matcher.method_name(name) }
184
+ end
185
+
186
+ # Ruby inherited hook to assign superclass attributes to subclasses
187
+ #
188
+ def inherited(subclass)
189
+ super
190
+ subclass.attributes = attributes.dup
191
+ end
192
+ end
36
193
  end
37
194
  end
@@ -1,17 +1,16 @@
1
1
  require 'active_model/callbacks'
2
- require 'active_attr/model'
3
2
 
4
3
  require 'active_remote/association'
5
- require 'active_remote/attribute_defaults'
4
+ require 'active_remote/attribute_assignment'
5
+ require 'active_remote/attribute_definition'
6
6
  require 'active_remote/attributes'
7
- require 'active_remote/bulk'
8
7
  require 'active_remote/config'
9
8
  require 'active_remote/dirty'
10
9
  require 'active_remote/dsl'
11
10
  require 'active_remote/integration'
12
11
  require 'active_remote/persistence'
13
12
  require 'active_remote/primary_key'
14
- require 'active_remote/publication'
13
+ require 'active_remote/query_attributes'
15
14
  require 'active_remote/rpc'
16
15
  require 'active_remote/scope_keys'
17
16
  require 'active_remote/search'
@@ -21,60 +20,66 @@ require 'active_remote/validations'
21
20
 
22
21
  module ActiveRemote
23
22
  class Base
24
- extend ActiveModel::Callbacks
25
-
26
- include ActiveAttr::BasicModel
27
- include ActiveAttr::Attributes
28
- include ActiveAttr::BlockInitialization
29
- include ActiveAttr::ChainableInitialization
30
- include ActiveAttr::Logger
31
- include ActiveAttr::MassAssignment
32
- include ActiveAttr::AttributeDefaults
33
- include ActiveAttr::QueryAttributes
34
- include ActiveAttr::Serialization
35
-
36
- include Association
37
- include AttributeDefaults
38
- include Attributes
39
- include Bulk
40
- include DSL
41
- include Integration
42
- include Persistence
43
- include PrimaryKey
44
- include Publication
45
- include RPC
46
- include ScopeKeys
47
- include Search
48
- include Serialization
49
- include Typecasting
23
+ extend ::ActiveModel::Callbacks
24
+ extend ::ActiveModel::Naming
25
+
26
+ include ::ActiveModel::Conversion
27
+ include ::ActiveModel::Validations
28
+
29
+ include ::ActiveRemote::Association
30
+ include ::ActiveRemote::AttributeAssignment
31
+ include ::ActiveRemote::Attributes
32
+ include ::ActiveRemote::DSL
33
+ include ::ActiveRemote::Integration
34
+ include ::ActiveRemote::Persistence
35
+ include ::ActiveRemote::PrimaryKey
36
+ include ::ActiveRemote::QueryAttributes
37
+ include ::ActiveRemote::RPC
38
+ include ::ActiveRemote::ScopeKeys
39
+ include ::ActiveRemote::Search
40
+ include ::ActiveRemote::Serialization
41
+ include ::ActiveRemote::Typecasting
50
42
 
51
43
  # Overrides some methods, providing support for dirty tracking,
52
44
  # so it needs to be included last.
53
- include Dirty
45
+ include ::ActiveRemote::Dirty
54
46
 
55
47
  # Overrides persistence methods, so it must included after
56
- include Validations
57
- include ActiveModel::Validations::Callbacks
48
+ include ::ActiveRemote::Validations
49
+ include ::ActiveModel::Validations::Callbacks
58
50
 
59
51
  attr_reader :last_request, :last_response
60
52
 
61
53
  define_model_callbacks :initialize, :only => :after
62
54
 
63
- def initialize(*)
55
+ def initialize(attributes = {})
64
56
  @attributes ||= begin
65
57
  attribute_names = self.class.attribute_names
66
- Hash[attribute_names.map { |key| [key, send(key)] }]
58
+ Hash[attribute_names.map { |key| [key, nil] }]
67
59
  end
68
60
 
61
+ assign_attributes(attributes) if attributes
62
+
69
63
  @new_record = true
70
64
 
71
65
  skip_dirty_tracking do
72
66
  run_callbacks :initialize do
73
- super
67
+ yield self if block_given?
74
68
  end
75
69
  end
76
70
  end
77
71
 
72
+ # Initialize an object with the attributes hash directly
73
+ # When used with allocate, bypasses initialize
74
+ def init_with(attributes)
75
+ @attributes = attributes
76
+ @new_record = false
77
+
78
+ run_callbacks :initialize
79
+
80
+ self
81
+ end
82
+
78
83
  def freeze
79
84
  @attributes.freeze; self
80
85
  end
@@ -57,15 +57,6 @@ module ActiveRemote
57
57
  enable_dirty_tracking
58
58
  end
59
59
 
60
- # Override #write_attribute (along with #[]=) so we can provide support for
61
- # ActiveModel::Dirty.
62
- #
63
- def write_attribute(name, value)
64
- __send__("#{name}_will_change!") if _active_remote_track_changes? && value != self[name]
65
- super
66
- end
67
- alias_method :[]=, :write_attribute
68
-
69
60
  private
70
61
 
71
62
  # Wether or not changes are currently being tracked for this class.
@@ -5,6 +5,9 @@ module ActiveRemote
5
5
  class ActiveRemoteError < StandardError
6
6
  end
7
7
 
8
+ class DangerousAttributeError < ActiveRemoteError
9
+ end
10
+
8
11
  # Raised by ActiveRemove::Base.save when the remote record is readonly.
9
12
  class ReadOnlyRemoteRecord < ActiveRemoteError
10
13
  end
@@ -41,4 +44,7 @@ module ActiveRemote
41
44
  # when remote record cannot be saved because it is invalid.
42
45
  class RemoteRecordNotSaved < ActiveRemoteError
43
46
  end
47
+
48
+ class UnknownAttributeError < ActiveRemoteError
49
+ end
44
50
  end
@@ -49,8 +49,9 @@ module ActiveRemote
49
49
  # Instantiate a record with the given remote attributes. Generally used
50
50
  # when retrieving records that already exist, so @new_record is set to false.
51
51
  #
52
- def instantiate(attributes, options = {})
53
- new_object = self.new.instantiate(attributes)
52
+ def instantiate(new_attributes, options = {})
53
+ attributes = self.build_from_rpc(new_attributes)
54
+ new_object = self.allocate.init_with(attributes)
54
55
  new_object.readonly! if options[:readonly]
55
56
 
56
57
  new_object
@@ -78,7 +79,7 @@ module ActiveRemote
78
79
  raise ReadOnlyRemoteRecord if readonly?
79
80
  response = rpc.execute(:delete, scope_key_hash)
80
81
 
81
- add_errors_from_response(response)
82
+ add_errors(response.errors) if response.respond_to?(:errors)
82
83
 
83
84
  return success? ? freeze : false
84
85
  end
@@ -103,7 +104,7 @@ module ActiveRemote
103
104
  raise ReadOnlyRemoteRecord if readonly?
104
105
  response = rpc.execute(:destroy, scope_key_hash)
105
106
 
106
- add_errors_from_response(response)
107
+ add_errors(response.errors) if response.respond_to?(:errors)
107
108
 
108
109
  return success? ? freeze : false
109
110
  end
@@ -127,19 +128,9 @@ module ActiveRemote
127
128
  # Instantiate a record with the given remote attributes. Generally used
128
129
  # when retrieving records that already exist, so @new_record is set to false.
129
130
  #
130
- def instantiate(record)
131
- skip_dirty_tracking do
132
- assign_attributes(record, :without_protection => true)
133
- end
134
-
135
- # TODO: figure out how to safely run after_find/search callbacks here
136
- # currently, several functions use this code path, so an alternate path
137
- # may need to be added
138
-
139
- run_callbacks :initialize
140
-
141
- @new_record = false
142
- self
131
+ def instantiate(new_attributes)
132
+ new_attributes = self.class.build_from_rpc(new_attributes)
133
+ init_with(new_attributes)
143
134
  end
144
135
 
145
136
  # Returns true if the remote record hasn't been saved yet; otherwise,
@@ -252,7 +243,7 @@ module ActiveRemote
252
243
  response = rpc.execute(:create, new_attributes)
253
244
 
254
245
  assign_attributes(response.to_hash)
255
- add_errors_from_response(response)
246
+ add_errors(response.errors) if response.respond_to?(:errors)
256
247
 
257
248
  @new_record = has_errors?
258
249
  success?
@@ -282,7 +273,7 @@ module ActiveRemote
282
273
  response = rpc.execute(:update, updated_attributes)
283
274
 
284
275
  assign_attributes(response.to_hash)
285
- add_errors_from_response(response)
276
+ add_errors(response.errors) if response.respond_to?(:errors)
286
277
 
287
278
  success?
288
279
  end