gutentag 2.1.0 → 2.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: 22821eb031cd1b00ea0f167035e557971b5c3c073ce56964394b96f05b364d5b
4
- data.tar.gz: d523b07744aa3159028c765284b91e4d83bfebff3ad5fdaab27383bfee76e1d7
3
+ metadata.gz: e6935bb3c51e7b225573d2330a1f95b858bed7cd130f0825177aef98c9f0d175
4
+ data.tar.gz: 52201a047a523da00bd33cee60fe72ac1901c1273eb8296b4cfa637349991cf2
5
5
  SHA512:
6
- metadata.gz: 04f2eb02cfd53bac7b394f07e0c2d035b79978b96388f7ea8c083adfb2bc5cbe59a2ae4a4535863455d3cdc62260c8927879ef987cd1596567c3402006b954e8
7
- data.tar.gz: fc75caec7d7a1b03f42136087c78f8cd5c0319595afca6e76b2977b8c7f11b2b75a456434342040f2c8b78a4e9f9091181a327785eae551a52adcbc2ebb3f496
6
+ metadata.gz: 75251e4214a914b48256c60a97241f0db275fd06441e4eb3def15a8ada9fdebf6150208237b81d6715d63c87eae15d2bbcca208c61cc767961f451be60c8737f
7
+ data.tar.gz: 2b8f4b94045a63adfae0bb8e762922f788e49d3b0f37e44d5f54f8a2cd5fb0390f453a532106cc6b4da38b4f9c5cd8bf51211f2ba64713b0fb649a35b5b71e5d
@@ -2,6 +2,16 @@
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.2.0 - 2018-03-04
6
+
7
+ ### Added
8
+
9
+ * Dirty state support for `tag_names` is now available for Rails 4.2 onwards, and is more reliable for Rails 3.2-4.1 (supporting all known methods, such as `tag_names_changed?`, `previous_changes`, etc.)
10
+
11
+ ### Changed
12
+
13
+ * Switch normalising of tag names from a callback within `Gutentag::Tag` to `#name=` ([Tomasz Ras](https://github.com/RasMachineMan) in [#47](https://github.com/pat/gutentag/pull/47)).
14
+
5
15
  ## 2.1.0 - 2018-02-01
6
16
 
7
17
  ### Added
data/README.md CHANGED
@@ -80,7 +80,7 @@ These are the versions the test suite runs against. It's possible it may work on
80
80
  Get it into your Gemfile - and don't forget the version constraint!
81
81
 
82
82
  ```Ruby
83
- gem 'gutentag', '~> 2.1.0'
83
+ gem 'gutentag', '~> 2.2.0'
84
84
  ```
85
85
 
86
86
  Next: your tags get persisted to your database, so let's import and run the migrations to get the tables set up:
@@ -11,8 +11,6 @@ class Gutentag::Tag < ActiveRecord::Base
11
11
 
12
12
  scope :by_weight, lambda { order("gutentag_tags.taggings_count DESC") }
13
13
 
14
- before_validation :normalise_name
15
-
16
14
  def self.find_by_name(name)
17
15
  where(:name => Gutentag.normaliser.call(name)).first
18
16
  end
@@ -21,9 +19,7 @@ class Gutentag::Tag < ActiveRecord::Base
21
19
  find_by_name(name) || create(:name => name)
22
20
  end
23
21
 
24
- private
25
-
26
- def normalise_name
27
- self.name = Gutentag.normaliser.call name
22
+ def name=(value)
23
+ super(Gutentag.normaliser.call(value))
28
24
  end
29
25
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "gutentag"
6
- s.version = "2.1.0"
6
+ s.version = "2.2.0"
7
7
  s.authors = ["Pat Allan"]
8
8
  s.email = ["pat@freelancing-gods.com"]
9
9
  s.homepage = "https://github.com/pat/gutentag"
@@ -17,10 +17,11 @@ Gem::Specification.new do |s|
17
17
 
18
18
  s.add_runtime_dependency "activerecord", ">= 3.2.0"
19
19
 
20
- s.add_development_dependency "appraisal", "~> 2.1.0"
21
- s.add_development_dependency "bundler", ">= 1.7.12"
22
- s.add_development_dependency "combustion", "0.8.0"
20
+ s.add_development_dependency "appraisal", "~> 2.1.0"
21
+ s.add_development_dependency "bundler", ">= 1.7.12"
22
+ s.add_development_dependency "combustion", "0.8.0"
23
+ s.add_development_dependency "database_cleaner", "~> 1.6"
23
24
  s.add_development_dependency "rails"
24
- s.add_development_dependency "rspec-rails", "~> 3.1"
25
- s.add_development_dependency "rubocop", "~> 0.52.1"
25
+ s.add_development_dependency "rspec-rails", "~> 3.1"
26
+ s.add_development_dependency "rubocop", "~> 0.52.1"
26
27
  end
@@ -36,11 +36,7 @@ require "gutentag/remove_unused"
36
36
  require "gutentag/tag_validations"
37
37
  require "gutentag/tagged_with"
38
38
 
39
- if ActiveRecord::VERSION::MAJOR == 3
40
- Gutentag.dirtier = Gutentag::Dirty
41
- elsif ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR < 2
42
- Gutentag.dirtier = Gutentag::Dirty
43
- end
39
+ Gutentag.dirtier = Gutentag::Dirty if ActiveRecord::VERSION::STRING.to_f < 4.2
44
40
 
45
41
  require "active_support/lazy_load_hooks"
46
42
  ActiveSupport.on_load(:gutentag) do
@@ -2,6 +2,25 @@
2
2
 
3
3
  class Gutentag::ActiveRecord
4
4
  def self.call(model)
5
+ new(model).call
6
+ end
7
+
8
+ def initialize(model)
9
+ @model = model
10
+ end
11
+
12
+ def call
13
+ add_associations
14
+ add_callbacks
15
+ add_methods
16
+ add_attribute
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :model
22
+
23
+ def add_associations
5
24
  model.has_many :taggings,
6
25
  :class_name => "Gutentag::Tagging",
7
26
  :as => :taggable,
@@ -9,13 +28,41 @@ class Gutentag::ActiveRecord
9
28
  model.has_many :tags,
10
29
  :class_name => "Gutentag::Tag",
11
30
  :through => :taggings
31
+ end
32
+
33
+ def add_attribute
34
+ if legacy?
35
+ model.define_attribute_method "tag_names"
36
+ else
37
+ model.attribute "tag_names", ActiveRecord::Type::Value.new, :default => []
38
+ end
39
+ end
12
40
 
41
+ def add_callbacks
13
42
  model.after_save :persist_tags
14
43
 
15
- model.send :extend, Gutentag::ActiveRecord::ClassMethods
16
- model.send :include, Gutentag::ActiveRecord::InstanceMethods
44
+ if legacy?
45
+ model.after_save :reset_tag_names
46
+ else
47
+ model.after_commit :reset_tag_names, :on => %i[ create update ]
48
+ end
49
+ end
50
+
51
+ def add_methods
52
+ model.send :extend, Gutentag::ActiveRecord::ClassMethods
53
+
54
+ if legacy?
55
+ model.send :include, Gutentag::ActiveRecord::LegacyInstanceMethods
56
+ else
57
+ model.send :include, Gutentag::ActiveRecord::ModernInstanceMethods
58
+ end
59
+ end
60
+
61
+ def legacy?
62
+ ActiveRecord::VERSION::STRING.to_f < 4.2
17
63
  end
18
64
  end
19
65
 
20
66
  require "gutentag/active_record/class_methods"
21
- require "gutentag/active_record/instance_methods"
67
+ require "gutentag/active_record/legacy_instance_methods"
68
+ require "gutentag/active_record/modern_instance_methods"
@@ -4,4 +4,20 @@ module Gutentag::ActiveRecord::ClassMethods
4
4
  def tagged_with(options)
5
5
  Gutentag::TaggedWith.call self, options
6
6
  end
7
+
8
+ if ActiveRecord::VERSION::STRING.to_f < 4.2
9
+ def skip_time_zone_conversion_for_attributes
10
+ super + [:tag_names]
11
+ end
12
+ end
13
+
14
+ if ActiveRecord::VERSION::STRING.to_f < 4.0
15
+ def create_time_zone_conversion_attribute?(attr_name, column)
16
+ attr_name != "tag_names" && super
17
+ end
18
+
19
+ def attribute_cast_code(attr_name)
20
+ attr_name == "tag_names" ? "v" : super
21
+ end
22
+ end
7
23
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Gutentag::ActiveRecord::InstanceMethods
3
+ # For Rails <= 4.1
4
+ module Gutentag::ActiveRecord::LegacyInstanceMethods
4
5
  def reset_tag_names
5
6
  @tag_names = nil
6
7
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # For Rails 4.2+
4
+ module Gutentag::ActiveRecord::ModernInstanceMethods
5
+ def reset_tag_names
6
+ self.tag_names = nil
7
+ end
8
+
9
+ def tag_names
10
+ self.tag_names = tags.pluck(:name) if super.nil?
11
+
12
+ super
13
+ end
14
+
15
+ def tag_names=(names)
16
+ Gutentag.dirtier.call self, names if Gutentag.dirtier
17
+
18
+ super
19
+ end
20
+
21
+ private
22
+
23
+ def persist_tags
24
+ Gutentag::Persistence.new(Gutentag::ChangeState.new(self)).persist
25
+ end
26
+ end
@@ -11,7 +11,10 @@ class Gutentag::Dirty
11
11
  end
12
12
 
13
13
  def call
14
- instance.changed_attributes[:tag_names] = existing if changes.present?
14
+ return unless changes.present?
15
+
16
+ instance.tag_names_will_change!
17
+ instance.changed_attributes[:tag_names] = existing
15
18
  end
16
19
 
17
20
  private
@@ -14,8 +14,6 @@ class Gutentag::Persistence
14
14
  def persist
15
15
  remove_old
16
16
  add_new
17
-
18
- taggable.reset_tag_names
19
17
  end
20
18
 
21
19
  private
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "Dirty state of tag names" do
6
+ let(:article) { Article.create! }
7
+
8
+ it "knows what tag names will change" do
9
+ article.tag_names = ["pancakes"]
10
+
11
+ expect(article).to be_changed
12
+ expect(article.tag_names_changed?).to eq(true)
13
+ expect(article.tag_names_was).to eq([])
14
+ expect(article.tag_names_change).to eq([[], ["pancakes"]])
15
+
16
+ if ActiveRecord::VERSION::STRING.to_f > 4.0
17
+ expect(article.tag_names_changed?(:from => [], :to => ["pancakes"])).
18
+ to eq(true)
19
+ end
20
+
21
+ if ActiveRecord::VERSION::STRING.to_f > 5.0
22
+ expect(article.will_save_change_to_tag_names?).to eq(true)
23
+ expect(article.tag_names_change_to_be_saved).to eq([[], ["pancakes"]])
24
+ expect(article.tag_names_in_database).to eq([])
25
+ end
26
+ end
27
+
28
+ it "knows what tag names have changed" do
29
+ article.tag_names = ["pancakes"]
30
+ article.save
31
+
32
+ expect(article.tag_names).to eq(["pancakes"])
33
+
34
+ expect(article).to_not be_changed
35
+ expect(article.tag_names_changed?).to eq(false)
36
+ expect(article.previous_changes["tag_names"]).to eq([[], ["pancakes"]])
37
+
38
+ if ActiveRecord::VERSION::STRING.to_f >= 5.0
39
+ expect(article.tag_names_previously_changed?).to eq(true)
40
+ expect(article.tag_names_previous_change).to eq([[], ["pancakes"]])
41
+ end
42
+
43
+ if ActiveRecord::VERSION::STRING.to_f > 5.0
44
+ expect(article.saved_change_to_tag_names?).to eq(true)
45
+ expect(article.saved_change_to_tag_names).to eq([[], ["pancakes"]])
46
+ expect(article.saved_changes["tag_names"]).to eq([[], ["pancakes"]])
47
+ expect(article.tag_names_before_last_save).to eq([])
48
+ expect(article.tag_names_change_to_be_saved).to eq(nil)
49
+ expect(article.tag_names_in_database).to eq(["pancakes"])
50
+ end
51
+ end
52
+ end
@@ -3,7 +3,7 @@
3
3
  require "spec_helper"
4
4
 
5
5
  describe "Managing tags via names" do
6
- let(:article) { Article.create }
6
+ let(:article) { Article.create }
7
7
 
8
8
  it "returns tag names" do
9
9
  melbourne = Gutentag::Tag.create :name => "melbourne"
@@ -20,25 +20,6 @@ describe "Managing tags via names" do
20
20
  expect(article.tags.collect(&:name)).to eq(["melbourne"])
21
21
  end
22
22
 
23
- it "makes model dirty when changing through tag_names" do
24
- article.tag_names << "melbourne"
25
- article.save!
26
-
27
- article.tag_names = ["sydney"]
28
-
29
- expect(article.changed_attributes.stringify_keys).
30
- to eq("tag_names" => ["melbourne"])
31
- end if Gutentag.dirtier
32
-
33
- it "does not make model dirty when changing through tag_names" do
34
- article.tag_names << "melbourne"
35
- article.save!
36
-
37
- article.tag_names = ["melbourne"]
38
-
39
- expect(article.changed_attributes).to eq({})
40
- end
41
-
42
23
  it "allows for different tag normalisation" do
43
24
  Gutentag.normaliser = lambda { |name| name.upcase }
44
25
 
@@ -11,8 +11,19 @@ require "rspec/rails"
11
11
 
12
12
  RSpec.configure do |config|
13
13
  if config.respond_to?(:use_transactional_tests)
14
- config.use_transactional_tests = true
14
+ config.use_transactional_tests = false
15
15
  else
16
- config.use_transactional_fixtures = true
16
+ config.use_transactional_fixtures = false
17
+ end
18
+
19
+ config.before(:suite) do
20
+ DatabaseCleaner.strategy = :truncation
21
+ DatabaseCleaner.clean_with(:truncation)
22
+ end
23
+
24
+ config.around(:each) do |example|
25
+ DatabaseCleaner.cleaning do
26
+ example.run
27
+ end
17
28
  end
18
29
  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.1.0
4
+ version: 2.2.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: 2018-01-31 00:00:00.000000000 Z
11
+ date: 2018-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - '='
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.8.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: database_cleaner
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.6'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.6'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rails
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -133,7 +147,8 @@ files:
133
147
  - lib/gutentag.rb
134
148
  - lib/gutentag/active_record.rb
135
149
  - lib/gutentag/active_record/class_methods.rb
136
- - lib/gutentag/active_record/instance_methods.rb
150
+ - lib/gutentag/active_record/legacy_instance_methods.rb
151
+ - lib/gutentag/active_record/modern_instance_methods.rb
137
152
  - lib/gutentag/change_state.rb
138
153
  - lib/gutentag/dirty.rb
139
154
  - lib/gutentag/engine.rb
@@ -144,6 +159,7 @@ files:
144
159
  - lib/gutentag/tagged_with/id_query.rb
145
160
  - lib/gutentag/tagged_with/name_query.rb
146
161
  - lib/gutentag/tagged_with/query.rb
162
+ - spec/acceptance/dirty_state_spec.rb
147
163
  - spec/acceptance/removing_unused_spec.rb
148
164
  - spec/acceptance/tag_names_spec.rb
149
165
  - spec/acceptance/tags_spec.rb
@@ -176,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
176
192
  version: '0'
177
193
  requirements: []
178
194
  rubyforge_project:
179
- rubygems_version: 2.7.3
195
+ rubygems_version: 2.7.6
180
196
  signing_key:
181
197
  specification_version: 4
182
198
  summary: Good Tags