plain_query 0.1.0 → 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: bd8f5297c612866609495705363e33a5b04d43acfc4169b6db99ac7fd831a58c
4
- data.tar.gz: 7a8adf2c33adbd013d68e1989970385834861e6c3d474d88785f94bb473501c9
3
+ metadata.gz: 9fff1e9e16625d80a9f3f62e6c3cffcda79974c4bfcd41a8a2717165cb3f35f9
4
+ data.tar.gz: 282cf0017c3d7b4ab771f17c67f3340a5a6d7cab1315ce821d7ffbb5e5040694
5
5
  SHA512:
6
- metadata.gz: 95163e316ad68b4c3dd0141030ccc5e58c6de3514aca0658bcb556d4494d42cdd6260a62f4e8e58206e3a804ed3f7ac26eb976c3272c8b5ad700cecf7ea16399
7
- data.tar.gz: 31f79976ab181c103bd62823c0be4e4978da81925beb9a885515970cd0301a5a1f86dd280b079d7660af771c0d4c33032d8924edf1ec68b987b91787ed444c78
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,15 +32,15 @@ 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
- query_step :filter_by_activity, if: { options[:only_active] }
43
+ query_step :filter_by_activity, if: -> { options[:only_active] }
44
44
  query_step :filter_by_phone_presence
45
45
  query_step :order_by_name
46
46
 
@@ -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
@@ -137,8 +137,6 @@ end
137
137
 
138
138
  ## Contributing
139
139
 
140
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/plain_query. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
141
-
142
140
  1. Fork it
143
141
  2. Create your feature branch (git checkout -b my-new-feature)
144
142
  3. Commit your changes (git commit -am 'Add some feature')
@@ -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.0"
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.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-10 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,75 +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
- # def filter_by_status
5
- # relation.where(status: options[:status])
6
- # end
7
- class PlainQuery
8
- attr_reader :relation, :options
9
-
10
- def initialize(relation, options)
11
- @relation = relation
12
- @options = options
13
- @steps = self.class.steps
14
-
15
- # Validates initial relation format. Allowed only ActiveRecord::Relation, model class.
16
- if relation.nil?
17
- raise(RelationRequired, 'Queries require a base relation defined')
18
- elsif !relation.is_a?(ActiveRecord::Relation)
19
- raise(RelationRequired, 'Queries accept only ActiveRecord::Relation as input')
20
- end
21
- end
22
-
23
- def exec_query
24
- @steps.each do |step_name, params|
25
- # Handles if condition
26
- if (params[:if] && !exec_condition(params[:if]))
27
- next
28
- end
29
-
30
- # Handles unless condition
31
- if (params[:unless] && exec_condition(params[:unless]))
32
- next
33
- end
34
-
35
- # Executes query mutation and checks that step returns ActiveRecord::Relation
36
- mutated_query = send(step_name)
37
- if !mutated_query.is_a?(ActiveRecord::Relation)
38
- raise(RelationIsIncorrect, 'Scope must be ActiveRecord::Relation')
39
- end
40
-
41
- @relation = mutated_query
42
- end
43
-
44
- @relation
45
- end
46
-
47
- # Executes if and unless conditions, conditions must contain object method name or proc (lambda)
48
- def exec_condition(condition)
49
- if condition.is_a?(String) || condition.is_a?(Symbol)
50
- !!send(condition)
51
- elsif condition.is_a?(Proc)
52
- !!instance_exec(&condition)
53
- else
54
- raise(ConditionFormatIsIncorrect, 'Condition must be method name or proc')
55
- end
56
- end
57
-
58
- class << self
59
- def call(relation = @model&.all, options = {})
60
- new(relation, options).exec_query
61
- end
62
-
63
- def query_step(step_name, params = {})
64
- steps << [step_name, params]
65
- end
66
-
67
- def steps
68
- @steps ||= []
69
- end
70
-
71
- def model(model)
72
- @model = model
73
- end
74
- end
75
- end