api_resource 0.2.11 → 0.3.0
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.
- data/Gemfile +8 -2
- data/Gemfile.lock +85 -61
- data/VERSION +1 -1
- data/api_resource.gemspec +19 -9
- data/lib/api_resource.rb +46 -24
- data/lib/api_resource/associations.rb +15 -16
- data/lib/api_resource/associations/scope.rb +2 -1
- data/lib/api_resource/attributes.rb +72 -76
- data/lib/api_resource/base.rb +69 -53
- data/lib/api_resource/callbacks.rb +18 -22
- data/lib/api_resource/connection.rb +24 -1
- data/lib/api_resource/custom_methods.rb +23 -25
- data/lib/api_resource/formats.rb +9 -1
- data/lib/api_resource/model_errors.rb +36 -40
- data/lib/api_resource/observing.rb +9 -11
- data/lib/api_resource/scopes.rb +12 -12
- data/spec/lib/api_resource_spec.rb +43 -0
- data/spec/lib/associations_spec.rb +4 -3
- data/spec/lib/base_spec.rb +28 -0
- data/spec/spec_helper.rb +0 -1
- metadata +84 -50
@@ -56,7 +56,8 @@ module ApiResource
|
|
56
56
|
|
57
57
|
# gets the current hash and calls to_query on it
|
58
58
|
def to_query
|
59
|
-
|
59
|
+
#We need to add the unescape because to_query breaks on nested arrays
|
60
|
+
CGI.unescape(self.to_hash.to_query)
|
60
61
|
end
|
61
62
|
|
62
63
|
def method_missing(method, *args, &block)
|
@@ -10,10 +10,10 @@ module ApiResource
|
|
10
10
|
|
11
11
|
alias_method_chain :save, :dirty_tracking
|
12
12
|
|
13
|
-
|
13
|
+
class_attribute :attribute_names, :public_attribute_names, :protected_attribute_names, :attribute_types
|
14
14
|
|
15
15
|
cattr_accessor :valid_typecasts; self.valid_typecasts = [:date, :time, :float, :integer, :int, :fixnum, :string]
|
16
|
-
|
16
|
+
|
17
17
|
attr_reader :attributes
|
18
18
|
|
19
19
|
self.attribute_names = []
|
@@ -41,24 +41,23 @@ module ApiResource
|
|
41
41
|
self.define_attribute_type(arg.first, arg.second)
|
42
42
|
arg = arg.first
|
43
43
|
end
|
44
|
-
|
45
|
-
self.
|
46
|
-
self.public_attribute_names << arg.to_sym
|
44
|
+
self.attribute_names += [arg.to_sym]
|
45
|
+
self.public_attribute_names += [arg.to_sym]
|
47
46
|
|
48
47
|
# Override the setter for dirty tracking
|
49
48
|
self.class_eval <<-EOE, __FILE__, __LINE__ + 1
|
50
49
|
def #{arg}
|
51
|
-
|
50
|
+
attributes[:#{arg}]
|
52
51
|
end
|
53
52
|
|
54
53
|
def #{arg}=(val)
|
55
54
|
real_val = typecast_attribute(:#{arg}, val)
|
56
55
|
#{arg}_will_change! unless self.#{arg} == real_val
|
57
|
-
|
56
|
+
attributes[:#{arg}] = real_val
|
58
57
|
end
|
59
58
|
|
60
59
|
def #{arg}?
|
61
|
-
|
60
|
+
attributes[:#{arg}].present?
|
62
61
|
end
|
63
62
|
EOE
|
64
63
|
end
|
@@ -74,8 +73,8 @@ module ApiResource
|
|
74
73
|
arg = arg.first
|
75
74
|
end
|
76
75
|
|
77
|
-
self.attribute_names
|
78
|
-
self.protected_attribute_names
|
76
|
+
self.attribute_names += [arg.to_sym]
|
77
|
+
self.protected_attribute_names += [arg.to_sym]
|
79
78
|
|
80
79
|
# These attributes cannot be set, throw an error if you try
|
81
80
|
self.class_eval <<-EOE, __FILE__, __LINE__ + 1
|
@@ -118,79 +117,76 @@ module ApiResource
|
|
118
117
|
end
|
119
118
|
end
|
120
119
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
new_attrs.each_pair do |k,v|
|
126
|
-
self.send("#{k}=",v) unless k.to_sym == :id
|
127
|
-
end
|
128
|
-
new_attrs
|
120
|
+
# set new attributes
|
121
|
+
def attributes=(new_attrs)
|
122
|
+
new_attrs.each_pair do |k,v|
|
123
|
+
self.send("#{k}=",v) unless k.to_sym == :id
|
129
124
|
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
def set_attributes_as_current(*attrs)
|
142
|
-
@changed_attributes.clear and return if attrs.blank?
|
143
|
-
attrs.each do |attr|
|
144
|
-
@changed_attributes.delete(attr.to_s)
|
145
|
-
end
|
125
|
+
new_attrs
|
126
|
+
end
|
127
|
+
|
128
|
+
def save_with_dirty_tracking(*args)
|
129
|
+
if save_without_dirty_tracking(*args)
|
130
|
+
@previously_changed = self.changes
|
131
|
+
@changed_attributes.clear
|
132
|
+
return true
|
133
|
+
else
|
134
|
+
return false
|
146
135
|
end
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
set_attributes_as_current(*attrs)
|
136
|
+
end
|
137
|
+
|
138
|
+
def set_attributes_as_current(*attrs)
|
139
|
+
@changed_attributes.clear and return if attrs.blank?
|
140
|
+
attrs.each do |attr|
|
141
|
+
@changed_attributes.delete(attr.to_s)
|
155
142
|
end
|
156
|
-
|
157
|
-
|
158
|
-
|
143
|
+
end
|
144
|
+
|
145
|
+
def reset_attribute_changes(*attrs)
|
146
|
+
attrs = self.class.public_attribute_names if attrs.blank?
|
147
|
+
attrs.each do |attr|
|
148
|
+
self.send("reset_#{attr}!")
|
159
149
|
end
|
160
150
|
|
161
|
-
|
162
|
-
|
151
|
+
set_attributes_as_current(*attrs)
|
152
|
+
end
|
153
|
+
|
154
|
+
def attribute?(name)
|
155
|
+
self.class.attribute?(name)
|
156
|
+
end
|
157
|
+
|
158
|
+
def protected_attribute?(name)
|
159
|
+
self.class.protected_attribute?(name)
|
160
|
+
end
|
161
|
+
|
162
|
+
def respond_to?(sym, include_private_methods = false)
|
163
|
+
if sym =~ /\?$/
|
164
|
+
return true if self.attribute?($`)
|
165
|
+
elsif sym =~ /=$/
|
166
|
+
return true if self.class.public_attribute_names.include?($`)
|
167
|
+
else
|
168
|
+
return true if self.attribute?(sym.to_sym)
|
163
169
|
end
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
+
super
|
171
|
+
end
|
172
|
+
|
173
|
+
protected
|
174
|
+
def typecast_attribute(field, val)
|
175
|
+
return val unless self.class.attribute_types.include?(field)
|
176
|
+
case self.class.attribute_types[field.to_sym]
|
177
|
+
when :date
|
178
|
+
return val.class == Date ? val.dup : Date.parse(val)
|
179
|
+
when :time
|
180
|
+
return val.class == Time ? val.dup : Time.parse(val)
|
181
|
+
when :integer, :int, :fixnum
|
182
|
+
return val.class == Fixnum ? val.dup : val.to_i rescue val
|
183
|
+
when :float
|
184
|
+
return val.class == Float ? val.dup : val.to_f rescue val
|
185
|
+
when :string
|
186
|
+
return val.class == String ? val.dup : val.to_s rescue val
|
170
187
|
else
|
171
|
-
|
172
|
-
|
173
|
-
super
|
174
|
-
end
|
175
|
-
|
176
|
-
protected
|
177
|
-
def typecast_attribute(field, val)
|
178
|
-
return val unless self.class.attribute_types.include?(field)
|
179
|
-
case self.class.attribute_types[field.to_sym]
|
180
|
-
when :date
|
181
|
-
return val.class == Date ? val.dup : Date.parse(val)
|
182
|
-
when :time
|
183
|
-
return val.class == Time ? val.dup : Time.parse(val)
|
184
|
-
when :integer, :int, :fixnum
|
185
|
-
return val.class == Fixnum ? val.dup : val.to_i rescue val
|
186
|
-
when :float
|
187
|
-
return val.class == Float ? val.dup : val.to_f rescue val
|
188
|
-
when :string
|
189
|
-
return val.class == String ? val.dup : val.to_s rescue val
|
190
|
-
else
|
191
|
-
# catches the nil case and just leaves it alone
|
192
|
-
return val.dup rescue val
|
193
|
-
end
|
188
|
+
# catches the nil case and just leaves it alone
|
189
|
+
return val.dup rescue val
|
194
190
|
end
|
195
191
|
end
|
196
192
|
|
data/lib/api_resource/base.rb
CHANGED
@@ -7,13 +7,24 @@ module ApiResource
|
|
7
7
|
|
8
8
|
class Base
|
9
9
|
|
10
|
-
|
10
|
+
class_attribute :site, :proxy, :user, :password, :auth_type, :format,
|
11
|
+
:timeout, :open_timeout, :ssl_options, :token, :ttl
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
|
14
|
+
class_attribute :include_root_in_json
|
15
|
+
self.include_root_in_json = true
|
16
|
+
|
17
|
+
class_attribute :include_blank_attributes_on_create
|
18
|
+
self.include_blank_attributes_on_create = false
|
19
|
+
|
20
|
+
class_attribute :include_all_attributes_on_update
|
21
|
+
self.include_blank_attributes_on_create = false
|
22
|
+
|
23
|
+
class_attribute :format
|
24
|
+
self.format = ApiResource::Formats::JsonFormat
|
15
25
|
|
16
|
-
|
26
|
+
class_attribute :primary_key
|
27
|
+
self.primary_key = "id"
|
17
28
|
|
18
29
|
attr_accessor :prefix_options
|
19
30
|
|
@@ -97,7 +108,11 @@ module ApiResource
|
|
97
108
|
return e.respond_to?(:request) ? e.request : nil
|
98
109
|
end
|
99
110
|
end
|
100
|
-
|
111
|
+
|
112
|
+
def reset_connection
|
113
|
+
remove_instance_variable(:@connection) if @connection.present?
|
114
|
+
end
|
115
|
+
|
101
116
|
def reload_class_attributes
|
102
117
|
# clear the public_attribute_names, protected_attribute_names
|
103
118
|
remove_instance_variable(:@class_data) if instance_variable_defined?(:@class_data)
|
@@ -106,24 +121,26 @@ module ApiResource
|
|
106
121
|
self.set_class_attributes_upon_load
|
107
122
|
end
|
108
123
|
|
109
|
-
def
|
110
|
-
self.
|
124
|
+
def token_with_new_token_set=(new_token)
|
125
|
+
self.token_without_new_token_set = new_token
|
111
126
|
self.descendants.each do |child|
|
112
127
|
child.send(:token=, new_token)
|
113
128
|
end
|
114
129
|
end
|
130
|
+
|
131
|
+
alias_method_chain :token=, :new_token_set
|
115
132
|
|
116
|
-
def
|
133
|
+
def site_with_connection_reset=(site)
|
117
134
|
# store so we can reload attributes if the site changed
|
118
135
|
old_site = self.site.to_s.clone
|
119
136
|
@connection = nil
|
120
137
|
|
121
138
|
if site.nil?
|
122
|
-
|
139
|
+
self.site_without_connection_reset = nil
|
123
140
|
# no site, so we'll skip the reload
|
124
141
|
return site
|
125
142
|
else
|
126
|
-
|
143
|
+
self.site_without_connection_reset = create_site_uri_from(site)
|
127
144
|
end
|
128
145
|
|
129
146
|
# reset class attributes and try to reload them if the site changed
|
@@ -134,38 +151,37 @@ module ApiResource
|
|
134
151
|
return site
|
135
152
|
end
|
136
153
|
|
154
|
+
alias_method_chain :site=, :connection_reset
|
155
|
+
|
137
156
|
|
138
|
-
def
|
157
|
+
def format_with_mimetype_or_format_set=(mime_type_or_format)
|
139
158
|
format = mime_type_or_format.is_a?(Symbol) ? ApiResource::Formats[mime_type_or_format] : mime_type_or_format
|
140
|
-
|
159
|
+
self.format_without_mimetype_or_format_set = format
|
141
160
|
self.connection.format = format if self.site
|
142
161
|
end
|
143
162
|
|
144
|
-
|
145
|
-
def format
|
146
|
-
read_inheritable_attribute(:format) || ApiResource::Formats::JsonFormat
|
147
|
-
end
|
163
|
+
alias_method_chain :format=, :mimetype_or_format_set
|
148
164
|
|
149
|
-
def
|
165
|
+
def timeout_with_connection_reset=(timeout)
|
150
166
|
@connection = nil
|
151
|
-
|
167
|
+
self.timeout_without_connection_reset = timeout
|
152
168
|
end
|
153
169
|
|
154
|
-
|
170
|
+
alias_method_chain :timeout=, :connection_reset
|
171
|
+
|
172
|
+
def open_timeout_with_connection_reset=(timeout)
|
155
173
|
@connection = nil
|
156
|
-
|
174
|
+
self.open_timeout_without_connection_reset = timeout
|
157
175
|
end
|
158
176
|
|
177
|
+
alias_method_chain :open_timeout=, :connection_reset
|
178
|
+
|
159
179
|
def connection(refresh = false)
|
160
180
|
@connection = Connection.new(self.site, self.format) if refresh || @connection.nil?
|
161
181
|
@connection.timeout = self.timeout
|
162
182
|
@connection
|
163
183
|
end
|
164
|
-
|
165
|
-
def reset_connection
|
166
|
-
remove_instance_variable(:@connection) if @connection.present?
|
167
|
-
end
|
168
|
-
|
184
|
+
|
169
185
|
def headers
|
170
186
|
{}.tap do |ret|
|
171
187
|
ret['Lifebooker-Token'] = self.token if self.token.present?
|
@@ -469,40 +485,40 @@ module ApiResource
|
|
469
485
|
return if attributes.nil?
|
470
486
|
raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
|
471
487
|
@prefix_options, attributes = split_options(attributes)
|
472
|
-
|
473
488
|
attributes.symbolize_keys.each do |key, value|
|
474
489
|
# If this attribute doesn't exist define it as a protected attribute
|
475
490
|
self.class.define_protected_attributes(key) unless self.respond_to?(key)
|
476
491
|
#self.send("#{key}_will_change!") if self.respond_to?("#{key}_will_change!")
|
477
492
|
self.attributes[key] =
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
493
|
+
case value
|
494
|
+
when Array
|
495
|
+
if self.has_many?(key)
|
496
|
+
MultiObjectProxy.new(self.has_many_class_name(key), value)
|
497
|
+
elsif self.association?(key)
|
498
|
+
raise ArgumentError, "Expected a hash value or nil, got: #{value.inspect}"
|
499
|
+
else
|
500
|
+
typecast_attribute(key, value)
|
501
|
+
end
|
502
|
+
when Hash
|
503
|
+
if self.has_many?(key)
|
504
|
+
MultiObjectProxy.new(self.has_many_class_name(key), value)
|
505
|
+
elsif self.association?(key)
|
506
|
+
#binding.pry
|
507
|
+
SingleObjectProxy.new(self.association_class_name(key), value)
|
508
|
+
else
|
509
|
+
typecast_attribute(key, value)
|
510
|
+
end
|
511
|
+
when NilClass
|
512
|
+
# If it's nil and an association then create a blank object
|
513
|
+
if self.has_many?(key)
|
514
|
+
return MultiObjectProxy.new(self.has_many_class_name(key), [])
|
515
|
+
elsif self.association?(key)
|
516
|
+
SingleObjectProxy.new(self.association_class_name(key), value)
|
517
|
+
end
|
492
518
|
else
|
519
|
+
raise ArgumentError, "expected an array or a hash for the association #{key}, got: #{value.inspect}" if self.association?(key)
|
493
520
|
typecast_attribute(key, value)
|
494
|
-
|
495
|
-
when NilClass
|
496
|
-
# If it's nil and an association then create a blank object
|
497
|
-
if self.has_many?(key)
|
498
|
-
return MultiObjectProxy.new(self.has_many_class_name(key), [])
|
499
|
-
elsif self.association?(key)
|
500
|
-
SingleObjectProxy.new(self.association_class_name(key), value)
|
501
|
-
end
|
502
|
-
else
|
503
|
-
raise ArgumentError, "expected an array or a hash for the association #{key}, got: #{value.inspect}" if self.association?(key)
|
504
|
-
typecast_attribute(key, value)
|
505
|
-
end
|
521
|
+
end
|
506
522
|
end
|
507
523
|
return self
|
508
524
|
end
|
@@ -16,32 +16,28 @@ module ApiResource
|
|
16
16
|
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
_run_save_callbacks do
|
23
|
-
save_without_callbacks(*args)
|
24
|
-
end
|
19
|
+
def save_with_callbacks(*args)
|
20
|
+
run_callbacks :save do
|
21
|
+
save_without_callbacks(*args)
|
25
22
|
end
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_with_callbacks(*args)
|
26
|
+
run_callbacks :create do
|
27
|
+
create_without_callbacks(*args)
|
31
28
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
end
|
30
|
+
|
31
|
+
def update_with_callbacks(*args)
|
32
|
+
run_callbacks :update do
|
33
|
+
update_without_callbacks(*args)
|
37
34
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
35
|
+
end
|
36
|
+
|
37
|
+
def destroy_with_callbacks(*args)
|
38
|
+
run_callbacks :destroy do
|
39
|
+
destroy_without_callbacks(*args)
|
43
40
|
end
|
44
|
-
|
45
41
|
end
|
46
42
|
|
47
43
|
end
|