litetags 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: d429447c6ac8c7de8a04aed16f1ccdde4e093d027544d90ba4d97bada4aefeb1
4
- data.tar.gz: 40ab43074326f34593f08d51d7363eed85345a867b3e6aff8d72e917add41ed6
3
+ metadata.gz: ca6f6ddb7c14a7e8348471c9d55b53818e4e6c0f52649998671ee819eb172122
4
+ data.tar.gz: f008123f5380ad4f0a623c91c39668b9db59184d907b447bd6fcd601db3e6d02
5
5
  SHA512:
6
- metadata.gz: ff42f221f0f1c551fb749d3c1b19b88707472c1f8c0075ca55760b674c7496ad2fc53ae5726fe77dd3f08fbef385b19d63a2cbd5749c270700f03d48538fd654
7
- data.tar.gz: b03cf9917b103ae62c984c8ecca3a2977f82bf7ec42c6f3fea925370101618a478073ea8e16ceaa5f879a40f1a561b7a3ce3904a588e4a64197b5dad91d7cdd7
6
+ metadata.gz: 3d681f1715cdfd3a2e12b4d00840aa16bc363c7ac48e48ca9622a0046635ad3918bb910f58314da7e80e772ba9df8d8ee3ebc9a0374cc13b50be6b299c6734dd
7
+ data.tar.gz: 64b52cbe30e8669e813270d3c1b2a842d658f9f62cf90ae4457f5b2080ddbd22bf87c3135314e0ca97daede33bd482c02113adbc862a8179b8a0181e75ad4672
data/Gemfile.lock CHANGED
@@ -1,12 +1,28 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- litetags (0.1.0)
4
+ litetags (0.2.0)
5
+ activerecord (~> 6.1)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ activemodel (6.1.7.4)
11
+ activesupport (= 6.1.7.4)
12
+ activerecord (6.1.7.4)
13
+ activemodel (= 6.1.7.4)
14
+ activesupport (= 6.1.7.4)
15
+ activesupport (6.1.7.4)
16
+ concurrent-ruby (~> 1.0, >= 1.0.2)
17
+ i18n (>= 1.6, < 2)
18
+ minitest (>= 5.1)
19
+ tzinfo (~> 2.0)
20
+ zeitwerk (~> 2.3)
9
21
  ast (2.4.2)
22
+ concurrent-ruby (1.2.2)
23
+ docile (1.4.0)
24
+ i18n (1.14.1)
25
+ concurrent-ruby (~> 1.0)
10
26
  json (2.6.3)
11
27
  language_server-protocol (3.17.0.3)
12
28
  lint_roller (1.1.0)
@@ -36,6 +52,13 @@ GEM
36
52
  rubocop (>= 1.7.0, < 2.0)
37
53
  rubocop-ast (>= 0.4.0)
38
54
  ruby-progressbar (1.13.0)
55
+ simplecov (0.22.0)
56
+ docile (~> 1.1)
57
+ simplecov-html (~> 0.11)
58
+ simplecov_json_formatter (~> 0.1)
59
+ simplecov-html (0.12.3)
60
+ simplecov_json_formatter (0.1.4)
61
+ sqlite3 (1.6.3-arm64-darwin)
39
62
  standard (1.30.1)
40
63
  language_server-protocol (~> 3.17.0.2)
41
64
  lint_roller (~> 1.0)
@@ -48,7 +71,10 @@ GEM
48
71
  standard-performance (1.1.2)
49
72
  lint_roller (~> 1.1)
50
73
  rubocop-performance (~> 1.18.0)
74
+ tzinfo (2.0.6)
75
+ concurrent-ruby (~> 1.0)
51
76
  unicode-display_width (2.4.2)
77
+ zeitwerk (2.6.11)
52
78
 
53
79
  PLATFORMS
54
80
  arm64-darwin-21
@@ -57,6 +83,8 @@ DEPENDENCIES
57
83
  litetags!
58
84
  minitest (~> 5.0)
59
85
  rake (~> 13.0)
86
+ simplecov
87
+ sqlite3
60
88
  standard (~> 1.3)
61
89
 
62
90
  BUNDLED WITH
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Litetags
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/litetags.rb CHANGED
@@ -1,8 +1,128 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "litetags/version"
4
+ require "active_record"
4
5
 
5
6
  module Litetags
6
- class Error < StandardError; end
7
- # Your code goes here...
7
+ def self.included(klass)
8
+ klass.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ def tag_columns(*column_names)
13
+ @tag_columns ||= {}
14
+
15
+ tag_columns_sanitize_list(column_names).each do |column_name|
16
+ @tag_columns[column_name] ||= false
17
+ end
18
+
19
+ @tag_columns.each do |column_name, initialized|
20
+ next if initialized
21
+
22
+ column_name = column_name.to_s
23
+ method_name = column_name.downcase
24
+
25
+ # JSON_EACH("{table}"."{column}")
26
+ json_each = Arel::Nodes::NamedFunction.new("JSON_EACH", [arel_table[column_name]])
27
+
28
+ # SELECT DISTINCT value FROM "{table}", JSON_EACH("{table}"."{column}")
29
+ define_singleton_method :"unique_#{method_name}" do |conditions = "true"|
30
+ select("value")
31
+ .from([arel_table, json_each])
32
+ .distinct
33
+ .pluck("value")
34
+ .sort
35
+ end
36
+
37
+ # SELECT value, COUNT(*) AS count FROM "{table}", JSON_EACH("{table}"."{column}") GROUP BY value ORDER BY value
38
+ define_singleton_method :"#{method_name}_cloud" do |conditions = "true"|
39
+ select("value")
40
+ .from([arel_table, json_each])
41
+ .group("value")
42
+ .order("value")
43
+ .pluck(Arel.sql("value, COUNT(*) AS count"))
44
+ .to_h
45
+ end
46
+
47
+ # SELECT "{table}".* FROM "{table}" WHERE "{table}"."{column}" IS NOT NULL AND "{table}"."{column}" != '[]'
48
+ scope :"with_#{method_name}", -> {
49
+ where.not(arel_table[column_name].eq(nil))
50
+ .where.not(arel_table[column_name].eq([]))
51
+ }
52
+
53
+ # SELECT "{table}".* FROM "{table}" WHERE ("{table}"."{column}" IS NULL OR "{table}"."{column}" = '[]')
54
+ scope :"without_#{method_name}", -> {
55
+ where(arel_table[column_name].eq(nil))
56
+ .or(where(arel_table[column_name].eq([])))
57
+ }
58
+
59
+ # SELECT "{table}".* FROM "{table}" WHERE EXISTS (SELECT 1 FROM JSON_EACH("{table}"."{column}") WHERE value IN ({values}) LIMIT 1)
60
+ scope :"with_any_#{method_name}", ->(*items) {
61
+ values = tag_columns_sanitize_list(items)
62
+ overlap = Arel::SelectManager.new(json_each)
63
+ .project(1)
64
+ .where(Arel.sql("value").in(values))
65
+ .take(1)
66
+ .exists
67
+
68
+ where overlap
69
+ }
70
+
71
+ # SELECT "{table}".* FROM "{table}" WHERE (SELECT COUNT(*) FROM JSON_EACH("{table}"."{column}") WHERE value IN ({values})) = {values.size};
72
+ scope :"with_all_#{method_name}", ->(*items) {
73
+ values = tag_columns_sanitize_list(items)
74
+ count = Arel::SelectManager.new(json_each)
75
+ .project(Arel.star.count)
76
+ .where(Arel.sql("value").in(values))
77
+ contains = Arel::Nodes::Equality.new(count, values.size)
78
+
79
+ where contains
80
+ }
81
+
82
+ # SELECT "{table}".* FROM "{table}" WHERE NOT EXISTS (SELECT 1 FROM JSON_EACH("{table}"."{column}") WHERE value IN ({values}) LIMIT 1)
83
+ scope :"without_any_#{method_name}", ->(*items) {
84
+ values = tag_columns_sanitize_list(items)
85
+ overlap = Arel::SelectManager.new(json_each)
86
+ .project(1)
87
+ .where(Arel.sql("value").in(values))
88
+ .take(1)
89
+ .exists
90
+ where.not overlap
91
+ }
92
+
93
+ # SELECT "{table}".* FROM "{table}" WHERE (SELECT COUNT(*) FROM JSON_EACH("{table}"."{column}") WHERE value IN ({values})) != {values.size};
94
+ scope :"without_all_#{method_name}", ->(*items) {
95
+ values = tag_columns_sanitize_list(items)
96
+ count = Arel::SelectManager.new(json_each)
97
+ .project(Arel.star.count)
98
+ .where(Arel.sql("value").in(values))
99
+ contains = Arel::Nodes::Equality.new(count, values.size)
100
+ where.not contains
101
+ }
102
+
103
+ before_validation -> { self[column_name] = self.class.tag_columns_sanitize_list(self[column_name]) }
104
+
105
+ define_method :"has_any_#{method_name}?" do |*values|
106
+ values = self.class.tag_columns_sanitize_list(values)
107
+ existing = self.class.tag_columns_sanitize_list(self[column_name])
108
+ (values & existing).present?
109
+ end
110
+
111
+ define_method :"has_all_#{method_name}?" do |*values|
112
+ values = self.class.tag_columns_sanitize_list(values)
113
+ existing = self.class.tag_columns_sanitize_list(self[column_name])
114
+ (values & existing).size == values.size
115
+ end
116
+
117
+ alias_method :"has_#{method_name.singularize}?", :"has_all_#{method_name}?"
118
+
119
+ @tag_columns[column_name] = true
120
+ end
121
+ end
122
+
123
+ def tag_columns_sanitize_list(values = [])
124
+ return [] if values.nil?
125
+ values.select(&:present?).map(&:to_s).uniq.sort
126
+ end
127
+ end
8
128
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: litetags
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
  - Stephen Margheim
@@ -9,7 +9,49 @@ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
11
  date: 2023-08-10 00:00:00.000000000 Z
12
- dependencies: []
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '6.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '6.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: simplecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
13
55
  description:
14
56
  email:
15
57
  - stephen.margheim@gmail.com