object_json_mapper 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +251 -0
- data/Rakefile +6 -0
- data/bin/console +34 -0
- data/bin/setup +8 -0
- data/lib/object_json_mapper/associations/association.rb +36 -0
- data/lib/object_json_mapper/associations/has_many.rb +13 -0
- data/lib/object_json_mapper/associations/has_one.rb +13 -0
- data/lib/object_json_mapper/associations/registry.rb +25 -0
- data/lib/object_json_mapper/associations.rb +96 -0
- data/lib/object_json_mapper/base.rb +163 -0
- data/lib/object_json_mapper/conversion.rb +24 -0
- data/lib/object_json_mapper/errors.rb +37 -0
- data/lib/object_json_mapper/extensions/kaminari.rb +48 -0
- data/lib/object_json_mapper/http.rb +9 -0
- data/lib/object_json_mapper/local.rb +58 -0
- data/lib/object_json_mapper/null_relation.rb +7 -0
- data/lib/object_json_mapper/persistence.rb +94 -0
- data/lib/object_json_mapper/relation.rb +100 -0
- data/lib/object_json_mapper/serialization.rb +7 -0
- data/lib/object_json_mapper/version.rb +3 -0
- data/lib/object_json_mapper.rb +37 -0
- data/object_json_mapper.gemspec +33 -0
- metadata +226 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e3c9d3a3c7d0b38d0cdc2fbc17841cca3ce028a7
|
4
|
+
data.tar.gz: 30cde2c2a57c26d300eec9564c138cc0c59bf226
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 018f71e9d07923bc2bd7c718cc0abd2353a7df1d34b9aadfbf7acedd0a03ae44db479308233266436df0769edddd88f2998f33240624c2f6d12123642131b270
|
7
|
+
data.tar.gz: ab609054b4a783bea7fec658dcdcd058cd9f98b3d3cf5ce1ebc93f09895f3148db56b4e85251117ddb4e0266cf2b2e01e3aa78f91a5a8d913deb0d3fed69f5e2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at novikov359@gmail.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 Inspire
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
# ObjectJSONMapper
|
2
|
+
|
3
|
+
[![CircleCI](https://circleci.com/gh/InspireNL/ObjectJSONMapper.svg?style=svg&circle-token=28455d7bc9acb59984023207070f1f4afdc60d15)](https://circleci.com/gh/InspireNL/ObjectJSONMapper)
|
4
|
+
|
5
|
+
* [Installation](#installation)
|
6
|
+
* [Usage](#usage)
|
7
|
+
* [Find](#find)
|
8
|
+
* [Where](#where)
|
9
|
+
* [Create](#create)
|
10
|
+
* [Update](#update)
|
11
|
+
* [Delete](#delete)
|
12
|
+
* [Attributes](#attributes)
|
13
|
+
* [Defaults](#defaults)
|
14
|
+
* [Coercion](#coercion)
|
15
|
+
* [Associations](#associations)
|
16
|
+
* [HasMany](#hasmany)
|
17
|
+
* [BelongsTo / HasOne](#belongsto--hasone)
|
18
|
+
* [Scope](#scope)
|
19
|
+
* [Pluck](#pluck)
|
20
|
+
* [None](#none)
|
21
|
+
* [Root](#root)
|
22
|
+
* [Configure](#configure)
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
Add this line to your application's Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem 'object_json_mapper'
|
30
|
+
```
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
$ bundle
|
35
|
+
|
36
|
+
Or install it yourself as:
|
37
|
+
|
38
|
+
$ gem install object_json_mapper
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
Set default url for your API in `config/initializers/object_json_mapper.rb`:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
require 'object_json_mapper'
|
46
|
+
|
47
|
+
ObjectJSONMapper.configure do |config|
|
48
|
+
# Required
|
49
|
+
config.base_url = 'http://localhost:3000'
|
50
|
+
|
51
|
+
# Optional
|
52
|
+
config.headers = {
|
53
|
+
'Authorization' => 'secret-token'
|
54
|
+
}
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
Define class, e.g. `User`:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
class User < ObjectJSONMapper::Base
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
### Find
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
User.find(1)
|
69
|
+
# => GET http://localhost:3000/users/1
|
70
|
+
```
|
71
|
+
|
72
|
+
### Where
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
User.where(id: 1)
|
76
|
+
# => GET http://localhost:3000/users?id=1
|
77
|
+
|
78
|
+
User.where(id: 1).where(name: 'Name')
|
79
|
+
# => GET http://localhost:3000/users?id=1&name=Name
|
80
|
+
```
|
81
|
+
|
82
|
+
### Create
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
User.create(name: 'Name')
|
86
|
+
# => POST http://localhost:3000/users
|
87
|
+
# => {
|
88
|
+
# => name: 'Name'
|
89
|
+
# => }
|
90
|
+
```
|
91
|
+
|
92
|
+
### Update
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
u = User.find(1)
|
96
|
+
u.update(name: 'Name')
|
97
|
+
# => PATCH http://localhost:3000/users/1
|
98
|
+
# => {
|
99
|
+
# => name: 'Name'
|
100
|
+
# => }
|
101
|
+
```
|
102
|
+
|
103
|
+
### Delete
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
u = User.find(1)
|
107
|
+
u.delete
|
108
|
+
# => DELETE http://localhost:3000/users/1
|
109
|
+
```
|
110
|
+
|
111
|
+
## Attributes
|
112
|
+
|
113
|
+
### Defaults
|
114
|
+
|
115
|
+
Executes given `Proc` if value is `nil`.
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
class User < ObjectJSONMapper::Base
|
119
|
+
attribute :created_at, default: -> { DateTime.now }
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
### Coercion
|
124
|
+
|
125
|
+
Expects a object with `#call` interface to return modified value.
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
class User < ObjectJSONMapper::Base
|
129
|
+
attribute :created_at, type: Dry::Types['form.date_time']
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
## Associations
|
134
|
+
|
135
|
+
### HasMany
|
136
|
+
|
137
|
+
Returns a `Relation` with model objects.
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
class User < ObjectJSONMapper::Base
|
141
|
+
has_many :posts
|
142
|
+
end
|
143
|
+
|
144
|
+
User.find(1).posts
|
145
|
+
# => GET http://localhost:3000/users/1/posts
|
146
|
+
```
|
147
|
+
|
148
|
+
### BelongsTo / HasOne
|
149
|
+
|
150
|
+
Returns a single model object.
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
class Post < ObjectJSONMapper::Base
|
154
|
+
belongs_to :user
|
155
|
+
has_one :image
|
156
|
+
end
|
157
|
+
|
158
|
+
Post.find(1).user
|
159
|
+
# => GET http://localhost:3000/posts/1/user
|
160
|
+
|
161
|
+
Post.find(1).image
|
162
|
+
# => GET http://localhost:3000/posts/1/image
|
163
|
+
```
|
164
|
+
|
165
|
+
## Scope
|
166
|
+
|
167
|
+
Defines a scope for current model and all it relations.
|
168
|
+
It's possible to chain `#where` methods and defined scopes in any order.
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
class User < ObjectJSONMapper::Base
|
172
|
+
scope :published, -> { where(published: true) }
|
173
|
+
scope :active, -> { where(active: true) }
|
174
|
+
end
|
175
|
+
|
176
|
+
User.published
|
177
|
+
# => GET http://localhost:3000/users?published=true
|
178
|
+
|
179
|
+
User.published.active
|
180
|
+
# => GET http://localhost:3000/users?published=true&active=true
|
181
|
+
|
182
|
+
User.published.where(active: true)
|
183
|
+
# => GET http://localhost:3000/users?published=true&active=true
|
184
|
+
|
185
|
+
User.where(active: true).published
|
186
|
+
# => GET http://localhost:3000/users?active=true&published=true
|
187
|
+
```
|
188
|
+
|
189
|
+
## Pluck
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
User.where(published: true).pluck(:id, :email)
|
193
|
+
# => GET http://localhost:3000/users?published=true
|
194
|
+
# => [[1, 'first@example.com', [2, 'second@example.com']]
|
195
|
+
```
|
196
|
+
|
197
|
+
## None
|
198
|
+
|
199
|
+
Returns a chainable relation without records, wouldn't make any queries.
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
User.none
|
203
|
+
# => []
|
204
|
+
|
205
|
+
User.where(id: 1).none
|
206
|
+
# => []
|
207
|
+
|
208
|
+
User.none.where(id: 1)
|
209
|
+
# => []
|
210
|
+
```
|
211
|
+
|
212
|
+
## Root
|
213
|
+
|
214
|
+
Allows to change resource path for model client.
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
User.root('people').where(name: 'Name')
|
218
|
+
# => GET http://localhost:3000/people?name=Name
|
219
|
+
```
|
220
|
+
|
221
|
+
## Configure
|
222
|
+
|
223
|
+
Available options:
|
224
|
+
|
225
|
+
* `root_url` - resource path for current model.
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
class User < ObjectJSONMapper::Base
|
229
|
+
configure do |c|
|
230
|
+
c.root_url = 'people'
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
User.all
|
235
|
+
# => GET http://localhost:3000/people
|
236
|
+
|
237
|
+
User.find(1)
|
238
|
+
# => GET http://localhost:3000/people/1
|
239
|
+
|
240
|
+
User.find(1).posts
|
241
|
+
# => GET http://localhost:3000/people/1
|
242
|
+
# => GET http://localhost:3000/people/1/posts
|
243
|
+
```
|
244
|
+
|
245
|
+
## License
|
246
|
+
|
247
|
+
ObjectJSONMapper is released under the [MIT License](https://github.com/InspireNL/ObjectJSONMapper/blob/master/LICENSE).
|
248
|
+
|
249
|
+
## About
|
250
|
+
|
251
|
+
ObjectJSONMapper is maintained by [Inspire](https://inspire.nl).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'object_json_mapper'
|
5
|
+
require 'pry'
|
6
|
+
# require 'irb'
|
7
|
+
|
8
|
+
ObjectJSONMapper.configure do |config|
|
9
|
+
config.base_url = 'http://localhost:3000/v1/'
|
10
|
+
config.headers = {
|
11
|
+
'Authorization-Consumer' => '65d064d8-4b52-4b4d-8f5b-acc36a21e3bf'
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
class User < ObjectJSONMapper::Base
|
16
|
+
attribute :id
|
17
|
+
attribute :email
|
18
|
+
attribute :password
|
19
|
+
|
20
|
+
has_many :subscriptions
|
21
|
+
|
22
|
+
scope :qwe, -> { where(id: 1) }
|
23
|
+
|
24
|
+
configure do |c|
|
25
|
+
c.root_url = 'children'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Subscription < ObjectJSONMapper::Base
|
30
|
+
belongs_to :user
|
31
|
+
end
|
32
|
+
|
33
|
+
Pry.start
|
34
|
+
# IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module ObjectJSONMapper
|
2
|
+
module Associations
|
3
|
+
# Base class for association builders
|
4
|
+
#
|
5
|
+
# @abstract
|
6
|
+
class Association
|
7
|
+
attr_reader :name, :params
|
8
|
+
|
9
|
+
# @param name [Symbol]
|
10
|
+
# @param options [Hash]
|
11
|
+
# @option options [Object] :klass
|
12
|
+
# @option options [String] :endpoint
|
13
|
+
# @option options [Hash] :params
|
14
|
+
def initialize(name, options = {})
|
15
|
+
@name = name
|
16
|
+
@klass = options.fetch(:class_name, name)
|
17
|
+
@endpoint = options.fetch(:endpoint, name)
|
18
|
+
@params = options.fetch(:params, {})
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(*)
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
def klass
|
26
|
+
return @klass.to_s.classify.constantize if @klass.is_a?(Symbol)
|
27
|
+
|
28
|
+
@klass
|
29
|
+
end
|
30
|
+
|
31
|
+
def endpoint
|
32
|
+
@endpoint.to_s.underscore
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ObjectJSONMapper
|
2
|
+
module Associations
|
3
|
+
class HasMany < Association
|
4
|
+
# @param object [ObjectJSONMapper::Base]
|
5
|
+
# @return [ObjectJSONMapper::Relation]
|
6
|
+
def call(object)
|
7
|
+
klass.where.tap do |relation|
|
8
|
+
relation.path = object.client[endpoint].url
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ObjectJSONMapper
|
2
|
+
module Associations
|
3
|
+
class HasOne < Association
|
4
|
+
# @param object [ObjectJSONMapper::Base]
|
5
|
+
# @return [ObjectJSONMapper::Base]
|
6
|
+
def call(object)
|
7
|
+
klass.persist(
|
8
|
+
HTTP.parse_json(object.client[endpoint].get.body)
|
9
|
+
)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ObjectJSONMapper
|
2
|
+
module Associations
|
3
|
+
# Collection of model associations
|
4
|
+
class Registry
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
attr_reader :associations
|
8
|
+
|
9
|
+
delegate :[],
|
10
|
+
:[]=,
|
11
|
+
:<<,
|
12
|
+
to: :associations
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@associations = []
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param name [Symbol]
|
19
|
+
# @return [ObjectJSONMapper::Associations::Association]
|
20
|
+
def find(name)
|
21
|
+
associations.find { |a| a.name == name }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ObjectJSONMapper
|
2
|
+
module Associations
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# rubocop:disable Style/PredicateName
|
9
|
+
#
|
10
|
+
# @param association_name [Symbol]
|
11
|
+
# @param class_name [Symbol]
|
12
|
+
# @return [ObjectJSONMapper::Relation<ObjectJSONMapper::Base>]
|
13
|
+
#
|
14
|
+
# @example Basic usage
|
15
|
+
# class User < ObjectJSONMapper::Base
|
16
|
+
# has_many :products
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# User.find(1).products
|
20
|
+
# # GET http://localhost:3000/v1/users/1/products
|
21
|
+
# # => #<ObjectJSONMapper::Relation @collection=[<Product>...]>
|
22
|
+
#
|
23
|
+
# @example With class_name
|
24
|
+
# class User < ObjectJSONMapper::Base
|
25
|
+
# has_many :things, class_name: :products
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# User.find(1).things
|
29
|
+
# # GET http://localhost:3000/v1/users/1/things
|
30
|
+
# # => #<ObjectJSONMapper::Relation @collection=[<Product>...]>
|
31
|
+
#
|
32
|
+
# @example With endpoint
|
33
|
+
# class User < ObjectJSONMapper::Base
|
34
|
+
# has_many :products, endpoint: :things
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# User.find(1).products
|
38
|
+
# # GET http://localhost:3000/v1/users/1/things
|
39
|
+
# # => #<ObjectJSONMapper::Relation @collection=[<Product>...]>
|
40
|
+
#
|
41
|
+
# @example With params
|
42
|
+
# class User < ObjectJSONMapper::Base
|
43
|
+
# has_many :products, params: { status: :active }
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# User.find(1).products
|
47
|
+
# # GET http://localhost:3000/v1/users/1/products?status=active
|
48
|
+
# # => #<ObjectJSONMapper::Relation @collection=[<Product>...]>
|
49
|
+
def has_many(name, options = {})
|
50
|
+
associations << HasMany.new(name, options)
|
51
|
+
|
52
|
+
define_method(name) do |reload = false|
|
53
|
+
cache_name = :"@#{__method__}"
|
54
|
+
|
55
|
+
if instance_variable_defined?(cache_name) && reload == false
|
56
|
+
return instance_variable_get(cache_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
instance_variable_set(
|
60
|
+
cache_name,
|
61
|
+
self.class.associations.find(__method__).call(self)
|
62
|
+
)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param association_name [Symbol]
|
67
|
+
# @return [ObjectJSONMapper::Base]
|
68
|
+
#
|
69
|
+
# @example Basic usage
|
70
|
+
# class User < ObjectJSONMapper::Base
|
71
|
+
# has_one :profile
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# User.find(1).profile
|
75
|
+
# # GET http://localhost:3000/v1/users/1/profile
|
76
|
+
# # => #<ObjectJSONMapper::Base @attributes={...}>
|
77
|
+
def has_one(name, options = {})
|
78
|
+
associations << HasOne.new(name, options)
|
79
|
+
|
80
|
+
define_method(name) do |reload = false|
|
81
|
+
cache_name = :"@#{__method__}"
|
82
|
+
|
83
|
+
if instance_variable_defined?(cache_name) && reload == false
|
84
|
+
return instance_variable_get(cache_name)
|
85
|
+
end
|
86
|
+
|
87
|
+
instance_variable_set(
|
88
|
+
cache_name,
|
89
|
+
self.class.associations.find(__method__).call(self)
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
alias belongs_to has_one
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|