like_query 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []