lhs 2.2.2 → 3.0.0

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.
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