active_remote 2.4.0 → 3.0.0.pre1

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.
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