gutentag 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 +4 -4
- data/.gitignore +1 -0
- data/README.md +5 -2
- data/app/models/gutentag/tag.rb +9 -1
- data/app/models/gutentag/tagging.rb +3 -0
- data/gutentag.gemspec +2 -1
- data/lib/gutentag.rb +1 -0
- data/lib/gutentag/tag_name.rb +17 -0
- data/lib/gutentag/tag_names.rb +21 -6
- data/spec/acceptance/tag_names_spec.rb +39 -0
- data/spec/gutentag/tag_name_spec.rb +9 -0
- data/spec/models/gutentag/tag_spec.rb +28 -0
- data/spec/models/gutentag/tagging_spec.rb +16 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34f0e855c14d22751ac57bf0ddc8be91effd346c
|
4
|
+
data.tar.gz: 59c1247fd49e8cc37bb6e22b7fb3903567a8f5af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03e82d9335afdbe03d9ac06625da1f2608d8394a6397ff405c293585ce38d3206f4bcc39285e8492ee4a49a8afabe101fe09f9772b557e88e8ab1c91999e9a97
|
7
|
+
data.tar.gz: ad34f1a97036b19f73d7e8edf48bac7a73bb8e1f25759107897ec2bfd43ad49943b7d61be7370b6f79756997b8bf646dadd9fbce4917d2ec96163b833eab806e
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
# Gutentag
|
2
2
|
|
3
|
+
[](https://travis-ci.org/pat/gutentag)
|
4
|
+
[](https://codeclimate.com/github/pat/gutentag)
|
5
|
+
|
3
6
|
A good, simple, solid tagging extension for ActiveRecord.
|
4
7
|
|
5
|
-
This was built partly as a proof-of-concept, and 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.
|
8
|
+
This was built partly as a proof-of-concept, and 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. If you want to know more, read [this blog post](http://freelancing-gods.com/posts/gutentag_simple_rails_tagging).
|
6
9
|
|
7
10
|
## Installation
|
8
11
|
|
9
12
|
Get it into your Gemfile - and don't forget the version constraint!
|
10
13
|
|
11
|
-
gem 'gutentag', '~> 0.
|
14
|
+
gem 'gutentag', '~> 0.2.0'
|
12
15
|
|
13
16
|
Next: your tags get persisted to your database, so let's import and run the migrations to get the tables set up:
|
14
17
|
|
data/app/models/gutentag/tag.rb
CHANGED
@@ -2,5 +2,13 @@ class Gutentag::Tag < ActiveRecord::Base
|
|
2
2
|
has_many :taggings, :class_name => 'Gutentag::Tagging',
|
3
3
|
:dependent => :destroy
|
4
4
|
|
5
|
-
validates :name, :presence => true, :uniqueness =>
|
5
|
+
validates :name, :presence => true, :uniqueness => {:case_sensitive => false}
|
6
|
+
|
7
|
+
before_validation :normalise_name
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def normalise_name
|
12
|
+
self.name = Gutentag::TagName.normalise name
|
13
|
+
end
|
6
14
|
end
|
data/gutentag.gemspec
CHANGED
@@ -1,12 +1,13 @@
|
|
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.2.0'
|
5
5
|
s.authors = ['Pat Allan']
|
6
6
|
s.email = ['pat@freelancing-gods.com']
|
7
7
|
s.homepage = 'https://github.com/pat/gutentag'
|
8
8
|
s.summary = 'Good Tags'
|
9
9
|
s.description = 'A good, simple, solid tagging extension for ActiveRecord'
|
10
|
+
s.license = 'MIT'
|
10
11
|
|
11
12
|
s.files = `git ls-files`.split("\n")
|
12
13
|
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
data/lib/gutentag.rb
CHANGED
data/lib/gutentag/tag_names.rb
CHANGED
@@ -4,7 +4,7 @@ class Gutentag::TagNames
|
|
4
4
|
def self.new_with_names(taggable, names)
|
5
5
|
tag_names = new(taggable)
|
6
6
|
tag_names.clear
|
7
|
-
names.each { |name| tag_names << name }
|
7
|
+
names.each { |name| tag_names << Gutentag::TagName.normalise(name) }
|
8
8
|
tag_names
|
9
9
|
end
|
10
10
|
|
@@ -17,20 +17,31 @@ class Gutentag::TagNames
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def +(array)
|
20
|
-
array.each { |name| self.<< name }
|
20
|
+
(normalised(array) - to_a).each { |name| self.<< name }
|
21
|
+
|
21
22
|
self
|
22
23
|
end
|
23
24
|
|
24
25
|
def -(array)
|
25
|
-
array.each { |name| self.delete name }
|
26
|
+
normalised(array).each { |name| self.delete name }
|
27
|
+
|
26
28
|
self
|
27
29
|
end
|
28
30
|
|
29
31
|
def <<(name)
|
30
|
-
|
31
|
-
|
32
|
+
name = Gutentag::TagName.normalise name
|
33
|
+
tag = Gutentag::Tag.where(:name => name).first ||
|
34
|
+
Gutentag::Tag.create(:name => name)
|
35
|
+
|
36
|
+
taggable.tags << tag unless taggable.tags.include?(tag)
|
37
|
+
end
|
32
38
|
|
33
|
-
|
39
|
+
def |(array)
|
40
|
+
to_a | normalised(array)
|
41
|
+
end
|
42
|
+
|
43
|
+
def &(array)
|
44
|
+
to_a & normalised(array)
|
34
45
|
end
|
35
46
|
|
36
47
|
def clear
|
@@ -48,4 +59,8 @@ class Gutentag::TagNames
|
|
48
59
|
private
|
49
60
|
|
50
61
|
attr_reader :taggable
|
62
|
+
|
63
|
+
def normalised(array)
|
64
|
+
array.collect { |name| Gutentag::TagName.normalise(name) }
|
65
|
+
end
|
51
66
|
end
|
@@ -17,6 +17,13 @@ describe "Managing tags via names" do
|
|
17
17
|
article.tags.collect(&:name).should == ['melbourne']
|
18
18
|
end
|
19
19
|
|
20
|
+
it "doesn't complain when adding an existing tag" do
|
21
|
+
article.tag_names << 'melbourne'
|
22
|
+
article.tag_names << 'melbourne'
|
23
|
+
|
24
|
+
article.tags.collect(&:name).should == ['melbourne']
|
25
|
+
end
|
26
|
+
|
20
27
|
it "accepts a completely new set of tags" do
|
21
28
|
article.tag_names = ['portland', 'oregon']
|
22
29
|
|
@@ -50,6 +57,13 @@ describe "Managing tags via names" do
|
|
50
57
|
article.tags.collect(&:name).should == ['portland', 'oregon', 'ruby']
|
51
58
|
end
|
52
59
|
|
60
|
+
it "does not repeat appended names that already exist" do
|
61
|
+
article.tag_names = ['portland', 'oregon']
|
62
|
+
article.tag_names += ['oregon', 'ruby']
|
63
|
+
|
64
|
+
article.tags.collect(&:name).should == ['portland', 'oregon', 'ruby']
|
65
|
+
end
|
66
|
+
|
53
67
|
it "removes a single tag name" do
|
54
68
|
article.tag_names = ['portland', 'oregon']
|
55
69
|
article.tag_names.delete 'oregon'
|
@@ -63,4 +77,29 @@ describe "Managing tags via names" do
|
|
63
77
|
|
64
78
|
article.tags.collect(&:name).should == ['portland']
|
65
79
|
end
|
80
|
+
|
81
|
+
it "provides union operators" do
|
82
|
+
article.tag_names = ['portland', 'ruby']
|
83
|
+
article.tag_names |= ['ruby', 'melbourne']
|
84
|
+
|
85
|
+
article.tags.collect(&:name).should == ['portland', 'ruby', 'melbourne']
|
86
|
+
end
|
87
|
+
|
88
|
+
it "provides intersection operators" do
|
89
|
+
article.tag_names = ['portland', 'ruby']
|
90
|
+
article.tag_names &= ['ruby', 'melbourne']
|
91
|
+
|
92
|
+
article.tags.collect(&:name).should == ['ruby']
|
93
|
+
end
|
94
|
+
|
95
|
+
it "matches tag names ignoring case" do
|
96
|
+
article.tag_names = ['portland']
|
97
|
+
article.tag_names += ['Portland']
|
98
|
+
|
99
|
+
article.tags.collect(&:name).should == ['portland']
|
100
|
+
|
101
|
+
article.tag_names << 'Portland'
|
102
|
+
|
103
|
+
article.tags.collect(&:name).should == ['portland']
|
104
|
+
end
|
66
105
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gutentag::Tag do
|
4
|
+
describe '#name' do
|
5
|
+
before :each do
|
6
|
+
Gutentag::TagName.stub :normalise => 'waffles'
|
7
|
+
end
|
8
|
+
|
9
|
+
it "normalises the provided name" do
|
10
|
+
Gutentag::TagName.should_receive(:normalise).with('Pancakes').
|
11
|
+
and_return('waffles')
|
12
|
+
|
13
|
+
Gutentag::Tag.create!(:name => 'Pancakes')
|
14
|
+
end
|
15
|
+
|
16
|
+
it "saves the normalised name" do
|
17
|
+
Gutentag::Tag.create!(:name => 'Pancakes').name.should == 'waffles'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#valid?' do
|
22
|
+
it "ignores case when enforcing uniqueness" do
|
23
|
+
Gutentag::Tag.create! :name => 'pancakes'
|
24
|
+
|
25
|
+
Gutentag::Tag.create(:name => 'Pancakes').should have(1).error_on(:name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gutentag::Tagging do
|
4
|
+
describe '#valid?' do
|
5
|
+
let(:tag) { Gutentag::Tag.create! :name => 'pancakes' }
|
6
|
+
let(:taggable) { Article.create! }
|
7
|
+
|
8
|
+
it "ensures tags are unique for any given taggable" do
|
9
|
+
Gutentag::Tagging.create! :tag => tag, :taggable => taggable
|
10
|
+
|
11
|
+
Gutentag::Tagging.create(
|
12
|
+
:tag => tag, :taggable => taggable
|
13
|
+
).should have(1).error_on(:tag_id)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
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.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: 2013-07-
|
11
|
+
date: 2013-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -86,16 +86,21 @@ files:
|
|
86
86
|
- lib/gutentag.rb
|
87
87
|
- lib/gutentag/active_record.rb
|
88
88
|
- lib/gutentag/engine.rb
|
89
|
+
- lib/gutentag/tag_name.rb
|
89
90
|
- lib/gutentag/tag_names.rb
|
90
91
|
- spec/acceptance/tag_names_spec.rb
|
91
92
|
- spec/acceptance/tags_spec.rb
|
93
|
+
- spec/gutentag/tag_name_spec.rb
|
92
94
|
- spec/internal/app/models/article.rb
|
93
95
|
- spec/internal/config/database.yml
|
94
96
|
- spec/internal/db/schema.rb
|
95
97
|
- spec/internal/log/.gitignore
|
98
|
+
- spec/models/gutentag/tag_spec.rb
|
99
|
+
- spec/models/gutentag/tagging_spec.rb
|
96
100
|
- spec/spec_helper.rb
|
97
101
|
homepage: https://github.com/pat/gutentag
|
98
|
-
licenses:
|
102
|
+
licenses:
|
103
|
+
- MIT
|
99
104
|
metadata: {}
|
100
105
|
post_install_message:
|
101
106
|
rdoc_options: []
|