api_resource 0.6.21 → 0.6.22
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.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/Gemfile.lock +1 -1
- data/LICENSE.txt +22 -0
- data/README.md +16 -9
- data/docs/Attributes.md +64 -0
- data/docs/Caching.md +45 -0
- data/docs/GettingStarted.md +149 -0
- data/docs/Relationships.md +136 -0
- data/docs/ResourceDefinition.md +80 -0
- data/docs/Retrieval.md +279 -0
- data/docs/Serialization.md +56 -0
- data/lib/api_resource/associations/has_many_remote_object_proxy.rb +2 -2
- data/lib/api_resource/attributes.rb +16 -4
- data/lib/api_resource/base.rb +98 -19
- data/lib/api_resource/conditions/abstract_condition.rb +241 -129
- data/lib/api_resource/conditions/include_condition.rb +7 -1
- data/lib/api_resource/conditions/pagination_condition.rb +37 -0
- data/lib/api_resource/conditions/where_condition.rb +19 -0
- data/lib/api_resource/conditions.rb +18 -2
- data/lib/api_resource/connection.rb +27 -13
- data/lib/api_resource/exceptions.rb +11 -11
- data/lib/api_resource/finders/abstract_finder.rb +176 -95
- data/lib/api_resource/finders/multi_object_association_finder.rb +10 -9
- data/lib/api_resource/finders/resource_finder.rb +59 -49
- data/lib/api_resource/finders/single_finder.rb +5 -6
- data/lib/api_resource/finders/single_object_association_finder.rb +52 -51
- data/lib/api_resource/finders.rb +1 -1
- data/lib/api_resource/formats/file_upload_format.rb +75 -0
- data/lib/api_resource/formats.rb +4 -1
- data/lib/api_resource/response.rb +108 -0
- data/lib/api_resource/scopes.rb +62 -5
- data/lib/api_resource/serializer.rb +1 -1
- data/lib/api_resource/typecasters/boolean_typecaster.rb +1 -0
- data/lib/api_resource/typecasters/integer_typecaster.rb +1 -0
- data/lib/api_resource/typecasters/time_typecaster.rb +12 -4
- data/lib/api_resource/version.rb +1 -1
- data/lib/api_resource.rb +1 -0
- data/spec/lib/associations/has_one_remote_object_proxy_spec.rb +4 -4
- data/spec/lib/associations_spec.rb +3 -3
- data/spec/lib/attributes_spec.rb +16 -1
- data/spec/lib/base_spec.rb +121 -39
- data/spec/lib/conditions/{abstract_conditions_spec.rb → abstract_condition_spec.rb} +23 -11
- data/spec/lib/conditions/pagination_condition_spec.rb +88 -0
- data/spec/lib/finders/multi_object_association_finder_spec.rb +55 -27
- data/spec/lib/finders/resource_finder_spec.rb +26 -2
- data/spec/lib/finders/single_object_association_finder_spec.rb +14 -6
- data/spec/lib/finders_spec.rb +81 -81
- data/spec/lib/observing_spec.rb +3 -4
- data/spec/lib/response_spec.rb +18 -0
- data/spec/lib/scopes_spec.rb +25 -1
- data/spec/lib/typecasters/boolean_typecaster_spec.rb +1 -1
- data/spec/lib/typecasters/integer_typecaster_spec.rb +1 -1
- data/spec/lib/typecasters/time_typecaster_spec.rb +6 -0
- data/spec/support/files/bg-awesome.jpg +0 -0
- data/spec/support/mocks/test_resource_mocks.rb +26 -16
- data/spec/support/requests/test_resource_requests.rb +27 -23
- metadata +24 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7509a2d4163d8d10f94db97575b59deec95663a2
|
4
|
+
data.tar.gz: a540de64f15a6196656ca31b43efdee89c816c66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1860666ecc1aa458a8f35121c6b98017af00161f1ce418476d12c823efa77b4f5e19b24ba6c559b9855cfccbe9ec1827d37d5c3f12400a1bedbf3aadf282a20
|
7
|
+
data.tar.gz: 9d22a6386bc3f13727f7a3e2ea56112b07012874237f340b78b77d5b07a7422c009f160ecb01a0311c6ffa341ea24f8fa63f1595b2137f815d8b8a9607ab7c0a
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown
|
data/Gemfile.lock
CHANGED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Ethan Langevin
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -2,23 +2,30 @@
|
|
2
2
|
|
3
3
|
[](https://travis-ci.org/LifebookerInc/api_resource)
|
4
4
|
|
5
|
-
|
5
|
+
ApiResource is an ActiveModel-compatible library for retrieving and
|
6
|
+
persisting data via APIs
|
6
7
|
|
7
|
-
|
8
|
+
## Getting Started
|
8
9
|
|
9
|
-
|
10
|
+
1. Add this line to your application's Gemfile:
|
10
11
|
|
11
|
-
|
12
|
+
gem 'api_resource'
|
12
13
|
|
13
|
-
|
14
|
+
1. And then execute:
|
14
15
|
|
15
|
-
|
16
|
+
$ bundle
|
16
17
|
|
17
|
-
|
18
|
+
1. Follow our {file:docs/GettingStarted.md Getting Started Guide} to learn more about how to use ApiResource
|
18
19
|
|
19
|
-
##
|
20
|
+
## Read more about some of the core concepts in ApiResource
|
21
|
+
|
22
|
+
### {file:docs/ResourceDefinition.md The Resource Definition}
|
23
|
+
### {file:docs/Attributes.md Attributes}
|
24
|
+
### {file:docs/Relationships.md Relationships}
|
25
|
+
### {file:docs/Retrieval.md Retrieving Records}
|
26
|
+
### {file:docs/Serialization.md Serialization}
|
27
|
+
### {file:docs/Caching.md Caching}
|
20
28
|
|
21
|
-
TODO: Write usage instructions here
|
22
29
|
|
23
30
|
## Contributing
|
24
31
|
|
data/docs/Attributes.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Attributes
|
2
|
+
|
3
|
+
Attributes are read from the Server application's
|
4
|
+
{file:docs/ResourceDefinition.md Resource Definition}.
|
5
|
+
|
6
|
+
## Visibility
|
7
|
+
|
8
|
+
### Public
|
9
|
+
|
10
|
+
Can read and write, data is sent to the server if it has changed
|
11
|
+
|
12
|
+
### Protected
|
13
|
+
|
14
|
+
Can read only. Cannot set or mass-assign
|
15
|
+
|
16
|
+
# Resource definition
|
17
|
+
# {
|
18
|
+
# ...
|
19
|
+
# protected : [
|
20
|
+
# ['updated_at', 'time']
|
21
|
+
# ]
|
22
|
+
# ...
|
23
|
+
# }
|
24
|
+
|
25
|
+
Resource::Person.new(updated_at: Time.now) # => Raises error
|
26
|
+
resource = Resource::Person.new
|
27
|
+
resource.updated_at = Time.now #=> Raises error
|
28
|
+
|
29
|
+
### Private
|
30
|
+
|
31
|
+
Field is not even definted or included in the
|
32
|
+
{file:docs/ResourceDefinition.md Resource Definition}. For more
|
33
|
+
info see {http://path/to/server/docs ApiResourceServer ApiResource Server}
|
34
|
+
|
35
|
+
|
36
|
+
## Typecasting
|
37
|
+
|
38
|
+
ApiResource supports the following types:
|
39
|
+
|
40
|
+
1. Array
|
41
|
+
1. Boolean
|
42
|
+
1. Date
|
43
|
+
1. Float
|
44
|
+
1. Integer
|
45
|
+
1. String
|
46
|
+
1. Time
|
47
|
+
|
48
|
+
Attributes are given a type in the
|
49
|
+
{file:docs/ResourceDefinition.md Resource Definition}. For more
|
50
|
+
info see {http://path/to/server/docs ApiResourceServer ApiResource Server}
|
51
|
+
|
52
|
+
## Dirty Tracking
|
53
|
+
|
54
|
+
ApiResource::Base includes ActiveModel::Dirty and uses Dirty Tracking to
|
55
|
+
determine which attributes to send to the server on save. Only attributes
|
56
|
+
that have changed are sent to the server.
|
57
|
+
|
58
|
+
person = Resource::Person.find(1)
|
59
|
+
person.attributes
|
60
|
+
# => { first_name: 'Aaron', last_name: 'Burr', ...}
|
61
|
+
person.last_name = 'Copeland'
|
62
|
+
|
63
|
+
person.save
|
64
|
+
# PUT /people/1.json { person: { last_name: 'Copeland' } }
|
data/docs/Caching.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# Caching
|
2
|
+
|
3
|
+
Caching is implemented at the connection level and caches the
|
4
|
+
respone body and headers of any GET request made while caching
|
5
|
+
is active for the period specified
|
6
|
+
|
7
|
+
## Where is it cached?
|
8
|
+
|
9
|
+
By default we use Rails.cache
|
10
|
+
|
11
|
+
# Configure the cache to use a MemoryStore
|
12
|
+
ApiResource::Base.cache = ActiveSupport::Cache::MemoryStore.new
|
13
|
+
|
14
|
+
|
15
|
+
## Activating caching globally
|
16
|
+
|
17
|
+
This will cache all GET requests for 30 seconds
|
18
|
+
|
19
|
+
ApiResource::Base.ttl = 30.seconds
|
20
|
+
|
21
|
+
## Activating caching for a given find call
|
22
|
+
|
23
|
+
Resource::Person.born_on(Date.today).expires_in(30.seconds).all
|
24
|
+
|
25
|
+
|
26
|
+
## Cache expiration
|
27
|
+
|
28
|
+
Resource::Person.born_on(Date.today).expires_in(30.seconds).all
|
29
|
+
|
30
|
+
# no HTTP request here
|
31
|
+
Resource::Person.born_on(Date.today).expires_in(30.seconds).all
|
32
|
+
|
33
|
+
sleep(30)
|
34
|
+
|
35
|
+
# cache has expired - new HTTP Request
|
36
|
+
Resource::Person.born_on(Date.today).expires_in(30.seconds).all
|
37
|
+
|
38
|
+
|
39
|
+
## Differing cache times for the same URL
|
40
|
+
|
41
|
+
Cache requests are specific to the cache interval specified. For example,
|
42
|
+
if a find with a 60 second TTL and then another with a 30 second TTL will
|
43
|
+
make two calls
|
44
|
+
|
45
|
+
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# Getting Started with ApiResource
|
2
|
+
|
3
|
+
## Creating a model in your Client application
|
4
|
+
|
5
|
+
All of your models will extend {ApiResource::Base}
|
6
|
+
|
7
|
+
# This must be loaded explicitly or created in a directory that is
|
8
|
+
# autoloaded
|
9
|
+
|
10
|
+
# lib/resources/person.rb
|
11
|
+
module Resources
|
12
|
+
class Person < ApiResource::Base
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
## Adding a resource definition in your Server application
|
17
|
+
|
18
|
+
On the server-side, you will need to activate the resource
|
19
|
+
|
20
|
+
# app/modeperson.rb
|
21
|
+
class Person < ActiveRecord::Base
|
22
|
+
|
23
|
+
# this gives Person access to the methods necessary
|
24
|
+
# to create a resrouce definition
|
25
|
+
include LifebookerCommon::Model::Resource
|
26
|
+
|
27
|
+
# Example attributes
|
28
|
+
#
|
29
|
+
# :first_name, :last_name, :birthday
|
30
|
+
|
31
|
+
# Example validation
|
32
|
+
#
|
33
|
+
validates :birthday,
|
34
|
+
presence: true
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
Read more about {file:docs/ResourceDefinition.md Resource Definitions}
|
39
|
+
|
40
|
+
## Adding basic routes and controller actions to your Server application
|
41
|
+
|
42
|
+
# config/routes.rb
|
43
|
+
resources :people
|
44
|
+
|
45
|
+
# app/controllers/people_controller.rb
|
46
|
+
class PeopleController < ApplicationController
|
47
|
+
|
48
|
+
respond_to :json
|
49
|
+
|
50
|
+
# GET /people/new
|
51
|
+
def new
|
52
|
+
respond_with(Person.resource_definition)
|
53
|
+
end
|
54
|
+
|
55
|
+
# GET /people
|
56
|
+
def index
|
57
|
+
respond_with(Person.all)
|
58
|
+
end
|
59
|
+
|
60
|
+
# GET /people/:id
|
61
|
+
def show
|
62
|
+
@person = Person.find(params[:id])
|
63
|
+
respond_with(@person)
|
64
|
+
end
|
65
|
+
|
66
|
+
# POST /people
|
67
|
+
def create
|
68
|
+
@person = Person.create(params[:person])
|
69
|
+
respond_with(@person)
|
70
|
+
end
|
71
|
+
|
72
|
+
# PUT /people/:id
|
73
|
+
def update
|
74
|
+
@person = Person.find(params[:id])
|
75
|
+
@person.update_attributes(params[:person])
|
76
|
+
respond_with(@person)
|
77
|
+
end
|
78
|
+
|
79
|
+
# DELETE /people/:id
|
80
|
+
def destory
|
81
|
+
@person = Person.find(params[:id])
|
82
|
+
@person.destory
|
83
|
+
respond_with(@person)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
## Creating a new record in your Client application
|
88
|
+
|
89
|
+
ApiResource knows about your Server model's attributes through its
|
90
|
+
{file:docs/ResourceDefinition.md Resource Definition}, so you can just
|
91
|
+
set its attributes and call {#save}
|
92
|
+
|
93
|
+
It attempts to replicate the behaviors of ActiveRecord as closely as possible
|
94
|
+
|
95
|
+
# in any part of your Client application
|
96
|
+
@person = Resources::Person.new(first_name: 'Aaron', last_name: 'Burr')
|
97
|
+
@person.save #=> true/false
|
98
|
+
|
99
|
+
# if we have errors
|
100
|
+
@person.errors #=> ActiveModel::Errors
|
101
|
+
@person.errors.full_messages #=> ['Birthday is required.']
|
102
|
+
|
103
|
+
Read more about {file:docs/Persistance.md Persistance}
|
104
|
+
|
105
|
+
## Finding a single record in your Client application
|
106
|
+
|
107
|
+
# raises ApiResource::ResourceNotFound if no Person is found on
|
108
|
+
# the server and a 404 is returned
|
109
|
+
#
|
110
|
+
# GET /people/#{params[:id]}.json
|
111
|
+
@person = Resource::Person.find(params[:id])
|
112
|
+
|
113
|
+
## Finding multiple records in your Client application
|
114
|
+
|
115
|
+
The query interface mimics ActiveRecord/Arel and reads scopes from the
|
116
|
+
{file:docs/ResourceDefinition.md Resource Definitions}
|
117
|
+
|
118
|
+
# GET /people.json
|
119
|
+
@people = Resource::Person.all # all people
|
120
|
+
|
121
|
+
# GET /people.json?first_name=Aaron
|
122
|
+
@people = Resource::Person.where(first_name: 'Aaron')
|
123
|
+
#=> ApiResource::ScopeCondition - Loaded on demand when you start
|
124
|
+
# iterating through the resource (e.g. @people.each {...})
|
125
|
+
|
126
|
+
For more information see {file:docs/Retrieval.md#scopes Scopes}
|
127
|
+
|
128
|
+
## Updating a record
|
129
|
+
|
130
|
+
To update, you just find one or more records and call
|
131
|
+
{ApiResource::Base#update_attributes #update_attributes} on each record
|
132
|
+
|
133
|
+
@person = Resource::Person.find(1)
|
134
|
+
@person.update_attributes(
|
135
|
+
first_name: 'Joseph',
|
136
|
+
last_name: 'Stalin',
|
137
|
+
birthday: nil
|
138
|
+
)
|
139
|
+
# => true/false
|
140
|
+
|
141
|
+
@person.errors.full_messages # => ['Birthday is required.']
|
142
|
+
|
143
|
+
## Deleting a record
|
144
|
+
|
145
|
+
To delete, just find a record and call {ApiResource::Base#destroy #destroy}
|
146
|
+
on it
|
147
|
+
|
148
|
+
@person = Resource::Person.find(1)
|
149
|
+
@person.destroy # => true/false
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# Relationships
|
2
|
+
|
3
|
+
ApiResource mimics ActiveRecord's relationships, but loads the data via an
|
4
|
+
API or instantiates a record when data is nested
|
5
|
+
|
6
|
+
## There are 3 ways to include associated data in a response for APIResource
|
7
|
+
|
8
|
+
The proper approach will depend on the specifics of the Server and Client
|
9
|
+
applications. For example, if an associated resource is used just about
|
10
|
+
every time the parent is retrieved, it makes sense to nest it.
|
11
|
+
|
12
|
+
In general, nesting increases the overall complexity of the Server app and
|
13
|
+
slows it down though, so it should be used with caution
|
14
|
+
|
15
|
+
Models in the Server application
|
16
|
+
|
17
|
+
class Person < ActiveRecord::Base
|
18
|
+
belongs_to :state
|
19
|
+
has_many :weapons, through: :person_weapons
|
20
|
+
end
|
21
|
+
|
22
|
+
class PersonWeapon < ActiveRecord::Base
|
23
|
+
belongs_to :weapon
|
24
|
+
belongs_to :person
|
25
|
+
end
|
26
|
+
|
27
|
+
class Weapon < ActiveRecord::Base
|
28
|
+
scope :sharp, -> { where(sharp: true) }
|
29
|
+
end
|
30
|
+
|
31
|
+
class State < ActiveRecord::Base
|
32
|
+
end
|
33
|
+
|
34
|
+
The Server application would need to have Controllers defined to expose these
|
35
|
+
models and their {file:docs/ResourceDefinition.md Resource Definitions}. For
|
36
|
+
an example of that visit {file:docs/GettingStarted.md Getting Started}
|
37
|
+
|
38
|
+
|
39
|
+
1. Include the data nested in the response
|
40
|
+
|
41
|
+
# GET /people/1.json
|
42
|
+
|
43
|
+
# Response
|
44
|
+
{
|
45
|
+
first_name: 'Aaron',
|
46
|
+
last_name: 'Burr',
|
47
|
+
weapons: [
|
48
|
+
{ id: 1, name: 'Ax', sharp: true },
|
49
|
+
{ id: 2, name: 'Pistol', sharp: false }
|
50
|
+
],
|
51
|
+
state: { id: 10, name: 'New York' }
|
52
|
+
}
|
53
|
+
|
54
|
+
# in the Client application
|
55
|
+
person = Resource::Person.find(1)
|
56
|
+
person.state.name # => 'New York'
|
57
|
+
person.weapons.length # => 2
|
58
|
+
|
59
|
+
# no additional HTTP calls are made
|
60
|
+
|
61
|
+
1. Include a link to the data in the response
|
62
|
+
|
63
|
+
# GET /people/1.json
|
64
|
+
|
65
|
+
# Response
|
66
|
+
{
|
67
|
+
first_name: 'Aaron',
|
68
|
+
last_name: 'Burr',
|
69
|
+
weapons: [ { service_uri: '/people/1/weapons' } ],
|
70
|
+
state: { service_uri: '/states/10' }
|
71
|
+
}
|
72
|
+
|
73
|
+
# in the Client application
|
74
|
+
person = Resource::Person.find(1)
|
75
|
+
person.state.name # => 'New York'
|
76
|
+
person.weapons.length # => 2
|
77
|
+
|
78
|
+
# 2 additional HTTP calls are made
|
79
|
+
# (1 to each :service_uri provided)
|
80
|
+
|
81
|
+
1. Include the ids for the associated objects in the response
|
82
|
+
|
83
|
+
# GET /people/1.json
|
84
|
+
|
85
|
+
# Response
|
86
|
+
{
|
87
|
+
first_name: 'Aaron',
|
88
|
+
last_name: 'Burr',
|
89
|
+
weapon_ids: [ 1, 2 ],
|
90
|
+
state_id: 10
|
91
|
+
}
|
92
|
+
|
93
|
+
# in the Client application
|
94
|
+
person = Resource::Person.find(1)
|
95
|
+
person.state.name # => 'New York'
|
96
|
+
person.weapons.length # => 2
|
97
|
+
|
98
|
+
# 2 additional HTTP calls are made
|
99
|
+
# GET /weapons.json?ids[]=1&ids[]=2
|
100
|
+
# and
|
101
|
+
# GET /states/10.json
|
102
|
+
|
103
|
+
|
104
|
+
## Applying a scope to a relationship
|
105
|
+
|
106
|
+
Any scope on the relationship model can be applied to the relationship and
|
107
|
+
will be passed on to the server
|
108
|
+
|
109
|
+
*Note:* This does not work with embedded data because it has already
|
110
|
+
be loaded with the parent resource
|
111
|
+
|
112
|
+
|
113
|
+
@person = Resource::Person.find(1)
|
114
|
+
|
115
|
+
# GET /people/1/weapons.json
|
116
|
+
@person.weapons
|
117
|
+
# => [ Resource::Weapon(id: 1, name: 'Ax', sharp: true), Resource::Weapon(id: 2, name: 'Pistol', sharp: false) ]
|
118
|
+
|
119
|
+
# GET /people/1/weapons.json?sharp=true
|
120
|
+
@person.weapons.sharp
|
121
|
+
# => [ Resource::Weapon(id: 1, name: 'Ax', sharp: true) ]
|
122
|
+
|
123
|
+
## Saving associated records
|
124
|
+
|
125
|
+
By default, ApiResource assumes that any modifications to individual records
|
126
|
+
in an Association will be saved directly via that record
|
127
|
+
|
128
|
+
ApiResource::Base does provide the option to `:include_associations` on save
|
129
|
+
to mass-update records. This includes the data with the parent record's save
|
130
|
+
call.
|
131
|
+
|
132
|
+
@person = Resource::Person.find(1)
|
133
|
+
|
134
|
+
@person.state.name = 'I renamed New York'
|
135
|
+
@person.save(include_associations: [:state])
|
136
|
+
# PUT /people.json { state: { name: 'I renamed New York', id: 10 } }
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# The Resource Definition
|
2
|
+
|
3
|
+
The Resource Definition is the way that the Server application communicates
|
4
|
+
the properties and scopes of its models to the Client application
|
5
|
+
|
6
|
+
## How do I set this up in my Server application?
|
7
|
+
|
8
|
+
Full documentation is available at {http://path/to/server/docs ApiResourceServer}
|
9
|
+
|
10
|
+
## Definition Components
|
11
|
+
|
12
|
+
### Attributes
|
13
|
+
|
14
|
+
Attributes are typically fields in the database, but can be declared using
|
15
|
+
`virtual_attribute` in the Server's model as well
|
16
|
+
|
17
|
+
#### Visibility
|
18
|
+
|
19
|
+
ApiResourceServer hooks into `attr_protected` and provides `attr_private` to
|
20
|
+
communicate visibility of different attributes
|
21
|
+
|
22
|
+
|
23
|
+
### Scopes
|
24
|
+
|
25
|
+
ApiResource also hooks into ActiveRecord's `scope` to communicate
|
26
|
+
the models in the Server applications' scopes
|
27
|
+
|
28
|
+
### Associations
|
29
|
+
|
30
|
+
ApiResource also hooks into ActiveRecord's `has_many`, `belongs_to` and
|
31
|
+
`has_one` associations to communicate the models in the Server applications'
|
32
|
+
associations
|
33
|
+
|
34
|
+
## Exposing the Resource Definition
|
35
|
+
|
36
|
+
The Server application is responsible for exposing the Resource Definition
|
37
|
+
to the Client application. It should do so at
|
38
|
+
`GET /PLURALIZED_RESOURCE_NAME/new.json`
|
39
|
+
|
40
|
+
|
41
|
+
## Examples
|
42
|
+
|
43
|
+
See {http://path/to/server/docs ApiResourceServer} for more information
|
44
|
+
on declaring your attributes
|
45
|
+
|
46
|
+
|
47
|
+
## Final Resource Definition
|
48
|
+
|
49
|
+
{
|
50
|
+
attributes: {
|
51
|
+
public: [
|
52
|
+
["birthday", Date],
|
53
|
+
["first_name", "string"],
|
54
|
+
["last_name", "string"]
|
55
|
+
],
|
56
|
+
protected: [
|
57
|
+
["created_at", "time"],
|
58
|
+
["id", "integer"]
|
59
|
+
["updated_at", "time"]
|
60
|
+
]
|
61
|
+
},
|
62
|
+
associations : {
|
63
|
+
belongs_to: {
|
64
|
+
state: {}
|
65
|
+
},
|
66
|
+
has_many: {
|
67
|
+
friends: {}
|
68
|
+
}
|
69
|
+
|
70
|
+
},
|
71
|
+
scopes: {
|
72
|
+
active: {},
|
73
|
+
born_on: { date: :req }
|
74
|
+
}
|
75
|
+
|
76
|
+
}
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
|