restly 0.0.1.alpha.1
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/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/restly/associations/base.rb +35 -0
- data/lib/restly/associations/belongs_to.rb +15 -0
- data/lib/restly/associations/builder.rb +30 -0
- data/lib/restly/associations/embeddable_resources/embeds_many.rb +7 -0
- data/lib/restly/associations/embeddable_resources/embeds_one.rb +7 -0
- data/lib/restly/associations/embeddable_resources.rb +28 -0
- data/lib/restly/associations/has_many.rb +23 -0
- data/lib/restly/associations/has_one.rb +23 -0
- data/lib/restly/associations.rb +90 -0
- data/lib/restly/base/fields.rb +79 -0
- data/lib/restly/base/generic_methods.rb +24 -0
- data/lib/restly/base/includes.rb +53 -0
- data/lib/restly/base/instance/actions.rb +35 -0
- data/lib/restly/base/instance/attributes.rb +88 -0
- data/lib/restly/base/instance/persistence.rb +20 -0
- data/lib/restly/base/instance.rb +76 -0
- data/lib/restly/base/mass_assignment_security.rb +19 -0
- data/lib/restly/base/resource/finders.rb +32 -0
- data/lib/restly/base/resource.rb +19 -0
- data/lib/restly/base/write_callbacks.rb +37 -0
- data/lib/restly/base.rb +82 -0
- data/lib/restly/client.rb +34 -0
- data/lib/restly/collection/pagination.rb +36 -0
- data/lib/restly/collection.rb +47 -0
- data/lib/restly/configuration.rb +43 -0
- data/lib/restly/connection.rb +99 -0
- data/lib/restly/controller_methods.rb +16 -0
- data/lib/restly/error.rb +30 -0
- data/lib/restly/middleware.rb +15 -0
- data/lib/restly/nested_attributes.rb +97 -0
- data/lib/restly/proxies/associations/collection.rb +22 -0
- data/lib/restly/proxies/associations/instance.rb +11 -0
- data/lib/restly/proxies/auth.rb +9 -0
- data/lib/restly/proxies/base.rb +39 -0
- data/lib/restly/proxies/params.rb +8 -0
- data/lib/restly/proxies.rb +18 -0
- data/lib/restly/railtie.rb +9 -0
- data/lib/restly/thread_local.rb +33 -0
- data/lib/restly/version.rb +3 -0
- data/lib/restly.rb +24 -0
- data/restly.gemspec +28 -0
- data/spec/fakewebs.rb +0 -0
- data/spec/helper.rb +31 -0
- data/spec/restly/base_spec.rb +60 -0
- metadata +210 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Jason Waldrip
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Restly
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'restly'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install restly
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Restly::Associations::Base
|
2
|
+
|
3
|
+
attr_reader :name, :association_class, :namespace, :polymorphic, :options
|
4
|
+
|
5
|
+
def initialize(owner, name, options={})
|
6
|
+
@name = name
|
7
|
+
@namespace = options.delete(:namespace) || owner.name.gsub(/::\w+$/, '')
|
8
|
+
@polymorphic = options.delete(:polymorphic)
|
9
|
+
options[:class_name] ||= name.to_s.classify
|
10
|
+
@owner = owner
|
11
|
+
@association_class = [@namespace, options.delete(:class_name)].compact.join('::').constantize
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def collection?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def build(*args)
|
20
|
+
new_instance = @association_class.new(*args)
|
21
|
+
new_instance.write_attribute("#{@owner.resource_name}_id") if @association_class.respond_to?("#{@owner.resource_name}_id") && !self.class.send(:reflect_on_resource_association, :custom_pages).embedded?
|
22
|
+
new_instance
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def authorize(klass, authorization = nil)
|
28
|
+
if (!klass.authorized? && @owner.respond_to?(:authorized?) && @owner.authorized?) || authorization
|
29
|
+
klass.authorize(authorization || @owner.connection)
|
30
|
+
else
|
31
|
+
klass
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Restly::Associations::BelongsTo < Restly::Associations::Base
|
2
|
+
|
3
|
+
def find_with_parent(parent, options)
|
4
|
+
options.reverse_merge!(self.options)
|
5
|
+
association_class = polymorphic ? [@namespace, instance.send("#{name}_type")] : self.association_class
|
6
|
+
association_class = authorize(association_class, options[:authorize])
|
7
|
+
instance = association_class.find(parent.attributes["#{name}_id"])
|
8
|
+
Restly::Proxies::Associations::Instance.new(instance, parent)
|
9
|
+
end
|
10
|
+
|
11
|
+
def collection?
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Restly::Associations::Builder
|
2
|
+
|
3
|
+
def build(relationship, opts)
|
4
|
+
|
5
|
+
# Base Model
|
6
|
+
model = opts[:class_name] || relationship.to_s.singularize.camelize
|
7
|
+
|
8
|
+
# Namespace
|
9
|
+
namespace = opts[:namespace] || self.class.name.gsub(/::\w+$/, '')
|
10
|
+
model = [namespace, model].compact.join('::')
|
11
|
+
|
12
|
+
# Polymorphic Relationships
|
13
|
+
if opts[:polymorphic]
|
14
|
+
polymorphic_type = send(:"#{resource}_type")
|
15
|
+
model = [namespace, polymorphic_type].compact.join('::')
|
16
|
+
end
|
17
|
+
|
18
|
+
# Constantize
|
19
|
+
model = model.constantize
|
20
|
+
|
21
|
+
# Auto-authorization, fail with error!
|
22
|
+
if (!model.authorized? && self.respond_to?(:authorized?) && self.authorized?) || (opts[:authorize])
|
23
|
+
model.authorize(opts[:authorize] || self.connection)
|
24
|
+
else
|
25
|
+
model
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Restly::Associations::EmbeddableResources
|
2
|
+
extend ActiveSupport::Autoload
|
3
|
+
|
4
|
+
autoload :EmbeddedIn
|
5
|
+
autoload :EmbedsMany
|
6
|
+
autoload :EmbedsOne
|
7
|
+
|
8
|
+
# Embeds One
|
9
|
+
def embeds_resource(name, options = {})
|
10
|
+
exclude_field(name) if ancestors.include?(Restly::Base)
|
11
|
+
self.resource_associations[name] = association = EmbedsOne.new(self, name, options)
|
12
|
+
|
13
|
+
define_method name do
|
14
|
+
association.build(attributes[name])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Embeds Many
|
19
|
+
def embeds_resources(name, options = {})
|
20
|
+
exclude_field(name) if ancestors.include?(Restly::Base)
|
21
|
+
self.resource_associations[name] = association = EmbedsMany.new(self, name, options)
|
22
|
+
|
23
|
+
define_method name do
|
24
|
+
( self.attributes[name] || [] ).map!{ |i| association.build(i) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Restly::Associations::HasMany < Restly::Associations::Base
|
2
|
+
|
3
|
+
attr_reader :joiner
|
4
|
+
|
5
|
+
def initialize(owner, name, options={})
|
6
|
+
@joiner = options.delete(:through)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def scope_with_parent(parent, options)
|
11
|
+
options.reverse_merge!(self.options)
|
12
|
+
association_class = polymorphic ? [@namespace, instance.send("#{name}_type")] : self.association_class
|
13
|
+
association_class = authorize(association_class, options[:authorize])
|
14
|
+
collection = association_class.with_params("with_#{parent.resource_name}_id" => parent.id).all # (parent.attributes["#{name}_id"])
|
15
|
+
collection.select{|i| i.attributes["#{parent.resource_name}_id"] == parent.id }
|
16
|
+
Restly::Proxies::Associations::Collection.new(collection, parent)
|
17
|
+
end
|
18
|
+
|
19
|
+
def collection?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Restly::Associations::HasOne < Restly::Associations::Base
|
2
|
+
|
3
|
+
attr_reader :joiner
|
4
|
+
|
5
|
+
def initialize(owner, name, options={})
|
6
|
+
@joiner = options.delete(:through)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def scope_with_parent(parent, options)
|
11
|
+
options.reverse_merge!(self.options)
|
12
|
+
association_class = polymorphic ? [@namespace, instance.send("#{name}_type")] : self.association_class
|
13
|
+
association_class = authorize(association_class, options[:authorize])
|
14
|
+
collection = association_class.with_params("with_#{parent.resource_name}_id" => parent.id).all(parent.attributes["#{name}_id"])
|
15
|
+
collection.select{|i| i.attributes["#{parent.resource_name}_id"] == parent.id }.first
|
16
|
+
Restly::Proxies::Associations::Instance.new(instance, parent)
|
17
|
+
end
|
18
|
+
|
19
|
+
def collection?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Restly::Associations
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
extend ActiveSupport::Autoload
|
4
|
+
|
5
|
+
autoload :Base
|
6
|
+
autoload :BelongsTo
|
7
|
+
autoload :HasMany
|
8
|
+
autoload :HasManyThrough
|
9
|
+
autoload :HasOne
|
10
|
+
autoload :HasOneThrough
|
11
|
+
autoload :EmbeddableResources
|
12
|
+
|
13
|
+
class AssociationHash < HashWithIndifferentAccess
|
14
|
+
end
|
15
|
+
|
16
|
+
included do
|
17
|
+
|
18
|
+
extend EmbeddableResources if self == Restly::Base
|
19
|
+
include Restly::NestedAttributes
|
20
|
+
|
21
|
+
delegate :resource_name, to: :klass
|
22
|
+
class_attribute :resource_associations, instance_reader: false, instance_writer: false
|
23
|
+
self.resource_associations = AssociationHash.new
|
24
|
+
|
25
|
+
inherited do
|
26
|
+
self.resource_associations = resource_associations.dup
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def stub_associations_from_response(response=self.response)
|
32
|
+
parsed = response.parsed || {}
|
33
|
+
parsed = parsed[resource_name] if parsed.is_a?(Hash) && parsed[resource_name]
|
34
|
+
associations = parsed.select{ |i| klass.reflect_on_all_resource_associations.keys.include?(i) }
|
35
|
+
associations.each do |relationship|
|
36
|
+
relationship.collection?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def klass
|
41
|
+
self.class
|
42
|
+
end
|
43
|
+
|
44
|
+
module ClassMethods
|
45
|
+
|
46
|
+
def resource_name
|
47
|
+
name.gsub(/.*::/,'').underscore
|
48
|
+
end
|
49
|
+
|
50
|
+
def reflect_on_all_resource_associations
|
51
|
+
resource_associations
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Belongs to
|
57
|
+
def belongs_to_resource(name, options = {})
|
58
|
+
exclude_field(name) if ancestors.include?(Restly::Base)
|
59
|
+
self.resource_associations[name] = association = BelongsTo.new(self, name, options)
|
60
|
+
define_method name do |options={}|
|
61
|
+
association.find_with_parent(self, options)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Has One
|
66
|
+
def has_one_resource(name, options = {})
|
67
|
+
exclude_field(name) if ancestors.include?(Restly::Base)
|
68
|
+
self.resource_associations[name] = association = HasOne.new(self, name, options)
|
69
|
+
define_method name do |options={}|
|
70
|
+
association.scope_with_parent(self, options)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Has One
|
75
|
+
def has_many_resources(name, options = {})
|
76
|
+
exclude_field(name) if ancestors.include?(Restly::Base)
|
77
|
+
self.resource_associations[name] = association = HasMany.new(self, name, options)
|
78
|
+
define_method name do |options={}|
|
79
|
+
association.scope_with_parent(self, options)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def reflect_on_resource_association(association_name)
|
85
|
+
reflect_on_all_resource_associations[association_name]
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Restly::Base::Fields
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
extend SharedMethods
|
6
|
+
include SharedMethods
|
7
|
+
extend ClassMethods
|
8
|
+
|
9
|
+
class_attribute :fields
|
10
|
+
self.fields = FieldSet.new
|
11
|
+
field :id
|
12
|
+
|
13
|
+
inherited do
|
14
|
+
self.fields = fields.dup
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
module SharedMethods
|
20
|
+
|
21
|
+
def set_field(attr)
|
22
|
+
base = self.is_a?(Class) ? self : singleton_class
|
23
|
+
unless base.send :instance_method_already_implemented?, attr
|
24
|
+
base.send :define_attribute_method, attr
|
25
|
+
self.fields += [attr]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
|
33
|
+
def field(attr)
|
34
|
+
if attr.is_a?(Hash) && attr[:from_spec]
|
35
|
+
before_initialize do
|
36
|
+
spec[:attributes].each{ |attr| set_field(attr) }
|
37
|
+
end
|
38
|
+
elsif attr.is_a?(Symbol) || attr.is_a?(String)
|
39
|
+
set_field(attr)
|
40
|
+
else
|
41
|
+
raise Restly::Error::InvalidField, "field must be a symbol or string."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def exclude_field(name)
|
46
|
+
|
47
|
+
# Remove from the class
|
48
|
+
self.fields -= [name]
|
49
|
+
|
50
|
+
# Remove from the instance
|
51
|
+
before_initialize do
|
52
|
+
self.fields -= [name]
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
class FieldSet < Set
|
60
|
+
|
61
|
+
def include?(value)
|
62
|
+
super(value.to_sym)
|
63
|
+
end
|
64
|
+
|
65
|
+
def <<(value)
|
66
|
+
super(value.to_sym)
|
67
|
+
end
|
68
|
+
|
69
|
+
def +(other_arry)
|
70
|
+
super(other_arry.map(&:to_sym))
|
71
|
+
end
|
72
|
+
|
73
|
+
def -(other_arry)
|
74
|
+
super(other_arry.map(&:to_sym))
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Restly::Base::GenericMethods
|
2
|
+
|
3
|
+
def authorize(token_object)
|
4
|
+
Restly::Proxies::Auth.new self, token_object
|
5
|
+
end
|
6
|
+
|
7
|
+
def with_params(params={})
|
8
|
+
Restly::Proxies::Params.new self, params
|
9
|
+
end
|
10
|
+
|
11
|
+
def path_with_format(*args)
|
12
|
+
path = [self.path, args].flatten.compact.join('/')
|
13
|
+
[path, format].compact.join('.') if path
|
14
|
+
end
|
15
|
+
|
16
|
+
def format
|
17
|
+
client.format
|
18
|
+
end
|
19
|
+
|
20
|
+
def authorized?
|
21
|
+
connection.token.present?
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Restly::Base::Includes
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
extend ClassMethods
|
6
|
+
|
7
|
+
class_attribute :inherited_callbacks
|
8
|
+
self.inherited_callbacks = []
|
9
|
+
|
10
|
+
inherited do
|
11
|
+
self.inherited_callbacks = inherited_callbacks
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
|
18
|
+
# Delegate stuff to client
|
19
|
+
delegate :site, :site=, :format, :format=, to: :client
|
20
|
+
|
21
|
+
def client
|
22
|
+
@client ||= Restly::Client.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def connection
|
26
|
+
connection = @connection || Restly::Connection.tokenize(client, current_token)
|
27
|
+
connection.cache ||= cache
|
28
|
+
connection.cache_options ||= cache_options
|
29
|
+
connection
|
30
|
+
end
|
31
|
+
|
32
|
+
def connection=(connection)
|
33
|
+
raise InvalidConnection, "#{connection} is not a valid Restly::Connection" unless connection.is_a?(Restly::Connection)
|
34
|
+
@connection = connection
|
35
|
+
end
|
36
|
+
|
37
|
+
def param_key
|
38
|
+
resource_name
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def inherited(subclass = nil, &block)
|
44
|
+
self.inherited_callbacks << block and return if block_given?
|
45
|
+
|
46
|
+
inherited_callbacks.each do |call_block|
|
47
|
+
subclass.class_eval(&call_block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Restly::Base::Instance::Actions
|
2
|
+
|
3
|
+
def save
|
4
|
+
run_callbacks :save do
|
5
|
+
@previously_changed = changes
|
6
|
+
@changed_attributes.clear
|
7
|
+
new_record? ? create : update
|
8
|
+
end
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def delete
|
13
|
+
run_callbacks :delete do
|
14
|
+
response = connection.delete(path_with_format, params: params)
|
15
|
+
false
|
16
|
+
freeze
|
17
|
+
end
|
18
|
+
response.status < 300
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def update
|
24
|
+
run_callbacks :update do
|
25
|
+
set_attributes_from_response(connection.put path_with_format, body: @request_body, params: params)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def create
|
30
|
+
run_callbacks :create do
|
31
|
+
set_attributes_from_response(connection.post path_with_format, body: @request_body, params: params)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Restly::Base::Instance::Attributes
|
2
|
+
|
3
|
+
def update_attributes(attributes)
|
4
|
+
self.attributes = attributes
|
5
|
+
save
|
6
|
+
end
|
7
|
+
|
8
|
+
def attributes=(attributes)
|
9
|
+
attributes.each do |k, v|
|
10
|
+
write_attribute k, v
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def attributes
|
15
|
+
nil_values = fields.inject({}) do |hash, key|
|
16
|
+
hash[key] = nil
|
17
|
+
hash
|
18
|
+
end
|
19
|
+
@attributes.reverse_merge!(nil_values)
|
20
|
+
end
|
21
|
+
|
22
|
+
def write_attribute(attr, val)
|
23
|
+
if fields.include?(attr)
|
24
|
+
send("#{attr}_will_change!".to_sym) unless val == @attributes[attr.to_sym] || !@loaded
|
25
|
+
@attributes[attr.to_sym] = val
|
26
|
+
else
|
27
|
+
puts "WARNING: Attribute `#{attr}` not written. ".colorize(:yellow) +
|
28
|
+
"To fix this add the following the the model. -- field :#{attr}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_attribute(attr)
|
33
|
+
raise NoMethodError, "undefined method #{attr} for #{klass}" unless fields.include?(attr)
|
34
|
+
attributes[attr.to_sym]
|
35
|
+
end
|
36
|
+
|
37
|
+
alias :attribute :read_attribute
|
38
|
+
|
39
|
+
def inspect
|
40
|
+
inspection = if @attributes
|
41
|
+
fields.collect { |name|
|
42
|
+
"#{name}: #{attribute_for_inspect(name)}"
|
43
|
+
}.compact.join(", ")
|
44
|
+
else
|
45
|
+
"not initialized"
|
46
|
+
end
|
47
|
+
"#<#{self.class} #{inspection}>"
|
48
|
+
end
|
49
|
+
|
50
|
+
def has_attribute?(attr)
|
51
|
+
attribute(attr)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def attribute_for_inspect(attr_name)
|
57
|
+
value = attribute(attr_name)
|
58
|
+
if value.is_a?(String) && value.length > 50
|
59
|
+
"#{value[0..50]}...".inspect
|
60
|
+
else
|
61
|
+
value.inspect
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_attributes_from_response(response=self.response)
|
66
|
+
parsed = response.parsed || {}
|
67
|
+
parsed = parsed[resource_name] if parsed.is_a?(Hash) && parsed[resource_name]
|
68
|
+
self.attributes = parsed
|
69
|
+
end
|
70
|
+
|
71
|
+
def method_missing(m, *args, &block)
|
72
|
+
if !!(/(?<attr>\w+)(?<setter>=)?$/ =~ m.to_s) && fields.include?(m)
|
73
|
+
case !!setter
|
74
|
+
when true
|
75
|
+
write_attribute(m, *args)
|
76
|
+
when false
|
77
|
+
read_attribute(m)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
super(m, *args, &block)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def respond_to_missing?(method_name, include_private = false)
|
85
|
+
!!(/(?<attr>\w+)=?$/ =~ method_name.to_s) && fields.include?(method_name)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Restly::Base::Instance::Persistence
|
2
|
+
|
3
|
+
def exists?
|
4
|
+
status = @response.try(:status).to_i
|
5
|
+
status < 300 && status >= 200
|
6
|
+
end
|
7
|
+
|
8
|
+
def persisted?
|
9
|
+
exists? && !changed?
|
10
|
+
end
|
11
|
+
|
12
|
+
def new_record?
|
13
|
+
!exists?
|
14
|
+
end
|
15
|
+
|
16
|
+
def reload!
|
17
|
+
self.class.send :instance_from_response, connection.get(path, force: true)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|