motion_model_resource 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/CHANGELOG +0 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +22 -0
- data/LICENSE +20 -0
- data/README.md +152 -0
- data/Rakefile +18 -0
- data/app/app_delegate.rb +2 -0
- data/lib/motion_model_resource.rb +5 -0
- data/motion/resource/data_parser.rb +13 -0
- data/motion/resource/wrapper.rb +206 -0
- data/motion/version.rb +3 -0
- data/motion_model_resource.gemspec +21 -0
- data/spec/wrapper_spec.rb +186 -0
- metadata +125 -0
data/.gitignore
ADDED
data/CHANGELOG
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
bubble-wrap (1.3.0.osx)
|
5
|
+
motion-require (0.0.6)
|
6
|
+
motion-support (0.2.4)
|
7
|
+
motion-require (>= 0.0.6)
|
8
|
+
motion_model (0.4.4)
|
9
|
+
bubble-wrap (= 1.3.0.osx)
|
10
|
+
motion-support (>= 0.1.0)
|
11
|
+
rake (10.0.4)
|
12
|
+
webstub (0.3.7)
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
ruby
|
16
|
+
|
17
|
+
DEPENDENCIES
|
18
|
+
bubble-wrap
|
19
|
+
motion-support
|
20
|
+
motion_model
|
21
|
+
rake
|
22
|
+
webstub
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Torben Toepper
|
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.md
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
MotionModelResource: Simple JSON API Wrapper for MotionModel on RubyMotion
|
2
|
+
==========================================================================
|
3
|
+
|
4
|
+
MotionModelResource is a simple wrapper to store your remote data in MotionModel objects. It is good for users who have a
|
5
|
+
REST API and want to have it in an iOS app.
|
6
|
+
|
7
|
+
You need to have MotionModel ready, if you want to use this implementation: https://github.com/sxross/MotionModel
|
8
|
+
|
9
|
+
|
10
|
+
### Overview
|
11
|
+
* [Installation](#installation)
|
12
|
+
* [Setup](#setup)
|
13
|
+
* [Usage](#usage)
|
14
|
+
|
15
|
+
|
16
|
+
Installation
|
17
|
+
------------
|
18
|
+
Add the following line to your `Gemfile`:
|
19
|
+
|
20
|
+
`gem "motion-model-resource"`
|
21
|
+
|
22
|
+
You need to require the Gem. Insert the
|
23
|
+
following immediately before `Motion::Project::App.setup`:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'motion_model' # If you haven't already
|
27
|
+
require 'motion_model_resource'
|
28
|
+
```
|
29
|
+
|
30
|
+
Then, update your bundle:
|
31
|
+
|
32
|
+
`bundle`
|
33
|
+
|
34
|
+
|
35
|
+
Setup
|
36
|
+
-----
|
37
|
+
|
38
|
+
First of all you will need a normal MotionModel::Model:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class Task
|
42
|
+
include MotionModel::Model
|
43
|
+
include MotionModel::ArrayModelAdapter
|
44
|
+
|
45
|
+
columns name: :string,
|
46
|
+
long_name: :string,
|
47
|
+
due_date: :date,
|
48
|
+
lastSyncedAt: :time
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
Tune this up, by adding:
|
53
|
+
```ruby
|
54
|
+
...
|
55
|
+
include MotionModelResource::ApiWrapper
|
56
|
+
|
57
|
+
def self.url
|
58
|
+
"https://example.com/tasks"
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.wrapper
|
62
|
+
@wrapper ||= {
|
63
|
+
fields: {
|
64
|
+
id: :id,
|
65
|
+
name: :name,
|
66
|
+
long_name: :long_name,
|
67
|
+
due_date: :due_date
|
68
|
+
},
|
69
|
+
relations: [:user]
|
70
|
+
}
|
71
|
+
end
|
72
|
+
...
|
73
|
+
```
|
74
|
+
The wrapper hash has two main keys. The first one is "fields". Here you specify the local and remote keys, you want to have from your API response. The hash-key is the remote key and the hash-value is the local key. It is used, if you have different names in your implementation (maybe camelcase with underscore).
|
75
|
+
The second key in the main hash is the "relations" part. Here you can specify the wanted relations from your remote response. It will automatacally look after the right relation, for example if you have a relation "tasks", and the response is an array, it will create a bunch of tasks for you.
|
76
|
+
|
77
|
+
The url method will be used for saving a remote model. Maybe in future for a routes prefix.
|
78
|
+
|
79
|
+
|
80
|
+
Usage
|
81
|
+
-----
|
82
|
+
|
83
|
+
### Getting Remote Models
|
84
|
+
|
85
|
+
Fetching your API by calling "fetch" on your model class:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
Task.fetch("https://example.com/tasks")
|
89
|
+
```
|
90
|
+
|
91
|
+
**Example Response**
|
92
|
+
```javascript
|
93
|
+
[{
|
94
|
+
"id": 1,
|
95
|
+
"name": 'Buy beer',
|
96
|
+
"long_name": 'Many, many, many beer!',
|
97
|
+
"due_date": "2013-11-03T20:40:00+01:00",
|
98
|
+
"updated_at": "2013-11-03T20:20:10+01:00",
|
99
|
+
"created_at": "2013-11-03T20:06:01+01:00"
|
100
|
+
},{
|
101
|
+
"id": 2,
|
102
|
+
"name": 'Drink beer',
|
103
|
+
"long_name": 'Beer, beer, beer, beer',
|
104
|
+
"due_date": "2013-11-03T21:40:00+01:00",
|
105
|
+
"updated_at": "2013-11-03T20:20:10+01:00",
|
106
|
+
"created_at": "2013-11-03T20266:01+01:00"
|
107
|
+
}]
|
108
|
+
```
|
109
|
+
|
110
|
+
After this call, you will have a bunch of records in your collection.
|
111
|
+
If you want to have a direct callback, after the new records have stored, you can call the method with a block:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
Task.fetch("https://example.com/tasks") do |tasks|
|
115
|
+
tasks.each do |task|
|
116
|
+
puts task.name
|
117
|
+
end
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
If you need specific URL parameters, you can add them as the second parameter:
|
122
|
+
```ruby
|
123
|
+
Task.fetch("https://example.com/tasks", {api_key: "top_secret!"})
|
124
|
+
```
|
125
|
+
|
126
|
+
If you want to update an existing record, you can use the instance method fetch.
|
127
|
+
```ruby
|
128
|
+
task = Task.first
|
129
|
+
task.fetch("https://example.com/tasks")
|
130
|
+
```
|
131
|
+
|
132
|
+
**Tip:** If you have the lastSyncedAt column in your model, this will automatacally set to the current timestamp!
|
133
|
+
|
134
|
+
|
135
|
+
### Saving Remote Models
|
136
|
+
|
137
|
+
If you want to store your local model, you can simply do this with MotionModelResource. You just need to configure an url class method and giving a block for the save method:
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
task = Task.first
|
141
|
+
task.name = "Drinking a lot of soda"
|
142
|
+
task.save do |model|
|
143
|
+
if model.present?
|
144
|
+
puts "task saved!"
|
145
|
+
else
|
146
|
+
puts "Damn, something went wrong..."
|
147
|
+
end
|
148
|
+
end
|
149
|
+
```
|
150
|
+
|
151
|
+
The task object will update, too. As you can see, if the model var is nil, the save process had failed.
|
152
|
+
The save method will store the relations too!! It will serialize the whole model and push this to the server. The mapping information comes also from the wrapper method.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
$:.unshift("/Library/RubyMotion/lib")
|
4
|
+
require 'motion/project/template/ios'
|
5
|
+
require 'bundler'
|
6
|
+
Bundler.require
|
7
|
+
|
8
|
+
$:.unshift(File.expand_path('../lib', __FILE__))
|
9
|
+
require 'motion_model_resource'
|
10
|
+
require 'bubble-wrap/core'
|
11
|
+
require 'bubble-wrap/http'
|
12
|
+
|
13
|
+
Motion::Project::App.setup do |app|
|
14
|
+
# Use `rake config' to see complete project settings.
|
15
|
+
app.name = 'MotionModelResource'
|
16
|
+
app.delegate_class = 'FakeDelegate'
|
17
|
+
app.files = (app.files + Dir.glob('./app/**/*.rb')).uniq
|
18
|
+
end
|
data/app/app_delegate.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module MotionModelResource
|
2
|
+
module DateParser
|
3
|
+
def self.parseDate(arg)
|
4
|
+
date_formatter = NSDateFormatter.alloc.init
|
5
|
+
date_formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZ"
|
6
|
+
date = date_formatter.dateFromString arg
|
7
|
+
|
8
|
+
return nil if date.blank?
|
9
|
+
|
10
|
+
date.description
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
module MotionModelResource
|
2
|
+
class WrapperNotDefinedError < Exception; end
|
3
|
+
class URLNotDefinedError < Exception; end
|
4
|
+
class ActionNotImplemented < Exception; end
|
5
|
+
|
6
|
+
module ApiWrapper
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(PublicClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
module PublicClassMethods
|
12
|
+
# Returns the last updated at or nil value of Model
|
13
|
+
def lastUpdate
|
14
|
+
order(:updated_at).first.try(:updated_at)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Loads the given URL and parse the JSON for new models.
|
18
|
+
# If the models are present, the model will update.
|
19
|
+
# If block given, the block will called, when the the models are saved. The model/s will be passed as an argument to the block.
|
20
|
+
def fetch(site, params = {}, &block)
|
21
|
+
raise MotionModelResource::WrapperNotDefinedError.new "Wrapper is not defined!" unless self.respond_to?(:wrapper)
|
22
|
+
BW::HTTP.get(site, params) do |response|
|
23
|
+
models = []
|
24
|
+
if response.ok? && response.body.present?
|
25
|
+
json = BW::JSON.parse(response.body.to_str)
|
26
|
+
models = updateModels(json)
|
27
|
+
end
|
28
|
+
|
29
|
+
block.call(models) if block.present? && block.respond_to?(:call)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Parses given JSON object and saves the founded models.
|
34
|
+
# Returns an array with models, or the founded model
|
35
|
+
def updateModels(json)
|
36
|
+
if json.is_a?(Array)
|
37
|
+
models = []
|
38
|
+
for jsonPart in json
|
39
|
+
model = buildModel(jsonPart)
|
40
|
+
if model.present?
|
41
|
+
model.save
|
42
|
+
models << model
|
43
|
+
end
|
44
|
+
end
|
45
|
+
return models
|
46
|
+
else
|
47
|
+
model = buildModel(json)
|
48
|
+
if model.present?
|
49
|
+
model.save
|
50
|
+
return model
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Builds a model for given JSON object. Returns a new or presend model.
|
56
|
+
def buildModel(json)
|
57
|
+
classname = name.downcase
|
58
|
+
|
59
|
+
model = where("id").eq(json["id"]).first
|
60
|
+
if model.present?
|
61
|
+
if model.wrap(json)
|
62
|
+
model.lastSyncAt = Time.now if model.respond_to?(:lastSyncAt)
|
63
|
+
return model
|
64
|
+
end
|
65
|
+
else
|
66
|
+
newModel = self.new
|
67
|
+
return newModel if newModel.wrap(json)
|
68
|
+
end
|
69
|
+
|
70
|
+
return nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Instance methods
|
75
|
+
|
76
|
+
# Saves the current model. Calls super when block is not given.
|
77
|
+
# If block is given, the url method will be needed to call the remote server.
|
78
|
+
# The answer of the server will be parsed and stored.
|
79
|
+
# If the record is a new one, a POST request will be fired, otherwise a PUT call comes to the server.
|
80
|
+
def save(options = {}, &block)
|
81
|
+
if block.present?
|
82
|
+
raise MotionModelResource::URLNotDefinedError.new "URL is not defined for #{self.class.name}!" unless self.class.respond_to?(:url)
|
83
|
+
|
84
|
+
action = if new_record?
|
85
|
+
"create"
|
86
|
+
elsif self.id.present?
|
87
|
+
"update"
|
88
|
+
else
|
89
|
+
raise MotionModelResource::ActionNotImplemented.new "Action ist not implemented for #{self.class.name}!"
|
90
|
+
end
|
91
|
+
|
92
|
+
model = self
|
93
|
+
|
94
|
+
model.id = nil if model.id.present? && action == "create"
|
95
|
+
|
96
|
+
hash = buildHashFromModel(self.class.name.downcase, self)
|
97
|
+
hash.merge!(options[:params]) if options[:params].present?
|
98
|
+
|
99
|
+
requestBlock = Proc.new do |response|
|
100
|
+
if response.ok? && response.body.present?
|
101
|
+
json = BW::JSON.parse(response.body.to_str)
|
102
|
+
|
103
|
+
model.wrap(json)
|
104
|
+
model.lastSyncAt = Time.now if model.respond_to?(:lastSyncAt)
|
105
|
+
model.save
|
106
|
+
else
|
107
|
+
model = nil
|
108
|
+
end
|
109
|
+
|
110
|
+
block.call(model) if block.present? && block.respond_to?(:call)
|
111
|
+
end
|
112
|
+
|
113
|
+
case action
|
114
|
+
when "create"
|
115
|
+
BW::HTTP.post(self.class.url, {payload: hash}, &requestBlock)
|
116
|
+
when "update"
|
117
|
+
BW::HTTP.put("#{self.class.url}/#{model.id}", {payload: hash}, &requestBlock)
|
118
|
+
end
|
119
|
+
else
|
120
|
+
super
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns a hash with given model
|
125
|
+
def buildHashFromModel(mainKey, model)
|
126
|
+
hash = {
|
127
|
+
mainKey => {}
|
128
|
+
}
|
129
|
+
hash[mainKey] = {}
|
130
|
+
|
131
|
+
model.attributes.each do |key, attribute|
|
132
|
+
if model.class.has_many_columns.keys.include?(key)
|
133
|
+
newKey = attribute.first.class.name.pluralize.downcase
|
134
|
+
hash[mainKey][newKey] = []
|
135
|
+
for a in attribute
|
136
|
+
hash[mainKey][newKey].push(buildHashFromModel(newKey, a)[newKey])
|
137
|
+
end
|
138
|
+
elsif attribute.respond_to?(:attributes)
|
139
|
+
newKey = attribute.class.name.downcase
|
140
|
+
h = buildHashFromModel(newKey, attribute)
|
141
|
+
hash[mainKey][newKey] = h[newKey] if h.has_key?(newKey)
|
142
|
+
else
|
143
|
+
model.class.wrapper[:fields].each do |wrapperKey, wrapperValue|
|
144
|
+
hash[mainKey][wrapperKey] = attribute if wrapperValue == key
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
return hash
|
150
|
+
end
|
151
|
+
|
152
|
+
# Loads the given URL and parse the JSON for a model.
|
153
|
+
# If the model is present, the model will updates.
|
154
|
+
# If block given, the block will called, when the the model is saved. The model will be passed as an argument to the block.
|
155
|
+
def fetch(site, params, &block)
|
156
|
+
raise MotionModelResource::WrapperNotDefinedError.new "Wrapper is not defined!" unless self.class.respond_to?(:wrapper)
|
157
|
+
model = self
|
158
|
+
BW::HTTP.get(site, params) do |response|
|
159
|
+
if response.ok? && response.body.present?
|
160
|
+
json = BW::JSON.parse(response.body.to_str)
|
161
|
+
model.wrap(json)
|
162
|
+
model.lastSyncAt = Time.now if model.respond_to?(:lastSyncAt)
|
163
|
+
|
164
|
+
model.save
|
165
|
+
end
|
166
|
+
|
167
|
+
block.call if block.present? && block.respond_to?(:call)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Wraps the current model with the given JSON.
|
172
|
+
# All the fields found in JSON and self.wrapper will be parsed.
|
173
|
+
# Returns true, when no error exists
|
174
|
+
def wrap(modelJson)
|
175
|
+
return unless self.class.respond_to?(:wrapper)
|
176
|
+
|
177
|
+
self.class.wrapper[:fields].each do |online, local|
|
178
|
+
if modelJson.respond_to?("key?") && modelJson.key?("#{online}")
|
179
|
+
value = parseValue(local, modelJson[online])
|
180
|
+
self.send("#{local}=", value)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
if self.class.wrapper[:relations].present?
|
185
|
+
self.class.wrapper[:relations].each do |relation|
|
186
|
+
if modelJson.respond_to?("key?") && modelJson.key?("#{relation}") && modelJson["#{relation}"].present?
|
187
|
+
klass = Object.const_get(relation.to_s.singularize.camelize)
|
188
|
+
newRelation = klass.updateModels(modelJson["#{relation}"])
|
189
|
+
self.send("#{relation}=", newRelation) rescue NoMethodError # not correct implemented in MotionModel
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
true
|
195
|
+
end
|
196
|
+
|
197
|
+
# Parses given value for key in the right format for MotionModel.
|
198
|
+
# Currently only Date/Time support needed
|
199
|
+
def parseValue(key, value)
|
200
|
+
case self.column_type(key.to_sym)
|
201
|
+
when :date, :time then MotionModelResource::DateParser.parseDate value
|
202
|
+
else value
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
data/motion/version.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../motion/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Torben Toepper"]
|
6
|
+
gem.email = ["lshadyl@googlemail.com"]
|
7
|
+
gem.description = "Simple JSON API Wrapper for MotionModel on RubyMotion"
|
8
|
+
gem.summary = "Simple JSON API Wrapper for MotionModel on RubyMotion"
|
9
|
+
gem.homepage = "https://github.com/torben/motion-resource"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
|
+
gem.name = "motion_model_resource"
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
gem.add_dependency 'bubble-wrap', '>= 1.3.0'
|
16
|
+
gem.add_dependency 'motion-support', '>=0.1.0'
|
17
|
+
gem.add_dependency 'motion_model', '>=0.4.4'
|
18
|
+
gem.add_dependency 'webstub', '>=0.3.0'
|
19
|
+
gem.version = MotionModelResource::VERSION
|
20
|
+
gem.licenses = ["MIT"]
|
21
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
class UserWithOutWrapper
|
2
|
+
include MotionModel::Model
|
3
|
+
include MotionModel::ArrayModelAdapter
|
4
|
+
include MotionModelResource::ApiWrapper
|
5
|
+
columns name: :string,
|
6
|
+
email: :string,
|
7
|
+
age: :integer,
|
8
|
+
admin: :boolean
|
9
|
+
end
|
10
|
+
|
11
|
+
class User
|
12
|
+
include MotionModel::Model
|
13
|
+
include MotionModel::ArrayModelAdapter
|
14
|
+
include MotionModelResource::ApiWrapper
|
15
|
+
|
16
|
+
def self.url
|
17
|
+
"http://example.com/users"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.wrapper
|
21
|
+
@wrapper ||= {
|
22
|
+
fields: {
|
23
|
+
id: :id,
|
24
|
+
plan_id: :plan_id,
|
25
|
+
name: :name,
|
26
|
+
email: :email,
|
27
|
+
age: :age,
|
28
|
+
admin: :admin
|
29
|
+
},
|
30
|
+
relations: [:tasks, :plan]
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
columns name: :string,
|
35
|
+
plan_id: :plan_id,
|
36
|
+
email: :string,
|
37
|
+
age: :integer,
|
38
|
+
admin: :boolean
|
39
|
+
|
40
|
+
has_many :tasks
|
41
|
+
belongs_to :plan
|
42
|
+
end
|
43
|
+
|
44
|
+
class Task
|
45
|
+
include MotionModel::Model
|
46
|
+
include MotionModel::ArrayModelAdapter
|
47
|
+
include MotionModelResource::ApiWrapper
|
48
|
+
|
49
|
+
def self.url
|
50
|
+
"http://example.com/tasks"
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.wrapper
|
54
|
+
@wrapper ||= {
|
55
|
+
fields: {
|
56
|
+
id: :id,
|
57
|
+
user_id: :user_id,
|
58
|
+
name: :name,
|
59
|
+
},
|
60
|
+
relations: [:user]
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
columns name: :string
|
65
|
+
belongs_to :user
|
66
|
+
end
|
67
|
+
|
68
|
+
class Plan
|
69
|
+
include MotionModel::Model
|
70
|
+
include MotionModel::ArrayModelAdapter
|
71
|
+
include MotionModelResource::ApiWrapper
|
72
|
+
|
73
|
+
def self.url
|
74
|
+
"http://example.com/plans"
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.wrapper
|
78
|
+
@wrapper ||= {
|
79
|
+
fields: {
|
80
|
+
id: :id,
|
81
|
+
name: :name,
|
82
|
+
},
|
83
|
+
relations: [:users]
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
columns name: :string
|
88
|
+
has_many :users
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "Fetching a model" do
|
92
|
+
extend WebStub::SpecHelpers
|
93
|
+
|
94
|
+
it "should not wrap a model without wrapper method" do
|
95
|
+
lambda{
|
96
|
+
UserWithOutWrapper.fetch("http://localhost:3000/users/1")
|
97
|
+
}.should.raise(MotionModelResource::WrapperNotDefinedError)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should create a new model with API call" do
|
101
|
+
user_url = "http://localhost:3000/users/1"
|
102
|
+
stub_request(:get, user_url).to_return(json: {
|
103
|
+
name: "Peter",
|
104
|
+
email: "peter@pan.de",
|
105
|
+
age: 14,
|
106
|
+
admin: false
|
107
|
+
})
|
108
|
+
|
109
|
+
User.fetch(user_url) do |model|
|
110
|
+
resume
|
111
|
+
end
|
112
|
+
|
113
|
+
wait_max 1.0 do
|
114
|
+
user = User.first
|
115
|
+
user.should.not == nil
|
116
|
+
User.count.should.equal(1)
|
117
|
+
user.name.should.equal("Peter")
|
118
|
+
user.email.should.equal("peter@pan.de")
|
119
|
+
user.age.should.equal(14)
|
120
|
+
user.admin.should.equal(false)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should create a new model with dependencies" do
|
125
|
+
User.delete_all
|
126
|
+
Task.delete_all
|
127
|
+
Plan.delete_all
|
128
|
+
|
129
|
+
user_url = "http://localhost:3000/users/10"
|
130
|
+
stub_request(:get, user_url).to_return(json: {
|
131
|
+
id: 10,
|
132
|
+
plan_id: 5,
|
133
|
+
name: "Manuel",
|
134
|
+
email: "manuel@dudda.de",
|
135
|
+
age: 28,
|
136
|
+
admin: true,
|
137
|
+
tasks: [
|
138
|
+
{
|
139
|
+
id: 1,
|
140
|
+
name: 'Cleaning up the closet',
|
141
|
+
user_id: 10
|
142
|
+
},
|
143
|
+
{
|
144
|
+
id: 2,
|
145
|
+
name: 'Drinking soda',
|
146
|
+
user_id: 10
|
147
|
+
}
|
148
|
+
],
|
149
|
+
plan: {
|
150
|
+
id: 5,
|
151
|
+
name: 'Gold'
|
152
|
+
}
|
153
|
+
})
|
154
|
+
|
155
|
+
User.fetch(user_url) do |model|
|
156
|
+
resume
|
157
|
+
end
|
158
|
+
|
159
|
+
wait_max 1.0 do
|
160
|
+
User.count.should.equal(1)
|
161
|
+
Plan.count.should.equal(1)
|
162
|
+
Task.count.should.equal(2)
|
163
|
+
|
164
|
+
user = User.find(10)
|
165
|
+
user.plan.name.should.equal("Gold")
|
166
|
+
user.tasks.first.name.should.equal("Cleaning up the closet")
|
167
|
+
user.tasks.last.name.should.equal("Drinking soda")
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '#parseValue' do
|
172
|
+
it 'should parse a date in the right format for MotionModel' do
|
173
|
+
time_string = "2013-11-03T16:59:35+01:00"
|
174
|
+
|
175
|
+
MotionModelResource::DateParser.parseDate(time_string).should.equal("2013-11-03 15:59:35 +0000")
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should return nil, when giving a wrong time' do
|
179
|
+
time_string = "2342"
|
180
|
+
time_string2 = "2013-21-03T16:59:35+01:00"
|
181
|
+
|
182
|
+
MotionModelResource::DateParser.parseDate(time_string).should.equal nil
|
183
|
+
MotionModelResource::DateParser.parseDate(time_string2).should.equal nil
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: motion_model_resource
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.6
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Torben Toepper
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-11-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bubble-wrap
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.3.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.3.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: motion-support
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.1.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.1.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: motion_model
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.4.4
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.4.4
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: webstub
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.3.0
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.3.0
|
78
|
+
description: Simple JSON API Wrapper for MotionModel on RubyMotion
|
79
|
+
email:
|
80
|
+
- lshadyl@googlemail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- CHANGELOG
|
87
|
+
- Gemfile
|
88
|
+
- Gemfile.lock
|
89
|
+
- LICENSE
|
90
|
+
- README.md
|
91
|
+
- Rakefile
|
92
|
+
- app/app_delegate.rb
|
93
|
+
- lib/motion_model_resource.rb
|
94
|
+
- motion/resource/data_parser.rb
|
95
|
+
- motion/resource/wrapper.rb
|
96
|
+
- motion/version.rb
|
97
|
+
- motion_model_resource.gemspec
|
98
|
+
- spec/wrapper_spec.rb
|
99
|
+
homepage: https://github.com/torben/motion-resource
|
100
|
+
licenses:
|
101
|
+
- MIT
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 1.8.24
|
121
|
+
signing_key:
|
122
|
+
specification_version: 3
|
123
|
+
summary: Simple JSON API Wrapper for MotionModel on RubyMotion
|
124
|
+
test_files:
|
125
|
+
- spec/wrapper_spec.rb
|