plain_query 0.1.1 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dee05c4d00595dfab9fd6e7d5a2f154a5859a4be75ef3f73e38260b2e06ea300
4
- data.tar.gz: d0e1115a8da4c3f78cb97fe3107743ca1f4aab9874260f9a26ea86e68da64594
3
+ metadata.gz: 70c15cb5669c0f7794a193a259ba966f97f6017fafa8db1d6b97ce63d4e2ef17
4
+ data.tar.gz: 37d7a7c0e1109f138a0e9860dd956002b6b68de6eb6b54cac501b62783b01bff
5
5
  SHA512:
6
- metadata.gz: 676f7357d5ece333078b7434451ee1d26f4512a2e7e9ede0ddba7530fad1a32da8568679f6835da4b1058d86ff5bf4914e9c1c04d6846da291f36466e49e7b3a
7
- data.tar.gz: bbb09631bb05e2ed2cbbc52346ea18fc42813a50c1f790b786a9da5d9cf6b302b5ee1b70c2335739176640e0668957dce4334be6dabffac9c495ff8f871bba1f
6
+ metadata.gz: d24c959954d7cf9a25fb512ab3c603c46a168891e84a120c980fda12675e398a81af9362cd1cc203b3f97342f611b7446663277ad793fb8f03fe6d01045d808c
7
+ data.tar.gz: 9cfb9811e1c2bd1ff8c8832481f4b1d084da6526c6562ef8547f4b48759ae669d58cabe00e1a4d97fdf9f6248e0f53d2c4c1a23c58990068bf30289e6bda1658
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # PlainQuery
1
+ # PlainQuery ![transp_logo_153](https://user-images.githubusercontent.com/95882437/189980991-9417395b-ff94-4480-89a5-bb97c9aaf8ca.png)
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,26 +32,22 @@ 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 inherit your query class from PlainQuery.
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 < PlainQuery
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
- query_step :filter_by_phone_presence
44
+ query_step :filter_by_phone_presence, query: UsersWithPhoneQuery
45
45
  query_step :order_by_name
46
46
 
47
47
  def filter_by_activity
48
48
  relation.where(active: true)
49
49
  end
50
50
 
51
- def filter_by_phone_presence
52
- relation.where.not(phone: nil)
53
- end
54
-
55
51
  def order_by_name
56
52
  relation.order(name: :asc)
57
53
  end
@@ -67,11 +63,11 @@ users = UsersQuery.call(User.all, only_active: true)
67
63
  Query object implements `#call` method with two arguments:
68
64
 
69
65
  `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 inside query object.
66
+ If you dont pass it - will be used default scope from model declaration from PlainQuery::Base module include `include PlainQuery::Base(model: User)`.
71
67
 
72
68
  `options` - Any data which will be used inside query steps or execution conditions. (`only_active: true` in example).
73
69
 
74
- Query object returns scope builded by query steps execution.
70
+ Query object returns scope builded by ordered query steps execution.
75
71
 
76
72
  ### query_step
77
73
  `query_step` is a main part of query building.
@@ -81,13 +77,15 @@ It declares which query change method will be executed, condition of execution a
81
77
  It has several arguments:
82
78
 
83
79
  ```rb
84
- query_step STEP_NAME, CONDITION_OF_EXECUTION
80
+ query_step STEP_NAME, EXECUTION_OPTIONS
85
81
  ```
86
82
 
87
83
  `STEP_NAME` is a name of method which will be executed.
88
84
 
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.
85
+ `EXECUTION_OPTIONS` is a collection of options related with step execution.
86
+ Available two types of options:
87
+ `Condition` - allows or denieds execution of query step. 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.
88
+ `Nested query` - realises DI for current query object by `query` key. It can execute any class which has call method and returns relation.
91
89
 
92
90
  ### Using in Active Record model scope.
93
91
  First of all you need to set correct model name inside query object.
@@ -95,18 +93,12 @@ First of all you need to set correct model name inside query object.
95
93
  It uses for correct base scope building without passing relation to query object.
96
94
 
97
95
  ```rb
98
- model User
99
- ```
100
-
101
- ```rb
102
- class User < ActiveRecord::Base
103
- scope :active_clients, ActiveClientsQuery
104
- end
96
+ include PlainQuery::Base(model: User)
105
97
  ```
106
98
 
107
99
  ```rb
108
- class ActiveClientsQuery < PlainQuery
109
- model User
100
+ class ActiveClientsQuery
101
+ include PlainQuery::Base(model: User)
110
102
 
111
103
  query_step :filter_by_activity
112
104
  query_step :filter_by_role
@@ -121,6 +113,12 @@ class ActiveClientsQuery < PlainQuery
121
113
  end
122
114
  ```
123
115
 
116
+ ```rb
117
+ class User < ActiveRecord::Base
118
+ scope :active_clients, ActiveClientsQuery
119
+ end
120
+ ```
121
+
124
122
  And then you can use scope from model:
125
123
 
126
124
  ```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 = params[:query] ? params[:query].call(@relation, @options) : 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
@@ -1,3 +1,5 @@
1
- class PlainQuery
2
- VERSION = "0.1.1"
1
+ # frozen_string_literal: true
2
+
3
+ module PlainQuery
4
+ VERSION = '0.2.1'.freeze
3
5
  end
data/lib/plain_query.rb CHANGED
@@ -1,2 +1,32 @@
1
- require "plain_query/version"
2
- require "plain_query/logic"
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{Builds query object by declaration sequence of query building steps}
13
- spec.description = %q{PlainQuery is a simple gem that helps you write clear and flexible query objects}
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.1.1
4
+ version: 0.2.1
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-12 00:00:00.000000000 Z
11
+ date: 2022-12-11 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 is a simple gem that helps you write clear and flexible query
98
- objects
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/logic.rb
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: Builds query object by declaration sequence of query building steps
147
+ summary: PlainQuery is a simple gem that helps you write clear and flexible query
148
+ objects
144
149
  test_files: []
@@ -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