restly 0.0.1.beta.3 → 0.0.1.beta.4

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.
@@ -25,13 +25,13 @@ module Restly::Base::Instance::Actions
25
25
 
26
26
  def update
27
27
  run_callbacks :update do
28
- set_response(connection.put path_with_format, body: @request_body, params: params)
28
+ set_response(connection.put path_with_format, body: formatted_for_request, params: params)
29
29
  end
30
30
  end
31
31
 
32
32
  def create
33
33
  run_callbacks :create do
34
- set_response(connection.post path_with_format, body: @request_body, params: params)
34
+ set_response(connection.post path_with_format, body: formatted_for_request, params: params)
35
35
  end
36
36
  end
37
37
 
@@ -1,6 +1,14 @@
1
1
  module Restly::Base::Instance::ErrorHandling
2
2
  extend ActiveSupport::Concern
3
3
 
4
+ def append_error(field, error)
5
+ self.errors.add field.to_sym, error
6
+ if /(?<association>\w+)\.(?<attr>.+)/ =~ field && respond_to_association?(association)
7
+ instance_eval(&association.to_sym).append_error(attr, error)
8
+ end
9
+
10
+ end
11
+
4
12
  private
5
13
 
6
14
  def response_has_errors?(response=self.response)
@@ -20,21 +28,23 @@ module Restly::Base::Instance::ErrorHandling
20
28
  case error
21
29
 
22
30
  when Array
23
- error.each { |e| self.errors.add(name.to_sym, e) }
31
+ error.each { |e| append_error name, e }
24
32
 
25
33
  when String
26
- self.errors.add(name.to_sym, error)
34
+ append_error name, error
27
35
 
28
36
  end
37
+
29
38
  end
30
39
 
31
40
  when Array
32
- response_errors.each do |error|
33
- self.errors.add(:base, error)
34
- end
41
+ response_errors.each { |error| append_error :base, error }
35
42
 
36
43
  when String
37
- self.errors.add(:base, response_errors)
44
+ append_error :base, response_errors
45
+
46
+ when NilClass
47
+ append_error :base, connection.status_string(response.status)
38
48
 
39
49
  end
40
50
 
@@ -4,7 +4,7 @@ module Restly::Base::Instance::Persistence
4
4
  return false unless id
5
5
 
6
6
  begin
7
- @response ||= connection.get(path, force: true)
7
+ @response = connection.get(path, force: true) unless @response.status.to_i < 400
8
8
 
9
9
  rescue OAuth2::Error => e
10
10
  @response = e.response
@@ -12,7 +12,7 @@ module Restly::Base::Instance::Persistence
12
12
  end
13
13
 
14
14
  status = @response.status.to_i
15
- status < 300 && status >= 200
15
+ status < 400 && status >= 200
16
16
 
17
17
  end
18
18
 
@@ -2,47 +2,63 @@ module Restly::Base::Instance::WriteCallbacks
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  included do
5
- before_save :format_request
6
- end
5
+ extend ClassMethods
7
6
 
8
- private
9
-
10
- def format_request
11
- @request_body = case format.to_sym
12
- when :json
13
- savable_resource.to_json
14
- when :xml
15
- savable_resource.to_xml
16
- else
17
- savable_resource.to_param
18
- end
19
- end
7
+ class_attribute :request_builders
8
+ self.request_builders = []
9
+
10
+ build_request :attributes
20
11
 
21
- def savable_resource
22
- {resource_name => attributes_with_present_values(writable_attributes)}
23
12
  end
24
13
 
25
- def attributes_with_present_values(attributes=self.attributes)
26
- attributes.as_json.reduce({}) do |hash, (key, val)|
27
- if val.is_a?(Hash)
28
- hash[key] = attributes_with_present_values(val)
29
- elsif val.present? && key.to_sym != :id
30
- hash[key] = val
31
- end
32
- hash
14
+ def formatted_for_request
15
+
16
+ case format.to_sym
17
+ when :json
18
+ built_for_request.to_json
19
+
20
+ when :xml
21
+ built_for_request.to_xml
22
+
23
+ else
24
+ built_for_request.to_param
25
+
33
26
  end
27
+
34
28
  end
35
29
 
36
- def writable_attributes(attributes=self.attributes)
30
+ def built_for_request(resource_key=resource_name)
31
+
32
+ attributes = request_builders.reduce(HashWithIndifferentAccess.new) do |attributes, builder|
33
+ attributes.merge! builder.is_a?(Symbol) ? send(builder) : instance_eval(&builder)
34
+ end
35
+
37
36
  maa = mass_assignment_authorizer(:default)
38
37
 
39
38
  if maa.is_a? ActiveModel::MassAssignmentSecurity::BlackList
40
- attributes.reject{ |key, val| maa.map(&:to_sym).include?(key.to_sym) }
39
+ attributes.except! *maa.map(&:to_sym)
41
40
 
42
41
  elsif maa.is_a? ActiveModel::MassAssignmentSecurity::WhiteList
43
- attributes.select{ |key, val| maa.map(&:to_sym).include?(key.to_sym) }
42
+ attributes.slice! *maa.map(&:to_sym)
43
+
44
+ end
45
+
46
+ if resource_key
47
+ { resource_key.to_sym => attributes.select { |k,v| v.present? } }
48
+ else
49
+ attributes.select { |k,v| v.present? }
50
+ end
44
51
 
52
+ end
53
+
54
+ module ClassMethods
55
+
56
+ private
57
+
58
+ def build_request(symbol=nil, &block)
59
+ self.request_builders << symbol || block
45
60
  end
61
+
46
62
  end
47
63
 
48
64
 
@@ -24,6 +24,7 @@ module Restly::Base::Resource::Finders
24
24
  end
25
25
 
26
26
  def instance_from_response(response)
27
+ raise Restly::Error::RecordNotFound, "Could not find a #{name} at the specified path." unless response.status < 400
27
28
  new(nil, response: response, connection: connection)
28
29
  end
29
30
 
@@ -10,12 +10,12 @@ class Restly::Base::Resource::Specification::Fields < Restly::Proxies::Base
10
10
  end
11
11
 
12
12
  def - field
13
- @removed << field
13
+ @removed += field
14
14
  replace(__getobj__.send __method__, field)
15
15
  end
16
16
 
17
17
  def + field
18
- @added << field
18
+ @added += field
19
19
  replace(__getobj__.send __method__, field)
20
20
  end
21
21
 
@@ -38,7 +38,7 @@ class Restly::Base::Resource::Specification::Fields < Restly::Proxies::Base
38
38
 
39
39
  def reload_specification!
40
40
  from_spec = spec[:attributes] || []
41
- fields = (from_spec - @removed.to_a) + @added.to_a
41
+ fields = (from_spec.map(&:to_sym) - @removed.map(&:to_sym)) + @added.map(&:to_sym)
42
42
  __setobj__ Restly::Base::Fields::FieldSet.new(spec.model, fields)
43
43
  end
44
44
 
@@ -61,10 +61,15 @@ class Restly::Connection < OAuth2::AccessToken
61
61
  }
62
62
  end
63
63
 
64
+ def status_string(int)
65
+ Rack::Utils::HTTP_STATUS_CODES[int.to_i]
66
+ end
67
+
64
68
  alias_method :forced_request, :request
65
69
 
66
70
  def request(verb, path, opts={}, &block)
67
- path = [base_path.gsub(/\/?$/, ''), path.gsub(/^\/?/, '')].join('/')
71
+
72
+ path = [base_path.gsub(/\/?$/, ''), path.gsub(/^\/?/, '')].join('/') unless /#{base_path}/.match path
68
73
 
69
74
  if cache && !opts[:force]
70
75
  request_log("Restly::CacheRequest", path, verb) do
@@ -77,6 +82,8 @@ class Restly::Connection < OAuth2::AccessToken
77
82
  end
78
83
  end
79
84
 
85
+ private
86
+
80
87
  def id_from_path(path)
81
88
  capture = path.match /(?<id>[0-9])\.\w*$/
82
89
  capture[:id] if capture
@@ -123,7 +130,7 @@ class Restly::Connection < OAuth2::AccessToken
123
130
 
124
131
  if response.status >= 500
125
132
  site = URI.parse(client.site)
126
- formatted_path = ["#{site.scheme}://#{site.host}", "#{site.port}", path].join
133
+ formatted_path = ["#{site.scheme}://#{site.host}", ":#{site.port}", path].join
127
134
  raise Restly::Error::ConnectionError, "#{response.status}: #{status_string(response.status)}\nurl: #{formatted_path}"
128
135
  end
129
136
 
@@ -142,8 +149,4 @@ class Restly::Connection < OAuth2::AccessToken
142
149
  ActiveSupport::Notifications.instrument("cache.restly", key: key, name: name, color: color, &block)
143
150
  end
144
151
 
145
- def status_string(int)
146
- Rack::Utils::HTTP_STATUS_CODES[int.to_i]
147
- end
148
-
149
152
  end
@@ -4,7 +4,7 @@ module Restly::EmbeddedAssociations::ClassMethods
4
4
 
5
5
  # Embeds One
6
6
  def embeds_resource(name, options = {})
7
- exclude_field(name) if ancestors.include?(Restly::Base)
7
+ exclude_field(name)
8
8
  self.resource_associations[name] = association = Restly::EmbeddedAssociations::EmbedsOne.new(self, name, options)
9
9
 
10
10
  define_method name do |options={}|
@@ -20,7 +20,7 @@ module Restly::EmbeddedAssociations::ClassMethods
20
20
 
21
21
  # Embeds Many
22
22
  def embeds_resources(name, options = {})
23
- exclude_field(name) if ancestors.include?(Restly::Base)
23
+ exclude_field(name)
24
24
  self.resource_associations[name] = association = Restly::EmbeddedAssociations::EmbedsMany.new(self, name, options)
25
25
 
26
26
  define_method name do |options={}|
@@ -43,6 +43,13 @@ module Restly::EmbeddedAssociations::ClassMethods
43
43
  define_method "#{name}=" do |value|
44
44
  set_association name, value
45
45
  end
46
+
47
+ [:save, :delete, :destroy].each do |method|
48
+ define_method method do
49
+ raise NotImplemented, "Embedded actions have not been implemented."
50
+ end
51
+ end
52
+
46
53
  end
47
54
 
48
55
  end
@@ -6,23 +6,28 @@ module Restly::NestedAttributes
6
6
 
7
7
  included do
8
8
  class_attribute :resource_nested_attributes_options, :instance_writer => false
9
- self.resource_nested_attributes_options = {}
9
+ self.resource_nested_attributes_options = HashWithIndifferentAccess.new
10
10
 
11
11
  inherited do
12
12
  self.resource_nested_attributes_options = resource_nested_attributes_options.dup
13
13
  end
14
14
 
15
+ if ancestors.include?(Restly::Base)
16
+ include Restly::Base::Instance::WriteCallbacks
17
+ build_request :nested_attributes_for_request
18
+ else
19
+ before_save :save_nested_attributes_associations
20
+ end
21
+
15
22
  end
16
23
 
17
24
  private
18
25
 
19
26
  # One To One Association
20
- def assign_nested_attributes_for_one_to_one_resource_association( association_name, attributes )
27
+ def assign_nested_attributes_for_one_to_one_resource_association( association_name, attributes={} )
21
28
 
22
- association_attributes[association_name] = attributes.delete("#{association_name}_attributes") || {}
23
- associated_instance = send(association_name) ||
24
- self.class.reflect_on_resource_association(association_name).build(self)
25
- associated_instance.attributes = association_attributes
29
+ associated_instance = send(association_name) || self.class.reflect_on_resource_association(association_name).build(self)
30
+ associated_instance.attributes = attributes
26
31
 
27
32
  end
28
33
 
@@ -57,15 +62,40 @@ module Restly::NestedAttributes
57
62
 
58
63
  end
59
64
 
60
- def set_nested_attributes_for_save
61
- @attributes = @attributes.reduce(HashWithIndifferentAccess.new) do |hash, (key, v)|
62
- options = resource_nested_attributes_options[key.to_sym]
63
- key = [ options[:write_prefix], key, options[:write_suffix] ].compact.join('_') if options.present?
64
- hash[key] = v
65
+ def nested_attributes_for_request
66
+ resource_nested_attributes_options.reduce(HashWithIndifferentAccess.new) do |hash, (association_name, options)|
67
+ association = self.class.reflect_on_resource_association association_name.to_sym
68
+ next unless association.options[:autosave]
69
+
70
+ associated_object = instance_eval(&association_name.to_sym)
71
+ built_for_request = if associated_object.is_a? Array
72
+ associated_object.map { |instance| instance.built_for_request(nil) if instance.changed? }.compact
73
+ else
74
+ associated_object.built_for_request(nil) if associated_object.changed?
75
+ end
76
+
77
+ key = [ options[:write_prefix], association_name, options[:write_suffix] ].compact.join('_')
78
+ hash[key] = built_for_request if built_for_request.present?
65
79
  hash
66
80
  end
67
81
  end
68
82
 
83
+ def save_nested_attributes_associations
84
+ resource_nested_attributes_options.each do |association_name, options|
85
+ association = self.class.reflect_on_resource_association association_name.to_sym
86
+ next unless association.options[:autosave]
87
+
88
+ associated_object = instance_eval(&association_name.to_sym)
89
+ if associated_object.is_a? Array
90
+ associated_object.each do |instance|
91
+ instance.save && instance.changed?
92
+ end
93
+ else
94
+ associated_object.save if associated_object.changed?
95
+ end
96
+ end
97
+ end
98
+
69
99
  def nested_attribute_missing(m, *args)
70
100
  if !!(matched = ATTR_MATCHER.match m) && (options = resource_nested_attributes_options[(attr = matched[:attr])])
71
101
  send( "assign_nested_attributes_for_#{options[:association_type]}_resource_association", attr, *args )
@@ -80,6 +110,14 @@ module Restly::NestedAttributes
80
110
  super
81
111
  end
82
112
 
113
+ def respond_to_nested_attributes?(m)
114
+ (matched = ATTR_MATCHER.match m) && resource_nested_attributes_options.has_key?(matched[:attr].to_sym)
115
+ end
116
+
117
+ def respond_to?(m, include_private = false)
118
+ respond_to_nested_attributes?(m) || super
119
+ end
120
+
83
121
  module ClassMethods
84
122
  REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }
85
123
 
@@ -89,8 +127,6 @@ module Restly::NestedAttributes
89
127
  options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only, :write_prefix, :write_suffix)
90
128
  options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
91
129
 
92
- before_save :set_nested_attributes_for_save
93
-
94
130
  attr_names.each do |association_name|
95
131
 
96
132
  if ( reflection = reflect_on_resource_association(association_name) )
@@ -1,3 +1,3 @@
1
1
  module Restly
2
- VERSION = "0.0.1.beta.3"
2
+ VERSION = "0.0.1.beta.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.beta.3
4
+ version: 0.0.1.beta.4
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-22 00:00:00.000000000 Z
12
+ date: 2012-11-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: oauth2