lhs 2.2.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +296 -30
- data/lhs.gemspec +3 -3
- data/lib/lhs.rb +0 -7
- data/lib/lhs/collection.rb +4 -4
- data/lib/lhs/concerns/item/destroy.rb +2 -2
- data/lib/lhs/concerns/item/save.rb +3 -3
- data/lib/lhs/concerns/item/update.rb +2 -2
- data/lib/lhs/concerns/item/validation.rb +4 -5
- data/lib/lhs/concerns/{service → record}/all.rb +2 -2
- data/lib/lhs/concerns/{service → record}/batch.rb +3 -3
- data/lib/lhs/concerns/{service → record}/create.rb +4 -3
- data/lib/lhs/concerns/{service → record}/endpoints.rb +6 -6
- data/lib/lhs/concerns/{service → record}/find.rb +3 -2
- data/lib/lhs/concerns/{service → record}/find_by.rb +3 -2
- data/lib/lhs/concerns/{service → record}/first.rb +1 -1
- data/lib/lhs/concerns/{service → record}/includes.rb +4 -2
- data/lib/lhs/concerns/{service → record}/mapping.rb +1 -1
- data/lib/lhs/concerns/{service → record}/model.rb +1 -1
- data/lib/lhs/concerns/{service → record}/request.rb +12 -12
- data/lib/lhs/concerns/{service → record}/where.rb +3 -2
- data/lib/lhs/data.rb +25 -53
- data/lib/lhs/endpoint.rb +2 -2
- data/lib/lhs/item.rb +8 -2
- data/lib/lhs/proxy.rb +2 -2
- data/lib/lhs/record.rb +42 -0
- data/lib/lhs/version.rb +1 -1
- data/spec/collection/meta_data_spec.rb +2 -2
- data/spec/collection/without_object_items_spec.rb +1 -1
- data/spec/data/item_spec.rb +2 -14
- data/spec/data/merge_spec.rb +3 -3
- data/spec/data/raw_spec.rb +1 -1
- data/spec/data/respond_to_spec.rb +3 -3
- data/spec/data/root_spec.rb +2 -2
- data/spec/data/to_json_spec.rb +2 -2
- data/spec/endpoint/for_url_spec.rb +1 -1
- data/spec/item/delegate_spec.rb +2 -2
- data/spec/item/destroy_spec.rb +4 -4
- data/spec/item/errors_spec.rb +4 -4
- data/spec/item/getter_spec.rb +2 -2
- data/spec/item/internal_data_structure_spec.rb +1 -1
- data/spec/item/save_spec.rb +3 -4
- data/spec/item/setter_spec.rb +2 -2
- data/spec/item/update_spec.rb +2 -2
- data/spec/item/validation_spec.rb +47 -75
- data/spec/proxy/load_spec.rb +2 -2
- data/spec/{service → record}/all_spec.rb +7 -7
- data/spec/{service → record}/build_spec.rb +5 -4
- data/spec/{service → record}/create_spec.rb +6 -5
- data/spec/{service → record}/creation_failed_spec.rb +6 -5
- data/spec/record/definitions_spec.rb +29 -0
- data/spec/{service → record}/endpoint_misconfiguration_spec.rb +3 -3
- data/spec/{service → record}/endpoint_options_spec.rb +4 -4
- data/spec/{service → record}/endpoints_spec.rb +15 -15
- data/spec/{service → record}/find_by_spec.rb +8 -8
- data/spec/{service → record}/find_each_spec.rb +3 -3
- data/spec/{service → record}/find_in_batches_spec.rb +6 -6
- data/spec/{service → record}/find_spec.rb +10 -9
- data/spec/{service → record}/first_spec.rb +7 -6
- data/spec/{service → record}/includes_spec.rb +7 -7
- data/spec/{service → record}/mapping_spec.rb +27 -16
- data/spec/{service → record}/model_name_spec.rb +2 -2
- data/spec/{service → record}/new_spec.rb +4 -3
- data/spec/{service → record}/request_spec.rb +3 -3
- data/spec/record/where_spec.rb +34 -0
- data/spec/support/cleanup_endpoints.rb +1 -1
- data/spec/support/{cleanup_services.rb → cleanup_records.rb} +2 -2
- metadata +60 -67
- data/docs/collections.md +0 -28
- data/docs/data.md +0 -39
- data/docs/items.md +0 -79
- data/docs/service.jpg +0 -0
- data/docs/service.pdf +2 -650
- data/docs/services.md +0 -266
- data/lib/lhs/concerns/service/build.rb +0 -19
- data/lib/lhs/service.rb +0 -18
- data/spec/data/pagination_spec.rb +0 -60
- data/spec/dummy/README.rdoc +0 -28
- data/spec/service/where_spec.rb +0 -33
data/docs/services.md
DELETED
@@ -1,266 +0,0 @@
|
|
1
|
-
Services
|
2
|
-
===
|
3
|
-
|
4
|
-
A LHS::Service makes data available using multiple endpoints.
|
5
|
-
|
6
|
-
![Service](service.jpg)
|
7
|
-
|
8
|
-
## Convention
|
9
|
-
|
10
|
-
Please store all defined services in `app/services` as they are autoloaded from this directory.
|
11
|
-
|
12
|
-
## Endpoints
|
13
|
-
|
14
|
-
You setup a service by configure one or multiple backend endpoints.
|
15
|
-
You can also add request options for an endpoint (see following example).
|
16
|
-
|
17
|
-
```ruby
|
18
|
-
class Feedback < LHS::Service
|
19
|
-
|
20
|
-
endpoint ':datastore/v2/content-ads/:campaign_id/feedbacks'
|
21
|
-
endpoint ':datastore/v2/content-ads/:campaign_id/feedbacks/:id'
|
22
|
-
endpoint ':datastore/v2/feedbacks', cache: true, cache_expires_in: 1.day
|
23
|
-
endpoint ':datastore/v2/feedbacks/:id', cache: true, cache_expires_in: 1.day
|
24
|
-
|
25
|
-
end
|
26
|
-
```
|
27
|
-
|
28
|
-
If you try to setup a service with clashing endpoints it will immediately raise an exception.
|
29
|
-
|
30
|
-
```ruby
|
31
|
-
class Feedback < LHS::Service
|
32
|
-
|
33
|
-
endpoint ':datastore/v2/reviews'
|
34
|
-
endpoint ':datastore/v2/feedbacks'
|
35
|
-
|
36
|
-
end
|
37
|
-
# raises: Clashing endpoints.
|
38
|
-
|
39
|
-
```
|
40
|
-
|
41
|
-
## Find multiple records
|
42
|
-
|
43
|
-
You can query the services by using `where`.
|
44
|
-
|
45
|
-
```ruby
|
46
|
-
Feedback.where(has_reviews: true) #<LHS::Data @_proxy=#<LHS::Collection>>
|
47
|
-
```
|
48
|
-
|
49
|
-
This uses the `:datastore/v2/feedbacks` endpoint, cause `:campaign_id` was not provided.
|
50
|
-
In addition it would add `?has_reviews=true` to the get parameters.
|
51
|
-
|
52
|
-
```ruby
|
53
|
-
Feedback.where(campaign_id: 'fq-a81ngsl1d') #<LHS::Data @_proxy=#<LHS::Collection>>
|
54
|
-
```
|
55
|
-
Uses the `:datastore/v2/content-ads/:campaign_id/feedbacks` endpoint.
|
56
|
-
|
57
|
-
→ [Read more about collections](collections.md)
|
58
|
-
|
59
|
-
## Find single records
|
60
|
-
|
61
|
-
`find` finds a unique item by uniqe identifier (usualy id).
|
62
|
-
|
63
|
-
If no record is found an error is raised.
|
64
|
-
|
65
|
-
```ruby
|
66
|
-
Feedback.find('z12f-3asm3ngals') #<LHS::Data @_proxy=#<LHS::Item>>
|
67
|
-
```
|
68
|
-
|
69
|
-
`find` can also be used to find a single uniqe item with parameters:
|
70
|
-
|
71
|
-
```ruby
|
72
|
-
Feedback.find(campaign_id: 123, id: 456)
|
73
|
-
```
|
74
|
-
|
75
|
-
`find_by` finds the first record matching the specified conditions.
|
76
|
-
|
77
|
-
If no record is found, `nil` is returned.
|
78
|
-
|
79
|
-
`find_by!` raises LHC::NotFound if nothing was found.
|
80
|
-
|
81
|
-
```ruby
|
82
|
-
Feedback.find_by(id: 'z12f-3asm3ngals') #<LHS::Data @_proxy=#<LHS::Item>>
|
83
|
-
Feedback.find_by(id: 'doesntexist') # nil
|
84
|
-
```
|
85
|
-
|
86
|
-
`first` is a alias for finding the first of a service without parameters.
|
87
|
-
|
88
|
-
```ruby
|
89
|
-
Feedback.first
|
90
|
-
```
|
91
|
-
|
92
|
-
If no record is found, `nil` is returned.
|
93
|
-
|
94
|
-
`first!` raises LHC::NotFound if nothing was found.
|
95
|
-
|
96
|
-
→ [Read more about items](items.md)
|
97
|
-
|
98
|
-
## Batch processing
|
99
|
-
|
100
|
-
** Be carefull using methods for batch processing. They could result in a lot of HTTP requests! **
|
101
|
-
|
102
|
-
`all` fetches all records from the backend by doing multiple requests if necessary.
|
103
|
-
|
104
|
-
```ruby
|
105
|
-
data = Feedback.all #<LHS::Data @_proxy=#<LHS::Collection>>
|
106
|
-
data.count # 998
|
107
|
-
data.total # 998
|
108
|
-
```
|
109
|
-
|
110
|
-
→ [Read more about collections](collections.md)
|
111
|
-
|
112
|
-
`find_each` is a more fine grained way to process single records that are fetched in batches.
|
113
|
-
|
114
|
-
```ruby
|
115
|
-
Feedback.find_each(start: 50, batch_size: 20, params: { has_reviews: true }) do |feedback|
|
116
|
-
# Iterates over each record. Starts with record nr. 50 and fetches 20 records each batch.
|
117
|
-
feedback #<LHS::Data @_proxy=#<LHS::Item>>
|
118
|
-
end
|
119
|
-
```
|
120
|
-
|
121
|
-
`find_in_batches` is used by `find_each` and processes batches.
|
122
|
-
```ruby
|
123
|
-
Feedback.find_in_batches(start: 50, batch_size: 20, params: { has_reviews: true }) do |feedbacks|
|
124
|
-
# Iterates over multiple records (batch size is 20). Starts with record nr. 50 and fetches 20 records each batch.
|
125
|
-
feedbacks #<LHS::Data @_proxy=#<LHS::Collection>>
|
126
|
-
end
|
127
|
-
```
|
128
|
-
|
129
|
-
## Create records
|
130
|
-
|
131
|
-
```ruby
|
132
|
-
feedback = Feedback.create(
|
133
|
-
recommended: true,
|
134
|
-
source_id: 'aaa',
|
135
|
-
content_ad_id: '1z-5r1fkaj'
|
136
|
-
) #<LHS::Data @_proxy=#<LHS::Item>>
|
137
|
-
```
|
138
|
-
|
139
|
-
When creation fails, the object contains errors in its `errors` attribute:
|
140
|
-
|
141
|
-
```ruby
|
142
|
-
feedback.errors #<LHS::Errors>
|
143
|
-
feedback.errors.include?(:ratings) # true
|
144
|
-
feedback.errors[:ratings] # ['REQUIRED_PROPERTY_VALUE']
|
145
|
-
record.errors.messages # {:ratings=>["REQUIRED_PROPERTY_VALUE"], :recommended=>["REQUIRED_PROPERTY_VALUE"]}
|
146
|
-
record.errors.message # ratings must be set when review or name or review_title is set | The property value is required; it cannot be null, empty, or blank."
|
147
|
-
```
|
148
|
-
|
149
|
-
## Build new records
|
150
|
-
|
151
|
-
Build and persist new items from scratch.
|
152
|
-
|
153
|
-
```ruby
|
154
|
-
feedback = Feedback.build(recommended: true)
|
155
|
-
feedback.save
|
156
|
-
```
|
157
|
-
|
158
|
-
`new` is an alias for `build`:
|
159
|
-
|
160
|
-
```ruby
|
161
|
-
Feedback.new(recommended: true)
|
162
|
-
```
|
163
|
-
|
164
|
-
→ [Read more about items](items.md)
|
165
|
-
|
166
|
-
|
167
|
-
## Include linked resources
|
168
|
-
|
169
|
-
A service lets you specify in advance all the linked resources that you want to include in the results. With includes, a service ensures that all matching and explicitly linked resources are loaded and merged.
|
170
|
-
|
171
|
-
The implementation is heavily influenced by [http://guides.rubyonrails.org/active_record_querying](http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations)
|
172
|
-
and you should read it to understand this feature in all its glory.
|
173
|
-
|
174
|
-
### One-Level `includes`
|
175
|
-
|
176
|
-
```ruby
|
177
|
-
# a claim has a localch_account
|
178
|
-
claims = Claims.includes(:localch_account).where(place_id: 'huU90mB_6vAfUdVz_uDoyA')
|
179
|
-
claims.first.localch_account.email # 'test@email.com'
|
180
|
-
```
|
181
|
-
* [see the JSON without include](examples/claim_no_include.json)
|
182
|
-
* [see the JSON with include](examples/claim_with_include.json)
|
183
|
-
|
184
|
-
### Two-Level `includes`
|
185
|
-
|
186
|
-
```ruby
|
187
|
-
# a feedback has a campaign, which has an entry
|
188
|
-
feedbacks = Feedback.includes(campaign: :entry).where(has_reviews: true)
|
189
|
-
feedbacks.first.campaign.entry.name # 'Casa Ferlin'
|
190
|
-
```
|
191
|
-
|
192
|
-
### Multiple `includes`
|
193
|
-
|
194
|
-
```ruby
|
195
|
-
# list of includes
|
196
|
-
claims = Claims.includes(:localch_account, :entry).where(place_id: 'huU90mB_6vAfUdVz_uDoyA')
|
197
|
-
|
198
|
-
# array of includes
|
199
|
-
claims = Claims.includes([:localch_account, :entry]).where(place_id: 'huU90mB_6vAfUdVz_uDoyA')
|
200
|
-
|
201
|
-
# Two-level with array of includes
|
202
|
-
feedbacks = Feedback.includes(campaign: [:entry, :user]).where(has_reviews: true)
|
203
|
-
```
|
204
|
-
|
205
|
-
### Known services are used to request linked resources
|
206
|
-
|
207
|
-
When including linked resources with `includes`, known/defined services and endpoints are used to make those requests.
|
208
|
-
That also means that options for endpoints of linked resources are applied when requesting those in addition.
|
209
|
-
This enables you to include protected resources (e.g. OAuth) as endpoint options for oauth authentication get applied.
|
210
|
-
|
211
|
-
The [Auth Inteceptor](https://github.com/local-ch/lhc-core-interceptors#auth-interceptor) from [lhc-core-interceptors](https://github.com/local-ch/lhc-core-interceptors) is used to configure the following endpoints.
|
212
|
-
```ruby
|
213
|
-
class Favorite < LHS::Service
|
214
|
-
|
215
|
-
endpoint ':datastore/:user_id/favorites', auth: { bearer: -> { bearer_token } }
|
216
|
-
endpoint ':datastore/:user_id/favorites/:id', auth: { bearer: -> { bearer_token } }
|
217
|
-
|
218
|
-
end
|
219
|
-
|
220
|
-
class Place < LHS::Service
|
221
|
-
|
222
|
-
endpoint ':datastore/v2/places', auth: { bearer: -> { bearer_token } }
|
223
|
-
endpoint ':datastore/v2/places/:id', auth: { bearer: -> { bearer_token } }
|
224
|
-
|
225
|
-
end
|
226
|
-
|
227
|
-
Favorite.includes(:place).where(user_id: current_user.id)
|
228
|
-
# Will include places and applies endpoint options to authenticate the request.
|
229
|
-
```
|
230
|
-
|
231
|
-
## Map data
|
232
|
-
|
233
|
-
To influence how data is accessed/provied, you can use mapping to either map deep nested data or to manipulate data when its accessed:
|
234
|
-
|
235
|
-
```ruby
|
236
|
-
class LocalEntry < LHS::Service
|
237
|
-
endpoint ':datastore/v2/local-entries'
|
238
|
-
|
239
|
-
map :name, ->{ addresses.first.business.identities.first.name }
|
240
|
-
|
241
|
-
end
|
242
|
-
```
|
243
|
-
|
244
|
-
### Known services when accessing mapped data from nested data
|
245
|
-
|
246
|
-
As LHS detects services from available service definitions as soon as a link is present, mappings will also be applied on nested data:
|
247
|
-
|
248
|
-
```
|
249
|
-
class Place < LHS::Service
|
250
|
-
endpoint ':datastore/v2/places'
|
251
|
-
|
252
|
-
map :name, ->{ addresses.first.business.identities.first.name }
|
253
|
-
|
254
|
-
end
|
255
|
-
|
256
|
-
class Favorite < LHS::Service
|
257
|
-
endpoint ':datastore/v2/favorites'
|
258
|
-
end
|
259
|
-
|
260
|
-
favorite = Favorite.includes(:place).find(1)
|
261
|
-
favorite.place.name # local.ch AG
|
262
|
-
```
|
263
|
-
|
264
|
-
## Validation
|
265
|
-
|
266
|
-
See: [Item Validation](../item.md).
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'active_support'
|
2
|
-
|
3
|
-
class LHS::Service
|
4
|
-
|
5
|
-
module Build
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
module ClassMethods
|
9
|
-
|
10
|
-
def build(data = {})
|
11
|
-
data = LHS::Data.new(data, nil, self)
|
12
|
-
item = LHS::Item.new(data)
|
13
|
-
LHS::Data.new(item, nil, self)
|
14
|
-
end
|
15
|
-
alias_method :new, :build
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
data/lib/lhs/service.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
Dir[File.dirname(__FILE__) + '/concerns/service/*.rb'].each {|file| require file }
|
2
|
-
|
3
|
-
# A Service makes data available by using backend endpoints.
|
4
|
-
class LHS::Service
|
5
|
-
include All
|
6
|
-
include Batch
|
7
|
-
include Build
|
8
|
-
include Create
|
9
|
-
include Endpoints
|
10
|
-
include Find
|
11
|
-
include FindBy
|
12
|
-
include First
|
13
|
-
include Mapping
|
14
|
-
include Model
|
15
|
-
include Includes
|
16
|
-
include Request
|
17
|
-
include Where
|
18
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
require 'rails_helper'
|
2
|
-
|
3
|
-
describe LHS::Data do
|
4
|
-
context 'pagination' do
|
5
|
-
let(:datastore) { 'http://local.ch/v2' }
|
6
|
-
let(:record) { Feedback.where(entry_id: 1) }
|
7
|
-
let(:total) { 200 }
|
8
|
-
let(:total_pages) { total / limit }
|
9
|
-
let(:current_page) { offset + 1 }
|
10
|
-
let(:prev_page) { current_page - 1 }
|
11
|
-
let(:next_page) { current_page + 1 }
|
12
|
-
let(:offset) { 0 }
|
13
|
-
let(:limit) { 10 }
|
14
|
-
let(:body_json) do
|
15
|
-
{
|
16
|
-
items: [],
|
17
|
-
total: total,
|
18
|
-
offset: offset,
|
19
|
-
limit: limit
|
20
|
-
}.to_json
|
21
|
-
end
|
22
|
-
|
23
|
-
before(:each) do
|
24
|
-
LHC.config.placeholder('datastore', datastore)
|
25
|
-
class Feedback < LHS::Service
|
26
|
-
endpoint ':datastore/feedbacks'
|
27
|
-
end
|
28
|
-
stub_request(:get, 'http://local.ch/v2/feedbacks?entry_id=1')
|
29
|
-
.to_return(body: body_json)
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'responds to limit_value' do
|
33
|
-
expect(record.limit_value).to eq(limit)
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'responds to total_pages' do
|
37
|
-
expect(record.total_pages).to eq(total_pages)
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'responds to current_page' do
|
41
|
-
expect(record.current_page).to eq(current_page)
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'responds to first_page' do
|
45
|
-
expect(record.first_page).to eq(1)
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'responds to last_page' do
|
49
|
-
expect(record.last_page).to eq(total_pages)
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'responds to prev_page' do
|
53
|
-
expect(record.prev_page).to eq(prev_page)
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'responds to next_page' do
|
57
|
-
expect(record.next_page).to eq(next_page)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
data/spec/dummy/README.rdoc
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
== README
|
2
|
-
|
3
|
-
This README would normally document whatever steps are necessary to get the
|
4
|
-
application up and running.
|
5
|
-
|
6
|
-
Things you may want to cover:
|
7
|
-
|
8
|
-
* Ruby version
|
9
|
-
|
10
|
-
* System dependencies
|
11
|
-
|
12
|
-
* Configuration
|
13
|
-
|
14
|
-
* Database creation
|
15
|
-
|
16
|
-
* Database initialization
|
17
|
-
|
18
|
-
* How to run the test suite
|
19
|
-
|
20
|
-
* Services (job queues, cache servers, search engines, etc.)
|
21
|
-
|
22
|
-
* Deployment instructions
|
23
|
-
|
24
|
-
* ...
|
25
|
-
|
26
|
-
|
27
|
-
Please feel free to use a different markup language if you do not plan to run
|
28
|
-
<tt>rake doc:app</tt>.
|
data/spec/service/where_spec.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'rails_helper'
|
2
|
-
|
3
|
-
describe LHS::Service do
|
4
|
-
|
5
|
-
let(:datastore) do
|
6
|
-
'http://datastore/v2'
|
7
|
-
end
|
8
|
-
|
9
|
-
before(:each) do
|
10
|
-
LHC.config.placeholder('datastore', datastore)
|
11
|
-
class SomeService < LHS::Service
|
12
|
-
endpoint ':datastore/v2/content-ads/:campaign_id/feedbacks'
|
13
|
-
endpoint ':datastore/v2/feedbacks'
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
context 'where' do
|
18
|
-
|
19
|
-
it 'is querying relevant endpoint when using where' do
|
20
|
-
stub_request(:get, "#{datastore}/v2/feedbacks?has_review=true").to_return(status: 200)
|
21
|
-
SomeService.where(has_review: true)
|
22
|
-
stub_request(:get, "#{datastore}/v2/content-ads/123/feedbacks?has_review=true").to_return(status: 200)
|
23
|
-
SomeService.where(campaign_id: '123', has_review: true)
|
24
|
-
stub_request(:get, "#{datastore}/v2/feedbacks").to_return(status: 200, body: '')
|
25
|
-
SomeService.where
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'is using params as query params explicitly when provided in params namespace' do
|
29
|
-
stub_request(:get, "#{datastore}/v2/content-ads/123/feedbacks?campaign_id=456").to_return(status: 200)
|
30
|
-
SomeService.where(campaign_id: '123', params: { campaign_id: '456' })
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|