storage_room 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +22 -0
  2. data/History.txt +3 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +51 -0
  5. data/Rakefile +50 -0
  6. data/TODO +9 -0
  7. data/VERSION +1 -0
  8. data/examples/authentication.rb +6 -0
  9. data/examples/create_resource.rb +24 -0
  10. data/examples/destroy_resource.rb +11 -0
  11. data/examples/get_collections.rb +11 -0
  12. data/examples/guidebooks.csv +21 -0
  13. data/examples/import_csv.rb +21 -0
  14. data/examples/search_resources.rb +13 -0
  15. data/examples/update_resource.rb +15 -0
  16. data/lib/storage_room/array.rb +64 -0
  17. data/lib/storage_room/attributes.rb +37 -0
  18. data/lib/storage_room/base.rb +56 -0
  19. data/lib/storage_room/embedded.rb +6 -0
  20. data/lib/storage_room/embeddeds/file.rb +5 -0
  21. data/lib/storage_room/embeddeds/location.rb +5 -0
  22. data/lib/storage_room/field.rb +7 -0
  23. data/lib/storage_room/model.rb +111 -0
  24. data/lib/storage_room/models/collection.rb +31 -0
  25. data/lib/storage_room/models/resource.rb +53 -0
  26. data/lib/storage_room.rb +89 -0
  27. data/spec/fixtures/collection.json +38 -0
  28. data/spec/fixtures/collections.json +42 -0
  29. data/spec/fixtures/validation_error.json +7 -0
  30. data/spec/spec_helper.rb +34 -0
  31. data/spec/storage_room/array_spec.rb +77 -0
  32. data/spec/storage_room/attributes_spec.rb +82 -0
  33. data/spec/storage_room/base_spec.rb +110 -0
  34. data/spec/storage_room/embedded_spec.rb +5 -0
  35. data/spec/storage_room/embeddeds/file_spec.rb +5 -0
  36. data/spec/storage_room/embeddeds/location_spec.rb +5 -0
  37. data/spec/storage_room/field_spec.rb +5 -0
  38. data/spec/storage_room/model_spec.rb +239 -0
  39. data/spec/storage_room/models/collection_spec.rb +54 -0
  40. data/spec/storage_room/models/resource_spec.rb +68 -0
  41. data/spec/storage_room_spec.rb +99 -0
  42. metadata +188 -0
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ lib/irb.rb
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ === Version 0.1.0
2
+
3
+ * Initial Version
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Thriventures UG (haftungsbeschränkt) - http://thriventures.com
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,51 @@
1
+ == StorageRoom Gem
2
+
3
+ This gem provides read and write access to the StorageRoom API (http://storageroomapp.com). It has an ActiveRecord/ActiveModel like interface.
4
+
5
+
6
+ == Installation
7
+
8
+ To install the library execute:
9
+
10
+ sudo gem install storage_room
11
+
12
+
13
+ == Basic Usage
14
+
15
+ This is a walkthrough with all steps you need to setup a devise resource, including model, migration, route files, and optional configuration.
16
+
17
+ StorageRoom.authenticate(YOUR_ACCOUNT_ID, YOUR_APPLICATION_API_KEY)
18
+ collection = StorageRoom::Collection.find('guidebooks')
19
+
20
+ resources = collection.resources
21
+ resource = resources.items.first
22
+
23
+ resource[:name] = 'Foobar'
24
+
25
+ if resource.save
26
+ puts "Resource saved."
27
+ else
28
+ puts "Could not save Resource: #{resource.errors.join(', )}"
29
+ end
30
+
31
+ == More Examples
32
+
33
+ See the examples folder.
34
+
35
+ == TODO
36
+
37
+ Please refer to TODO file.
38
+
39
+ == Maintainers
40
+
41
+ * Sascha Konietzke
42
+
43
+ == Bugs and Feedback
44
+
45
+ If you discover any bugs, please create an issue on GitHub.
46
+
47
+ http://github.com/thriventures/storage_room_gem/issues
48
+
49
+ == License
50
+
51
+ MIT License. Copyright 2010 Thriventures UG (haftungsbeschränkt) - http://www.thriventures.com
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "storage_room"
8
+ gem.summary = %Q{StorageRoom API Wrapper (ActiveModel style)}
9
+ gem.description = %Q{StorageRoom is a CMS system for Mobile Applications (iPhone, Android, BlackBerry, ...). This library gives you an ActiveModel-like interface to your data.}
10
+ gem.email = "sascha@thriventures.com"
11
+ gem.homepage = "http://github.com/thriventures/storage_room_gem"
12
+ gem.authors = ["Sascha Konietzke"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ gem.add_development_dependency "webmock"
15
+
16
+ gem.add_dependency 'httparty', '>= 0.6.1'
17
+ gem.add_dependency 'activesupport', '>= 3.0.0'
18
+
19
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
20
+ end
21
+ Jeweler::GemcutterTasks.new
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
24
+ end
25
+
26
+ require 'spec/rake/spectask'
27
+ Spec::Rake::SpecTask.new(:spec) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.spec_files = FileList['spec/**/*_spec.rb']
30
+ end
31
+
32
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
33
+ spec.libs << 'lib' << 'spec'
34
+ spec.pattern = 'spec/**/*_spec.rb'
35
+ spec.rcov = true
36
+ end
37
+
38
+ task :spec => :check_dependencies
39
+
40
+ task :default => :spec
41
+
42
+ require 'rake/rdoctask'
43
+ Rake::RDocTask.new do |rdoc|
44
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
45
+
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "storage_room #{version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
data/TODO ADDED
@@ -0,0 +1,9 @@
1
+ - rdoc
2
+ - gemcutter
3
+
4
+
5
+
6
+ == Version X
7
+
8
+ - have collection fields
9
+ - executable/cli
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,6 @@
1
+ require File.dirname(__FILE__) + '/../lib/storage_room'
2
+
3
+ ACCOUNT_ID = '4c8fd48542507175aa00002f' # your account id
4
+ APPLICATION_API_KEY = '43ruv6DDcNYlPZ5xH7en' # your application's API key with read/write access
5
+
6
+ StorageRoom.authenticate(ACCOUNT_ID, APPLICATION_API_KEY)
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+
3
+ require File.dirname(__FILE__) + '/authentication'
4
+
5
+ # create a resource without fetching the collection
6
+ klass = StorageRoom.class_for_name('Guidebook')
7
+ resource1 = klass.new(:name => 'Foo', :price => 1.23)
8
+
9
+ if resource1.save
10
+ puts "Resource saved"
11
+ else
12
+ puts "Resource could not be saved: #{resource1.errors.join(', ')}"
13
+ end
14
+
15
+ # fetch the collection first
16
+ collection = StorageRoom::Collection.find('guidebooks')
17
+
18
+ resource2 = collection.resource_class.new(:name => 'Bar', :price => 2.23)
19
+
20
+ if resource2.save
21
+ puts "Resource saved"
22
+ else
23
+ puts "Resource could not be saved: #{resource2.errors.join(', ')}"
24
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+
3
+ require File.dirname(__FILE__) + '/authentication'
4
+
5
+ collection = StorageRoom::Collection.find('guidebooks')
6
+
7
+ resource = collection.resources.items.first
8
+
9
+ resource.destroy
10
+
11
+ puts "Destroyed #{resource[:name]}"
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+
3
+ require File.dirname(__FILE__) + '/authentication'
4
+
5
+ collections = StorageRoom::Collection.all
6
+
7
+ puts "Collections:"
8
+
9
+ collections.items.each do |collection|
10
+ puts "- #{collection[:name]}"
11
+ end
@@ -0,0 +1,21 @@
1
+ "Name","Price"
2
+ "Name 1",1.1
3
+ "Name 2",2.1
4
+ "Name 3",3.1
5
+ "Name 4",4.1
6
+ "Name 5",5.1
7
+ "Name 6",6.1
8
+ "Name 7",7.1
9
+ "Name 8",8.1
10
+ "Name 9",9.1
11
+ "Name 10",10.1
12
+ "Name 11",11.1
13
+ "Name 12",12.1
14
+ "Name 13",13.1
15
+ "Name 14",14.1
16
+ "Name 15",15.1
17
+ "Name 16",16.1
18
+ "Name 17",17.1
19
+ "Name 18",18.1
20
+ "Name 19",19.1
21
+ "Name 20",20.1
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+
3
+ require File.dirname(__FILE__) + '/authentication'
4
+
5
+ require 'faster_csv'
6
+
7
+ lines = FasterCSV.read(File.dirname(__FILE__) + '/guidebooks.csv') # Exported an Excel file as CSV
8
+
9
+ lines.slice!(0) # remove header line
10
+
11
+ klass = StorageRoom.class_for_name('Guidebook')
12
+
13
+ lines.each do |row|
14
+ guidebook = klass.new(:name => row[0], :price => row[1].to_f)
15
+
16
+ if guidebook.save
17
+ puts "Guidebook saved: #{guidebook[:name]}, #{guidebook[:price]}"
18
+ else
19
+ puts "Guidebook could not be saved: #{guidebook.errors.join(', ')}"
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+
3
+ require File.dirname(__FILE__) + '/authentication'
4
+
5
+ collection = StorageRoom::Collection.find('guidebooks')
6
+
7
+ resources = collection.resource_class.search(:name => 'Bar')
8
+
9
+ puts "Resources with name 'Bar':"
10
+
11
+ resources.items.each do |resource|
12
+ puts "- #{resource[:name]}"
13
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+
3
+ require File.dirname(__FILE__) + '/authentication'
4
+
5
+ collection = StorageRoom::Collection.find('guidebooks')
6
+
7
+ resource = collection.resources.items.first
8
+
9
+ resource[:name] = 'Foobar'
10
+
11
+ if resource.save
12
+ puts "Resource saved"
13
+ else
14
+ puts "Resource could not be saved: #{resource.errors.join(', ')}"
15
+ end
@@ -0,0 +1,64 @@
1
+ module StorageRoom
2
+ class Array < Base
3
+ attr_accessor :items
4
+
5
+ class << self
6
+
7
+ end
8
+
9
+
10
+ def initialize(attributes = {})
11
+ self.items = []
12
+ super
13
+ end
14
+
15
+ def set_from_api(attributes)
16
+ super(attributes)
17
+
18
+ self.items = attributes['items'].map{|item| self.class.create_from_api(item)} # transform hashes to real objects
19
+ attributes.delete('items')
20
+ end
21
+
22
+ def reset!
23
+ super
24
+ @items = []
25
+ end
26
+
27
+ def load_next_page!
28
+ if self[:@next_page].present?
29
+ reload(self[:@next_page])
30
+ true
31
+ else
32
+ false
33
+ end
34
+ end
35
+
36
+ # Replace the Collection with the privious page of elements if there is one
37
+ def load_previous_page!
38
+ if self[:@previous_page].present?
39
+ reload(self[:@previous_page])
40
+ true
41
+ else
42
+ false
43
+ end
44
+ end
45
+
46
+
47
+ # Iterate over all pages
48
+ def each_page(args={})
49
+ begin
50
+ yield(self)
51
+ end while(args[:reverse] == true ? load_previous_page! : load_next_page!)
52
+ end
53
+
54
+ # Iterate over all items with pagination
55
+ def each_page_each_item(args={})
56
+ self.each_page(args) do |page|
57
+ page.items.each do |item|
58
+ yield(item)
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,37 @@
1
+ module StorageRoom
2
+ module Attributes
3
+ def initialize(attributes={})
4
+ self.attributes = attributes
5
+ end
6
+
7
+ def set_from_api(attributes)
8
+ reset!
9
+
10
+ self.attributes = attributes
11
+ end
12
+
13
+ def [](name)
14
+ self.attributes[name]
15
+ end
16
+
17
+ def []=(name, value)
18
+ self.attributes[name] = value
19
+ end
20
+
21
+ def attributes
22
+ @attributes ||= Hash.new.with_indifferent_access
23
+ end
24
+
25
+ def attributes=(hash = {})
26
+ hash.each do |k, v|
27
+ self.attributes[k] = v
28
+ end
29
+ end
30
+
31
+ def reset!
32
+ @attributes = Hash.new.with_indifferent_access
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,56 @@
1
+ module StorageRoom
2
+ class Base
3
+ include Attributes
4
+ include HTTParty
5
+
6
+ headers 'User-Agent' => 'StorageRoom Ruby Gem', 'Accept' => 'application/json', 'Content-Type' => 'application/json'
7
+ format :json
8
+
9
+
10
+ class << self
11
+ def load(url, parameters = {})
12
+ httparty = get(url, parameters)
13
+
14
+ handle_critical_response_errors(httparty)
15
+ create_from_api(httparty.parsed_response.first[1])
16
+ end
17
+
18
+ def handle_critical_response_errors(httparty)
19
+ case httparty.response.code
20
+ when '200', '201', '422' then true
21
+ else
22
+ raise StorageRoom::RequestFailed.new("Invalid HTTP Response: #{httparty.response.code}")
23
+ end
24
+ end
25
+
26
+ def create_from_api(hash)
27
+ type = hash['@type']
28
+
29
+ object = case type
30
+ when 'Array' then Array.new
31
+ when 'Collection' then Collection.new
32
+ else
33
+ StorageRoom.class_for_name(type).new
34
+ end
35
+
36
+ object.set_from_api(hash)
37
+ object
38
+ end
39
+
40
+ def meta_data?(key)
41
+ key[0...1] == '@'
42
+ end
43
+ end
44
+
45
+ def reload(url = nil, parameters = {})
46
+ httparty = self.class.get(url || self[:@url], parameters)
47
+ set_from_api(httparty.parsed_response.first[1])
48
+ true
49
+ end
50
+
51
+ def url
52
+ self[:@url]
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,6 @@
1
+ module StorageRoom
2
+ class Embedded
3
+ include Attributes
4
+
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module StorageRoom
2
+ class File < Embedded
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module StorageRoom
2
+ class Location < Embedded
3
+
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module StorageRoom
2
+ class Field
3
+
4
+
5
+
6
+ end
7
+ end
@@ -0,0 +1,111 @@
1
+ module StorageRoom
2
+ class Model < Base
3
+ class << self
4
+ def create(attributes={})
5
+ resource = new(attributes)
6
+ resource.create
7
+ resource
8
+ end
9
+
10
+ def all
11
+ load(index_path)
12
+ end
13
+
14
+ def find(id)
15
+ load(show_path(id))
16
+ end
17
+
18
+ def index_path
19
+ raise StorageRoom::AbstractMethod.new
20
+ end
21
+
22
+ def show_path(id)
23
+ raise StorageRoom::AbstractMethod.new
24
+ end
25
+
26
+ def json_name
27
+ raise StorageRoom::AbstractMethod.new
28
+ end
29
+ end
30
+
31
+ def initialize(attributes={})
32
+ @new_record = true
33
+ @errors = []
34
+
35
+ super
36
+ end
37
+
38
+ def set_from_api(attributes)
39
+ super
40
+ @new_record = false
41
+
42
+ self.attributes
43
+ end
44
+
45
+ def reset!
46
+ super
47
+ @new_record = true
48
+ @errors = []
49
+ true
50
+ end
51
+
52
+ def new_record?
53
+ @new_record ? true : false
54
+ end
55
+
56
+ def save
57
+ new_record? ? create : update
58
+ end
59
+
60
+ def create
61
+ return false unless new_record?
62
+ httparty = self.class.post(self.class.index_path, :body => to_json)
63
+ handle_save_response(httparty)
64
+ end
65
+
66
+ def update
67
+ return false if new_record?
68
+ httparty = self.class.put(url, :body => to_json)
69
+ handle_save_response(httparty)
70
+ end
71
+
72
+ def destroy
73
+ return false if new_record?
74
+
75
+ httparty = self.class.delete(url)
76
+ self.class.handle_critical_response_errors(httparty)
77
+
78
+ true
79
+ end
80
+
81
+ def valid?
82
+ self.errors.empty?
83
+ end
84
+
85
+ def as_json(args = {})
86
+ {self.class.json_name => self.attributes.reject{|k, v| self.class.meta_data?(k)}}
87
+ end
88
+
89
+ def errors
90
+ @errors ||= []
91
+ end
92
+
93
+ # ===================
94
+ # = Private Methods =
95
+ # ===================
96
+
97
+ private
98
+ def handle_save_response(httparty)
99
+ self.class.handle_critical_response_errors(httparty)
100
+
101
+ if httparty.response.code == '200' || httparty.response.code == '201'
102
+ self.set_from_api(httparty.parsed_response.first[1])
103
+ true
104
+ else
105
+ @errors = httparty.parsed_response['error']['message']
106
+ false
107
+ end
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,31 @@
1
+ module StorageRoom
2
+ class Collection < Model
3
+ class << self
4
+ def index_path
5
+ '/collections'
6
+ end
7
+
8
+ def show_path(collection_id)
9
+ "#{index_path}/#{collection_id}"
10
+ end
11
+
12
+ def resources_path(collection_id)
13
+ "#{show_path(collection_id)}/resources"
14
+ end
15
+
16
+ def json_name
17
+ 'collection'
18
+ end
19
+ end
20
+
21
+ def resources
22
+ Array.load(self[:@resources_url])
23
+ end
24
+
25
+ def resource_class
26
+ StorageRoom.class_for_name(self[:identifier].classify)
27
+ end
28
+
29
+
30
+ end
31
+ end
@@ -0,0 +1,53 @@
1
+ module StorageRoom
2
+ class Resource < Model
3
+ class << self
4
+ def index_path
5
+ "/collections/#{collection_id}/resources"
6
+ end
7
+
8
+ def show_path(resource_id)
9
+ "#{index_path}/#{resource_id}"
10
+ end
11
+
12
+ def collection_path
13
+ "/collections/#{collection_id}"
14
+ end
15
+
16
+ def collection_id
17
+ self.name.gsub('StorageRoom::', '').tableize
18
+ end
19
+
20
+ def json_name
21
+ 'resource'
22
+ end
23
+
24
+ def search_path(parameters = {})
25
+ parameters.present? ? "#{index_path}?#{parameters.to_query}" : index_path
26
+ end
27
+
28
+ def search(parameters = {})
29
+ Array.load(search_path(parameters))
30
+ end
31
+ end
32
+
33
+ def set_from_api(attributes)
34
+ super(attributes)
35
+
36
+ self.attributes.each do |k, v|
37
+ if v.is_a?(Hash) && v[:@type].present?
38
+ object = StorageRoom.class_for_name(v[:@type]).new
39
+ object.set_from_api(v)
40
+ self.attributes[k] = object
41
+ end
42
+ end
43
+
44
+ self.attributes
45
+ end
46
+
47
+ def collection
48
+ Collection.load(self[:@collection_url] || self.class.collection_path)
49
+ end
50
+
51
+
52
+ end
53
+ end