plain_query 0.1.1 → 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 +16 -16
- data/lib/plain_query/builder.rb +22 -0
- data/lib/plain_query/builder_options.rb +17 -0
- data/lib/plain_query/class_methods.rb +15 -0
- data/lib/plain_query/instance_methods.rb +53 -0
- data/lib/plain_query/version.rb +4 -2
- data/lib/plain_query.rb +32 -2
- data/plain_query.gemspec +2 -2
- metadata +11 -6
- data/lib/plain_query/logic.rb +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9fff1e9e16625d80a9f3f62e6c3cffcda79974c4bfcd41a8a2717165cb3f35f9
|
4
|
+
data.tar.gz: 282cf0017c3d7b4ab771f17c67f3340a5a6d7cab1315ce821d7ffbb5e5040694
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30e9043ea80059f42b536998decf73e7b672a54da0724a139dab572f41460118b1635f0ad00f30ef22d8c4825b1abf8d29cd49edc4cd6761fae4bd4787371f57
|
7
|
+
data.tar.gz: f66cdc23a9feb4143ef6bb7cba086872bc9c47be6ab2073795c3f40921affb56c432100ad4c82d6624e712d3704b3df93a8561a3ef971bddaaa19635638a989f
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# PlainQuery
|
1
|
+
# PlainQuery 
|
2
2
|
|
3
3
|
PlainQuery is a simple gem that helps you write clear and flexible query objects.
|
4
4
|
Main task for this gem is performing complex querying on active record relation, make it intuitive.
|
@@ -32,13 +32,13 @@ Or install it yourself as:
|
|
32
32
|
|
33
33
|
## Usage
|
34
34
|
### Setting up a query object
|
35
|
-
For setting up a query object you need to
|
35
|
+
For setting up a query object you need to include PlainQuery::Base into your your query class.
|
36
36
|
Then you need to describe query steps by using query_step method.
|
37
37
|
Query steps perform in writing order.
|
38
38
|
|
39
39
|
```rb
|
40
|
-
class UsersQuery
|
41
|
-
model User
|
40
|
+
class UsersQuery
|
41
|
+
include PlainQuery::Base(model: User)
|
42
42
|
|
43
43
|
query_step :filter_by_activity, if: -> { options[:only_active] }
|
44
44
|
query_step :filter_by_phone_presence
|
@@ -67,11 +67,11 @@ users = UsersQuery.call(User.all, only_active: true)
|
|
67
67
|
Query object implements `#call` method with two arguments:
|
68
68
|
|
69
69
|
`relation` - Base scope which will be mutated inside query object. (`User.all` in example).
|
70
|
-
If you dont pass it - will be used default scope from model declaration
|
70
|
+
If you dont pass it - will be used default scope from model declaration from PlainQuery::Base module include `include PlainQuery::Base(model: User)`.
|
71
71
|
|
72
72
|
`options` - Any data which will be used inside query steps or execution conditions. (`only_active: true` in example).
|
73
73
|
|
74
|
-
Query object returns scope builded by query steps execution.
|
74
|
+
Query object returns scope builded by ordered query steps execution.
|
75
75
|
|
76
76
|
### query_step
|
77
77
|
`query_step` is a main part of query building.
|
@@ -87,7 +87,7 @@ query_step STEP_NAME, CONDITION_OF_EXECUTION
|
|
87
87
|
`STEP_NAME` is a name of method which will be executed.
|
88
88
|
|
89
89
|
`CONDITION_OF_EXECUTION` is a method which allows or denieds execution of query step.
|
90
|
-
Type of condition can be `if:` or `unless:`. Condition definition can be proc (lambda) or some query object method name.
|
90
|
+
Type of condition can be `if:` or `unless:`. Condition definition can be proc (lambda) or some query object method name. Result of query object method used in condition will be used as a boolean value.
|
91
91
|
|
92
92
|
### Using in Active Record model scope.
|
93
93
|
First of all you need to set correct model name inside query object.
|
@@ -95,18 +95,12 @@ First of all you need to set correct model name inside query object.
|
|
95
95
|
It uses for correct base scope building without passing relation to query object.
|
96
96
|
|
97
97
|
```rb
|
98
|
-
model User
|
98
|
+
include PlainQuery::Base(model: User)
|
99
99
|
```
|
100
100
|
|
101
101
|
```rb
|
102
|
-
class
|
103
|
-
|
104
|
-
end
|
105
|
-
```
|
106
|
-
|
107
|
-
```rb
|
108
|
-
class ActiveClientsQuery < PlainQuery
|
109
|
-
model User
|
102
|
+
class ActiveClientsQuery
|
103
|
+
include PlainQuery::Base(model: User)
|
110
104
|
|
111
105
|
query_step :filter_by_activity
|
112
106
|
query_step :filter_by_role
|
@@ -121,6 +115,12 @@ class ActiveClientsQuery < PlainQuery
|
|
121
115
|
end
|
122
116
|
```
|
123
117
|
|
118
|
+
```rb
|
119
|
+
class User < ActiveRecord::Base
|
120
|
+
scope :active_clients, ActiveClientsQuery
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
124
124
|
And then you can use scope from model:
|
125
125
|
|
126
126
|
```rb
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'plain_query/builder_options'
|
4
|
+
require 'plain_query/class_methods'
|
5
|
+
require 'plain_query/instance_methods'
|
6
|
+
require 'plain_query/version'
|
7
|
+
|
8
|
+
module PlainQuery
|
9
|
+
class Builder < Module
|
10
|
+
attr_reader :builder_options
|
11
|
+
|
12
|
+
def initialize(model: nil)
|
13
|
+
@builder_options = BuilderOptions.new(model: model)
|
14
|
+
end
|
15
|
+
|
16
|
+
def included(klass)
|
17
|
+
klass.extend(builder_options)
|
18
|
+
klass.extend(ClassMethods)
|
19
|
+
klass.include(InstanceMethods)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlainQuery
|
4
|
+
class BuilderOptions < Module
|
5
|
+
def initialize(model: nil)
|
6
|
+
define_model(model)
|
7
|
+
end
|
8
|
+
|
9
|
+
def define_model(model)
|
10
|
+
module_exec(model) do |model_name|
|
11
|
+
define_method(:model) do
|
12
|
+
model_name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PlainQuery
|
2
|
+
module ClassMethods
|
3
|
+
def call(relation = model&.all, options = {})
|
4
|
+
new(relation, options).exec_query
|
5
|
+
end
|
6
|
+
|
7
|
+
def steps
|
8
|
+
@steps ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def query_step(step_name, params = {})
|
12
|
+
steps << [step_name, params]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlainQuery
|
4
|
+
module InstanceMethods
|
5
|
+
attr_reader :relation, :options
|
6
|
+
|
7
|
+
def initialize(relation, options)
|
8
|
+
@relation = relation
|
9
|
+
@options = options
|
10
|
+
@steps = self.class.steps
|
11
|
+
|
12
|
+
# Validates initial relation format. Allowed only ActiveRecord::Relation.
|
13
|
+
unless @relation.is_a?(ActiveRecord::Relation)
|
14
|
+
raise(StandardError, 'Queries accept only ActiveRecord::Relation as input')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def exec_query
|
19
|
+
@steps.each do |step_name, params|
|
20
|
+
# Handles if condition
|
21
|
+
if (params[:if] && !exec_condition(params[:if]))
|
22
|
+
next
|
23
|
+
end
|
24
|
+
|
25
|
+
# Handles unless condition
|
26
|
+
if (params[:unless] && exec_condition(params[:unless]))
|
27
|
+
next
|
28
|
+
end
|
29
|
+
|
30
|
+
# Executes query mutation and checks that step returns ActiveRecord::Relation
|
31
|
+
mutated_query = send(step_name)
|
32
|
+
unless mutated_query.is_a?(ActiveRecord::Relation)
|
33
|
+
raise(StandardError, 'Scope must be ActiveRecord::Relation')
|
34
|
+
end
|
35
|
+
|
36
|
+
@relation = mutated_query
|
37
|
+
end
|
38
|
+
|
39
|
+
@relation
|
40
|
+
end
|
41
|
+
|
42
|
+
# Executes if and unless conditions, conditions must contain object method name or proc (lambda)
|
43
|
+
def exec_condition(condition)
|
44
|
+
if [String, Symbol].member?(condition.class)
|
45
|
+
!!send(condition)
|
46
|
+
elsif condition.is_a?(Proc)
|
47
|
+
!!instance_exec(&condition)
|
48
|
+
else
|
49
|
+
raise(StandardError, 'Condition must be method name or proc')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/plain_query/version.rb
CHANGED
data/lib/plain_query.rb
CHANGED
@@ -1,2 +1,32 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'plain_query/builder'
|
4
|
+
|
5
|
+
module PlainQuery
|
6
|
+
# Realises methods for scope building by execution of sequence of steps
|
7
|
+
# Query class defines query steps:
|
8
|
+
# query_step :filter_by_status, if: -> { options[:status].present? }
|
9
|
+
#
|
10
|
+
# def filter_by_status
|
11
|
+
# relation.where(status: options[:status])
|
12
|
+
# end
|
13
|
+
module Base
|
14
|
+
def self.included(klass)
|
15
|
+
klass.include(PlainQuery::Base())
|
16
|
+
end
|
17
|
+
end
|
18
|
+
# This method allows to declare model parameter associated with this query object.
|
19
|
+
# Model parameter is useful for model scopes building
|
20
|
+
# Example:
|
21
|
+
# class UsersQuery
|
22
|
+
# include PlainQuery::Base(model: User)
|
23
|
+
# ...
|
24
|
+
# end
|
25
|
+
# class User < ActiveRecord::Base
|
26
|
+
# scope :available, UsersQuery
|
27
|
+
# end
|
28
|
+
def self.Base(model: nil)
|
29
|
+
PlainQuery::Builder.new(model: model)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
data/plain_query.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["gl-pv"]
|
10
10
|
spec.email = ["gleeb812812@yandex.ru"]
|
11
11
|
|
12
|
-
spec.summary = %q{
|
13
|
-
spec.description = %q{PlainQuery
|
12
|
+
spec.summary = %q{PlainQuery is a simple gem that helps you write clear and flexible query objects}
|
13
|
+
spec.description = %q{PlainQuery helps in decomposing your fat ActiveRecord models and keeping your code slim and readable by extracting complex SQL queries or scopes into the separated classes that are easy to write, read and use.}
|
14
14
|
spec.homepage = "https://github.com/gl-pv/plain_query"
|
15
15
|
spec.license = "MIT"
|
16
16
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plain_query
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- gl-pv
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-09-
|
11
|
+
date: 2022-09-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -94,8 +94,9 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 1.4.2
|
97
|
-
description: PlainQuery
|
98
|
-
|
97
|
+
description: PlainQuery helps in decomposing your fat ActiveRecord models and keeping
|
98
|
+
your code slim and readable by extracting complex SQL queries or scopes into the
|
99
|
+
separated classes that are easy to write, read and use.
|
99
100
|
email:
|
100
101
|
- gleeb812812@yandex.ru
|
101
102
|
executables: []
|
@@ -113,7 +114,10 @@ files:
|
|
113
114
|
- bin/console
|
114
115
|
- bin/setup
|
115
116
|
- lib/plain_query.rb
|
116
|
-
- lib/plain_query/
|
117
|
+
- lib/plain_query/builder.rb
|
118
|
+
- lib/plain_query/builder_options.rb
|
119
|
+
- lib/plain_query/class_methods.rb
|
120
|
+
- lib/plain_query/instance_methods.rb
|
117
121
|
- lib/plain_query/version.rb
|
118
122
|
- plain_query.gemspec
|
119
123
|
homepage: https://github.com/gl-pv/plain_query
|
@@ -140,5 +144,6 @@ requirements: []
|
|
140
144
|
rubygems_version: 3.0.9
|
141
145
|
signing_key:
|
142
146
|
specification_version: 4
|
143
|
-
summary:
|
147
|
+
summary: PlainQuery is a simple gem that helps you write clear and flexible query
|
148
|
+
objects
|
144
149
|
test_files: []
|
data/lib/plain_query/logic.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
# Realises methods for scope building by execution of sequence of steps
|
2
|
-
# Query class defines query steps:
|
3
|
-
# query_step :filter_by_status, if: -> { options[:status].present? }
|
4
|
-
#
|
5
|
-
# def filter_by_status
|
6
|
-
# relation.where(status: options[:status])
|
7
|
-
# end
|
8
|
-
class PlainQuery
|
9
|
-
attr_reader :relation, :options
|
10
|
-
|
11
|
-
def initialize(relation, options)
|
12
|
-
@relation = relation
|
13
|
-
@options = options
|
14
|
-
@steps = self.class.steps
|
15
|
-
|
16
|
-
# Validates initial relation format. Allowed only ActiveRecord::Relation.
|
17
|
-
unless relation.is_a?(ActiveRecord::Relation)
|
18
|
-
raise(RelationRequired, 'Queries accept only ActiveRecord::Relation as input')
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def exec_query
|
23
|
-
@steps.each do |step_name, params|
|
24
|
-
# Handles if condition
|
25
|
-
if (params[:if] && !exec_condition(params[:if]))
|
26
|
-
next
|
27
|
-
end
|
28
|
-
|
29
|
-
# Handles unless condition
|
30
|
-
if (params[:unless] && exec_condition(params[:unless]))
|
31
|
-
next
|
32
|
-
end
|
33
|
-
|
34
|
-
# Executes query mutation and checks that step returns ActiveRecord::Relation
|
35
|
-
mutated_query = send(step_name)
|
36
|
-
unless mutated_query.is_a?(ActiveRecord::Relation)
|
37
|
-
raise(RelationIsIncorrect, 'Scope must be ActiveRecord::Relation')
|
38
|
-
end
|
39
|
-
|
40
|
-
@relation = mutated_query
|
41
|
-
end
|
42
|
-
|
43
|
-
@relation
|
44
|
-
end
|
45
|
-
|
46
|
-
# Executes if and unless conditions, conditions must contain object method name or proc (lambda)
|
47
|
-
def exec_condition(condition)
|
48
|
-
if [String, Symbol].member?(condition.class)
|
49
|
-
!!send(condition)
|
50
|
-
elsif condition.is_a?(Proc)
|
51
|
-
!!instance_exec(&condition)
|
52
|
-
else
|
53
|
-
raise(ConditionFormatIsIncorrect, 'Condition must be method name or proc')
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
class << self
|
58
|
-
def call(relation = @model&.all, options = {})
|
59
|
-
new(relation, options).exec_query
|
60
|
-
end
|
61
|
-
|
62
|
-
def query_step(step_name, params = {})
|
63
|
-
steps << [step_name, params]
|
64
|
-
end
|
65
|
-
|
66
|
-
def steps
|
67
|
-
@steps ||= []
|
68
|
-
end
|
69
|
-
|
70
|
-
def model(model)
|
71
|
-
@model = model
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|