pg_taggable 0.1.0 → 0.3.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: ec57f99287e3bb16b29e459e42c0f3c5a24d6f967917906d88dadfe491d133e3
4
- data.tar.gz: 525995c96c17957ba46a5025d250c1703efec6da6841277864befd0278072449
3
+ metadata.gz: d69f3ca42c6a0521d3c78f45b02aabd4ac22a2d2fc24a7292bff7cfffc55f954
4
+ data.tar.gz: 138eb372bdba0b11dd34d284c3c2d86afc81821140fb894c9b5787e5038f948d
5
5
  SHA512:
6
- metadata.gz: caa213bb0b77a0c5f71922337563d9bf3222cf98a17571e90f700b5fabf5b345c1c4494c71c45889e8e081b1c0c44c200fa4c228638408d39d2039d26b5bca73
7
- data.tar.gz: b4760ae519a53f6b414e5578ac6ec241b30b3342acdc82057a9a437599c65ce71bd473ef2be8ccd8c6ee99bff393fc775e647fee75ed5333f519b1ddb8617eda
6
+ metadata.gz: 3e7f136c064d12711f2bc8432881976df03332e3882e57dd548f107c7ebcecf97adac266569c17aada7c3c4d3fc3118fc68036d95869745ff16d6f7cf5e9d162
7
+ data.tar.gz: a49d82d021946a137e0b3ed82a644c346804f042df6c9b921a41d49dcf7c5040b2f266d28e189654cc41a99433cf48d8316bb1b88ad83e0c361d11be05feb1d7
data/README.md CHANGED
@@ -87,12 +87,6 @@ Find records that have exact same tags as the list, order is not important
87
87
  Post.where(tags_eq: ['food', 'travel'])
88
88
  ```
89
89
 
90
- #### #{tag_name}
91
- Find records that have exact same tags as the list, order is important. This is the default behavior.
92
- ```Ruby
93
- Post.where(tags: ['food', 'travel'])
94
- ```
95
-
96
90
  Assume a post has tags: 'A', 'B'
97
91
  |Method|Query|Matched|
98
92
  |-|-|-|
@@ -112,10 +106,6 @@ Assume a post has tags: 'A', 'B'
112
106
  |tags_eq|A, B|True|
113
107
  |tags_eq|B, A|True|
114
108
  |tags_eq|A, B, C|False|
115
- |tags|A|False|
116
- |tags|A, B|True|
117
- |tags|B, A|False|
118
- |tags|A, B, C|False|
119
109
 
120
110
  ### Class Methods
121
111
  #### taggable(name, unique: true)
@@ -145,7 +135,7 @@ Post.tags
145
135
  Post.tags.size
146
136
  # => 4
147
137
 
148
- Post.tags.distinct.size
138
+ Post.tags.select(:tag).distinct.size
149
139
  # => 3
150
140
 
151
141
  Post.tags.distinct.pluck(:tag)
@@ -155,6 +145,16 @@ Post.tags.group(:tag).count
155
145
  # => {"food"=>1, "travel"=>2, "technology"=>1}
156
146
  ```
157
147
 
148
+ #### distinct_#{tag_name}
149
+ Return an array of distinct tag records. It can be used for paging, count or other query.
150
+ ```Ruby
151
+ Post.distinct_tags
152
+ # => #<ActiveRecord::Relation [#<Post tag: "food", id: nil>, #<Post tag: "travel", id: nil>, #<Post tag: "technology", id: nil>]>
153
+
154
+ # equal to
155
+ Post.tags.select(:tag).distinct
156
+ ```
157
+
158
158
  #### uniq_#{tag_name}
159
159
  Return an array of unique tag strings.
160
160
  ```Ruby
@@ -175,6 +175,77 @@ Post.count_tags
175
175
  Post.tags.group(:tag).count
176
176
  ```
177
177
 
178
+ #### any_#{tag_name}(value, delimiter = ',')
179
+ It will create some scopes, this is useful for using ransack
180
+ ```Ruby
181
+ Post.any_tags(['food', 'travel'])
182
+
183
+ # equal to
184
+ Post.where(any_tags: ['food', 'travel'])
185
+ ```
186
+
187
+ Scope support string input
188
+ ```Ruby
189
+ Post.any_tags('food,travel')
190
+ Post.any_tags('food|travel', '|')
191
+ ```
192
+
193
+ #### all_#{tag_name}(value, delimiter = ',')
194
+ ```Ruby
195
+ Post.all_tags(['food', 'travel'])
196
+
197
+ # equal to
198
+ Post.where(all_tags: ['food', 'travel'])
199
+ ```
200
+
201
+ #### #{tag_name}_in(value, delimiter = ',')
202
+ ```Ruby
203
+ Post.tags_in(['food', 'travel'])
204
+
205
+ # equal to
206
+ Post.where(tags_in: ['food', 'travel'])
207
+ ```
208
+
209
+ #### #{tag_name}_eq(value, delimiter = ',')
210
+ ```Ruby
211
+ Post.tags_eq(['food', 'travel'])
212
+
213
+ # equal to
214
+ Post.where(tags_eq: ['food', 'travel'])
215
+ ```
216
+
217
+ #### not_any_#{tag_name}(value, delimiter = ',')
218
+ ```Ruby
219
+ Post.not_any_tags(['food', 'travel'])
220
+
221
+ # equal to
222
+ Post.where.not(any_tags: ['food', 'travel'])
223
+ ```
224
+
225
+ #### not_all_#{tag_name}(value, delimiter = ',')
226
+ ```Ruby
227
+ Post.not_all_tags(['food', 'travel'])
228
+
229
+ # equal to
230
+ Post.where.not(all_tags: ['food', 'travel'])
231
+ ```
232
+
233
+ #### not_#{tag_name}_in(value, delimiter = ',')
234
+ ```Ruby
235
+ Post.not_tags_in(['food', 'travel'])
236
+
237
+ # equal to
238
+ Post.where.not(tags_in: ['food', 'travel'])
239
+ ```
240
+
241
+ #### not_#{tag_name}_eq(value, delimiter = ',')
242
+ ```Ruby
243
+ Post.not_tags_eq(['food', 'travel'])
244
+
245
+ # equal to
246
+ Post.where.not(tags_eq: ['food', 'travel'])
247
+ ```
248
+
178
249
  ### Case Insensitive
179
250
  If you use `string` type, it is case sensitive.
180
251
  ```Ruby
@@ -209,6 +280,21 @@ post.tags
209
280
  # => ['food', 'travel']
210
281
  ```
211
282
 
283
+ ### Ransack
284
+ You can use with ransack
285
+ ```Ruby
286
+ class Post < ActiveRecord::Base
287
+ def self.ransackable_scopes(_auth_object = nil)
288
+ %i[all_tags]
289
+ end
290
+ end
291
+ ```
292
+
293
+ And you can search
294
+ ```Ruby
295
+ Post.ransack(all_tags: 'foold,travel')
296
+ ```
297
+
212
298
  ## License
213
299
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
214
300
 
@@ -15,7 +15,7 @@ module PgTaggable
15
15
  bind_param = Arel::Nodes::BindParam.new(query_attribute)
16
16
  if operator == '='
17
17
  operator = '@>'
18
- Arel::Nodes::NamedFunction.new('ARRAY_LENGTH', [attribute, 1]).eq(query.size).and(
18
+ Arel::Nodes::NamedFunction.new('ARRAY_LENGTH', [ attribute, 1 ]).eq(query.size).and(
19
19
  Arel::Nodes::InfixOperation.new(operator, attribute, bind_param)
20
20
  )
21
21
  else
@@ -16,13 +16,14 @@ module PgTaggable
16
16
 
17
17
  def taggable(name, unique: true)
18
18
  type = type_for_attribute(name)
19
- @@taggable_attributes ||= {}
20
- @@taggable_attributes = @@taggable_attributes.merge(
19
+ taggable_attributes = {
21
20
  "any_#{name}" => [ name, type, '&&' ],
22
21
  "all_#{name}" => [ name, type, '@>' ],
23
22
  "#{name}_in" => [ name, type, '<@' ],
24
23
  "#{name}_eq" => [ name, type, '=' ]
25
- )
24
+ }
25
+ @@taggable_attributes ||= {}
26
+ @@taggable_attributes = @@taggable_attributes.merge(taggable_attributes)
26
27
 
27
28
  if unique
28
29
  if type.type == :citext
@@ -33,8 +34,13 @@ module PgTaggable
33
34
  end
34
35
 
35
36
  scope name, -> { unscope(:where).from(select("UNNEST(#{table_name}.#{name}) AS tag"), table_name) }
36
- scope "uniq_#{name}", -> { public_send(name).distinct.pluck(:tag) }
37
+ scope "distinct_#{name}", -> { public_send(name).select(:tag).distinct }
38
+ scope "uniq_#{name}", -> { public_send("distinct_#{name}").pluck(:tag) }
37
39
  scope "count_#{name}", -> { public_send(name).group(:tag).count }
40
+ taggable_attributes.keys.each do |key|
41
+ scope key, ->(value, delimiter = ',') { where(key => value.is_a?(Array) ? value : value.split(delimiter)) }
42
+ scope "not_#{key}", ->(value, delimiter = ',') { where.not(key => value.is_a?(Array) ? value : value.split(delimiter)) }
43
+ end
38
44
  end
39
45
  end
40
46
  end
@@ -1,3 +1,3 @@
1
1
  module PgTaggable
2
- VERSION = '0.1.0'
2
+ VERSION = '0.3.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_taggable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yi-Cyuan Chen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-05 00:00:00.000000000 Z
11
+ date: 2025-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails