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.
Files changed (81) hide show
  1. data/History.txt +6 -0
  2. data/README.rdoc +10 -4
  3. data/Rakefile +3 -1
  4. data/TODO +7 -2
  5. data/VERSION +1 -1
  6. data/examples/authentication.rb +5 -3
  7. data/examples/create_entry.rb +8 -5
  8. data/examples/destroy_entry.rb +2 -2
  9. data/examples/get_collections.rb +2 -2
  10. data/examples/import_csv.rb +4 -4
  11. data/examples/search_entries.rb +2 -2
  12. data/examples/update_entry.rb +2 -2
  13. data/lib/console.rb +12 -0
  14. data/lib/storage_room.rb +73 -34
  15. data/lib/storage_room/accessors.rb +190 -0
  16. data/lib/storage_room/array.rb +3 -23
  17. data/lib/storage_room/embedded.rb +1 -1
  18. data/lib/storage_room/embeddeds/field.rb +28 -0
  19. data/lib/storage_room/embeddeds/fields/association_field.rb +17 -0
  20. data/lib/storage_room/embeddeds/fields/associations/many_association_field.rb +9 -0
  21. data/lib/storage_room/embeddeds/fields/associations/one_association_field.rb +9 -0
  22. data/lib/storage_room/embeddeds/fields/atomic/boolean_field.rb +6 -0
  23. data/lib/storage_room/embeddeds/fields/atomic/date_field.rb +6 -0
  24. data/lib/storage_room/embeddeds/fields/atomic/float_field.rb +6 -0
  25. data/lib/storage_room/embeddeds/fields/atomic/integer_field.rb +6 -0
  26. data/lib/storage_room/embeddeds/fields/atomic/string_field.rb +6 -0
  27. data/lib/storage_room/embeddeds/fields/atomic/time_field.rb +6 -0
  28. data/lib/storage_room/embeddeds/fields/atomic_field.rb +15 -0
  29. data/lib/storage_room/embeddeds/fields/compound/attachment_field.rb +6 -0
  30. data/lib/storage_room/embeddeds/fields/compound/file_field.rb +6 -0
  31. data/lib/storage_room/embeddeds/fields/compound/image_field.rb +6 -0
  32. data/lib/storage_room/embeddeds/fields/compound/location_field.rb +6 -0
  33. data/lib/storage_room/embeddeds/fields/compound_field.rb +10 -0
  34. data/lib/storage_room/embeddeds/file.rb +1 -1
  35. data/lib/storage_room/embeddeds/image.rb +6 -0
  36. data/lib/storage_room/embeddeds/location.rb +2 -1
  37. data/lib/storage_room/extensions/const_defined.rb +12 -0
  38. data/lib/storage_room/identity_map.rb +117 -0
  39. data/lib/storage_room/model.rb +19 -23
  40. data/lib/storage_room/models/collection.rb +76 -11
  41. data/lib/storage_room/models/entry.rb +6 -32
  42. data/lib/storage_room/plugins.rb +22 -0
  43. data/lib/storage_room/proxy.rb +49 -0
  44. data/lib/storage_room/{base.rb → resource.rb} +13 -36
  45. data/spec/fixtures/collection.json +0 -16
  46. data/spec/spec_helper.rb +1 -0
  47. data/spec/storage_room/accessors_spec.rb +107 -0
  48. data/spec/storage_room/array_spec.rb +13 -9
  49. data/spec/storage_room/embedded_spec.rb +5 -1
  50. data/spec/storage_room/embeddeds/field_spec.rb +25 -0
  51. data/spec/storage_room/embeddeds/fields/association_field_spec.rb +29 -0
  52. data/spec/storage_room/embeddeds/fields/associations/many_association_field_spec.rb +21 -0
  53. data/spec/storage_room/embeddeds/fields/associations/one_association_field_spec.rb +19 -0
  54. data/spec/storage_room/embeddeds/fields/atomic/boolean_field_spec.rb +5 -0
  55. data/spec/storage_room/embeddeds/fields/atomic/date_field_spec.rb +5 -0
  56. data/spec/storage_room/embeddeds/fields/atomic/float_field_spec.rb +5 -0
  57. data/spec/storage_room/embeddeds/fields/atomic/integer_field_spec.rb +5 -0
  58. data/spec/storage_room/embeddeds/fields/atomic/string_field_spec.rb +5 -0
  59. data/spec/storage_room/embeddeds/fields/atomic/time_field_spec.rb +5 -0
  60. data/spec/storage_room/embeddeds/fields/atomic_field_spec.rb +27 -0
  61. data/spec/storage_room/embeddeds/fields/compound/attachment_field_spec.rb +5 -0
  62. data/spec/storage_room/embeddeds/fields/compound/file_field_spec.rb +5 -0
  63. data/spec/storage_room/embeddeds/fields/compound/image_field_spec.rb +5 -0
  64. data/spec/storage_room/embeddeds/fields/compound/location_field_spec.rb +5 -0
  65. data/spec/storage_room/embeddeds/fields/compound_field_spec.rb +17 -0
  66. data/spec/storage_room/embeddeds/location_spec.rb +13 -1
  67. data/spec/storage_room/identity_map_spec.rb +53 -0
  68. data/spec/storage_room/model_spec.rb +70 -50
  69. data/spec/storage_room/models/collection_spec.rb +57 -14
  70. data/spec/storage_room/models/entry_spec.rb +16 -20
  71. data/spec/storage_room/proxy_spec.rb +58 -0
  72. data/spec/storage_room/resource_spec.rb +98 -0
  73. data/spec/storage_room_spec.rb +45 -9
  74. data/storage_room.gemspec +48 -9
  75. data/tasks/storage_room.rake +3 -0
  76. metadata +49 -10
  77. data/lib/storage_room/attributes.rb +0 -46
  78. data/lib/storage_room/field.rb +0 -8
  79. data/spec/storage_room/attributes_spec.rb +0 -82
  80. data/spec/storage_room/base_spec.rb +0 -118
  81. data/spec/storage_room/field_spec.rb +0 -5
data/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ === Version 0.3.0
2
+ * Complete refactoring of internals.
3
+ * Added support for associations
4
+ * Added support for identity map
5
+ * Added configurable Resources
6
+
1
7
  === Version 0.1.1
2
8
 
3
9
  * Added rdoc
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). It has an ActiveRecord/ActiveModel like interface.
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
- entries = collection.entries
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[:name] = 'Foobar'
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
@@ -1,5 +1,10 @@
1
1
 
2
+
2
3
  == Version X
3
4
 
4
- - have collection fields
5
- - executable/cli
5
+ - have a gemfile?
6
+ - make on/many only accept objects of correct class?
7
+ - executable/cli
8
+ - file uploads/removals
9
+ - get files via api
10
+ - look at arguments with identity map
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
@@ -1,6 +1,8 @@
1
1
  require File.dirname(__FILE__) + '/../lib/storage_room'
2
2
 
3
- ACCOUNT_ID = '4cef9a8c425071fa6900002f' # your account id
4
- APPLICATION_API_KEY = 'c499kx9L6aBfvJlvSKbF' # your application's API key with read/write access
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"
@@ -2,13 +2,16 @@
2
2
 
3
3
  require File.dirname(__FILE__) + '/authentication'
4
4
 
5
+
5
6
  # fetch the collection first
6
- collection = StorageRoom::Collection.find('4ddaf68b4d085d374a000003')
7
+ guidebook_collection = StorageRoom::Collection.find('4dda7761b65245fde100005d')
8
+ category_collection = StorageRoom::Collection.find('4dda7761b65245fde100001a')
7
9
 
8
- entry2 = collection.entry_class.new(:title => 'Bar', :price => 2.23)
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 entry2.save
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 "Entry could not be saved: #{entry2.errors.join(', ')}"
16
+ puts "Guidebook could not be saved: #{entry2.errors.join(', ')}"
14
17
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  require File.dirname(__FILE__) + '/authentication'
4
4
 
5
- collection = StorageRoom::Collection.find('4ddaf68b4d085d374a000003')
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[:title]}"
11
+ puts "Destroyed #{entry.title}"
@@ -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[:name]}"
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
@@ -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('4ddaf68b4d085d374a000003')
12
- klass = collection.entry_class
11
+ collection = StorageRoom::Collection.find('4dda7761b65245fde100005d')
12
+ Guidebook = collection.entry_class
13
13
 
14
14
  lines.each do |row|
15
- guidebook = klass.new(:title => row[0], :price => row[1].to_f)
15
+ guidebook = Guidebook.new(:title => row[0], :price => row[1].to_f)
16
16
 
17
17
  if guidebook.save
18
- puts "Guidebook saved: #{guidebook[:title]}, #{guidebook[:price]}"
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
@@ -2,12 +2,12 @@
2
2
 
3
3
  require File.dirname(__FILE__) + '/authentication'
4
4
 
5
- collection = StorageRoom::Collection.find('4ddaf68b4d085d374a000003')
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[:title]} : #{entry.url}"
12
+ puts "- #{entry.title} : #{entry[:@url]}"
13
13
  end
@@ -2,11 +2,11 @@
2
2
 
3
3
  require File.dirname(__FILE__) + '/authentication'
4
4
 
5
- collection = StorageRoom::Collection.find('4ddaf68b4d085d374a000003')
5
+ collection = StorageRoom::Collection.find('4dda7761b65245fde100005d')
6
6
 
7
7
  entry = collection.entries.resources.first
8
8
 
9
- entry[:title] = 'Foobar'
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 < RuntimeError; end
12
- class RequestFailed < RuntimeError; end
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 :Attributes, 'storage_room/attributes'
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 :Base, 'storage_room/base'
17
- autoload :Model, 'storage_room/model'
18
- autoload :Array, 'storage_room/array'
19
- autoload :Field, 'storage_room/field'
20
- autoload :Embedded, 'storage_room/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 :Collection, 'storage_room/models/collection'
23
- autoload :Entry, 'storage_room/models/entry'
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 :File, 'storage_room/embeddeds/file'
26
- autoload :Location, 'storage_room/embeddeds/location'
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
- attr_reader :user_agent
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
- Base.basic_auth(api_key, 'X')
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
- Base.headers.merge!('User-Agent' => agent)
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
- Base.http_proxy(server, port)
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
- is_ruby_19 = method(:const_defined?).arity == 1 # ruby 1.9 check
77
-
78
- if is_ruby_19 ? StorageRoom.const_defined?(name) : StorageRoom.const_defined?(name, false)
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 is_ruby_19 ? Object.const_defined?(name) : Object.const_defined?(name, false)
81
- name.constantize
118
+ elsif Object.is_constant_defined?(name_with_mapping)
119
+ name_with_mapping.constantize
82
120
  else
83
- klass = Class.new(Entry)
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
- Base.base_uri "#{protocol}://#{self.server}/accounts/#{self.account_id}"
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