get 0.1.6 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +87 -51
- data/lib/get/builders/ancestry_builder.rb +13 -7
- data/lib/get/builders/query_builder.rb +5 -10
- data/lib/get/db.rb +0 -4
- data/spec/get_spec.rb +88 -3
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b65d91c034544c94bc5f69f130fd281ea846d32d
|
4
|
+
data.tar.gz: 3ba99734b8435d50b2fb2372e798b26860dec7db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae40513b32b4063b5bfe7c2d2bcf025e107eeba4aa87013402b298d6601f09815755e1967c0db38c436e92290444820f06034b20a6b6b4b5f6e5541213c136aa
|
7
|
+
data.tar.gz: f99002ee16cdd582571be08ea99512dc2eaa6a9e4e9ff661ba9322b5741d75040a27e899eeee37d76201a6c8fd68180e14ea25e08a06d5f15a6e0f117dcf0a26
|
data/README.md
CHANGED
@@ -2,6 +2,92 @@
|
|
2
2
|
|
3
3
|
Dynamically generate classes to encapsulate common database queries in Rails.
|
4
4
|
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
#### Singular Queries - Return a single record
|
8
|
+
|
9
|
+
With field being queried in the class name
|
10
|
+
```ruby
|
11
|
+
Get::UserById.run(123)
|
12
|
+
```
|
13
|
+
|
14
|
+
Fail loudly
|
15
|
+
```ruby
|
16
|
+
Get::UserById.run!(123)
|
17
|
+
```
|
18
|
+
|
19
|
+
Slightly more flexible model:
|
20
|
+
```ruby
|
21
|
+
Get::UserBy.run(id: 123, employer_id: 88)
|
22
|
+
```
|
23
|
+
|
24
|
+
#### Plural Queries - Return a collection of records
|
25
|
+
|
26
|
+
_Note the plurality of 'Users'_
|
27
|
+
```ruby
|
28
|
+
Get::UsersByLastName.run('Turner')
|
29
|
+
```
|
30
|
+
|
31
|
+
With Options
|
32
|
+
```ruby
|
33
|
+
Get::UsersByLastName.run('Turner', limit: 10, offset: 20, order: { last_name: :desc })
|
34
|
+
```
|
35
|
+
|
36
|
+
#### Associations
|
37
|
+
|
38
|
+
Associations use 'From', and are sugar for the chains we so often write in rails.
|
39
|
+
|
40
|
+
_You can pass either an entity or an id, the only requirement is that it responds to #id_
|
41
|
+
|
42
|
+
Parent relationship (user.employer):
|
43
|
+
```ruby
|
44
|
+
Get::EmployerFromUser.run(user)
|
45
|
+
```
|
46
|
+
|
47
|
+
Child relationship (employer.users):
|
48
|
+
```ruby
|
49
|
+
Get::UsersFromEmployer.run(employer_id)
|
50
|
+
```
|
51
|
+
|
52
|
+
Complex relationship (user.employer.sportscars)
|
53
|
+
```ruby
|
54
|
+
Get::SportscarsFromUser.run(user, via: :employer)
|
55
|
+
```
|
56
|
+
|
57
|
+
Eager Loading
|
58
|
+
```ruby
|
59
|
+
Get::SportscarsFromUser.run(user, via: :employer, eager_load: true)
|
60
|
+
```
|
61
|
+
|
62
|
+
Query Associations
|
63
|
+
```ruby
|
64
|
+
Get::SportscarsFromUser.run(user, via: :employer, conditions: { make: 'Audi' }, limit: 10, offset: 20)
|
65
|
+
```
|
66
|
+
|
67
|
+
Keep the plurality of associations in mind. If an Employer has many Users, UsersFromEmployer works,
|
68
|
+
but UserFromEmployer will throw `Get::Errors::InvalidAncestry`.
|
69
|
+
|
70
|
+
## Options
|
71
|
+
|
72
|
+
**Base Options**
|
73
|
+
|
74
|
+
Key | Type | Details
|
75
|
+
--- | ---- | -------
|
76
|
+
`order` | Hash | { `field` => `:asc`/`:desc` }
|
77
|
+
`limit` | Integer | Number of records to return
|
78
|
+
`offset` | Integer | Number of records to offset
|
79
|
+
`id` | Integer | The id of the root object (associations only)
|
80
|
+
`target` | Symbol | The target of the association - ie. employer.users would have a target of :users (associations only)
|
81
|
+
`eager_load` | Boolean | Whether to eager_load the association (associations only)
|
82
|
+
|
83
|
+
**Association Options**
|
84
|
+
|
85
|
+
Key | Type | Details
|
86
|
+
--- | ---- | -------
|
87
|
+
`conditions` | Hash | Key value pairs for the query
|
88
|
+
`eager_load` | Boolean | Whether to eager_load the association
|
89
|
+
`via` | [Symbol] | The associations that need to be traversed in order to reach the desired record(s). These must be in the correct order, ie user.employer.parent.children would be Get::ChildrenFromUser.run(user_id, via: [:employer, :parent]). You can also pass a single symbol instead of an array of length 1.
|
90
|
+
|
5
91
|
## Why is this necessary?
|
6
92
|
|
7
93
|
#### Problem 1: Encapsulation
|
@@ -87,56 +173,6 @@ Get identifies four themes in common queries:
|
|
87
173
|
|
88
174
|
These themes are not mutually exclusive; **Query** and **Association** can be either **Singular** or **Plural**.
|
89
175
|
|
90
|
-
## Usage
|
91
|
-
|
92
|
-
#### Singular Queries - Return a single record
|
93
|
-
|
94
|
-
With field being queried in the class name
|
95
|
-
```ruby
|
96
|
-
Get::UserById.run(123)
|
97
|
-
```
|
98
|
-
|
99
|
-
Fail loudly
|
100
|
-
```ruby
|
101
|
-
Get::UserById.run!(123)
|
102
|
-
```
|
103
|
-
|
104
|
-
Slightly more flexible model:
|
105
|
-
```ruby
|
106
|
-
Get::UserBy.run(id: 123, employer_id: 88)
|
107
|
-
```
|
108
|
-
|
109
|
-
#### Plural Queries - Return a collection of records
|
110
|
-
|
111
|
-
_Note the plurality of 'Users'_
|
112
|
-
```ruby
|
113
|
-
Get::UsersByLastName.run('Turner')
|
114
|
-
```
|
115
|
-
|
116
|
-
#### Associations
|
117
|
-
|
118
|
-
Associations use 'From', and are sugar for the chains we so often write in rails.
|
119
|
-
|
120
|
-
_You can pass either an entity or an id, the only requirement is that it responds to #id_
|
121
|
-
|
122
|
-
Parent relationship (user.employer):
|
123
|
-
```ruby
|
124
|
-
Get::EmployerFromUser.run(user)
|
125
|
-
```
|
126
|
-
|
127
|
-
Child relationship (employer.users):
|
128
|
-
```ruby
|
129
|
-
Get::UsersFromEmployer.run(employer_id)
|
130
|
-
```
|
131
|
-
|
132
|
-
Complex relationship (user.employer.sportscars)
|
133
|
-
```ruby
|
134
|
-
Get::SportscarsFromUser.run(user, via: :employer)
|
135
|
-
```
|
136
|
-
|
137
|
-
Keep the plurality of associations in mind. If an Employer has many Users, UsersFromEmployer works,
|
138
|
-
but UserFromEmployer will throw `Get::Errors::InvalidAncestry`.
|
139
|
-
|
140
176
|
## Entities
|
141
177
|
|
142
178
|
Ironically, one of the "features" of Get is its removal of the ability to query associations from the ORM response object.
|
@@ -234,7 +270,7 @@ Some attributes contain the word 'by', ie. `Customer.invited_by`.
|
|
234
270
|
Because of the way Get parses classnames, you won't be able to use the attribute-specific format.
|
235
271
|
Use the more general form instead.
|
236
272
|
|
237
|
-
```
|
273
|
+
```ruby
|
238
274
|
Get::CustomerByInvitedBy.run('John') #=> throws Get::Errors::InvalidClassName
|
239
275
|
Get::CustomerBy.run(invited_by: 'John') #=> will work
|
240
276
|
```
|
@@ -29,17 +29,18 @@ module Get
|
|
29
29
|
|
30
30
|
private
|
31
31
|
|
32
|
-
def id
|
33
|
-
return @model.id if @model.respond_to? :id
|
34
|
-
@model
|
35
|
-
end
|
36
|
-
|
37
32
|
def query_params
|
38
|
-
|
39
|
-
{ ancestors: ancestor_params }.merge(options)
|
33
|
+
{ association: ancestor_params }
|
40
34
|
end
|
41
35
|
|
42
36
|
def ancestor_params
|
37
|
+
# Add options to hash only if they exist - empty objects/nil values can wreak havoc
|
38
|
+
[:conditions, :limit, :offset, :order, :eager_load].reduce(required_params) do |params, key|
|
39
|
+
@options[key] ? params.merge(key => @options[key]) : params
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def required_params
|
43
44
|
{
|
44
45
|
id: id,
|
45
46
|
via: via,
|
@@ -47,6 +48,11 @@ module Get
|
|
47
48
|
}
|
48
49
|
end
|
49
50
|
|
51
|
+
def id
|
52
|
+
return @model.id if @model.respond_to? :id
|
53
|
+
@model
|
54
|
+
end
|
55
|
+
|
50
56
|
def via
|
51
57
|
case @options[:via]
|
52
58
|
when Symbol
|
@@ -22,15 +22,15 @@ module Get
|
|
22
22
|
|
23
23
|
@field, @entity, @collection, @store = args[:key], args[:result_entity], args[:collection], args[:store]
|
24
24
|
|
25
|
-
def initialize(params)
|
26
|
-
@params = params
|
25
|
+
def initialize(params, options = {})
|
26
|
+
@params, @options = params, options
|
27
27
|
super(query_params)
|
28
28
|
end
|
29
29
|
|
30
30
|
private
|
31
31
|
|
32
32
|
def query_params
|
33
|
-
{ query_action => conditions }
|
33
|
+
{ query_action => conditions.merge(@options) }
|
34
34
|
end
|
35
35
|
|
36
36
|
def query_action
|
@@ -39,13 +39,8 @@ module Get
|
|
39
39
|
|
40
40
|
# find_first
|
41
41
|
def conditions
|
42
|
-
return @params unless self.class.field
|
43
|
-
{ self.class.field => @params }
|
44
|
-
end
|
45
|
-
|
46
|
-
def single_params
|
47
|
-
return {} if self.class.collection
|
48
|
-
{ limit: 1, first: true }
|
42
|
+
return { conditions: @params } unless self.class.field
|
43
|
+
{ conditions: { self.class.field => @params } }
|
49
44
|
end
|
50
45
|
end
|
51
46
|
end
|
data/lib/get/db.rb
CHANGED
data/spec/get_spec.rb
CHANGED
@@ -210,6 +210,41 @@ describe Get do
|
|
210
210
|
end
|
211
211
|
end
|
212
212
|
|
213
|
+
context 'with options' do
|
214
|
+
let(:last_name) { 'Turner' }
|
215
|
+
let(:match_count) { 20 }
|
216
|
+
let(:miss_count) { 7 }
|
217
|
+
|
218
|
+
before do
|
219
|
+
match_count.times { GetSpec::User.create(last_name: last_name) }
|
220
|
+
miss_count.times { GetSpec::User.create }
|
221
|
+
end
|
222
|
+
|
223
|
+
context 'when limit is passed' do
|
224
|
+
it 'limits the records' do
|
225
|
+
result = Get::UsersBy.run({ last_name: last_name }, limit: 2)
|
226
|
+
expect(result.length).to eq 2
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context 'when offset is passed' do
|
231
|
+
it 'offsets the response' do
|
232
|
+
result = Get::UsersBy.run({ last_name: last_name }, offset: 5)
|
233
|
+
expect(result.length).to eq 15
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context 'when order is passed' do
|
238
|
+
it 'orders the response' do
|
239
|
+
result = Get::UsersBy.run({ last_name: last_name }, order: { id: :asc })
|
240
|
+
ar_result = GetSpec::User.where(last_name: last_name).order('id asc')
|
241
|
+
|
242
|
+
expect(result.first.id).to eq ar_result.first.id
|
243
|
+
expect(result.last.id).to eq ar_result.last.id
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
213
248
|
context 'when no records exist' do
|
214
249
|
it 'returns empty collection' do
|
215
250
|
expect(Get::UsersBy.run(last_name: last_name).empty?).to be true
|
@@ -217,7 +252,7 @@ describe Get do
|
|
217
252
|
end
|
218
253
|
end
|
219
254
|
|
220
|
-
context '
|
255
|
+
context 'associations' do
|
221
256
|
context 'direct relation' do
|
222
257
|
let(:employer) { GetSpec::Employer.create }
|
223
258
|
let!(:user1) { GetSpec::User.create(employer: employer) }
|
@@ -232,8 +267,9 @@ describe Get do
|
|
232
267
|
context 'ChildrenFromParent' do
|
233
268
|
it 'returns children' do
|
234
269
|
result = Get::UsersFromEmployer.run(employer)
|
235
|
-
|
236
|
-
expect(result.
|
270
|
+
ar_result = GetSpec::User.where(employer_id: employer.id).order('id desc')
|
271
|
+
expect(result.first.id).to eq ar_result.first.id
|
272
|
+
expect(result.last.id).to eq ar_result.last.id
|
237
273
|
end
|
238
274
|
end
|
239
275
|
|
@@ -277,6 +313,55 @@ describe Get do
|
|
277
313
|
expect(result.first.to_h).to eq sportscar.attributes
|
278
314
|
end
|
279
315
|
end
|
316
|
+
|
317
|
+
context 'with options' do
|
318
|
+
let(:employer) { GetSpec::Employer.create }
|
319
|
+
let(:match_count) { 20 }
|
320
|
+
let(:miss_count) { 7 }
|
321
|
+
|
322
|
+
before do
|
323
|
+
match_count.times { employer.users << GetSpec::User.create(employer: employer, last_name: last_name) }
|
324
|
+
miss_count.times { employer.users << GetSpec::User.create(employer: employer) }
|
325
|
+
end
|
326
|
+
|
327
|
+
context 'when conditions are passed' do
|
328
|
+
it 'filters response' do
|
329
|
+
result = Get::UsersFromEmployer.run(employer.id, conditions: { last_name: last_name })
|
330
|
+
expect(result.length).to eq match_count
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
context 'when limit is passed' do
|
335
|
+
it 'limits response' do
|
336
|
+
result = Get::UsersFromEmployer.run(employer.id, conditions: { last_name: last_name }, limit: 5)
|
337
|
+
expect(result.length).to eq 5
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
context 'when offset is passed' do
|
342
|
+
it 'offsets response' do
|
343
|
+
result = Get::UsersFromEmployer.run(employer.id, conditions: { last_name: last_name }, offset: 16)
|
344
|
+
expect(result.length).to eq match_count - 16
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
context 'when order is passed' do
|
349
|
+
it 'orders response' do
|
350
|
+
result = Get::UsersFromEmployer.run(employer.id, conditions: { last_name: last_name }, order: { id: :asc })
|
351
|
+
ar_result = GetSpec::User.where(employer_id: employer.id, last_name: last_name).order('id asc')
|
352
|
+
|
353
|
+
expect(result.first.id).to eq ar_result.first.id
|
354
|
+
expect(result.last.id).to eq ar_result.last.id
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
context 'when eager_load is passed' do
|
359
|
+
it 'behaves as expected' do
|
360
|
+
result = Get::UsersFromEmployer.run(employer.id, conditions: { last_name: last_name }, eager_load: true)
|
361
|
+
expect(result.length).to eq match_count
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
280
365
|
end
|
281
366
|
end
|
282
367
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: get
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Blake Turner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: horza
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.3.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.3.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|