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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -0
- data/CHANGES.md +22 -0
- data/README.md +2 -0
- data/active_remote.gemspec +2 -2
- data/bin/console +10 -0
- data/lib/active_remote/association.rb +4 -0
- data/lib/active_remote/attribute_assignment.rb +53 -0
- data/lib/active_remote/attribute_definition.rb +106 -0
- data/lib/active_remote/attributes.rb +165 -8
- data/lib/active_remote/base.rb +41 -36
- data/lib/active_remote/dirty.rb +0 -9
- data/lib/active_remote/errors.rb +6 -0
- data/lib/active_remote/persistence.rb +10 -19
- data/lib/active_remote/query_attributes.rb +45 -0
- data/lib/active_remote/rpc.rb +17 -67
- data/lib/active_remote/search.rb +0 -20
- data/lib/active_remote/serialization.rb +1 -32
- data/lib/active_remote/typecasting.rb +3 -12
- data/lib/active_remote/version.rb +1 -1
- data/lib/active_remote.rb +0 -2
- data/spec/lib/active_remote/association_spec.rb +11 -2
- data/spec/lib/active_remote/attributes_spec.rb +177 -0
- data/spec/lib/active_remote/persistence_spec.rb +7 -16
- data/spec/lib/active_remote/query_attribute_spec.rb +171 -0
- data/spec/lib/active_remote/rpc_spec.rb +33 -75
- data/spec/lib/active_remote/search_spec.rb +0 -21
- data/spec/lib/active_remote/serialization_spec.rb +0 -23
- data/spec/support/models/no_attributes.rb +2 -0
- data/spec/support/models.rb +1 -0
- metadata +21 -66
- data/lib/active_remote/attribute_defaults.rb +0 -100
- data/lib/active_remote/bulk.rb +0 -168
- data/lib/active_remote/core_ext/date.rb +0 -7
- data/lib/active_remote/core_ext/date_time.rb +0 -7
- data/lib/active_remote/core_ext/integer.rb +0 -19
- data/lib/active_remote/core_ext.rb +0 -3
- data/lib/active_remote/publication.rb +0 -54
- data/lib/active_remote/serializers/json.rb +0 -16
- data/spec/core_ext/date_time_spec.rb +0 -9
- data/spec/lib/active_remote/attribute_defaults_spec.rb +0 -26
- data/spec/lib/active_remote/bulk_spec.rb +0 -83
- data/spec/lib/active_remote/publication_spec.rb +0 -18
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6501c9a34d84239729afd215a01cea574af7a5c
|
4
|
+
data.tar.gz: 55a6b558a7fa1fba40a415266743f7e01fc31fc9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57f951cd6dc0f19d37942880c2da4066d9cd7773c1ee16def3c0068cfb337d68aa4f596b80fe679ef1e8f83bf5685a5f5396b8a9204293ca273f0db24428ef78
|
7
|
+
data.tar.gz: f861f24788ce54b8dfaa0693ee236cc5aad887945bba35670687b9a8fb57f56f4eb06ec9e090ce052eef1c8f4242d6c026910d92f607dd79c5f1b07d700e11dc
|
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
|
+
[](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.
|
data/active_remote.gemspec
CHANGED
@@ -19,8 +19,8 @@ Gem::Specification.new do |s|
|
|
19
19
|
##
|
20
20
|
# Dependencies
|
21
21
|
#
|
22
|
-
s.add_dependency "
|
23
|
-
s.add_dependency "activesupport", ">=
|
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?
|
41
|
+
if respond_to?(name)
|
17
42
|
attribute(name)
|
18
43
|
else
|
19
|
-
raise
|
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?
|
54
|
+
if respond_to?("#{name}=")
|
30
55
|
__send__("attribute=", name, value)
|
31
56
|
else
|
32
|
-
raise
|
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
|
data/lib/active_remote/base.rb
CHANGED
@@ -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/
|
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/
|
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
|
-
|
27
|
-
include
|
28
|
-
include
|
29
|
-
|
30
|
-
include
|
31
|
-
include
|
32
|
-
include
|
33
|
-
include
|
34
|
-
include
|
35
|
-
|
36
|
-
include
|
37
|
-
include
|
38
|
-
include
|
39
|
-
include
|
40
|
-
include
|
41
|
-
include
|
42
|
-
include
|
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,
|
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
|
-
|
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
|
data/lib/active_remote/dirty.rb
CHANGED
@@ -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.
|
data/lib/active_remote/errors.rb
CHANGED
@@ -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(
|
53
|
-
|
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
|
-
|
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
|
-
|
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(
|
131
|
-
|
132
|
-
|
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
|
-
|
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
|
-
|
276
|
+
add_errors(response.errors) if response.respond_to?(:errors)
|
286
277
|
|
287
278
|
success?
|
288
279
|
end
|