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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dee05c4d00595dfab9fd6e7d5a2f154a5859a4be75ef3f73e38260b2e06ea300
4
- data.tar.gz: d0e1115a8da4c3f78cb97fe3107743ca1f4aab9874260f9a26ea86e68da64594
3
+ metadata.gz: 9fff1e9e16625d80a9f3f62e6c3cffcda79974c4bfcd41a8a2717165cb3f35f9
4
+ data.tar.gz: 282cf0017c3d7b4ab771f17c67f3340a5a6d7cab1315ce821d7ffbb5e5040694
5
5
  SHA512:
6
- metadata.gz: 676f7357d5ece333078b7434451ee1d26f4512a2e7e9ede0ddba7530fad1a32da8568679f6835da4b1058d86ff5bf4914e9c1c04d6846da291f36466e49e7b3a
7
- data.tar.gz: bbb09631bb05e2ed2cbbc52346ea18fc42813a50c1f790b786a9da5d9cf6b302b5ee1b70c2335739176640e0668957dce4334be6dabffac9c495ff8f871bba1f
6
+ metadata.gz: 30e9043ea80059f42b536998decf73e7b672a54da0724a139dab572f41460118b1635f0ad00f30ef22d8c4825b1abf8d29cd49edc4cd6761fae4bd4787371f57
7
+ data.tar.gz: f66cdc23a9feb4143ef6bb7cba086872bc9c47be6ab2073795c3f40921affb56c432100ad4c82d6624e712d3704b3df93a8561a3ef971bddaaa19635638a989f
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,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 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
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 inside query object.
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 User < ActiveRecord::Base
103
- scope :active_clients, ActiveClientsQuery
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
@@ -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.0'.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.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-12 00:00:00.000000000 Z
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 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