restly 0.0.1.alpha.16 → 0.0.1.alpha.18
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/README.md +2 -112
- data/lib/restly.rb +26 -1
- data/lib/restly/associations.rb +25 -12
- data/lib/restly/associations/base/conditionals.rb +2 -2
- data/lib/restly/base.rb +1 -0
- data/lib/restly/base/fields.rb +21 -17
- data/lib/restly/base/includes.rb +7 -2
- data/lib/restly/base/instance.rb +31 -12
- data/lib/restly/base/instance/actions.rb +2 -2
- data/lib/restly/base/instance/attributes.rb +46 -40
- data/lib/restly/base/instance/comparable.rb +13 -0
- data/lib/restly/base/instance/error_handling.rb +60 -0
- data/lib/restly/base/instance/persistence.rb +1 -1
- data/lib/restly/base/resource/specification/fields.rb +2 -2
- data/lib/restly/base/resource/specification/mass_assignment_security.rb +1 -1
- data/lib/restly/client.rb +20 -7
- data/lib/restly/collection.rb +48 -12
- data/lib/restly/collection/error_handling.rb +17 -0
- data/lib/restly/connection.rb +55 -11
- data/lib/restly/error.rb +5 -9
- data/lib/restly/middleware.rb +15 -2
- data/lib/restly/nested_attributes.rb +29 -23
- data/lib/restly/notifications.rb +65 -0
- data/lib/restly/version.rb +1 -1
- data/spec/restly/active_model_lint_spec.rb +11 -0
- data/spec/restly/associations/base/builders_spec.rb +8 -0
- data/spec/restly/associations/base/conditionals_spec.rb +8 -0
- data/spec/restly/associations/base/loaders_spec.rb +8 -0
- data/spec/restly/associations/base/modifiers_spec.rb +8 -0
- data/spec/restly/associations/base/stubs_spec.rb +8 -0
- data/spec/restly/associations/class_methods.rb +0 -0
- data/spec/restly/base/instance/comparable_spec.rb +8 -0
- metadata +22 -2
@@ -0,0 +1,13 @@
|
|
1
|
+
module Restly::Base::Instance::Comparable
|
2
|
+
|
3
|
+
def ==(object)
|
4
|
+
reload!
|
5
|
+
object.reload!
|
6
|
+
|
7
|
+
this_object = Marshal.dump attributes.except(:id, :created_at, :updated_at)
|
8
|
+
other_object = Marshal.dump object.attributes.except(:id, :created_at, :updated_at)
|
9
|
+
|
10
|
+
Marshal.dump(this_object) == Marshal.dump(other_object) && self.class == object.class
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Restly::Base::Instance::ErrorHandling
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def response_has_errors?(response=self.response)
|
7
|
+
@response.status >= 400 ||
|
8
|
+
(parsed_response(response).is_a?(Hash) &&
|
9
|
+
(parsed_response(response)[:errors] || parsed_response(response)[:error]))
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_errors_from_response(response = self.response)
|
13
|
+
|
14
|
+
response_errors = parsed_response(response)[:errors] || parsed_response(response)[:error]
|
15
|
+
|
16
|
+
case response_errors
|
17
|
+
|
18
|
+
when Hash
|
19
|
+
response_errors.each do |name, error|
|
20
|
+
case error
|
21
|
+
|
22
|
+
when Array
|
23
|
+
error.each { |e| self.errors.add(name.to_sym, e) }
|
24
|
+
|
25
|
+
when String
|
26
|
+
self.errors.add(name.to_sym, error)
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
when Array
|
32
|
+
response_errors.each do |error|
|
33
|
+
self.errors.add(:base, error)
|
34
|
+
end
|
35
|
+
|
36
|
+
when String
|
37
|
+
self.errors.add(:base, response_errors)
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
self.errors
|
42
|
+
end
|
43
|
+
|
44
|
+
def read_attribute_for_validation(attr)
|
45
|
+
send attr
|
46
|
+
end
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
|
50
|
+
def human_attribute_name(attr, options = {})
|
51
|
+
attr.to_s.humanize
|
52
|
+
end
|
53
|
+
|
54
|
+
def lookup_ancestors
|
55
|
+
[self]
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -25,7 +25,7 @@ module Restly::Base::Instance::Persistence
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def reload!
|
28
|
-
raise Restly::Error::MissingId, "Cannot reload #{resource_name}, either it hasn't been created or it is missing an ID." unless
|
28
|
+
raise Restly::Error::MissingId, "Cannot reload #{resource_name}, either it hasn't been created or it is missing an ID." unless new_record?
|
29
29
|
set_attributes_from_response connection.get(path_with_format, force: true)
|
30
30
|
@loaded = true
|
31
31
|
self
|
@@ -6,7 +6,7 @@ class Restly::Base::Resource::Specification::Fields < Restly::Proxies::Base
|
|
6
6
|
@spec = spec
|
7
7
|
@removed = Set.new
|
8
8
|
@added = Set.new
|
9
|
-
super Restly::Base::Fields::FieldSet.new
|
9
|
+
super Restly::Base::Fields::FieldSet.new(spec.model)
|
10
10
|
end
|
11
11
|
|
12
12
|
def -(field)
|
@@ -34,7 +34,7 @@ class Restly::Base::Resource::Specification::Fields < Restly::Proxies::Base
|
|
34
34
|
def reload_specification!
|
35
35
|
from_spec = spec[:attributes] || []
|
36
36
|
fields = (from_spec - @removed.to_a) + @added.to_a
|
37
|
-
__setobj__ Restly::Base::Fields::FieldSet.new(fields)
|
37
|
+
__setobj__ Restly::Base::Fields::FieldSet.new(spec.model, fields)
|
38
38
|
end
|
39
39
|
|
40
40
|
end
|
@@ -45,7 +45,7 @@ module Restly::Base::Resource::Specification::MassAssignmentSecurity
|
|
45
45
|
|
46
46
|
def reload_specification!
|
47
47
|
accepts = spec.actions.map { |action| action['accepts_parameters'] }.flatten if spec.actions.present?
|
48
|
-
__setobj__ ActiveModel::MassAssignmentSecurity::
|
48
|
+
__setobj__ ActiveModel::MassAssignmentSecurity::WhiteList.new accepts
|
49
49
|
end
|
50
50
|
|
51
51
|
end
|
data/lib/restly/client.rb
CHANGED
@@ -1,17 +1,30 @@
|
|
1
1
|
class Restly::Client < OAuth2::Client
|
2
2
|
|
3
3
|
attr_accessor :id, :secret, :site
|
4
|
-
attr_reader :format
|
4
|
+
attr_reader :format, :resource
|
5
5
|
|
6
6
|
def initialize(*args, &block)
|
7
7
|
opts = args.extract_options!
|
8
|
-
|
9
|
-
|
10
|
-
self.
|
11
|
-
self.
|
12
|
-
self.
|
13
|
-
self.
|
8
|
+
opts.merge!(raise_errors: false)
|
9
|
+
|
10
|
+
self.resource = opts.delete(:resource) if opts[:resource]
|
11
|
+
self.id = args[0] || Restly::Configuration.oauth_options[:client_id]
|
12
|
+
self.secret = args[1] || Restly::Configuration.oauth_options[:client_secret]
|
13
|
+
self.site = opts.delete(:site) || Restly::Configuration.site
|
14
|
+
self.options = Restly::Configuration.client_options.merge(opts)
|
15
|
+
self.ssl = opts.delete(:ssl) || Restly::Configuration.ssl
|
16
|
+
self.format = @format = opts.delete(:format) || Restly::Configuration.default_format
|
14
17
|
self.options[:connection_build] ||= block
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def resource=(resource)
|
22
|
+
raise InvalidObject, "Resource must be a descendant of Restly::Base" unless resource.ancestors.include?(Restly::Base)
|
23
|
+
@resource = resource
|
24
|
+
end
|
25
|
+
|
26
|
+
def resource_name
|
27
|
+
resource.name.parameterize
|
15
28
|
end
|
16
29
|
|
17
30
|
def ssl=(val)
|
data/lib/restly/collection.rb
CHANGED
@@ -1,21 +1,28 @@
|
|
1
1
|
class Restly::Collection < Array
|
2
2
|
extend ActiveSupport::Autoload
|
3
3
|
autoload :Pagination
|
4
|
+
autoload :ErrorHandling
|
4
5
|
|
5
6
|
include Restly::Base::Resource::Finders
|
6
7
|
include Restly::Base::Resource::BatchActions
|
7
8
|
include Restly::Base::GenericMethods
|
9
|
+
include ErrorHandling
|
8
10
|
|
9
11
|
delegate :resource_name, :new, :client, to: :resource
|
10
12
|
|
11
|
-
attr_reader :resource
|
13
|
+
attr_reader :resource, :response
|
12
14
|
|
13
15
|
def initialize(resource, array=[], opts={})
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
ActiveSupport::Notifications.instrument("load_collection.restly", model: resource.name) do
|
17
|
+
@errors = []
|
18
|
+
@resource = resource
|
19
|
+
@connection
|
20
|
+
if opts[:response]
|
21
|
+
set_response(opts[:response])
|
22
|
+
else
|
23
|
+
replace array
|
24
|
+
end
|
25
|
+
end
|
19
26
|
end
|
20
27
|
|
21
28
|
[:path, :connection, :params].each do |attr|
|
@@ -52,24 +59,41 @@ class Restly::Collection < Array
|
|
52
59
|
super(instance)
|
53
60
|
end
|
54
61
|
|
62
|
+
def replace(array)
|
63
|
+
array.each do |instance|
|
64
|
+
raise Restly::Error::InvalidObject, "Object is not an instance of #{resource}" unless accepts?(instance)
|
65
|
+
end
|
66
|
+
super
|
67
|
+
end
|
68
|
+
|
55
69
|
def reload!
|
56
70
|
replace collection_from_response(connection.get path)
|
57
71
|
end
|
58
72
|
|
59
73
|
private
|
60
74
|
|
75
|
+
def set_response(response)
|
76
|
+
raise Restly::Error::InvalidResponse unless response.is_a? OAuth2::Response
|
77
|
+
@response = response
|
78
|
+
if response.try(:body)
|
79
|
+
if response_has_errors?(response)
|
80
|
+
set_errors_from_response
|
81
|
+
else
|
82
|
+
set_items_from_response
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
61
87
|
def serializable_hash(options = nil)
|
62
|
-
self.
|
88
|
+
self.map do |i|
|
63
89
|
i.serializable_hash(options)
|
64
90
|
end
|
65
91
|
end
|
66
92
|
|
67
|
-
def
|
68
|
-
|
69
|
-
parsed = parsed[resource_name.pluralize] if parsed.is_a?(Hash) && parsed[resource_name.pluralize]
|
70
|
-
parsed.collect do |instance|
|
93
|
+
def set_items_from_response(response=self.response)
|
94
|
+
parsed_response(response).reduce(self) do |collection, instance|
|
71
95
|
instance = instance[resource_name] if instance[resource_name]
|
72
|
-
resource.new(instance, connection: connection)
|
96
|
+
collection << resource.new(instance, connection: connection, loaded: false)
|
73
97
|
end
|
74
98
|
end
|
75
99
|
|
@@ -77,4 +101,16 @@ class Restly::Collection < Array
|
|
77
101
|
instance.class.name == resource.name
|
78
102
|
end
|
79
103
|
|
104
|
+
def parsed_response(response=self.response)
|
105
|
+
return {} unless response
|
106
|
+
parsed = response.parsed || {}
|
107
|
+
if parsed.is_a?(Hash) && parsed[resource_name.pluralize]
|
108
|
+
parsed[resource_name.pluralize]
|
109
|
+
elsif parsed.is_a?(Hash)
|
110
|
+
parsed.with_indifferent_access
|
111
|
+
else
|
112
|
+
parsed
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
80
116
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Restly::Collection::ErrorHandling
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
def response_has_errors?(response=self.response)
|
5
|
+
@response.status >= 400 ||
|
6
|
+
(parsed_response(response).is_a?(Hash) &&
|
7
|
+
(parsed_response(response)[:errors] || parsed_response(response)[:error]))
|
8
|
+
end
|
9
|
+
|
10
|
+
def set_errors_from_response(response = self.response)
|
11
|
+
if (error = parsed_response(response)[:errors] || parsed_response(response)[:error])
|
12
|
+
@errors << error
|
13
|
+
end
|
14
|
+
replace []
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/lib/restly/connection.rb
CHANGED
@@ -2,6 +2,8 @@ class Restly::Connection < OAuth2::AccessToken
|
|
2
2
|
|
3
3
|
attr_accessor :cache, :cache_options
|
4
4
|
|
5
|
+
delegate :resource, :resource_name, to: :client
|
6
|
+
|
5
7
|
# TODO: Refactor with subclasses that have their own tokenize methods.
|
6
8
|
def self.tokenize(client, object)
|
7
9
|
|
@@ -63,37 +65,79 @@ class Restly::Connection < OAuth2::AccessToken
|
|
63
65
|
|
64
66
|
def request(verb, path, opts={}, &block)
|
65
67
|
if cache && !opts[:force]
|
66
|
-
|
68
|
+
request_log("Restly::CacheRequest", path, verb) do
|
69
|
+
cached_request(verb, path, opts, &block)
|
70
|
+
end
|
67
71
|
else
|
68
|
-
|
72
|
+
request_log("Restly::Request", path, verb) do
|
73
|
+
forced_request(verb, path, opts, &block)
|
74
|
+
end
|
69
75
|
end
|
70
76
|
end
|
71
77
|
|
72
|
-
|
78
|
+
def id_from_path(path)
|
79
|
+
capture = path.match /(?<id>[0-9])\.\w*$/
|
80
|
+
capture[:id] if capture
|
81
|
+
end
|
73
82
|
|
74
83
|
def cached_request(verb, path, opts={}, &block)
|
75
|
-
|
84
|
+
id = id_from_path(path)
|
85
|
+
|
86
|
+
cache_options = self.cache_options.dup
|
87
|
+
options_hash = { path: path, verb: verb, token: token, opts: opts, cache_opts: cache_options, block: block }
|
76
88
|
options_packed = [Marshal.dump(options_hash)].pack('m')
|
77
89
|
options_hex = Digest::MD5.hexdigest(options_packed)
|
78
|
-
|
90
|
+
|
91
|
+
# Keys
|
92
|
+
collection_expire_key = [resource_name, "*"].compact.join('_')
|
93
|
+
instance_expire_key = [id, resource_name, "*"].compact.join('_')
|
94
|
+
cache_key = [id, resource_name, options_hex].compact.join('_')
|
95
|
+
|
79
96
|
|
80
97
|
# Force a cache miss for all methods except get
|
81
98
|
cache_options[:force] = true unless [:get, :options].include?(verb)
|
82
99
|
|
83
100
|
# Set the response
|
84
|
-
|
101
|
+
unless verb.to_s.upcase =~ /GET|OPTIONS/
|
85
102
|
|
86
|
-
|
87
|
-
|
88
|
-
|
103
|
+
# Expire Collections
|
104
|
+
cache_log("Restly::CacheExpire", instance_expire_key, :yellow) do
|
105
|
+
Rails.cache.delete_matched(collection_expire_key)
|
106
|
+
end
|
89
107
|
|
108
|
+
# Expire Instances
|
109
|
+
cache_log("Restly::CacheExpire", instance_expire_key, :yellow) do
|
110
|
+
Rails.cache.delete_matched(instance_expire_key)
|
111
|
+
end
|
90
112
|
end
|
91
113
|
|
92
|
-
|
93
|
-
|
114
|
+
response = Rails.cache.fetch cache_key, cache_options.symbolize_keys do
|
115
|
+
cache_log("Restly::CacheMiss", cache_key, :red) do
|
116
|
+
forced_request(verb, path, opts, &block)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
cache_log("Restly::CacheExpire", cache_key, :yellow) { Rails.cache.delete(cache_key) } if response.error
|
121
|
+
|
122
|
+
raise Restly::Error::ConnectionError, "#{response.status}: #{status_string(response.status)}" if response.status >= 500
|
94
123
|
|
95
124
|
# Return the response
|
96
125
|
response
|
97
126
|
|
98
127
|
end
|
128
|
+
|
129
|
+
def request_log(name, path, verb, color=:light_green, &block)
|
130
|
+
site = URI.parse(client.site)
|
131
|
+
formatted_path = ["#{site.scheme}://#{site.host}", path].join("/")
|
132
|
+
ActiveSupport::Notifications.instrument("request.restly", url: formatted_path, method: verb, name: name, color: color, &block)
|
133
|
+
end
|
134
|
+
|
135
|
+
def cache_log(name, key, color=:light_green, &block)
|
136
|
+
ActiveSupport::Notifications.instrument("cache.restly", key: key, name: name, color: color, &block)
|
137
|
+
end
|
138
|
+
|
139
|
+
def status_string(int)
|
140
|
+
Rack::Utils::HTTP_STATUS_CODES[int.to_i]
|
141
|
+
end
|
142
|
+
|
99
143
|
end
|
data/lib/restly/error.rb
CHANGED
@@ -1,26 +1,22 @@
|
|
1
1
|
module Restly::Error
|
2
2
|
|
3
|
-
class StandardError < ::StandardError
|
4
|
-
|
5
|
-
def message
|
6
|
-
defined?(IRB) ? super.red : super
|
7
|
-
end
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
3
|
errors = %w{
|
12
4
|
RecordNotFound
|
13
5
|
InvalidClient
|
14
6
|
InvalidObject
|
15
7
|
InvalidConnection
|
8
|
+
ConnectionError
|
16
9
|
MissingId
|
17
10
|
InvalidSpec
|
18
11
|
InvalidField
|
12
|
+
InvalidAssociation
|
13
|
+
InvalidAttribute
|
14
|
+
InvalidNestedAttribute
|
19
15
|
AssociationError
|
20
16
|
}
|
21
17
|
|
22
18
|
errors.each do |error|
|
23
|
-
const_set
|
19
|
+
const_set error.to_sym, Class.new(StandardError)
|
24
20
|
end
|
25
21
|
|
26
22
|
end
|
data/lib/restly/middleware.rb
CHANGED
@@ -8,8 +8,21 @@ class Restly::Middleware
|
|
8
8
|
|
9
9
|
def call(env)
|
10
10
|
@env = env
|
11
|
-
|
12
|
-
|
11
|
+
|
12
|
+
Restly::Base.current_token = nil
|
13
|
+
|
14
|
+
token = Restly::Connection.tokenize(Restly::Base.client, self).to_hash
|
15
|
+
|
16
|
+
if token[:access_token].present? && !@env['PATH_INFO'].match(/^\/assets\//)
|
17
|
+
Restly::Base.current_token = token
|
18
|
+
end
|
19
|
+
|
20
|
+
self.app.call(env)
|
21
|
+
|
22
|
+
ensure
|
23
|
+
|
24
|
+
Restly::Base.current_token = nil
|
25
|
+
|
13
26
|
end
|
14
27
|
|
15
28
|
end
|
@@ -15,19 +15,21 @@ module Restly::NestedAttributes
|
|
15
15
|
private
|
16
16
|
|
17
17
|
# One To One Association
|
18
|
-
def assign_nested_attributes_for_one_to_one_resource_association(association_name, attributes
|
19
|
-
|
18
|
+
def assign_nested_attributes_for_one_to_one_resource_association( association_name, attributes )
|
19
|
+
|
20
20
|
association_attributes[association_name] = attributes.delete("#{association_name}_attributes") || {}
|
21
21
|
associated_instance = send(association_name) ||
|
22
22
|
self.class.reflect_on_resource_association(association_name).build(self)
|
23
23
|
associated_instance.attributes = association_attributes
|
24
|
+
|
24
25
|
end
|
25
26
|
|
26
27
|
# Collection Association
|
27
|
-
def assign_nested_attributes_for_collection_resource_association(association_name, attributes_collection
|
28
|
+
def assign_nested_attributes_for_collection_resource_association(association_name, attributes_collection)
|
28
29
|
|
29
30
|
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
|
30
|
-
raise ArgumentError,
|
31
|
+
raise ArgumentError,
|
32
|
+
"Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
|
31
33
|
end
|
32
34
|
|
33
35
|
if attributes_collection.is_a? Hash
|
@@ -46,7 +48,7 @@ module Restly::NestedAttributes
|
|
46
48
|
attributes = attributes.with_indifferent_access
|
47
49
|
if attributes[:id].blank?
|
48
50
|
send(association_name) << association.build(self, attributes.except(:id))
|
49
|
-
elsif existing_record = existing_records.find{ |record| record.id.to_s == attributes['id'].to_s }
|
51
|
+
elsif (existing_record = existing_records.find{ |record| record.id.to_s == attributes['id'].to_s })
|
50
52
|
existing_record.attributes = attributes
|
51
53
|
end
|
52
54
|
end
|
@@ -54,13 +56,28 @@ module Restly::NestedAttributes
|
|
54
56
|
end
|
55
57
|
|
56
58
|
def set_nested_attributes_for_save
|
57
|
-
@attributes = @attributes.
|
58
|
-
|
59
|
-
|
59
|
+
@attributes = @attributes.reduce(HashWithIndifferentAccess.new) do |hash, (key, v)|
|
60
|
+
options = resource_nested_attributes_options[key.to_sym]
|
61
|
+
key = [ options[:write_prefix], key, options[:write_suffix] ].compact.join('_') if options.present?
|
62
|
+
hash[key] = v
|
60
63
|
hash
|
61
64
|
end
|
62
65
|
end
|
63
66
|
|
67
|
+
def nested_attribute_missing(m, *args)
|
68
|
+
if !!(/(?<attr>\w+)_attributes=$/ =~ m.to_s) && (options = resource_nested_attributes_options[attr])
|
69
|
+
send( "assign_nested_attributes_for_#{options[:association_type]}_resource_association", attr, *args )
|
70
|
+
else
|
71
|
+
raise Restly::Error::InvalidNestedAttribute, "Nested Attribute does not exist!"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def method_missing(m, *args, &block)
|
76
|
+
nested_attribute_missing(m, *args)
|
77
|
+
rescue Restly::Error::InvalidNestedAttribute
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
64
81
|
module ClassMethods
|
65
82
|
REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }
|
66
83
|
|
@@ -73,22 +90,11 @@ module Restly::NestedAttributes
|
|
73
90
|
before_save :set_nested_attributes_for_save
|
74
91
|
|
75
92
|
attr_names.each do |association_name|
|
76
|
-
if reflection = reflect_on_resource_association(association_name)
|
77
|
-
reflection.options[:autosave] = true
|
78
|
-
|
79
|
-
resource_nested_attributes_options = self.resource_nested_attributes_options.dup
|
80
|
-
resource_nested_attributes_options[association_name.to_sym] = options
|
81
|
-
self.resource_nested_attributes_options = resource_nested_attributes_options
|
82
93
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
88
|
-
|
89
|
-
define_method "#{association_name}_attributes=" do |attributes|
|
90
|
-
send("assign_nested_attributes_for_#{type}_resource_association", association_name.to_sym, attributes, mass_assignment_options)
|
91
|
-
end
|
94
|
+
if ( reflection = reflect_on_resource_association(association_name) )
|
95
|
+
reflection.options[:autosave] = true
|
96
|
+
options[:association_type] = (reflection.collection? ? :collection : :one_to_one)
|
97
|
+
self.resource_nested_attributes_options[association_name.to_sym] = options
|
92
98
|
|
93
99
|
else
|
94
100
|
raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
|