storage_room 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/README.rdoc +10 -4
- data/Rakefile +3 -1
- data/TODO +7 -2
- data/VERSION +1 -1
- data/examples/authentication.rb +5 -3
- data/examples/create_entry.rb +8 -5
- data/examples/destroy_entry.rb +2 -2
- data/examples/get_collections.rb +2 -2
- data/examples/import_csv.rb +4 -4
- data/examples/search_entries.rb +2 -2
- data/examples/update_entry.rb +2 -2
- data/lib/console.rb +12 -0
- data/lib/storage_room.rb +73 -34
- data/lib/storage_room/accessors.rb +190 -0
- data/lib/storage_room/array.rb +3 -23
- data/lib/storage_room/embedded.rb +1 -1
- data/lib/storage_room/embeddeds/field.rb +28 -0
- data/lib/storage_room/embeddeds/fields/association_field.rb +17 -0
- data/lib/storage_room/embeddeds/fields/associations/many_association_field.rb +9 -0
- data/lib/storage_room/embeddeds/fields/associations/one_association_field.rb +9 -0
- data/lib/storage_room/embeddeds/fields/atomic/boolean_field.rb +6 -0
- data/lib/storage_room/embeddeds/fields/atomic/date_field.rb +6 -0
- data/lib/storage_room/embeddeds/fields/atomic/float_field.rb +6 -0
- data/lib/storage_room/embeddeds/fields/atomic/integer_field.rb +6 -0
- data/lib/storage_room/embeddeds/fields/atomic/string_field.rb +6 -0
- data/lib/storage_room/embeddeds/fields/atomic/time_field.rb +6 -0
- data/lib/storage_room/embeddeds/fields/atomic_field.rb +15 -0
- data/lib/storage_room/embeddeds/fields/compound/attachment_field.rb +6 -0
- data/lib/storage_room/embeddeds/fields/compound/file_field.rb +6 -0
- data/lib/storage_room/embeddeds/fields/compound/image_field.rb +6 -0
- data/lib/storage_room/embeddeds/fields/compound/location_field.rb +6 -0
- data/lib/storage_room/embeddeds/fields/compound_field.rb +10 -0
- data/lib/storage_room/embeddeds/file.rb +1 -1
- data/lib/storage_room/embeddeds/image.rb +6 -0
- data/lib/storage_room/embeddeds/location.rb +2 -1
- data/lib/storage_room/extensions/const_defined.rb +12 -0
- data/lib/storage_room/identity_map.rb +117 -0
- data/lib/storage_room/model.rb +19 -23
- data/lib/storage_room/models/collection.rb +76 -11
- data/lib/storage_room/models/entry.rb +6 -32
- data/lib/storage_room/plugins.rb +22 -0
- data/lib/storage_room/proxy.rb +49 -0
- data/lib/storage_room/{base.rb → resource.rb} +13 -36
- data/spec/fixtures/collection.json +0 -16
- data/spec/spec_helper.rb +1 -0
- data/spec/storage_room/accessors_spec.rb +107 -0
- data/spec/storage_room/array_spec.rb +13 -9
- data/spec/storage_room/embedded_spec.rb +5 -1
- data/spec/storage_room/embeddeds/field_spec.rb +25 -0
- data/spec/storage_room/embeddeds/fields/association_field_spec.rb +29 -0
- data/spec/storage_room/embeddeds/fields/associations/many_association_field_spec.rb +21 -0
- data/spec/storage_room/embeddeds/fields/associations/one_association_field_spec.rb +19 -0
- data/spec/storage_room/embeddeds/fields/atomic/boolean_field_spec.rb +5 -0
- data/spec/storage_room/embeddeds/fields/atomic/date_field_spec.rb +5 -0
- data/spec/storage_room/embeddeds/fields/atomic/float_field_spec.rb +5 -0
- data/spec/storage_room/embeddeds/fields/atomic/integer_field_spec.rb +5 -0
- data/spec/storage_room/embeddeds/fields/atomic/string_field_spec.rb +5 -0
- data/spec/storage_room/embeddeds/fields/atomic/time_field_spec.rb +5 -0
- data/spec/storage_room/embeddeds/fields/atomic_field_spec.rb +27 -0
- data/spec/storage_room/embeddeds/fields/compound/attachment_field_spec.rb +5 -0
- data/spec/storage_room/embeddeds/fields/compound/file_field_spec.rb +5 -0
- data/spec/storage_room/embeddeds/fields/compound/image_field_spec.rb +5 -0
- data/spec/storage_room/embeddeds/fields/compound/location_field_spec.rb +5 -0
- data/spec/storage_room/embeddeds/fields/compound_field_spec.rb +17 -0
- data/spec/storage_room/embeddeds/location_spec.rb +13 -1
- data/spec/storage_room/identity_map_spec.rb +53 -0
- data/spec/storage_room/model_spec.rb +70 -50
- data/spec/storage_room/models/collection_spec.rb +57 -14
- data/spec/storage_room/models/entry_spec.rb +16 -20
- data/spec/storage_room/proxy_spec.rb +58 -0
- data/spec/storage_room/resource_spec.rb +98 -0
- data/spec/storage_room_spec.rb +45 -9
- data/storage_room.gemspec +48 -9
- data/tasks/storage_room.rake +3 -0
- metadata +49 -10
- data/lib/storage_room/attributes.rb +0 -46
- data/lib/storage_room/field.rb +0 -8
- data/spec/storage_room/attributes_spec.rb +0 -82
- data/spec/storage_room/base_spec.rb +0 -118
- data/spec/storage_room/field_spec.rb +0 -5
data/History.txt
CHANGED
data/README.rdoc
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
== StorageRoom Gem
|
2
2
|
|
3
|
-
This gem provides read and write access to the StorageRoom API (http://storageroomapp.com).
|
3
|
+
This gem provides read and write access to the StorageRoom API (http://storageroomapp.com).
|
4
|
+
|
5
|
+
== Main Features
|
6
|
+
|
7
|
+
* ActiveRecord/ActiveModel like interface.
|
8
|
+
* Automatic creation of Entry Classes from a Collection, you don't have to configure anything
|
9
|
+
* Supports lazy-loading of associations (e.g. post.category will fetch a category transparently if it has not yet been loaded)
|
10
|
+
* Supports caching through an identity map, so that Resources don't have to be loaded multiple times
|
4
11
|
|
5
12
|
|
6
13
|
== Installation
|
@@ -17,10 +24,9 @@ This is a walkthrough with all steps you need to setup a devise entry, including
|
|
17
24
|
StorageRoom.authenticate(YOUR_ACCOUNT_ID, YOUR_APPLICATION_API_KEY)
|
18
25
|
collection = StorageRoom::Collection.find('4ddaf68b4d085d374a000003')
|
19
26
|
|
20
|
-
|
21
|
-
entry = entries.resources.first
|
27
|
+
entry = collection.entries.resources.first # collection.entries contains meta information, the resources key contains the returned objects
|
22
28
|
|
23
|
-
entry
|
29
|
+
entry.name = 'Foobar'
|
24
30
|
|
25
31
|
if entry.save
|
26
32
|
puts "Entry saved."
|
data/Rakefile
CHANGED
@@ -12,7 +12,7 @@ begin
|
|
12
12
|
gem.authors = ["Sascha Konietzke"]
|
13
13
|
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
14
|
gem.add_development_dependency "webmock"
|
15
|
-
|
15
|
+
|
16
16
|
gem.add_dependency 'httparty', '>= 0.6.1'
|
17
17
|
gem.add_dependency 'activesupport', '>= 3.0.0'
|
18
18
|
|
@@ -45,3 +45,5 @@ Rake::RDocTask.new do |rdoc|
|
|
45
45
|
rdoc.rdoc_files.include('README*')
|
46
46
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
47
|
end
|
48
|
+
|
49
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
data/TODO
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/examples/authentication.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../lib/storage_room'
|
2
2
|
|
3
|
-
ACCOUNT_ID = '
|
4
|
-
APPLICATION_API_KEY = '
|
3
|
+
ACCOUNT_ID = '4dda7761b65245fde1000051' # your account id
|
4
|
+
APPLICATION_API_KEY = 'kCWTmS1wxYnxzJyteuIn' # your application's API key with read/write access
|
5
5
|
|
6
|
-
StorageRoom.authenticate(ACCOUNT_ID, APPLICATION_API_KEY)
|
6
|
+
StorageRoom.authenticate(ACCOUNT_ID, APPLICATION_API_KEY)
|
7
|
+
|
8
|
+
StorageRoom.server = "api.lvh.me:3000"
|
data/examples/create_entry.rb
CHANGED
@@ -2,13 +2,16 @@
|
|
2
2
|
|
3
3
|
require File.dirname(__FILE__) + '/authentication'
|
4
4
|
|
5
|
+
|
5
6
|
# fetch the collection first
|
6
|
-
|
7
|
+
guidebook_collection = StorageRoom::Collection.find('4dda7761b65245fde100005d')
|
8
|
+
category_collection = StorageRoom::Collection.find('4dda7761b65245fde100001a')
|
7
9
|
|
8
|
-
|
10
|
+
category = category_collection.entries.resources.first # find the first category
|
11
|
+
guidebook = guidebook_collection.entry_class.new(:title => 'Bar', :price => 2.23, :category => category)
|
9
12
|
|
10
|
-
if
|
11
|
-
puts "Entry saved"
|
13
|
+
if guidebook.save # save the guidebook with the associated category
|
14
|
+
puts "Guidebook Entry saved (#{guidebook[:@url]})"
|
12
15
|
else
|
13
|
-
puts "
|
16
|
+
puts "Guidebook could not be saved: #{entry2.errors.join(', ')}"
|
14
17
|
end
|
data/examples/destroy_entry.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
require File.dirname(__FILE__) + '/authentication'
|
4
4
|
|
5
|
-
collection = StorageRoom::Collection.find('
|
5
|
+
collection = StorageRoom::Collection.find('4dda7761b65245fde100005d')
|
6
6
|
|
7
7
|
entry = collection.entries.resources.first
|
8
8
|
|
9
9
|
entry.destroy
|
10
10
|
|
11
|
-
puts "Destroyed #{entry
|
11
|
+
puts "Destroyed #{entry.title}"
|
data/examples/get_collections.rb
CHANGED
@@ -6,6 +6,6 @@ collections = StorageRoom::Collection.all
|
|
6
6
|
|
7
7
|
puts "Collections:"
|
8
8
|
|
9
|
-
collections.resources.each do |collection|
|
10
|
-
puts "- #{collection
|
9
|
+
collections.resources.each do |collection| # The array returned by Collection.all contains all the items in the resources key
|
10
|
+
puts "- #{collection.name}"
|
11
11
|
end
|
data/examples/import_csv.rb
CHANGED
@@ -8,14 +8,14 @@ lines = CSV.read(File.dirname(__FILE__) + '/guidebooks.csv') # Exported an Excel
|
|
8
8
|
|
9
9
|
lines.slice!(0) # remove header line
|
10
10
|
|
11
|
-
collection = StorageRoom::Collection.find('
|
12
|
-
|
11
|
+
collection = StorageRoom::Collection.find('4dda7761b65245fde100005d')
|
12
|
+
Guidebook = collection.entry_class
|
13
13
|
|
14
14
|
lines.each do |row|
|
15
|
-
guidebook =
|
15
|
+
guidebook = Guidebook.new(:title => row[0], :price => row[1].to_f)
|
16
16
|
|
17
17
|
if guidebook.save
|
18
|
-
puts "Guidebook saved: #{guidebook
|
18
|
+
puts "Guidebook saved: #{guidebook.title}, #{guidebook.price}"
|
19
19
|
else
|
20
20
|
puts "Guidebook could not be saved: #{guidebook.errors.join(', ')}"
|
21
21
|
end
|
data/examples/search_entries.rb
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
require File.dirname(__FILE__) + '/authentication'
|
4
4
|
|
5
|
-
collection = StorageRoom::Collection.find('
|
5
|
+
collection = StorageRoom::Collection.find('4dda7761b65245fde100005d')
|
6
6
|
|
7
7
|
entries = collection.entry_class.search(:title => 'Hitchhikers Guide to the Galaxy')
|
8
8
|
|
9
9
|
puts "Entries with title 'Hitchhikers Guide to the Galaxy':"
|
10
10
|
|
11
11
|
entries.resources.each do |entry|
|
12
|
-
puts "- #{entry
|
12
|
+
puts "- #{entry.title} : #{entry[:@url]}"
|
13
13
|
end
|
data/examples/update_entry.rb
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
require File.dirname(__FILE__) + '/authentication'
|
4
4
|
|
5
|
-
collection = StorageRoom::Collection.find('
|
5
|
+
collection = StorageRoom::Collection.find('4dda7761b65245fde100005d')
|
6
6
|
|
7
7
|
entry = collection.entries.resources.first
|
8
8
|
|
9
|
-
entry
|
9
|
+
entry.title = 'Foobar'
|
10
10
|
|
11
11
|
if entry.save
|
12
12
|
puts "Entry saved"
|
data/lib/console.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'storage_room'
|
2
|
+
|
3
|
+
StorageRoom.debug = true
|
4
|
+
|
5
|
+
# Optionally have an init file in your home directory
|
6
|
+
file = File.expand_path('~/.storage_room_gem.rb')
|
7
|
+
|
8
|
+
begin
|
9
|
+
require file
|
10
|
+
rescue LoadError
|
11
|
+
StorageRoom.log("Could not load init file: #{file}")
|
12
|
+
end
|
data/lib/storage_room.rb
CHANGED
@@ -5,38 +5,62 @@ begin; require 'rubygems'; rescue LoadError; end
|
|
5
5
|
|
6
6
|
require 'httparty'
|
7
7
|
require 'active_support/all'
|
8
|
+
require 'storage_room/extensions/const_defined'
|
8
9
|
|
10
|
+
require 'storage_room/plugins'
|
9
11
|
|
10
|
-
module StorageRoom
|
11
|
-
class AbstractMethod
|
12
|
-
class RequestFailed
|
12
|
+
module StorageRoom
|
13
|
+
class AbstractMethod < RuntimeError; end
|
14
|
+
class RequestFailed < RuntimeError; end
|
15
|
+
class ResourceNotLoaded < RuntimeError; end
|
16
|
+
class ClassNotFound < RuntimeError; end
|
13
17
|
|
14
|
-
autoload :
|
18
|
+
autoload :Plugins, 'storage_room/plugins'
|
19
|
+
autoload :Accessors, 'storage_room/accessors'
|
20
|
+
autoload :Proxy, 'storage_room/proxy'
|
21
|
+
autoload :IdentityMap, 'storage_room/identity_map'
|
15
22
|
|
16
|
-
autoload :
|
17
|
-
autoload :Model,
|
18
|
-
autoload :Array,
|
19
|
-
|
20
|
-
autoload :Embedded,
|
23
|
+
autoload :Resource, 'storage_room/resource'
|
24
|
+
autoload :Model, 'storage_room/model'
|
25
|
+
autoload :Array, 'storage_room/array'
|
26
|
+
|
27
|
+
autoload :Embedded, 'storage_room/embedded'
|
28
|
+
|
29
|
+
autoload :Collection, 'storage_room/models/collection'
|
30
|
+
autoload :Entry, 'storage_room/models/entry'
|
31
|
+
|
32
|
+
autoload :Field, 'storage_room/embeddeds/field'
|
33
|
+
|
34
|
+
autoload :AtomicField, 'storage_room/embeddeds/fields/atomic_field'
|
35
|
+
autoload :StringField, 'storage_room/embeddeds/fields/atomic/string_field'
|
36
|
+
autoload :TimeField, 'storage_room/embeddeds/fields/atomic/time_field'
|
37
|
+
autoload :IntegerField, 'storage_room/embeddeds/fields/atomic/integer_field'
|
38
|
+
autoload :FloatField, 'storage_room/embeddeds/fields/atomic/float_field'
|
39
|
+
autoload :DateField, 'storage_room/embeddeds/fields/atomic/date_field'
|
40
|
+
autoload :BooleanField, 'storage_room/embeddeds/fields/atomic/boolean_field'
|
21
41
|
|
22
|
-
autoload :
|
23
|
-
autoload :
|
42
|
+
autoload :CompoundField, 'storage_room/embeddeds/fields/compound_field'
|
43
|
+
autoload :AttachmentField, 'storage_room/embeddeds/fields/compound/attachment_field'
|
44
|
+
autoload :FileField, 'storage_room/embeddeds/fields/compound/file_field'
|
45
|
+
autoload :ImageField, 'storage_room/embeddeds/fields/compound/image_field'
|
46
|
+
autoload :LocationField, 'storage_room/embeddeds/fields/compound/location_field'
|
24
47
|
|
25
|
-
autoload :
|
26
|
-
autoload :
|
48
|
+
autoload :AssociationField, 'storage_room/embeddeds/fields/association_field'
|
49
|
+
autoload :OneAssociationField, 'storage_room/embeddeds/fields/associations/one_association_field'
|
50
|
+
autoload :ManyAssociationField, 'storage_room/embeddeds/fields/associations/many_association_field'
|
51
|
+
|
52
|
+
autoload :File, 'storage_room/embeddeds/file'
|
53
|
+
autoload :Image, 'storage_room/embeddeds/image'
|
54
|
+
autoload :Location, 'storage_room/embeddeds/location'
|
27
55
|
|
28
56
|
|
29
57
|
class << self
|
30
|
-
attr_reader :api_key
|
31
|
-
|
32
|
-
attr_reader :account_id
|
33
|
-
attr_reader :ssl
|
34
|
-
attr_reader :proxy_server
|
35
|
-
attr_reader :proxy_port
|
58
|
+
attr_reader :api_key, :user_agent, :account_id, :ssl, :proxy_server, :proxy_port
|
59
|
+
attr_accessor :debug
|
36
60
|
|
37
61
|
# Authenticate once before making any requests with your account id and the application's api key
|
38
62
|
def authenticate(account_id, api_key)
|
39
|
-
|
63
|
+
Resource.basic_auth(api_key, 'X')
|
40
64
|
@api_key = api_key
|
41
65
|
@account_id = account_id
|
42
66
|
update_uri
|
@@ -44,7 +68,7 @@ module StorageRoom
|
|
44
68
|
|
45
69
|
# Change the user agent that is sent with all requests
|
46
70
|
def user_agent=(agent)
|
47
|
-
|
71
|
+
Resource.headers.merge!('User-Agent' => agent)
|
48
72
|
@user_agent = agent
|
49
73
|
end
|
50
74
|
|
@@ -58,6 +82,20 @@ module StorageRoom
|
|
58
82
|
@server || 'api.storageroomapp.com'
|
59
83
|
end
|
60
84
|
|
85
|
+
# Hash of all mappings from an Entry's @type to a local Ruby class, if a mapping doesn't exist a class name will be created automatically
|
86
|
+
def entry_class_mappings
|
87
|
+
@entry_class_mappings ||= {}
|
88
|
+
end
|
89
|
+
|
90
|
+
# Add a new mapping
|
91
|
+
def add_entry_class_mapping(name, class_name)
|
92
|
+
self.entry_class_mappings[name] = class_name.to_s
|
93
|
+
end
|
94
|
+
|
95
|
+
def entry_class_for_name(name) #:nodoc:
|
96
|
+
self.entry_class_mappings[name] || name.gsub(/\s+/, "").classify
|
97
|
+
end
|
98
|
+
|
61
99
|
# Requests are made with SSL
|
62
100
|
def ssl=(ssl)
|
63
101
|
@ssl = ssl
|
@@ -68,32 +106,33 @@ module StorageRoom
|
|
68
106
|
def http_proxy(server, port)
|
69
107
|
@proxy_server = server
|
70
108
|
@proxy_port = port
|
71
|
-
|
109
|
+
Resource.http_proxy(server, port)
|
72
110
|
end
|
73
111
|
|
74
112
|
# Return a Ruby class for a StorageRoom type
|
75
|
-
def class_for_name(name)
|
76
|
-
|
77
|
-
|
78
|
-
if
|
113
|
+
def class_for_name(name)
|
114
|
+
name_with_mapping = entry_class_for_name(name)
|
115
|
+
|
116
|
+
if StorageRoom.is_constant_defined?(name)
|
79
117
|
"StorageRoom::#{name}".constantize
|
80
|
-
elsif
|
81
|
-
|
118
|
+
elsif Object.is_constant_defined?(name_with_mapping)
|
119
|
+
name_with_mapping.constantize
|
82
120
|
else
|
83
|
-
|
84
|
-
Object.const_set(name, klass)
|
85
|
-
|
86
|
-
klass
|
121
|
+
raise ClassNotFound.new("Unknown class: #{name}")
|
87
122
|
end
|
88
123
|
end
|
89
124
|
|
125
|
+
def log(msg) #:nodoc:
|
126
|
+
puts("[DEBUG] #{msg}") if debug
|
127
|
+
end
|
128
|
+
|
90
129
|
private
|
91
130
|
|
92
131
|
def update_uri
|
93
132
|
protocol = self.ssl == true ? 'https' : 'http'
|
94
133
|
|
95
|
-
|
134
|
+
url = Resource.base_uri("#{protocol}://#{self.server}/accounts/#{self.account_id}")
|
96
135
|
end
|
97
136
|
|
98
137
|
end
|
99
|
-
end
|
138
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module StorageRoom
|
2
|
+
|
3
|
+
# Module that contains attributes methods shared between StorageRoom::Resource and StorageRoom::Embedded
|
4
|
+
module Accessors
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
self.class_inheritable_accessor :attribute_options
|
9
|
+
self.attribute_options ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
# Optionally pass attributes to set up the object
|
14
|
+
def initialize(hash={})
|
15
|
+
self.attributes = hash
|
16
|
+
end
|
17
|
+
|
18
|
+
# Shortcut to get an attribute.
|
19
|
+
def [](name)
|
20
|
+
self.response_data[name]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Takes a response data hash, saves it in the record and initializes the class from the response data
|
24
|
+
def set_from_response_data(hash)
|
25
|
+
self.response_data = hash
|
26
|
+
self.initialize_from_response_data
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return all of the objects attributes
|
31
|
+
def response_data
|
32
|
+
@_response_data ||= Hash.new.with_indifferent_access
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set the objects attributes with a hash. Only attributes passed in the hash are changed, existing ones are not overridden.
|
36
|
+
def response_data=(hash = {})
|
37
|
+
response_data.merge!(hash)
|
38
|
+
end
|
39
|
+
|
40
|
+
# The attributes as they were defined with key, one, many
|
41
|
+
def attributes
|
42
|
+
@_attributes ||= Hash.new.with_indifferent_access
|
43
|
+
end
|
44
|
+
|
45
|
+
def attributes=(args = {})
|
46
|
+
attributes.merge!(args)
|
47
|
+
end
|
48
|
+
|
49
|
+
def as_json(args = {}) # :nodoc:
|
50
|
+
to_hash(args)
|
51
|
+
end
|
52
|
+
|
53
|
+
# ActiveSupport seemed to cause problems when just using as_json, so using to_hash
|
54
|
+
def to_hash(args = {}) # :nodoc:
|
55
|
+
args ||= {}
|
56
|
+
hash = {}
|
57
|
+
|
58
|
+
self.attributes.each do |name, value|
|
59
|
+
hash[name] = if value.is_a?(::Array)
|
60
|
+
value.map{|x| x.respond_to?(:to_hash) ? x.to_hash(:nested => true) : x }
|
61
|
+
elsif value.respond_to?(:to_hash)
|
62
|
+
value.to_hash(:nested => true)
|
63
|
+
elsif value.respond_to?(:as_json)
|
64
|
+
value.as_json(:nested => true)
|
65
|
+
else
|
66
|
+
value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
hash
|
71
|
+
end
|
72
|
+
|
73
|
+
def inspect # :nodoc:
|
74
|
+
body = attributes.map{|k, v| "#{k}: #{attribute_for_inspect(v)}"}.join(', ')
|
75
|
+
"#<#{self.class} #{body}>"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Reset an object to its initial state with all attributes unset
|
79
|
+
def reset!
|
80
|
+
@_response_data = Hash.new.with_indifferent_access
|
81
|
+
@_attributes = Hash.new.with_indifferent_access
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
# Has a Resource been loaded from the API
|
86
|
+
def loaded?
|
87
|
+
self.response_data.present?
|
88
|
+
end
|
89
|
+
|
90
|
+
def proxy? # :nodoc:
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
# Iterate over the response data and initialize the attributes
|
96
|
+
def initialize_from_response_data # :nodoc:
|
97
|
+
self.class.attribute_options.each do |name, options|
|
98
|
+
value = if options[:type] == :key
|
99
|
+
self[name].blank? ? options[:default] : self[name]
|
100
|
+
elsif options[:type] == :one
|
101
|
+
hash = self[name.to_s]
|
102
|
+
hash && hash['@type'] ? self.class.new_from_response_data(hash) : nil
|
103
|
+
elsif options[:type] == :many
|
104
|
+
response_data[name] && response_data[name].map{|hash| self.class.new_from_response_data(hash)} || []
|
105
|
+
else
|
106
|
+
raise "Invalid type: #{options[:type]}"
|
107
|
+
end
|
108
|
+
|
109
|
+
send("#{name}=", value)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def ensure_loaded # :nodoc:
|
114
|
+
if loaded?
|
115
|
+
yield if block_given?
|
116
|
+
else
|
117
|
+
raise StorageRoom::ResourceNotLoaded
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def attribute_for_inspect(value) # :nodoc:
|
122
|
+
if value.is_a?(String) && value.length > 50
|
123
|
+
"#{value[0..50]}...".inspect
|
124
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
125
|
+
%("#{value.iso8601}")
|
126
|
+
else
|
127
|
+
value.inspect
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
module ClassMethods
|
133
|
+
# Load an object with the specified URL from the API
|
134
|
+
def load(url, parameters = {})
|
135
|
+
return nil if url.blank?
|
136
|
+
|
137
|
+
StorageRoom.log("Loading #{url}")
|
138
|
+
httparty = get(url, parameters)
|
139
|
+
|
140
|
+
handle_critical_response_errors(httparty)
|
141
|
+
new_from_response_data(httparty.parsed_response.first[1])
|
142
|
+
end
|
143
|
+
|
144
|
+
# Creates a new object of the correct class and initializes it from the response data
|
145
|
+
def new_from_response_data(response_data)
|
146
|
+
object = StorageRoom.class_for_name(response_data['@type']).new.set_from_response_data(response_data)
|
147
|
+
|
148
|
+
if object.is_a?(Entry) && !object.loaded? && !object.proxy?
|
149
|
+
StorageRoom.log("Return #{response_data['url']} proxied")
|
150
|
+
Proxy.new(object)
|
151
|
+
else
|
152
|
+
object
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Defines a basic key for a Resource
|
157
|
+
def key(name, options = {})
|
158
|
+
options.merge!(:type => :key, :default => nil)
|
159
|
+
define_attribute_methods(name, options)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Defines a to-one association for a Resource (embedded or association)
|
163
|
+
def one(name, options = {})
|
164
|
+
options.merge!(:type => :one, :default => nil)
|
165
|
+
define_attribute_methods(name, options)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Defines a to-many association for a Resource (embedded or association)
|
169
|
+
def many(name, options = {})
|
170
|
+
options.merge!(:type => :many, :default => [])
|
171
|
+
define_attribute_methods(name, options)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Creates getter and setter for an attribute
|
175
|
+
def define_attribute_methods(name, options) # :nodoc:
|
176
|
+
define_method name do
|
177
|
+
attributes[name] || options[:default]
|
178
|
+
end
|
179
|
+
|
180
|
+
define_method "#{name}=" do |object|
|
181
|
+
attributes[name] = object
|
182
|
+
end
|
183
|
+
|
184
|
+
self.attribute_options[name] = options
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|