api_resource 0.2.11 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|