lhs 2.2.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +296 -30
  3. data/lhs.gemspec +3 -3
  4. data/lib/lhs.rb +0 -7
  5. data/lib/lhs/collection.rb +4 -4
  6. data/lib/lhs/concerns/item/destroy.rb +2 -2
  7. data/lib/lhs/concerns/item/save.rb +3 -3
  8. data/lib/lhs/concerns/item/update.rb +2 -2
  9. data/lib/lhs/concerns/item/validation.rb +4 -5
  10. data/lib/lhs/concerns/{service → record}/all.rb +2 -2
  11. data/lib/lhs/concerns/{service → record}/batch.rb +3 -3
  12. data/lib/lhs/concerns/{service → record}/create.rb +4 -3
  13. data/lib/lhs/concerns/{service → record}/endpoints.rb +6 -6
  14. data/lib/lhs/concerns/{service → record}/find.rb +3 -2
  15. data/lib/lhs/concerns/{service → record}/find_by.rb +3 -2
  16. data/lib/lhs/concerns/{service → record}/first.rb +1 -1
  17. data/lib/lhs/concerns/{service → record}/includes.rb +4 -2
  18. data/lib/lhs/concerns/{service → record}/mapping.rb +1 -1
  19. data/lib/lhs/concerns/{service → record}/model.rb +1 -1
  20. data/lib/lhs/concerns/{service → record}/request.rb +12 -12
  21. data/lib/lhs/concerns/{service → record}/where.rb +3 -2
  22. data/lib/lhs/data.rb +25 -53
  23. data/lib/lhs/endpoint.rb +2 -2
  24. data/lib/lhs/item.rb +8 -2
  25. data/lib/lhs/proxy.rb +2 -2
  26. data/lib/lhs/record.rb +42 -0
  27. data/lib/lhs/version.rb +1 -1
  28. data/spec/collection/meta_data_spec.rb +2 -2
  29. data/spec/collection/without_object_items_spec.rb +1 -1
  30. data/spec/data/item_spec.rb +2 -14
  31. data/spec/data/merge_spec.rb +3 -3
  32. data/spec/data/raw_spec.rb +1 -1
  33. data/spec/data/respond_to_spec.rb +3 -3
  34. data/spec/data/root_spec.rb +2 -2
  35. data/spec/data/to_json_spec.rb +2 -2
  36. data/spec/endpoint/for_url_spec.rb +1 -1
  37. data/spec/item/delegate_spec.rb +2 -2
  38. data/spec/item/destroy_spec.rb +4 -4
  39. data/spec/item/errors_spec.rb +4 -4
  40. data/spec/item/getter_spec.rb +2 -2
  41. data/spec/item/internal_data_structure_spec.rb +1 -1
  42. data/spec/item/save_spec.rb +3 -4
  43. data/spec/item/setter_spec.rb +2 -2
  44. data/spec/item/update_spec.rb +2 -2
  45. data/spec/item/validation_spec.rb +47 -75
  46. data/spec/proxy/load_spec.rb +2 -2
  47. data/spec/{service → record}/all_spec.rb +7 -7
  48. data/spec/{service → record}/build_spec.rb +5 -4
  49. data/spec/{service → record}/create_spec.rb +6 -5
  50. data/spec/{service → record}/creation_failed_spec.rb +6 -5
  51. data/spec/record/definitions_spec.rb +29 -0
  52. data/spec/{service → record}/endpoint_misconfiguration_spec.rb +3 -3
  53. data/spec/{service → record}/endpoint_options_spec.rb +4 -4
  54. data/spec/{service → record}/endpoints_spec.rb +15 -15
  55. data/spec/{service → record}/find_by_spec.rb +8 -8
  56. data/spec/{service → record}/find_each_spec.rb +3 -3
  57. data/spec/{service → record}/find_in_batches_spec.rb +6 -6
  58. data/spec/{service → record}/find_spec.rb +10 -9
  59. data/spec/{service → record}/first_spec.rb +7 -6
  60. data/spec/{service → record}/includes_spec.rb +7 -7
  61. data/spec/{service → record}/mapping_spec.rb +27 -16
  62. data/spec/{service → record}/model_name_spec.rb +2 -2
  63. data/spec/{service → record}/new_spec.rb +4 -3
  64. data/spec/{service → record}/request_spec.rb +3 -3
  65. data/spec/record/where_spec.rb +34 -0
  66. data/spec/support/cleanup_endpoints.rb +1 -1
  67. data/spec/support/{cleanup_services.rb → cleanup_records.rb} +2 -2
  68. metadata +60 -67
  69. data/docs/collections.md +0 -28
  70. data/docs/data.md +0 -39
  71. data/docs/items.md +0 -79
  72. data/docs/service.jpg +0 -0
  73. data/docs/service.pdf +2 -650
  74. data/docs/services.md +0 -266
  75. data/lib/lhs/concerns/service/build.rb +0 -19
  76. data/lib/lhs/service.rb +0 -18
  77. data/spec/data/pagination_spec.rb +0 -60
  78. data/spec/dummy/README.rdoc +0 -28
  79. data/spec/service/where_spec.rb +0 -33
@@ -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
@@ -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
@@ -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>.
@@ -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