motion-resource 0.0.1
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/.gitignore +9 -0
- data/Gemfile +8 -0
- data/README.md +53 -0
- data/Rakefile +14 -0
- data/app/app_delegate.rb +7 -0
- data/examples/Colr/.gitignore +13 -0
- data/examples/Colr/Rakefile +10 -0
- data/examples/Colr/app/app_delegate.rb +10 -0
- data/examples/Colr/app/color.rb +15 -0
- data/examples/Colr/app/color_view_controller.rb +46 -0
- data/examples/Colr/app/initializer.rb +2 -0
- data/examples/Colr/app/main_navigation_controller.rb +6 -0
- data/examples/Colr/app/tag.rb +5 -0
- data/examples/Colr/app/tags_view_controller.rb +23 -0
- data/lib/motion-resource.rb +5 -0
- data/lib/motion-resource/associations.rb +94 -0
- data/lib/motion-resource/attributes.rb +37 -0
- data/lib/motion-resource/base.rb +51 -0
- data/lib/motion-resource/crud.rb +33 -0
- data/lib/motion-resource/find.rb +67 -0
- data/lib/motion-resource/requests.rb +64 -0
- data/lib/motion-resource/string.rb +23 -0
- data/lib/motion-resource/urls.rb +29 -0
- data/lib/motion-resource/version.rb +3 -0
- data/motion-resource.gemspec +20 -0
- data/spec/env.rb +41 -0
- data/spec/motion-resource/associations/belongs_to_spec.rb +116 -0
- data/spec/motion-resource/associations/has_many_spec.rb +128 -0
- data/spec/motion-resource/associations/has_one_spec.rb +69 -0
- data/spec/motion-resource/associations/scope_spec.rb +21 -0
- data/spec/motion-resource/attributes_spec.rb +64 -0
- data/spec/motion-resource/base_spec.rb +54 -0
- data/spec/motion-resource/crud_spec.rb +141 -0
- data/spec/motion-resource/find_spec.rb +90 -0
- data/spec/motion-resource/requests_spec.rb +119 -0
- data/spec/motion-resource/string_spec.rb +26 -0
- data/spec/motion-resource/urls_spec.rb +52 -0
- metadata +140 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
module MotionResource
|
2
|
+
class Base
|
3
|
+
class << self
|
4
|
+
def find(id, params = {}, &block)
|
5
|
+
fetch_member(member_url.fill_url_params(params.merge(id: id)), &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def find_all(params = {}, &block)
|
9
|
+
fetch_collection(collection_url.fill_url_params(params), &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def fetch_member(url, &block)
|
13
|
+
get(url) do |response, json|
|
14
|
+
if response.ok?
|
15
|
+
obj = instantiate(json)
|
16
|
+
request_block_call(block, obj, response)
|
17
|
+
else
|
18
|
+
request_block_call(block, nil, response)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch_collection(url, &block)
|
24
|
+
get(url) do |response, json|
|
25
|
+
if response.ok?
|
26
|
+
objs = []
|
27
|
+
arr_rep = nil
|
28
|
+
if json.class == Array
|
29
|
+
arr_rep = json
|
30
|
+
elsif json.class == Hash
|
31
|
+
plural = self.name.underscore.pluralize
|
32
|
+
if json.has_key?(plural) || json.has_key?(plural.to_sym)
|
33
|
+
arr_rep = json[plural] || json[plural.to_sym]
|
34
|
+
end
|
35
|
+
else
|
36
|
+
# the returned data was something else
|
37
|
+
# ie a string, number
|
38
|
+
request_block_call(block, nil, response)
|
39
|
+
return
|
40
|
+
end
|
41
|
+
arr_rep.each { |one_obj_hash|
|
42
|
+
objs << instantiate(one_obj_hash)
|
43
|
+
}
|
44
|
+
request_block_call(block, objs, response)
|
45
|
+
else
|
46
|
+
request_block_call(block, nil, response)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def request_block_call(block, default_arg, extra_arg)
|
53
|
+
if block
|
54
|
+
if block.arity == 1
|
55
|
+
block.call default_arg
|
56
|
+
elsif block.arity == 2
|
57
|
+
block.call default_arg, extra_arg
|
58
|
+
else
|
59
|
+
raise "Not enough arguments to block"
|
60
|
+
end
|
61
|
+
else
|
62
|
+
raise "No block given"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module MotionResource
|
2
|
+
class Base
|
3
|
+
HTTP_METHODS = [:get, :post, :put, :delete]
|
4
|
+
|
5
|
+
HTTP_METHODS.each do |method|
|
6
|
+
define_method method do |*args, &block|
|
7
|
+
self.class.send(method, *args, &block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def get(url, params = {}, &block)
|
13
|
+
http_call(:get, url, params, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def post(url, params = {}, &block)
|
17
|
+
http_call(:post, url, params, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def put(url, params = {}, &block)
|
21
|
+
http_call(:put, url, params, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(url, params = {}, &block)
|
25
|
+
http_call(:delete, url, params, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def complete_url(fragment)
|
30
|
+
if fragment[0..3] == "http"
|
31
|
+
return fragment
|
32
|
+
end
|
33
|
+
(self.root_url || MotionResource::Base.root_url) + fragment
|
34
|
+
end
|
35
|
+
|
36
|
+
def http_call(method, url, call_options = {}, &block)
|
37
|
+
options = call_options
|
38
|
+
options.merge!(MotionResource::Base.default_url_options || {})
|
39
|
+
url += self.extension
|
40
|
+
if query = options.delete(:query)
|
41
|
+
if url.index("?").nil?
|
42
|
+
url += "?"
|
43
|
+
end
|
44
|
+
url += query.map{|k,v| "#{k}=#{v}"}.join('&')
|
45
|
+
end
|
46
|
+
if self.default_url_options
|
47
|
+
options.merge!(self.default_url_options)
|
48
|
+
end
|
49
|
+
BubbleWrap::HTTP.send(method, complete_url(url), options) do |response|
|
50
|
+
if response.ok?
|
51
|
+
body = response.body.to_str.strip rescue nil
|
52
|
+
if body.blank?
|
53
|
+
block.call(response, {})
|
54
|
+
else
|
55
|
+
block.call response, BubbleWrap::JSON.parse(body)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
block.call response, nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class String
|
2
|
+
# Takes in a hash and spits out the formatted string
|
3
|
+
# Checks the delegate first
|
4
|
+
def fill_url_params(params = {}, delegate = nil)
|
5
|
+
params ||= {}
|
6
|
+
split = self.split '/'
|
7
|
+
split.collect { |path|
|
8
|
+
ret = path
|
9
|
+
if path[0] == ':'
|
10
|
+
path_sym = path[1..-1].to_sym
|
11
|
+
|
12
|
+
curr = nil
|
13
|
+
if delegate && delegate.respond_to?(path_sym)
|
14
|
+
curr = delegate.send(path_sym)
|
15
|
+
end
|
16
|
+
|
17
|
+
ret = (curr || params[path_sym] || path).to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
ret
|
21
|
+
}.join '/'
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module MotionResource
|
2
|
+
class Base
|
3
|
+
class_inheritable_accessor :collection_url, :member_url
|
4
|
+
class_inheritable_accessor :root_url, :default_url_options
|
5
|
+
class_inheritable_accessor :extension
|
6
|
+
self.extension = '.json'
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def custom_urls(params = {})
|
10
|
+
params.each do |name, url_format|
|
11
|
+
define_method name do |params = {}|
|
12
|
+
url_format.fill_url_params(params, self)
|
13
|
+
end
|
14
|
+
define_singleton_method name do
|
15
|
+
url_format
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def collection_url(params = {})
|
22
|
+
self.class.collection_url.fill_url_params(params, self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def member_url(params = {})
|
26
|
+
self.class.member_url.fill_url_params(params, self)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/motion-resource/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "motion-resource"
|
6
|
+
s.version = MotionResource::VERSION
|
7
|
+
s.authors = ["Thomas Kadauke"]
|
8
|
+
s.email = ["thomas.kadauke@googlemail.com"]
|
9
|
+
s.homepage = "https://github.com/tkadauke/motion-resource"
|
10
|
+
s.summary = "Access RESTful resources from your iOS app"
|
11
|
+
s.description = "Access RESTful resources from your iOS app. Inspired by ActiveResource."
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split($\)
|
14
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
|
17
|
+
s.add_dependency 'bubble-wrap'
|
18
|
+
s.add_dependency 'motion-support'
|
19
|
+
s.add_development_dependency 'rake'
|
20
|
+
end
|
data/spec/env.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
MotionResource::Base.root_url = 'http://example.com/'
|
2
|
+
|
3
|
+
class Post < MotionResource::Base
|
4
|
+
attr_accessor :text
|
5
|
+
|
6
|
+
self.member_url = 'posts/:id'
|
7
|
+
|
8
|
+
has_many :comments
|
9
|
+
end
|
10
|
+
|
11
|
+
class Comment < MotionResource::Base
|
12
|
+
attr_accessor :post_id, :text
|
13
|
+
|
14
|
+
self.member_url = 'comments/:id'
|
15
|
+
self.collection_url = 'comments'
|
16
|
+
|
17
|
+
belongs_to :post
|
18
|
+
|
19
|
+
scope :recent, :url => 'comments/recent'
|
20
|
+
|
21
|
+
custom_urls :by_user_url => 'comments/by_user/:name'
|
22
|
+
end
|
23
|
+
|
24
|
+
class User < MotionResource::Base
|
25
|
+
self.member_url = 'users/:id'
|
26
|
+
|
27
|
+
has_one :profile
|
28
|
+
end
|
29
|
+
|
30
|
+
class Profile < MotionResource::Base
|
31
|
+
attr_accessor :name, :email
|
32
|
+
end
|
33
|
+
|
34
|
+
class Shape < MotionResource::Base
|
35
|
+
attribute :contents, :position
|
36
|
+
attr_accessor :created_at
|
37
|
+
end
|
38
|
+
|
39
|
+
class Rectangle < Shape
|
40
|
+
attribute :size
|
41
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
describe "belongs_to" do
|
2
|
+
extend WebStub::SpecHelpers
|
3
|
+
|
4
|
+
it "should define reader" do
|
5
|
+
Comment.new.should.respond_to :post
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should define writer" do
|
9
|
+
Comment.new.should.respond_to :post=
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should define reset method" do
|
13
|
+
Comment.new.should.respond_to :reset_post
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should reset" do
|
17
|
+
comment = Comment.new
|
18
|
+
comment.post = Post.new
|
19
|
+
comment.reset_post
|
20
|
+
comment.post.should.be.nil
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "reader" do
|
24
|
+
extend WebStub::SpecHelpers
|
25
|
+
|
26
|
+
before do
|
27
|
+
stub_request(:get, "http://example.com/posts/1.json").to_return(json: { id: 1, text: 'Hello' })
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should by default return nil when called without a block" do
|
31
|
+
Comment.new.post.should.be.nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return any cached values when called without a block" do
|
35
|
+
comment = Comment.new
|
36
|
+
post = Post.new
|
37
|
+
comment.post = post
|
38
|
+
comment.post.should == post
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should fetch resource when called with a block" do
|
42
|
+
@comment = Comment.new(:post_id => 1)
|
43
|
+
@comment.post do |result|
|
44
|
+
@result = result
|
45
|
+
resume
|
46
|
+
end
|
47
|
+
|
48
|
+
wait_max 1.0 do
|
49
|
+
@result.text.should == 'Hello'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should return cached resource immediately if exist when called with a block" do
|
54
|
+
post = Post.new
|
55
|
+
@comment = Comment.new
|
56
|
+
@comment.post = post
|
57
|
+
@comment.post do |result|
|
58
|
+
result.should == post
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should cache resource after fetching" do
|
63
|
+
@comment = Comment.new(:post_id => 1)
|
64
|
+
@comment.post do |result|
|
65
|
+
@result = result
|
66
|
+
resume
|
67
|
+
end
|
68
|
+
|
69
|
+
wait_max 1.0 do
|
70
|
+
@comment.post.should == @result
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "writer" do
|
76
|
+
it "should create model when assigned with hash" do
|
77
|
+
comment = Comment.new
|
78
|
+
comment.post = { :id => 1, :text => 'Hello' }
|
79
|
+
comment.post.should.is_a Post
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should set attributes when assigned with hash" do
|
83
|
+
comment = Comment.new
|
84
|
+
comment.post = { :id => 1, :text => 'Hello' }
|
85
|
+
comment.post.text.should == 'Hello'
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should use identity map when assigned with hash" do
|
89
|
+
post = Post.instantiate(:id => 10)
|
90
|
+
comment = Comment.new
|
91
|
+
comment.post = { :id => 10 }
|
92
|
+
comment.post.should == post
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should act like a regular setter when assigned with object" do
|
96
|
+
post = Post.new
|
97
|
+
comment = Comment.new
|
98
|
+
comment.post = post
|
99
|
+
comment.post.should == post
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "piggybacking" do
|
104
|
+
it "should set association when returned with model" do
|
105
|
+
stub_request(:get, "http://example.com/comments/1.json").to_return(json: { id: 1, post: { id: 2, text: 'Hello' } })
|
106
|
+
Comment.find(1) do |result|
|
107
|
+
@result = result
|
108
|
+
resume
|
109
|
+
end
|
110
|
+
|
111
|
+
wait_max 1.0 do
|
112
|
+
@result.post.text.should == 'Hello'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
describe "has_many" do
|
2
|
+
extend WebStub::SpecHelpers
|
3
|
+
|
4
|
+
it "should define reader" do
|
5
|
+
Post.new.should.respond_to :comments
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should define writer" do
|
9
|
+
Post.new.should.respond_to :comments=
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should define reset method" do
|
13
|
+
Post.new.should.respond_to :reset_comments
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should reset" do
|
17
|
+
post = Post.new
|
18
|
+
post.comments = [Comment.new]
|
19
|
+
post.reset_comments
|
20
|
+
post.comments.should == []
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "reader" do
|
24
|
+
extend WebStub::SpecHelpers
|
25
|
+
|
26
|
+
before do
|
27
|
+
stub_request(:get, "http://example.com/comments.json").to_return(json: [{ id: 1, text: 'Whats up?' }])
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should by default return an empty array when called without a block" do
|
31
|
+
Post.new.comments.should == []
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return any cached values when called without a block" do
|
35
|
+
comment = Comment.new
|
36
|
+
post = Post.new
|
37
|
+
post.comments = [comment]
|
38
|
+
post.comments.should == [comment]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should fetch resources when called with a block" do
|
42
|
+
@post = Post.new
|
43
|
+
@post.comments do |results|
|
44
|
+
@results = results
|
45
|
+
resume
|
46
|
+
end
|
47
|
+
|
48
|
+
wait_max 1.0 do
|
49
|
+
@results.size.should == 1
|
50
|
+
@results.first.text.should == 'Whats up?'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should return cached resources immediately if exist when called with a block" do
|
55
|
+
comment = Comment.new
|
56
|
+
@post = Post.new
|
57
|
+
@post.comments = [comment]
|
58
|
+
@post.comments do |results|
|
59
|
+
results.should == [comment]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should assign backward associations when fetching resources" do
|
64
|
+
@post = Post.new
|
65
|
+
@post.comments do |results|
|
66
|
+
@results = results
|
67
|
+
resume
|
68
|
+
end
|
69
|
+
|
70
|
+
wait_max 1.0 do
|
71
|
+
@results.first.post.should == @post
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should cache resources after fetching" do
|
76
|
+
@post = Post.new
|
77
|
+
@post.comments do |results|
|
78
|
+
@results = results
|
79
|
+
resume
|
80
|
+
end
|
81
|
+
|
82
|
+
wait_max 1.0 do
|
83
|
+
@post.comments.should == @results
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "writer" do
|
89
|
+
it "should create model when assigned with hash" do
|
90
|
+
post = Post.new
|
91
|
+
post.comments = [{ :id => 1, :text => 'Whats up?' }]
|
92
|
+
post.comments.first.should.is_a Comment
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should set attributes when assigned with hash" do
|
96
|
+
post = Post.new
|
97
|
+
post.comments = [{ :id => 1, :text => 'Whats up?' }]
|
98
|
+
post.comments.first.text.should == 'Whats up?'
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should use identity map when assigned with hash" do
|
102
|
+
comment = Comment.instantiate(:id => 10)
|
103
|
+
post = Post.new
|
104
|
+
post.comments = [{ :id => 10 }]
|
105
|
+
post.comments.first.should == comment
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should act like a regular setter when assigned with array" do
|
109
|
+
post = Post.new
|
110
|
+
post.comments = [Comment.new]
|
111
|
+
post.comments.size.should == 1
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "piggybacking" do
|
116
|
+
it "should set association when returned with model" do
|
117
|
+
stub_request(:get, "http://example.com/posts/1.json").to_return(json: { id: 1, comments: [{ id: 2, text: 'Whats up?' }] })
|
118
|
+
Post.find(1) do |result|
|
119
|
+
@result = result
|
120
|
+
resume
|
121
|
+
end
|
122
|
+
|
123
|
+
wait_max 1.0 do
|
124
|
+
@result.comments.first.text.should == 'Whats up?'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|