like_query 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 16e59dc644a7dc678761df657971741c56ac76b3a0dcd773cb51b9e7ed241ecf
4
+ data.tar.gz: 7abd672371cb6ec62799ed15d882f41ed2d88a66f5828f25541eaa96cf1d49ce
5
+ SHA512:
6
+ metadata.gz: adb0afaf4ef8a6bc4a5c1c5571958702c751405a55a9f50954f71c63e2ac6283e3f1b62488778ba0ecf3d144dd98dbad62c1585e7ed86e8c6c65ea40b6a11a6d
7
+ data.tar.gz: 3fb0bd08e5e6c3853f1ab63ae306080a17354d277c7ac0ede1310404c9e2f68777edf6df0770161bc13bc4247efffd5d5c9b7229d1442ffbe4df9f7897d53eaf
data/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # like_query
2
+
3
+ For my customers apps, newly built with turbo, search queries are mostly for two purposes:
4
+
5
+ Index view callable by a url like `/customers?find=müller screw` or a javascript dropdown on the front, in our case built with svelte, which receives a json and renders a table in a dropdown.
6
+
7
+ This query generator is built for this two purposes.
8
+
9
+ ## Installation
10
+
11
+ add
12
+
13
+ `gem "like_query"`
14
+
15
+ to Gemfile
16
+
17
+ this adds the methods `#like` and `#like_result` to all models.
18
+
19
+ ## Usage
20
+
21
+ **.like**
22
+
23
+ ```ruby
24
+ customer = Customer.create(name: 'Ambühl')
25
+ art1 = Article.create(name: 'first', number: '01', customer: customer)
26
+
27
+ Article.like('fir', :name, :number)
28
+ # => would find art1
29
+ # => searches by: "where name like '%fir%' or number like '%fir%'"
30
+ # => queries are built with Article.arel_table[:name].matches('%fir%')
31
+
32
+ Article.like(['fir', 'ambühl'], :name, :number, customer: :name)
33
+ # => :customer is the name of the association
34
+ # => search-tag as array adds a ".and(where( ... like '%ambühl%'))" for all scoped columns
35
+ # => would also find art1
36
+ ```
37
+
38
+ **.like_result**
39
+
40
+ returns a hash that can easily be transformed by `#to_json` for a javascript-frontend, by example
41
+
42
+ can only be chained behind `#like`.
43
+
44
+ ```ruby
45
+ customer = Customer.create(name: 'Ambühl')
46
+ art1 = Article.create(name: 'first', number: '01', customer: customer)
47
+
48
+ Article.like('fir', :name).like_result(limit: 10)
49
+ # returns:
50
+ # {
51
+ # :data=>[
52
+ # {
53
+ # :attributes=>[[:name, "first"]],
54
+ # :id=>1}],
55
+ # :length=>1,
56
+ # :overflow=>false,
57
+ # :columns_count=>1,
58
+ # :sub_records_columns_count=>0
59
+ # }
60
+
61
+ Article.like('fir', :name).like_result( :number, limit: 10)
62
+ # would query like the above example: Search scope is only :name
63
+ # but would return article-number instead of article-name inside the data block
64
+ ```
65
+
66
+ **Class LikeQuery::Collect**
67
+
68
+ ```ruby
69
+ cust = Customer.create(name: 'Müller')
70
+ 20.times {Article.create(name: 'any-article', customer: cust)}
71
+
72
+ c = LikeQuery::Collect.new(4)
73
+ # => 4 is the limit
74
+
75
+ c.receive { Article.like('any-art', :name) }
76
+ # => would add 4 articles to the result hash because of limit
77
+
78
+ c.receive { Customer.like('any-art', :name, articles: :name) }
79
+ # => limit is already exhausted: does nothing
80
+ # => otherwise it would add Customers to the result hash
81
+
82
+ c.result
83
+ # => would return anything like:
84
+ {
85
+ :data=>[
86
+ {:attributes=>[["customer.name", "Müller"]], :id=>1},
87
+ {:attributes=>[["customer.name", "Müller"]], :id=>2},
88
+ {
89
+ :attributes=>[[:name, "second"], [:number, "01"]], :id=>3,
90
+ :associations=>{:customer=>[{:attributes=>[[:name, "Ambühl"]], :id=>2}]}}],
91
+ :length=>4,
92
+ :overflow=>true,
93
+ :columns_count=>2,
94
+ :sub_records_columns_count=>1
95
+ }
96
+ ```
97
+
98
+ ## Tests
99
+
100
+ Tests for this gem, by rspec, are included not inside this gem, they can be found in a [test project](https://gitlab.com/sedl/like_query_project)
101
+
102
+ - [ ] [Set up project integrations](https://gitlab.com/sedl/like_query/-/settings/integrations)
103
+
104
+ ## Collaborate with your team
105
+
106
+ - [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
107
+ - [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
108
+ - [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
109
+ - [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
110
+ - [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
111
+
112
+ ## Test and Deploy
113
+
114
+ Use the built-in continuous integration in GitLab.
115
+
116
+ - [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
117
+ - [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
118
+ - [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
119
+ - [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
120
+ - [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
121
+
122
+ ***
123
+
124
+ ## Hope this helps
125
+
126
+ I created this only for one of my customers. If it may help others, i am happy.
127
+
128
+ ## License
129
+
130
+ [MIT](https://mit-license.org/)
@@ -0,0 +1,95 @@
1
+ module LikeQuery
2
+ class Collect
3
+ def initialize(limit = 50)
4
+ @limit = limit
5
+ @length = 0
6
+ @data = []
7
+ @overflow = false
8
+ @columns_count = 0
9
+ @sub_records_columns_count = 0
10
+ end
11
+
12
+ def receive(*result_pattern, image: nil, &block)
13
+
14
+ return false if @length >= @limit
15
+
16
+ recs = yield
17
+
18
+ pattern = (result_pattern.present? ? result_pattern : recs.like_query_pattern)
19
+
20
+ associations = []
21
+ pattern.each do |p|
22
+ if p.is_a?(Hash)
23
+ associations += p.keys
24
+ end
25
+ end
26
+
27
+ recs.includes(associations).each do |r|
28
+ rec_attr = {}
29
+ pattern.each do |p|
30
+ if p.is_a?(Hash)
31
+
32
+ # ASSOCIATED RECORDS
33
+
34
+ p.each do |assoc, cols|
35
+ rec_attr[:associations] ||= {}
36
+ rec_attr[:associations][assoc] ||= []
37
+ sub = r.send(assoc)
38
+ _cols = (cols.is_a?(Enumerable) ? cols : [cols])
39
+ (sub.is_a?(Enumerable) ? sub : [sub]).each do |_sub|
40
+ sub_attr = []
41
+ _cols.each do |c|
42
+ sub_attr.push([c, _sub.send(c)])
43
+ end
44
+
45
+ if @length >= @limit
46
+ @overflow = true
47
+ break
48
+ elsif @length < @limit
49
+ c = sub_attr.length
50
+ @sub_records_columns_count = c if c > @sub_records_columns_count
51
+ rec_attr[:associations][assoc] ||= []
52
+ rec_attr[:associations][assoc].push({ attributes: sub_attr, id: _sub.id })
53
+ @length += 1
54
+ end
55
+
56
+ end
57
+ end
58
+ elsif p.is_a?(Symbol) || p.is_a?(String)
59
+
60
+ # MAIN RECORD
61
+
62
+ v = nil
63
+ p.to_s.split('.').each { |_p| v = (v ? v : r).send(_p) }
64
+ rec_attr[:attributes] ||= []
65
+ rec_attr[:attributes].push([p, v])
66
+ rec_attr[:id] = r.id
67
+
68
+ end
69
+ end
70
+ if @length >= @limit
71
+ @overflow = true
72
+ break
73
+ elsif @length < @limit
74
+ c = (image ? 1 : 0) + rec_attr[:attributes].length
75
+ @columns_count = c if c > @columns_count
76
+ @data.push(rec_attr)
77
+ @length += 1
78
+ end
79
+ end
80
+
81
+ true
82
+ end
83
+
84
+ def result
85
+ {
86
+ data: @data,
87
+ length: @length,
88
+ overflow: @overflow,
89
+ columns_count: @columns_count,
90
+ sub_records_columns_count: @sub_records_columns_count
91
+ }
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,70 @@
1
+ module LikeQuery
2
+ module ModelExtensions
3
+
4
+ def like(search_string, *pattern)
5
+
6
+ raise 'like can only be called from a model' if self == ApplicationRecord
7
+
8
+ queries = nil
9
+ associations = []
10
+ @like_query_pattern = pattern
11
+
12
+ (search_string.is_a?(String) ? search_string.split(' ') : search_string).each do |s|
13
+ str = "%#{s}%"
14
+ q = nil
15
+ if pattern.first.is_a?(Array) && pattern.length >= 2
16
+ raise "only one array can be given: Either pattern as one array or as multiple args, not as array"
17
+ end
18
+ (pattern.first.is_a?(Array) ? pattern.first : pattern).each do |p|
19
+ if p.is_a?(Symbol) || p.is_a?(String)
20
+ _q = arel_table[p].matches(str)
21
+ q = (q ? q.or(_q) : _q)
22
+ elsif p.is_a?(Hash)
23
+ p.each do |k, v|
24
+ assoc = reflect_on_association(k)
25
+ raise "Unknown association: :#{k}" unless assoc.present?
26
+ associations.push(k.to_sym) unless associations.include?(k.to_sym)
27
+ if v.is_a?(Symbol) || v.is_a?(String)
28
+ _q = assoc.klass.arel_table[v].matches(str)
29
+ q = (q ? q.or(_q) : _q)
30
+ elsif v.is_a?(Array)
31
+ v.each do |_v|
32
+ _q = assoc.klass.arel_table[_v].matches(str)
33
+ q = (q ? q.or(_q) : _q)
34
+ end
35
+ else
36
+ raise "unknown element: #{p}"
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+ queries = (queries ? queries.and(q) : q)
43
+ end
44
+
45
+ @query = if associations.present?
46
+ left_outer_joins(associations).where(
47
+ queries
48
+ )
49
+ else
50
+ where(
51
+ queries
52
+ )
53
+ end
54
+ end
55
+
56
+ def like_result(*pattern, limit: 50, image: nil)
57
+
58
+
59
+ raise 'has to be called behind #like' unless @like_query_pattern.is_a?(Array)
60
+ c = LikeQuery::Collect.new(limit)
61
+ c.receive(*pattern){@query}
62
+ c.result
63
+ end
64
+
65
+ def like_query_pattern
66
+ @like_query_pattern
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,7 @@
1
+ module LikeQuery
2
+ class Railtie < Rails::Railtie
3
+ config.after_initialize do
4
+ ApplicationRecord.send(:extend, LikeQuery::ModelExtensions)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module LikeQuery
2
+ VERSION = "0.0.1"
3
+ end
data/lib/like_query.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'like_query/version'
2
+ require 'like_query/model_extensions'
3
+ require 'like_query/railtie'
4
+ require 'like_query/collect'
5
+
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: like_query
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - christian
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-08-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 7.0.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 7.0.3
27
+ description: helper for building active record calls.
28
+ email:
29
+ - christian@sedlmair.ch
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README.md
35
+ - lib/like_query.rb
36
+ - lib/like_query/collect.rb
37
+ - lib/like_query/model_extensions.rb
38
+ - lib/like_query/railtie.rb
39
+ - lib/like_query/version.rb
40
+ homepage: https://gitlab.com/sedl/like_query
41
+ licenses:
42
+ - MIT
43
+ metadata:
44
+ homepage_uri: https://gitlab.com/sedl/like_query
45
+ source_code_uri: https://gitlab.com/sedl/like_query
46
+ changelog_uri: https://gitlab.com/sedl/like_query
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubygems_version: 3.4.10
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: helper for building active record calls.
66
+ test_files: []