gutentag 2.5.0 → 2.6.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 +5 -5
- data/.github/workflows/ci.yml +90 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +22 -0
- data/Appraisals +15 -7
- data/CHANGELOG.md +37 -1
- data/Gemfile +10 -1
- data/README.md +29 -7
- data/app/models/gutentag/tag.rb +3 -1
- data/db/migrate/1_gutentag_tables.rb +1 -3
- data/db/migrate/2_gutentag_cache_counter.rb +1 -3
- data/db/migrate/3_no_null_counters.rb +1 -3
- data/gutentag.gemspec +8 -7
- data/lib/gutentag/active_record.rb +1 -1
- data/lib/gutentag/active_record/instance_methods.rb +8 -1
- data/lib/gutentag/active_record/instance_methods_4_2.rb +12 -9
- data/lib/gutentag/engine.rb +4 -0
- data/lib/gutentag/generators/migration_versions_generator.rb +48 -0
- data/lib/gutentag/tag_names.rb +3 -1
- data/lib/gutentag/tag_validations.rb +2 -1
- data/lib/gutentag/tagged_with/query.rb +10 -2
- data/spec/acceptance/removing_unused_spec.rb +1 -1
- data/spec/acceptance/tag_names_for_scope_spec.rb +8 -0
- data/spec/acceptance/tag_names_spec.rb +17 -1
- data/spec/acceptance/tags_spec.rb +1 -1
- data/spec/gutentag/active_record_spec.rb +21 -1
- data/spec/gutentag_spec.rb +1 -1
- data/spec/internal/app/models/comment.rb +5 -0
- data/spec/internal/config/database.yml +16 -1
- data/spec/internal/db/schema.rb +6 -0
- data/spec/models/gutentag/tag_spec.rb +1 -1
- data/spec/models/gutentag/tagging_spec.rb +1 -1
- data/spec/spec_helper.rb +5 -2
- data/spec/support/adjust_migrations.rb +63 -0
- metadata +25 -23
- data/.travis.yml +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b49f3b3dd099ef714f2b23fa1d0e1b08059c59c15447cdece6ee07ac1a0a1272
|
4
|
+
data.tar.gz: c0ef3dd657f3c0b6fe216b80807f74bed2abbdefcbd2c6c467176a8ca16b38a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffeef43eb46712c0a261fc569db0571cc2ad66b68254cac89afbba58df15b8f41142b9401f170997608d692d81c353b744b1d71756520f7a80e4cb9c84caeb37
|
7
|
+
data.tar.gz: 80996c6d6b7f8addfb86092b80c8ffed4c28f60629db98e126fab4303865b215c4efbf76de30e9d5c8ab74e7a76859fbdfb9fff5d1c16905153f9a91d1aa6e7c
|
@@ -0,0 +1,90 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
test:
|
7
|
+
runs-on: ubuntu-18.04
|
8
|
+
|
9
|
+
strategy:
|
10
|
+
fail-fast: false
|
11
|
+
matrix:
|
12
|
+
ruby: [ '2.4', '2.5', '2.6', '2.7', '3.0' ]
|
13
|
+
bundler: [ '1.17.3', '2.1.4' ]
|
14
|
+
database: [ 'mysql', 'postgresql', 'sqlite' ]
|
15
|
+
exclude:
|
16
|
+
- ruby: '2.4'
|
17
|
+
bundler: '2.1.4'
|
18
|
+
- ruby: '2.5'
|
19
|
+
bundler: '2.1.4'
|
20
|
+
- ruby: '2.6'
|
21
|
+
bundler: '2.1.4'
|
22
|
+
- ruby: '2.7'
|
23
|
+
bundler: '1.17.3'
|
24
|
+
- ruby: '3.0'
|
25
|
+
bundler: '1.17.3'
|
26
|
+
|
27
|
+
services:
|
28
|
+
postgres:
|
29
|
+
image: postgres:10
|
30
|
+
env:
|
31
|
+
POSTGRES_USER: root
|
32
|
+
POSTGRES_PASSWORD: gutentag
|
33
|
+
POSTGRES_DB: test
|
34
|
+
ports: ['5432:5432']
|
35
|
+
# needed because the postgres container does not provide a healthcheck
|
36
|
+
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
37
|
+
|
38
|
+
mysql:
|
39
|
+
image: mysql:5.7
|
40
|
+
env:
|
41
|
+
MYSQL_ROOT_PASSWORD: gutentag
|
42
|
+
MYSQL_DATABASE: test
|
43
|
+
ports: ['3306:3306']
|
44
|
+
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
45
|
+
|
46
|
+
steps:
|
47
|
+
- name: Check out code
|
48
|
+
uses: actions/checkout@v2
|
49
|
+
- name: Set up ruby
|
50
|
+
uses: ruby/setup-ruby@v1
|
51
|
+
with:
|
52
|
+
ruby-version: ${{ matrix.ruby }}
|
53
|
+
bundler: ${{ matrix.bundler }}
|
54
|
+
bundler-cache: true
|
55
|
+
|
56
|
+
- name: Set up Bundler
|
57
|
+
run: |
|
58
|
+
export BUNDLER_VERSION=${{ matrix.bundler }}
|
59
|
+
export BUNDLE_PATH=$PWD/vendor/bundle
|
60
|
+
|
61
|
+
gem update --system
|
62
|
+
|
63
|
+
bundle _${{ matrix.bundler }}_ config set path $PWD/$BUNDLE_PATH
|
64
|
+
bundle _${{ matrix.bundler }}_ install --jobs=4 --retry=3
|
65
|
+
bundle _${{ matrix.bundler }}_ update
|
66
|
+
- name: Set up Appraisal
|
67
|
+
run: bundle exec appraisal update
|
68
|
+
- name: Test
|
69
|
+
env:
|
70
|
+
CI: "true"
|
71
|
+
DATABASE: ${{ matrix.database }}
|
72
|
+
DB_USERNAME: root
|
73
|
+
DB_PASSWORD: gutentag
|
74
|
+
DB_HOST: 127.0.0.1
|
75
|
+
run: bundle exec appraisal rspec
|
76
|
+
|
77
|
+
rubocop:
|
78
|
+
runs-on: ubuntu-18.04
|
79
|
+
|
80
|
+
steps:
|
81
|
+
- name: Check out code
|
82
|
+
uses: actions/checkout@v2
|
83
|
+
- name: Set up ruby
|
84
|
+
uses: ruby/setup-ruby@v1
|
85
|
+
with:
|
86
|
+
ruby-version: 2.7
|
87
|
+
bundler: 2.1.4
|
88
|
+
bundler-cache: true
|
89
|
+
- name: Rubocop
|
90
|
+
run: bundle exec rubocop
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
inherit_from:
|
2
2
|
- https://gist.githubusercontent.com/pat/ba3b8ffb1901bfe5439b460943b6b019/raw/.rubocop.yml
|
3
3
|
|
4
|
+
require: rubocop-performance
|
5
|
+
|
4
6
|
AllCops:
|
5
7
|
TargetRubyVersion: 2.3
|
6
8
|
Exclude:
|
7
9
|
- gemfiles/*.gemfile
|
10
|
+
- vendor/**/*
|
11
|
+
|
12
|
+
Bundler/DuplicatedGem:
|
13
|
+
Enabled: false
|
8
14
|
|
9
15
|
Metrics/MethodLength:
|
10
16
|
Exclude:
|
@@ -18,3 +24,19 @@ Style/MultilineIfModifier:
|
|
18
24
|
Style/MultilineTernaryOperator:
|
19
25
|
Exclude:
|
20
26
|
- db/migrate/*.rb
|
27
|
+
|
28
|
+
# 0.80
|
29
|
+
|
30
|
+
Style/HashEachMethods:
|
31
|
+
Enabled: true
|
32
|
+
Style/HashTransformKeys:
|
33
|
+
Enabled: true
|
34
|
+
Style/HashTransformValues:
|
35
|
+
Enabled: true
|
36
|
+
|
37
|
+
# 0.81
|
38
|
+
|
39
|
+
Lint/RaiseException:
|
40
|
+
Enabled: true
|
41
|
+
Lint/StructNewOverride:
|
42
|
+
Enabled: true
|
data/Appraisals
CHANGED
@@ -17,26 +17,34 @@ appraise "rails_4_2" do
|
|
17
17
|
gem "activerecord-jdbcmysql-adapter", "~> 1.3.23", :platform => :jruby
|
18
18
|
gem "activerecord-jdbcpostgresql-adapter", "~> 1.3.23", :platform => :jruby
|
19
19
|
gem "activerecord-jdbcsqlite3-adapter", "~> 1.3.23", :platform => :jruby
|
20
|
-
end
|
20
|
+
end if RUBY_VERSION.to_f < 2.7
|
21
21
|
|
22
22
|
appraise "rails_5_0" do
|
23
23
|
gem "rails", "~> 5.0.3"
|
24
24
|
gem "mysql2", "~> 0.4.0", :platform => :ruby
|
25
|
-
end if RUBY_VERSION.to_f >= 2.2
|
25
|
+
end if RUBY_VERSION.to_f >= 2.2 && RUBY_VERSION.to_f <= 2.7
|
26
26
|
|
27
27
|
appraise "rails_5_1" do
|
28
28
|
gem "rails", "~> 5.1.1"
|
29
29
|
gem "mysql2", "~> 0.4.0", :platform => :ruby
|
30
|
-
end if RUBY_VERSION.to_f >= 2.2
|
30
|
+
end if RUBY_VERSION.to_f >= 2.2 && RUBY_VERSION.to_f <= 2.7
|
31
31
|
|
32
32
|
appraise "rails_5_2" do
|
33
33
|
gem "rails", "~> 5.2.0"
|
34
34
|
gem "pg", "~> 1.0", :platform => :ruby
|
35
35
|
gem "mysql2", "~> 0.5.0", :platform => :ruby
|
36
|
-
end if RUBY_VERSION.to_f >= 2.2
|
36
|
+
end if RUBY_VERSION.to_f >= 2.2 && RUBY_VERSION.to_f <= 2.7
|
37
37
|
|
38
38
|
appraise "rails_6_0" do
|
39
|
-
gem "rails",
|
40
|
-
gem "pg",
|
41
|
-
gem "mysql2",
|
39
|
+
gem "rails", "~> 6.0.0"
|
40
|
+
gem "pg", "~> 1.0", :platform => :ruby
|
41
|
+
gem "mysql2", "~> 0.5.0", :platform => :ruby
|
42
|
+
gem "sqlite3", "~> 1.4", :platform => :ruby
|
43
|
+
end if RUBY_VERSION.to_f >= 2.5 && RUBY_PLATFORM != "java"
|
44
|
+
|
45
|
+
appraise "rails_6_1" do
|
46
|
+
gem "rails", "~> 6.1.0"
|
47
|
+
gem "pg", "~> 1.0", :platform => :ruby
|
48
|
+
gem "mysql2", "~> 0.5.0", :platform => :ruby
|
49
|
+
gem "sqlite3", "~> 1.4", :platform => :ruby
|
42
50
|
end if RUBY_VERSION.to_f >= 2.5 && RUBY_PLATFORM != "java"
|
data/CHANGELOG.md
CHANGED
@@ -2,9 +2,45 @@
|
|
2
2
|
|
3
3
|
All notable changes to this project (at least, from v0.5.0 onwards) will be documented in this file.
|
4
4
|
|
5
|
+
## 2.6.0 - 2021-07-10
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
* Queries can now be made for objects that have _none_ of the specified tags using `:match => :none` ([Rares S](https://github.com/laleshii) in [#79](https://github.com/pat/gutentag/pull/79)).
|
10
|
+
* Added a generator `gutentag:migration_versions` to update generated migrations so they use the current version of Rails/ActiveRecord's Migration superclass. See discussion in [#80](https://github.com/pat/gutentag/issues/80).
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
* When adding Gutentag to a new app, the migrations require the `gutentag:migration_versions` generator to be run to ensure the latest ActiveRecord migration superclass is used. This change has no impact to existing apps. See discussion in [#80](https://github.com/pat/gutentag/issues/80).
|
15
|
+
|
16
|
+
## 2.5.4 - 2021-02-21
|
17
|
+
|
18
|
+
### Fixed
|
19
|
+
|
20
|
+
* Don't apply the tag length validation when `ActiveRecord::ConnectionNotEstablished` exceptions are raised. ([John Duff](https://github.com/jduff) in [#77](https://github.com/pat/gutentag/pull/77)).
|
21
|
+
|
22
|
+
## 2.5.3 - 2020-06-28
|
23
|
+
|
24
|
+
### Fixed
|
25
|
+
|
26
|
+
* Use `saved_change_to_tag_names?` instead of `tag_names_previously_changed?` for Rails 5.1+ ([Morten Trolle](https://github.com/mtrolle) in [#70](https://github.com/pat/gutentag/pull/70)).
|
27
|
+
* `Gutentag::Tag.names_for_scope` now handles empty scopes ([Mike Gunderloy](https://github.com/ffmike) in [#73](https://github.com/pat/gutentag/pull/73)).
|
28
|
+
|
29
|
+
## 2.5.2 - 2019-07-08
|
30
|
+
|
31
|
+
### Fixed
|
32
|
+
|
33
|
+
* `tag_names` will no longer be referenced as a database column when tagged models are requested in joins in Rails 4.2 (as reported in [issue #67](https://github.com/pat/gutentag/issues/67)).
|
34
|
+
|
35
|
+
## 2.5.1 - 2019-05-10
|
36
|
+
|
37
|
+
### Fixed
|
38
|
+
|
39
|
+
* Ensuring consistent behaviour for tag_names array - names are not duplicated, and are normalised prior to saving (as discussed in [issue #66](https://github.com/pat/gutentag/issues/66)).
|
40
|
+
|
5
41
|
## 2.5.0 - 2019-03-15
|
6
42
|
|
7
|
-
**
|
43
|
+
**Please note this release ends official support of Rails 3.2 and Ruby (MRI) 2.2.** The code currently still works on Ruby 2.2, and all features except for the new `Gutentag::Tag.names_for_scope` method work in Rails 3.2, but they're no longer tested against, and cannot be guaranteed to work in future releases.
|
8
44
|
|
9
45
|
### Added
|
10
46
|
|
data/Gemfile
CHANGED
@@ -4,11 +4,20 @@ source "https://rubygems.org"
|
|
4
4
|
|
5
5
|
gemspec
|
6
6
|
|
7
|
+
gem "appraisal",
|
8
|
+
:git => "https://github.com/marcotc/appraisal.git",
|
9
|
+
:branch => "explicit-require-set"
|
10
|
+
|
7
11
|
gem "test-unit", :platform => :ruby_22
|
8
12
|
|
9
13
|
gem "mysql2", "~> 0.3", :platform => :ruby
|
10
14
|
gem "pg", "~> 0.18", :platform => :ruby
|
11
|
-
|
15
|
+
|
16
|
+
if RUBY_VERSION.to_f < 3.0
|
17
|
+
gem "sqlite3", "~> 1.3.13"
|
18
|
+
else
|
19
|
+
gem "sqlite3", "~> 1.4"
|
20
|
+
end
|
12
21
|
|
13
22
|
gem "activerecord-jdbcmysql-adapter", ">= 1.3.23", :platform => :jruby
|
14
23
|
gem "activerecord-jdbcpostgresql-adapter", ">= 1.3.23", :platform => :jruby
|
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# Gutentag
|
2
2
|
|
3
|
-
[](http://badge.fury.io/rb/gutentag)
|
4
|
+
[](https://travis-ci.org/pat/gutentag)
|
5
|
+
[](https://codeclimate.com/github/pat/gutentag)
|
6
6
|
|
7
7
|
A good, simple, solid tagging extension for ActiveRecord.
|
8
8
|
|
9
9
|
This was initially built partly as a proof-of-concept, partly to see how a tagging gem could work when it's not all stuffed within models, and partly just because I wanted a simpler tagging library. It's now a solid little tagging Rails engine.
|
10
10
|
|
11
|
-
If you want to know more, read [this blog post](http://freelancing-gods.com/posts/gutentag_simple_rails_tagging).
|
11
|
+
If you want to know more, read [this blog post](http://freelancing-gods.com/posts/gutentag_simple_rails_tagging), or have a look at [the Examples page](https://github.com/pat/gutentag/wiki/Examples) in the wiki (which includes a starting point for accepting tag values in a form).
|
12
12
|
|
13
13
|
## Contents
|
14
14
|
|
@@ -16,6 +16,7 @@ If you want to know more, read [this blog post](http://freelancing-gods.com/post
|
|
16
16
|
* [Installation](#installation)
|
17
17
|
* [Upgrading](#upgrading)
|
18
18
|
* [Configuration](#configuration)
|
19
|
+
* [Extending](#extending)
|
19
20
|
* [Contribution](#contribution)
|
20
21
|
* [Licence](#licence)
|
21
22
|
|
@@ -66,6 +67,13 @@ To return records that have _all_ specified tags, use `:match => :all`:
|
|
66
67
|
Article.tagged_with(:ids => [tag_a.id, tag_b.id], :match => :all)
|
67
68
|
```
|
68
69
|
|
70
|
+
To return records that have _none_ of the specified tags, use `:match => :none`:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
# Returns all articles that have *neither* tag_a nor tag_b.
|
74
|
+
Article.tagged_with(:ids => [tag_a.id, tag_b.id], :match => :none)
|
75
|
+
```
|
76
|
+
|
69
77
|
<h2 id="installation">Installation</h2>
|
70
78
|
|
71
79
|
### Dependencies
|
@@ -85,11 +93,12 @@ Get it into your Gemfile - and don't forget the version constraint!
|
|
85
93
|
gem 'gutentag', '~> 2.5'
|
86
94
|
```
|
87
95
|
|
88
|
-
Next: your tags get persisted to your database, so let's import
|
96
|
+
Next: your tags get persisted to your database, so let's import the migrations, update them to your current version of Rails, and then migrate:
|
89
97
|
|
90
98
|
```Bash
|
91
|
-
rake gutentag:install:migrations
|
92
|
-
|
99
|
+
bundle exec rake gutentag:install:migrations
|
100
|
+
bundle exec rails generate gutentag:migration_versions
|
101
|
+
bundle exec rake db:migrate
|
93
102
|
```
|
94
103
|
|
95
104
|
If you're using UUID primary keys, make sure you alter the migration files before running `db:migrate` to use UUIDs for the `taggable_id` foreign key column (as noted in [issue 57](https://github.com/pat/gutentag/issues/57).)
|
@@ -151,6 +160,19 @@ Gutentag.normaliser = lambda { |value| value.to_s.upcase }
|
|
151
160
|
|
152
161
|
Gutentag ignores case by default, but can be customised to be case-sensitive by supplying your own validations and normaliser, as outlined by [Robin Mehner](https://github.com/rmehner) in [issue 42](https://github.com/pat/gutentag/issues/42). Further changes may be required for your schema though, depending on your database.
|
153
162
|
|
163
|
+
<h2 id="extending">Extending</h2>
|
164
|
+
|
165
|
+
If you need to extend Gutentag's models, you will need to wrap the `include` inside a `to_prepare` hook to ensure it's loaded consistently in all Rails environments:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
# config/initializers/gutentag.rb or equivalent
|
169
|
+
Rails.application.config.to_prepare do
|
170
|
+
Gutentag::Tag.include TagExtensions
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
Further discussion and examples of this can be found in [issue 65](https://github.com/pat/gutentag/issues/65).
|
175
|
+
|
154
176
|
<h2 id="contribution">Contribution</h2>
|
155
177
|
|
156
178
|
Please note that this project now has a [Contributor Code of Conduct](http://contributor-covenant.org/version/1/0/0/). By participating in this project you agree to abide by its terms.
|
data/app/models/gutentag/tag.rb
CHANGED
@@ -21,7 +21,9 @@ class Gutentag::Tag < ActiveRecord::Base
|
|
21
21
|
|
22
22
|
def self.names_for_scope(scope)
|
23
23
|
join_conditions = {:taggable_type => scope.name}
|
24
|
-
if scope.
|
24
|
+
if scope.is_a?(ActiveRecord::Relation)
|
25
|
+
return Gutentag::Tag.none unless scope.current_scope.present?
|
26
|
+
|
25
27
|
join_conditions[:taggable_id] = scope.select(:id)
|
26
28
|
end
|
27
29
|
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
ActiveRecord::Migration : ActiveRecord::Migration[4.2]
|
5
|
-
class GutentagTables < superclass
|
3
|
+
class GutentagTables < ActiveRecord::Migration
|
6
4
|
def up
|
7
5
|
create_table :gutentag_taggings do |t|
|
8
6
|
t.integer :tag_id, :null => false
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
ActiveRecord::Migration : ActiveRecord::Migration[4.2]
|
5
|
-
class GutentagCacheCounter < superclass
|
3
|
+
class GutentagCacheCounter < ActiveRecord::Migration
|
6
4
|
def up
|
7
5
|
add_column :gutentag_tags, :taggings_count, :integer, :default => 0
|
8
6
|
add_index :gutentag_tags, :taggings_count
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
ActiveRecord::Migration : ActiveRecord::Migration[4.2]
|
5
|
-
class NoNullCounters < superclass
|
3
|
+
class NoNullCounters < ActiveRecord::Migration
|
6
4
|
def up
|
7
5
|
change_column :gutentag_tags, :taggings_count, :integer,
|
8
6
|
:default => 0,
|
data/gutentag.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "gutentag"
|
5
|
-
s.version = "2.
|
5
|
+
s.version = "2.6.0"
|
6
6
|
s.authors = ["Pat Allan"]
|
7
7
|
s.email = ["pat@freelancing-gods.com"]
|
8
8
|
s.homepage = "https://github.com/pat/gutentag"
|
@@ -16,11 +16,12 @@ Gem::Specification.new do |s|
|
|
16
16
|
|
17
17
|
s.add_runtime_dependency "activerecord", ">= 3.2.0"
|
18
18
|
|
19
|
-
s.add_development_dependency "appraisal",
|
20
|
-
s.add_development_dependency "bundler",
|
21
|
-
s.add_development_dependency "combustion",
|
22
|
-
s.add_development_dependency "database_cleaner",
|
19
|
+
# s.add_development_dependency "appraisal", "~> 2.3"
|
20
|
+
s.add_development_dependency "bundler", ">= 1.17"
|
21
|
+
s.add_development_dependency "combustion", "~> 1.1"
|
22
|
+
s.add_development_dependency "database_cleaner", "~> 1.6"
|
23
23
|
s.add_development_dependency "rails"
|
24
|
-
s.add_development_dependency "rspec-rails",
|
25
|
-
s.add_development_dependency "rubocop",
|
24
|
+
s.add_development_dependency "rspec-rails", "~> 3.1"
|
25
|
+
s.add_development_dependency "rubocop", "~> 0.81.0"
|
26
|
+
s.add_development_dependency "rubocop-performance", "~> 1"
|
26
27
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
# For Rails 5.0+
|
4
4
|
module Gutentag::ActiveRecord::InstanceMethods
|
5
|
+
AR_VERSION = ActiveRecord::VERSION::STRING.to_f
|
6
|
+
|
5
7
|
# If the tag_names attribute was one of the modified values, then let's just
|
6
8
|
# use the modifications, rather than overwriting the stored value.
|
7
9
|
#
|
@@ -9,7 +11,12 @@ module Gutentag::ActiveRecord::InstanceMethods
|
|
9
11
|
# the instance directly (e.g. article.tags << tag), which invokes the save
|
10
12
|
# callbacks, but the old tag_names value is stored but not updated.
|
11
13
|
def reset_tag_names
|
12
|
-
|
14
|
+
# Rails 5.1 introduces major changes to how ActiveModel::Dirty works:
|
15
|
+
# https://github.com/pat/gutentag/pull/70#issuecomment-524605448
|
16
|
+
# For Rails <5.1 we'll use *_previously_changed?
|
17
|
+
# and for 5.1+ we'll use saved_change_to_*?
|
18
|
+
return if AR_VERSION < 5.1 && tag_names_previously_changed?
|
19
|
+
return if AR_VERSION >= 5.1 && saved_change_to_tag_names?
|
13
20
|
|
14
21
|
# Update the underlying value rather than going through the setter, to
|
15
22
|
# ensure this update doesn't get marked as a 'change'.
|
@@ -8,22 +8,25 @@ module Gutentag::ActiveRecord::InstanceMethods
|
|
8
8
|
def reset_tag_names
|
9
9
|
# Update the underlying value rather than going through the setter, to
|
10
10
|
# ensure this update doesn't get marked as a 'change'.
|
11
|
-
|
11
|
+
@tag_names = nil
|
12
12
|
end
|
13
13
|
|
14
14
|
def tag_names
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
@tag_names ||= begin
|
16
|
+
raw = tags.pluck(:name)
|
17
|
+
raw_write_attribute "tag_names", raw
|
18
|
+
raw
|
19
19
|
end
|
20
|
-
|
21
|
-
# Use ActiveRecord's underlying implementation with change tracking.
|
22
|
-
super
|
23
20
|
end
|
24
21
|
|
25
22
|
def tag_names=(names)
|
26
|
-
|
23
|
+
new_names = Gutentag::TagNames.call names
|
24
|
+
return if new_names.sort == tag_names.sort
|
25
|
+
|
26
|
+
tag_names_will_change!
|
27
|
+
|
28
|
+
write_attribute "tag_names", new_names
|
29
|
+
@tag_names = new_names
|
27
30
|
end
|
28
31
|
|
29
32
|
private
|
data/lib/gutentag/engine.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
|
5
|
+
module Gutentag
|
6
|
+
module Generators
|
7
|
+
class MigrationVersionsGenerator < Rails::Generators::Base
|
8
|
+
desc "Update the ActiveRecord version in Gutentag migrations"
|
9
|
+
|
10
|
+
def update_migration_versions
|
11
|
+
if ::ActiveRecord::VERSION::MAJOR < 5
|
12
|
+
puts "No changes required"
|
13
|
+
else
|
14
|
+
migration_files.each do |file|
|
15
|
+
gsub_file file,
|
16
|
+
/< ActiveRecord::Migration$/,
|
17
|
+
"< ActiveRecord::Migration[#{rails_version}]"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def migration_files
|
25
|
+
Dir[Rails.root.join("db/migrate/*.rb")].select do |path|
|
26
|
+
known_migration_names.any? do |known|
|
27
|
+
File.basename(path)[/\A\d+_#{known}\.gutentag.rb\z/]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def known_migration_names
|
33
|
+
@known_migration_names ||= begin
|
34
|
+
Dir[File.join(__dir__, "../../../db/migrate/*.rb")].collect do |path|
|
35
|
+
File.basename(path).gsub(/\A\d+_/, "").gsub(/\.rb\z/, "")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def rails_version
|
41
|
+
@rails_version ||= [
|
42
|
+
::ActiveRecord::VERSION::MAJOR,
|
43
|
+
::ActiveRecord::VERSION::MINOR
|
44
|
+
].join(".")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/gutentag/tag_names.rb
CHANGED
@@ -13,6 +13,7 @@ class Gutentag::TagValidations
|
|
13
13
|
if ActiveRecord::VERSION::STRING.to_f > 4.0
|
14
14
|
classes << ActiveRecord::NoDatabaseError
|
15
15
|
end
|
16
|
+
classes << ActiveRecord::ConnectionNotEstablished
|
16
17
|
classes << Mysql2::Error if defined?(::Mysql2)
|
17
18
|
classes << PG::ConnectionBad if defined?(::PG)
|
18
19
|
classes
|
@@ -36,7 +37,7 @@ class Gutentag::TagValidations
|
|
36
37
|
|
37
38
|
def add_length_validation?
|
38
39
|
klass.table_exists? && limit.present?
|
39
|
-
rescue *DATABASE_ERROR_CLASSES
|
40
|
+
rescue *DATABASE_ERROR_CLASSES => _error
|
40
41
|
warn <<-MESSAGE
|
41
42
|
The database is not currently available, and so Gutentag was not able to set
|
42
43
|
up tag validations completely (in particular: adding a length limit to match
|
@@ -8,7 +8,7 @@ class Gutentag::TaggedWith::Query
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def call
|
11
|
-
model.where "#{model_id}
|
11
|
+
model.where "#{model_id} #{operator} (#{query.to_sql})"
|
12
12
|
end
|
13
13
|
|
14
14
|
private
|
@@ -20,8 +20,16 @@ class Gutentag::TaggedWith::Query
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def query
|
23
|
-
return taggable_ids_query if
|
23
|
+
return taggable_ids_query if match_any_or_none? || values.length == 1
|
24
24
|
|
25
25
|
taggable_ids_query.having("COUNT(*) = #{values.length}").group(:taggable_id)
|
26
26
|
end
|
27
|
+
|
28
|
+
def operator
|
29
|
+
match == :none ? "NOT IN" : "IN"
|
30
|
+
end
|
31
|
+
|
32
|
+
def match_any_or_none?
|
33
|
+
%i[any none].include?(match)
|
34
|
+
end
|
27
35
|
end
|
@@ -27,4 +27,12 @@ RSpec.describe "Tag names for scopes" do
|
|
27
27
|
expect(Gutentag::Tag.names_for_scope(Article)).
|
28
28
|
to match_array(%w[ koala wombat cassowary ])
|
29
29
|
end
|
30
|
+
|
31
|
+
it "returns an empty array for an empty scope" do
|
32
|
+
Article.create :title => "mammals", :tag_names => %w[ koala wombat ]
|
33
|
+
Article.create :title => "birds", :tag_names => %w[ cassowary ]
|
34
|
+
|
35
|
+
expect(Gutentag::Tag.names_for_scope(Article.where(:title => "reptiles"))).
|
36
|
+
to match_array([])
|
37
|
+
end
|
30
38
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
-
describe "Managing tags via names" do
|
5
|
+
RSpec.describe "Managing tags via names" do
|
6
6
|
let(:article) { Article.create }
|
7
7
|
|
8
8
|
it "returns tag names" do
|
@@ -70,6 +70,13 @@ describe "Managing tags via names" do
|
|
70
70
|
expect(article.tags.collect(&:name)).to eq(%w[ portland oregon ruby ])
|
71
71
|
end
|
72
72
|
|
73
|
+
it "does not repeat appended names that exist in the array of strings" do
|
74
|
+
article.tag_names = %w[ portland oregon ]
|
75
|
+
article.tag_names += %w[ oregon ruby ]
|
76
|
+
|
77
|
+
expect(article.tag_names).to match(%w[ portland oregon ruby ])
|
78
|
+
end
|
79
|
+
|
73
80
|
it "removes a single tag name" do
|
74
81
|
article.tag_names = %w[ portland oregon ]
|
75
82
|
article.tag_names.delete "oregon"
|
@@ -99,6 +106,11 @@ describe "Managing tags via names" do
|
|
99
106
|
expect(article.tags.collect(&:name)).to eq(%w[ portland ])
|
100
107
|
end
|
101
108
|
|
109
|
+
it "normalises tag names" do
|
110
|
+
article.tag_names = %w[ Perth ]
|
111
|
+
expect(article.tag_names).to eq(%w[ perth ])
|
112
|
+
end
|
113
|
+
|
102
114
|
it "allows setting of tag names on unpersisted objects" do
|
103
115
|
article = Article.new :tag_names => %w[ melbourne pancakes ]
|
104
116
|
article.save!
|
@@ -123,4 +135,8 @@ describe "Managing tags via names" do
|
|
123
135
|
|
124
136
|
expect(article.tag_names).to eq(%w[ melbourne ])
|
125
137
|
end
|
138
|
+
|
139
|
+
it "allows eager-loading of the model via an association" do
|
140
|
+
expect { Comment.eager_load(:article).to_a }.to_not raise_error
|
141
|
+
end
|
126
142
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
-
describe Gutentag::ActiveRecord do
|
5
|
+
RSpec.describe Gutentag::ActiveRecord do
|
6
6
|
describe ".tagged_with" do
|
7
7
|
let!(:melbourne_article) do
|
8
8
|
article = Article.create :title => "Overview"
|
@@ -160,6 +160,26 @@ describe Gutentag::ActiveRecord do
|
|
160
160
|
it { is_expected.not_to include oregon_article }
|
161
161
|
end
|
162
162
|
|
163
|
+
context "matching excluding one tag" do
|
164
|
+
subject do
|
165
|
+
Article.tagged_with(:names => %w[ melbourne ], :match => :none)
|
166
|
+
end
|
167
|
+
|
168
|
+
it { expect(subject.count).to eq 1 }
|
169
|
+
it { is_expected.to include oregon_article }
|
170
|
+
it do
|
171
|
+
is_expected.not_to include melbourne_oregon_article, melbourne_article
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "matching excluding all tags" do
|
176
|
+
subject do
|
177
|
+
Article.tagged_with(:names => %w[ melbourne oregon ], :match => :none)
|
178
|
+
end
|
179
|
+
|
180
|
+
it { expect(subject.count).to eq 0 }
|
181
|
+
end
|
182
|
+
|
163
183
|
it "should work on STI subclasses" do
|
164
184
|
thinkpiece = Thinkpiece.create! :tag_names => ["pancakes"]
|
165
185
|
|
data/spec/gutentag_spec.rb
CHANGED
@@ -2,11 +2,26 @@
|
|
2
2
|
test:
|
3
3
|
adapter: mysql2
|
4
4
|
database: gutentag
|
5
|
-
username: root
|
5
|
+
username: <%= ENV.fetch("DB_USERNAME", "root") %>
|
6
|
+
<% if ENV["DB_HOST"] %>
|
7
|
+
host: <%= ENV["DB_HOST"] %>
|
8
|
+
<% end %>
|
9
|
+
<% if ENV["DB_PASSWORD"] %>
|
10
|
+
password: <%= ENV["DB_PASSWORD"] %>
|
11
|
+
<% end %>
|
6
12
|
<% elsif ENV["DATABASE"] == "postgres" %>
|
7
13
|
test:
|
8
14
|
adapter: postgresql
|
9
15
|
database: gutentag
|
16
|
+
<% if ENV["DB_HOST"] %>
|
17
|
+
host: <%= ENV["DB_HOST"] %>
|
18
|
+
<% end %>
|
19
|
+
<% if ENV["DB_USERNAME"] %>
|
20
|
+
username: <%= ENV["DB_USERNAME"] %>
|
21
|
+
<% end %>
|
22
|
+
<% if ENV["DB_PASSWORD"] %>
|
23
|
+
password: <%= ENV["DB_PASSWORD"] %>
|
24
|
+
<% end %>
|
10
25
|
<% else %>
|
11
26
|
test:
|
12
27
|
adapter: sqlite3
|
data/spec/internal/db/schema.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -4,14 +4,17 @@ require "bundler/setup"
|
|
4
4
|
|
5
5
|
Bundler.require :default, :development
|
6
6
|
|
7
|
-
Dir["#{__dir__}/support/**/*.rb"].each { |file| require file }
|
7
|
+
Dir["#{__dir__}/support/**/*.rb"].sort.each { |file| require file }
|
8
8
|
|
9
|
-
Combustion.initialize! :active_record
|
9
|
+
Combustion.initialize! :active_record, :database_migrate => false
|
10
|
+
AdjustMigrations.call(Combustion::Application)
|
10
11
|
ActiveSupport.run_load_hooks :gutentag unless defined?(Gutentag::Engine)
|
11
12
|
|
12
13
|
require "rspec/rails"
|
13
14
|
|
14
15
|
RSpec.configure do |config|
|
16
|
+
config.disable_monkey_patching!
|
17
|
+
|
15
18
|
if config.respond_to?(:use_transactional_tests)
|
16
19
|
config.use_transactional_tests = false
|
17
20
|
else
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "gutentag/generators/migration_versions_generator"
|
5
|
+
|
6
|
+
class AdjustMigrations
|
7
|
+
def self.call(application)
|
8
|
+
new(application).call
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(application)
|
12
|
+
@application = application
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
copy_migrations_to_app
|
17
|
+
run_generator
|
18
|
+
run_migrations
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :application
|
24
|
+
|
25
|
+
def copy_migrations_to_app
|
26
|
+
FileUtils.mkdir_p application.root.join("db/migrate")
|
27
|
+
|
28
|
+
Dir["#{__dir__}/../../db/migrate/*.rb"].each do |file|
|
29
|
+
name = File.basename(file.gsub(/rb\z/, "gutentag.rb"))
|
30
|
+
destination = application.root.join("db/migrate/#{name}")
|
31
|
+
|
32
|
+
FileUtils.cp file, destination.to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def migration_context
|
37
|
+
if ActiveRecord::MigrationContext.instance_method(:initialize).arity <= 1
|
38
|
+
ActiveRecord::MigrationContext.new migration_paths
|
39
|
+
else
|
40
|
+
ActiveRecord::MigrationContext.new(
|
41
|
+
migration_paths, ActiveRecord::Base.connection.schema_migration
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def migration_paths
|
47
|
+
application.root.join("db/migrate")
|
48
|
+
end
|
49
|
+
|
50
|
+
def run_generator
|
51
|
+
Gutentag::Generators::MigrationVersionsGenerator.start(
|
52
|
+
["--quiet"], :destination_root => application.root
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def run_migrations
|
57
|
+
if ActiveRecord::VERSION::STRING.to_f >= 5.2
|
58
|
+
migration_context.migrate
|
59
|
+
else
|
60
|
+
ActiveRecord::Migrator.migrate migration_paths, nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gutentag
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pat Allan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -24,32 +24,18 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 3.2.0
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: appraisal
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 2.1.0
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 2.1.0
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: bundler
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
44
30
|
requirements:
|
45
|
-
- - "
|
31
|
+
- - ">="
|
46
32
|
- !ruby/object:Gem::Version
|
47
33
|
version: '1.17'
|
48
34
|
type: :development
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
|
-
- - "
|
38
|
+
- - ">="
|
53
39
|
- !ruby/object:Gem::Version
|
54
40
|
version: '1.17'
|
55
41
|
- !ruby/object:Gem::Dependency
|
@@ -114,14 +100,28 @@ dependencies:
|
|
114
100
|
requirements:
|
115
101
|
- - "~>"
|
116
102
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0.
|
103
|
+
version: 0.81.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.81.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-performance
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
124
|
+
version: '1'
|
125
125
|
description: A good, simple, solid tagging extension for ActiveRecord
|
126
126
|
email:
|
127
127
|
- pat@freelancing-gods.com
|
@@ -129,9 +129,9 @@ executables: []
|
|
129
129
|
extensions: []
|
130
130
|
extra_rdoc_files: []
|
131
131
|
files:
|
132
|
+
- ".github/workflows/ci.yml"
|
132
133
|
- ".gitignore"
|
133
134
|
- ".rubocop.yml"
|
134
|
-
- ".travis.yml"
|
135
135
|
- Appraisals
|
136
136
|
- CHANGELOG.md
|
137
137
|
- Gemfile
|
@@ -153,6 +153,7 @@ files:
|
|
153
153
|
- lib/gutentag/change_state.rb
|
154
154
|
- lib/gutentag/dirty.rb
|
155
155
|
- lib/gutentag/engine.rb
|
156
|
+
- lib/gutentag/generators/migration_versions_generator.rb
|
156
157
|
- lib/gutentag/persistence.rb
|
157
158
|
- lib/gutentag/remove_unused.rb
|
158
159
|
- lib/gutentag/tag_names.rb
|
@@ -169,6 +170,7 @@ files:
|
|
169
170
|
- spec/gutentag/active_record_spec.rb
|
170
171
|
- spec/gutentag_spec.rb
|
171
172
|
- spec/internal/app/models/article.rb
|
173
|
+
- spec/internal/app/models/comment.rb
|
172
174
|
- spec/internal/app/models/thinkpiece.rb
|
173
175
|
- spec/internal/config/database.yml
|
174
176
|
- spec/internal/db/schema.rb
|
@@ -176,6 +178,7 @@ files:
|
|
176
178
|
- spec/models/gutentag/tag_spec.rb
|
177
179
|
- spec/models/gutentag/tagging_spec.rb
|
178
180
|
- spec/spec_helper.rb
|
181
|
+
- spec/support/adjust_migrations.rb
|
179
182
|
- spec/support/mysql.rb
|
180
183
|
homepage: https://github.com/pat/gutentag
|
181
184
|
licenses:
|
@@ -196,8 +199,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
196
199
|
- !ruby/object:Gem::Version
|
197
200
|
version: '0'
|
198
201
|
requirements: []
|
199
|
-
|
200
|
-
rubygems_version: 2.4.5.5
|
202
|
+
rubygems_version: 3.1.2
|
201
203
|
signing_key:
|
202
204
|
specification_version: 4
|
203
205
|
summary: Good Tags
|
data/.travis.yml
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
dist: xenial
|
3
|
-
script: bundle exec appraisal rake
|
4
|
-
rvm:
|
5
|
-
- 2.3.8
|
6
|
-
- 2.4.5
|
7
|
-
- 2.5.3
|
8
|
-
- 2.6.1
|
9
|
-
- jruby-9.2.5.0
|
10
|
-
before_install:
|
11
|
-
- gem install bundler --version 1.17.3
|
12
|
-
install:
|
13
|
-
- bundle _1.17.3_ install --jobs=3 --retry=3
|
14
|
-
before_script:
|
15
|
-
- bundle exec appraisal install
|
16
|
-
env:
|
17
|
-
- DATABASE=postgres
|
18
|
-
- DATABASE=mysql
|
19
|
-
- DATABASE=sqlite
|
20
|
-
services:
|
21
|
-
- mysql
|
22
|
-
- postgresql
|