storage_room 0.2.1 → 0.3.0
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/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
|