gutentag 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -1
- data/Appraisals +20 -8
- data/Gemfile +2 -0
- data/README.md +15 -1
- data/app/models/gutentag/tag.rb +1 -1
- data/gemfiles/rails_3_2.gemfile +3 -1
- data/gemfiles/rails_4_0.gemfile +2 -0
- data/gemfiles/rails_4_1.gemfile +3 -1
- data/gemfiles/rails_4_2.gemfile +3 -1
- data/gemfiles/rails_5_0.gemfile +8 -0
- data/gutentag.gemspec +3 -3
- data/lib/gutentag/active_record.rb +15 -1
- data/lib/gutentag/tag_names.rb +21 -0
- data/lib/gutentag/tag_validations.rb +18 -0
- data/lib/gutentag.rb +11 -1
- data/spec/gutentag/active_record_spec.rb +79 -0
- data/spec/spec_helper.rb +5 -6
- metadata +11 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a963454a3117dc737b8d4e70e8a02c22c2c2693e
|
4
|
+
data.tar.gz: bc7c540a51edbabb378370b9157451a29dcbbfdb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aaf39b46f22f2348b87dbc8d574d17d5d2b5728810980c16b1c344d894900701ff3434e43f2794c6ce3ce2515039de6fe887161110ee0aa4f0aa54db8152b1b1
|
7
|
+
data.tar.gz: 835f63c380604816cd1e5014960f3e75e199f167705635f45cea36467c288b0da7e504c1fa4ad8d35763aec26714cb6a754d58821ed54a244ca906e39cac7d0d
|
data/.travis.yml
CHANGED
data/Appraisals
CHANGED
@@ -1,15 +1,27 @@
|
|
1
1
|
appraise 'rails_3_2' do
|
2
|
-
gem 'rails',
|
3
|
-
|
2
|
+
gem 'rails', '~> 3.2.22.5'
|
3
|
+
gem 'rack', '~> 1.0', :platforms => [:ruby_20, :ruby_21]
|
4
|
+
gem 'nokogiri', '~> 1.6.0', :platforms => [:ruby_20]
|
5
|
+
end if RUBY_VERSION.to_f < 2.2
|
4
6
|
|
5
7
|
appraise 'rails_4_0' do
|
6
|
-
gem 'rails',
|
7
|
-
|
8
|
+
gem 'rails', '~> 4.0.13'
|
9
|
+
gem 'rack', '~> 1.0', :platforms => [:ruby_20, :ruby_21]
|
10
|
+
gem 'nokogiri', '~> 1.6.0', :platforms => [:ruby_20]
|
11
|
+
end if RUBY_VERSION.to_f < 2.4
|
8
12
|
|
9
13
|
appraise 'rails_4_1' do
|
10
|
-
gem 'rails',
|
11
|
-
|
14
|
+
gem 'rails', '~> 4.1.16'
|
15
|
+
gem 'rack', '~> 1.0', :platforms => [:ruby_20, :ruby_21]
|
16
|
+
gem 'nokogiri', '~> 1.6.0', :platforms => [:ruby_20]
|
17
|
+
end if RUBY_VERSION.to_f < 2.4
|
12
18
|
|
13
19
|
appraise 'rails_4_2' do
|
14
|
-
gem 'rails',
|
15
|
-
|
20
|
+
gem 'rails', '~> 4.2.7'
|
21
|
+
gem 'rack', '~> 1.0', :platforms => [:ruby_20, :ruby_21]
|
22
|
+
gem 'nokogiri', '~> 1.6.0', :platforms => [:ruby_20]
|
23
|
+
end if RUBY_VERSION.to_f < 2.4
|
24
|
+
|
25
|
+
appraise 'rails_5_0' do
|
26
|
+
gem 'rails', '~> 5.0.1'
|
27
|
+
end if RUBY_VERSION.to_f >= 2.2
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -12,7 +12,7 @@ This was built partly as a proof-of-concept, and partly to see how a tagging gem
|
|
12
12
|
|
13
13
|
Get it into your Gemfile - and don't forget the version constraint!
|
14
14
|
|
15
|
-
gem 'gutentag', '~> 0.
|
15
|
+
gem 'gutentag', '~> 0.8.0'
|
16
16
|
|
17
17
|
Next: your tags get persisted to your database, so let's import and run the migrations to get the tables set up:
|
18
18
|
|
@@ -44,6 +44,14 @@ If you want to use Gutentag outside of Rails, you can. However, this means you l
|
|
44
44
|
|
45
45
|
## Upgrading
|
46
46
|
|
47
|
+
### 0.8.0
|
48
|
+
|
49
|
+
No breaking changes.
|
50
|
+
|
51
|
+
### 0.7.0
|
52
|
+
|
53
|
+
No breaking changes.
|
54
|
+
|
47
55
|
### 0.6.0
|
48
56
|
|
49
57
|
Rails 4.2 is supported as of Gutentag 0.6.0 - but please note that due to internal changes in ActiveRecord, changes to tag_names will no longer be tracked by your model's dirty state. This feature will continue to work in Rails 3.2 through to 4.1 though.
|
@@ -80,6 +88,12 @@ Changes to tag_names are not persisted immediately - you must save your taggable
|
|
80
88
|
article.tag_names << 'ruby'
|
81
89
|
article.save
|
82
90
|
|
91
|
+
You can also query for instances with specified tags. This is OR logic, not AND - it'll match any instances that have *any* of the tags or tag names.
|
92
|
+
|
93
|
+
Article.tagged_with('tag1', 'tag2')
|
94
|
+
Article.tagged_with(Gutentag::Tag.where(name: ['tag1', 'tag2'])
|
95
|
+
# => [#<Article id: "123">]
|
96
|
+
|
83
97
|
## Contribution
|
84
98
|
|
85
99
|
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
@@ -8,7 +8,7 @@ class Gutentag::Tag < ActiveRecord::Base
|
|
8
8
|
|
9
9
|
scope :by_weight, ->{ order('gutentag_tags.taggings_count DESC') }
|
10
10
|
|
11
|
-
|
11
|
+
Gutentag.tag_validations.call self
|
12
12
|
|
13
13
|
before_validation :normalise_name
|
14
14
|
|
data/gemfiles/rails_3_2.gemfile
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
5
|
gem "test-unit", :platform => :ruby_22
|
6
|
-
gem "
|
6
|
+
gem "rack", "~> 1.0", :platforms => [:ruby_20, :ruby_21]
|
7
|
+
gem "rails", "~> 3.2.22.5"
|
8
|
+
gem "nokogiri", "~> 1.6.0", :platforms => [:ruby_20]
|
7
9
|
|
8
10
|
gemspec :path => "../"
|
data/gemfiles/rails_4_0.gemfile
CHANGED
data/gemfiles/rails_4_1.gemfile
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
5
|
gem "test-unit", :platform => :ruby_22
|
6
|
-
gem "rails", "~> 4.1.
|
6
|
+
gem "rails", "~> 4.1.16"
|
7
|
+
gem "rack", "~> 1.0", :platforms => [:ruby_20, :ruby_21]
|
8
|
+
gem "nokogiri", "~> 1.6.0", :platforms => [:ruby_20]
|
7
9
|
|
8
10
|
gemspec :path => "../"
|
data/gemfiles/rails_4_2.gemfile
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
5
|
gem "test-unit", :platform => :ruby_22
|
6
|
-
gem "rails", "~> 4.2.
|
6
|
+
gem "rails", "~> 4.2.7"
|
7
|
+
gem "rack", "~> 1.0", :platforms => [:ruby_20, :ruby_21]
|
8
|
+
gem "nokogiri", "~> 1.6.0", :platforms => [:ruby_20]
|
7
9
|
|
8
10
|
gemspec :path => "../"
|
data/gutentag.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
Gem::Specification.new do |s|
|
3
3
|
s.name = 'gutentag'
|
4
|
-
s.version = '0.
|
4
|
+
s.version = '0.8.0'
|
5
5
|
s.authors = ['Pat Allan']
|
6
6
|
s.email = ['pat@freelancing-gods.com']
|
7
7
|
s.homepage = 'https://github.com/pat/gutentag'
|
@@ -15,9 +15,9 @@ Gem::Specification.new do |s|
|
|
15
15
|
|
16
16
|
s.add_runtime_dependency 'activerecord', '>= 3.2.0'
|
17
17
|
|
18
|
-
s.add_development_dependency 'appraisal', '~> 1.0
|
18
|
+
s.add_development_dependency 'appraisal', '~> 2.1.0'
|
19
19
|
s.add_development_dependency 'bundler', '>= 1.7.12'
|
20
|
-
s.add_development_dependency 'combustion', '0.5.
|
20
|
+
s.add_development_dependency 'combustion', '0.5.5'
|
21
21
|
s.add_development_dependency 'rails'
|
22
22
|
s.add_development_dependency 'rspec-rails', '~> 3.1'
|
23
23
|
s.add_development_dependency 'sqlite3', '~> 1.3.7'
|
@@ -3,6 +3,8 @@ require 'active_support/concern'
|
|
3
3
|
module Gutentag::ActiveRecord
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
+
UNIQUENESS_METHOD = ActiveRecord::VERSION::MAJOR == 3 ? :uniq : :distinct
|
7
|
+
|
6
8
|
module ClassMethods
|
7
9
|
def has_many_tags
|
8
10
|
has_many :taggings, :class_name => 'Gutentag::Tagging', :as => :taggable,
|
@@ -10,7 +12,13 @@ module Gutentag::ActiveRecord
|
|
10
12
|
has_many :tags, :class_name => 'Gutentag::Tag',
|
11
13
|
:through => :taggings
|
12
14
|
|
13
|
-
after_save
|
15
|
+
after_save :persist_tags
|
16
|
+
end
|
17
|
+
|
18
|
+
def tagged_with(*tags)
|
19
|
+
joins(:tags).where(
|
20
|
+
Gutentag::Tag.table_name => {:name => Gutentag::TagNames.call(tags)}
|
21
|
+
).public_send UNIQUENESS_METHOD
|
14
22
|
end
|
15
23
|
end
|
16
24
|
|
@@ -27,4 +35,10 @@ module Gutentag::ActiveRecord
|
|
27
35
|
|
28
36
|
@tag_names = names
|
29
37
|
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def persist_tags
|
42
|
+
Gutentag::Persistence.new(self).persist
|
43
|
+
end
|
30
44
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Gutentag::TagNames
|
2
|
+
def self.call(*names)
|
3
|
+
new(names).call
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize(names)
|
7
|
+
@names = names.flatten
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
name_values.collect { |name| Gutentag::TagName.call name }
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :names
|
17
|
+
|
18
|
+
def name_values
|
19
|
+
names.collect { |name| name.respond_to?(:name) ? name.name : name.to_s }
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Gutentag::TagValidations
|
2
|
+
def self.call(klass)
|
3
|
+
new(klass).call
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize(klass)
|
7
|
+
@klass = klass
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
klass.validates :name, :presence => true,
|
12
|
+
:uniqueness => {:case_sensitive => false}
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :klass
|
18
|
+
end
|
data/lib/gutentag.rb
CHANGED
@@ -16,12 +16,22 @@ module Gutentag
|
|
16
16
|
def self.normaliser=(normaliser)
|
17
17
|
@normaliser = normaliser
|
18
18
|
end
|
19
|
+
|
20
|
+
def self.tag_validations
|
21
|
+
@tag_validations ||= Gutentag::TagValidations
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.tag_validations=(tag_validations)
|
25
|
+
@tag_validations = tag_validations
|
26
|
+
end
|
19
27
|
end
|
20
28
|
|
21
29
|
require 'gutentag/active_record'
|
22
30
|
require 'gutentag/dirty'
|
23
31
|
require 'gutentag/persistence'
|
24
32
|
require 'gutentag/tag_name'
|
33
|
+
require 'gutentag/tag_names'
|
34
|
+
require 'gutentag/tag_validations'
|
25
35
|
|
26
36
|
if ActiveRecord::VERSION::MAJOR == 3
|
27
37
|
Gutentag.dirtier = Gutentag::Dirty
|
@@ -33,7 +43,7 @@ if defined?(Rails::Engine)
|
|
33
43
|
require 'gutentag/engine'
|
34
44
|
else
|
35
45
|
require 'active_record'
|
36
|
-
ActiveRecord::Base.include Gutentag::ActiveRecord
|
46
|
+
ActiveRecord::Base.send :include, Gutentag::ActiveRecord
|
37
47
|
require File.expand_path(
|
38
48
|
'./../app/models/gutentag/tag', File.dirname(__FILE__)
|
39
49
|
)
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gutentag::ActiveRecord do
|
4
|
+
describe '.tagged_with' do
|
5
|
+
let!(:melborne_article) do
|
6
|
+
article = Article.create :title => 'Overview'
|
7
|
+
article.tag_names << 'melborne'
|
8
|
+
article.save!
|
9
|
+
article
|
10
|
+
end
|
11
|
+
|
12
|
+
let!(:oregon_article) do
|
13
|
+
article = Article.create
|
14
|
+
article.tag_names << 'oregon'
|
15
|
+
article.save!
|
16
|
+
article
|
17
|
+
end
|
18
|
+
|
19
|
+
let!(:melborne_oregon_article) do
|
20
|
+
article = Article.create
|
21
|
+
article.tag_names = %w(oregon melborne)
|
22
|
+
article.save!
|
23
|
+
article
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'given a single tag name' do
|
27
|
+
subject { Article.tagged_with('melborne') }
|
28
|
+
|
29
|
+
it { expect(subject.count).to eq 2 }
|
30
|
+
it { is_expected.to include melborne_article, melborne_oregon_article }
|
31
|
+
it { is_expected.not_to include oregon_article }
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'given a single tag name[symbol]' do
|
35
|
+
subject { Article.tagged_with(:melborne) }
|
36
|
+
|
37
|
+
it { expect(subject.count).to eq 2 }
|
38
|
+
it { is_expected.to include melborne_article, melborne_oregon_article }
|
39
|
+
it { is_expected.not_to include oregon_article }
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'given multiple tag names' do
|
43
|
+
subject { Article.tagged_with('melborne', 'oregon') }
|
44
|
+
|
45
|
+
it { expect(subject.count).to eq 3 }
|
46
|
+
it { is_expected.to include melborne_article, oregon_article, melborne_oregon_article }
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'given an array of tag names' do
|
50
|
+
subject { Article.tagged_with(%w(melborne oregon)) }
|
51
|
+
|
52
|
+
it { expect(subject.count).to eq 3 }
|
53
|
+
it { is_expected.to include melborne_article, oregon_article, melborne_oregon_article }
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'given a single tag instance' do
|
57
|
+
subject { Article.tagged_with(Gutentag::Tag.find_by_name('melborne')) }
|
58
|
+
|
59
|
+
it { expect(subject.count).to eq 2 }
|
60
|
+
it { is_expected.to include melborne_article, melborne_oregon_article }
|
61
|
+
it { is_expected.not_to include oregon_article }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'given multiple tag objects' do
|
65
|
+
subject { Article.tagged_with(Gutentag::Tag.where(name: %w(melborne oregon))) }
|
66
|
+
|
67
|
+
it { expect(subject.count).to eq 3 }
|
68
|
+
it { is_expected.to include melborne_article, oregon_article, melborne_oregon_article }
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'chaining where clause' do
|
72
|
+
subject { Article.tagged_with(%w(melborne oregon)).where(title: 'Overview') }
|
73
|
+
|
74
|
+
it { expect(subject.count).to eq 1 }
|
75
|
+
it { is_expected.to include melborne_article }
|
76
|
+
it { is_expected.not_to include oregon_article, melborne_oregon_article }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
require 'bundler'
|
2
2
|
|
3
|
-
# Get Bundler set up
|
4
|
-
Bundler.setup :default, :development
|
5
|
-
# Require Rails first
|
6
|
-
require 'rails'
|
7
|
-
# Then require everything else
|
8
3
|
Bundler.require :default, :development
|
9
4
|
|
10
5
|
Combustion.initialize! :active_record
|
@@ -12,5 +7,9 @@ Combustion.initialize! :active_record
|
|
12
7
|
require 'rspec/rails'
|
13
8
|
|
14
9
|
RSpec.configure do |config|
|
15
|
-
config.
|
10
|
+
if config.respond_to?(:use_transactional_tests)
|
11
|
+
config.use_transactional_tests = true
|
12
|
+
else
|
13
|
+
config.use_transactional_fixtures = true
|
14
|
+
end
|
16
15
|
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: 0.
|
4
|
+
version: 0.8.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: 2017-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 1.0
|
33
|
+
version: 2.1.0
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 1.0
|
40
|
+
version: 2.1.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.5.
|
61
|
+
version: 0.5.5
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.5.
|
68
|
+
version: 0.5.5
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rails
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -131,6 +131,7 @@ files:
|
|
131
131
|
- gemfiles/rails_4_0.gemfile
|
132
132
|
- gemfiles/rails_4_1.gemfile
|
133
133
|
- gemfiles/rails_4_2.gemfile
|
134
|
+
- gemfiles/rails_5_0.gemfile
|
134
135
|
- gutentag.gemspec
|
135
136
|
- lib/gutentag.rb
|
136
137
|
- lib/gutentag/active_record.rb
|
@@ -138,8 +139,11 @@ files:
|
|
138
139
|
- lib/gutentag/engine.rb
|
139
140
|
- lib/gutentag/persistence.rb
|
140
141
|
- lib/gutentag/tag_name.rb
|
142
|
+
- lib/gutentag/tag_names.rb
|
143
|
+
- lib/gutentag/tag_validations.rb
|
141
144
|
- spec/acceptance/tag_names_spec.rb
|
142
145
|
- spec/acceptance/tags_spec.rb
|
146
|
+
- spec/gutentag/active_record_spec.rb
|
143
147
|
- spec/gutentag/tag_name_spec.rb
|
144
148
|
- spec/internal/app/models/article.rb
|
145
149
|
- spec/internal/config/database.yml
|
@@ -168,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
168
172
|
version: '0'
|
169
173
|
requirements: []
|
170
174
|
rubyforge_project:
|
171
|
-
rubygems_version: 2.
|
175
|
+
rubygems_version: 2.6.8
|
172
176
|
signing_key:
|
173
177
|
specification_version: 4
|
174
178
|
summary: Good Tags
|