gutentag 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 69d6508868235e19bd72405fac2df27f9c54ecb9
4
- data.tar.gz: 836f4a7342bb7846878bb679e18473a99c671f21
3
+ metadata.gz: 34f0e855c14d22751ac57bf0ddc8be91effd346c
4
+ data.tar.gz: 59c1247fd49e8cc37bb6e22b7fb3903567a8f5af
5
5
  SHA512:
6
- metadata.gz: 45c8b280565560bfb936d517f9169402f6790b7057705fac454e4e06404c0a60eac90abbdbc45be19b159a7f7390a5d5633d541cb46a3a4e00e1e85ecebc7d89
7
- data.tar.gz: d720d6e90ba3450fc55e3a231b4a784c6957864fd02f8cd58c050d026b26ad47d6bb1caf89f9365f08db75f8803b33bbed2f885d47336b8c50f6e35b46a2a415
6
+ metadata.gz: 03e82d9335afdbe03d9ac06625da1f2608d8394a6397ff405c293585ce38d3206f4bcc39285e8492ee4a49a8afabe101fe09f9772b557e88e8ab1c91999e9a97
7
+ data.tar.gz: ad34f1a97036b19f73d7e8edf48bac7a73bb8e1f25759107897ec2bfd43ad49943b7d61be7370b6f79756997b8bf646dadd9fbce4917d2ec96163b833eab806e
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  Gemfile.lock
2
+ pkg/*
2
3
  spec/internal/db/*.sqlite
data/README.md CHANGED
@@ -1,14 +1,17 @@
1
1
  # Gutentag
2
2
 
3
+ [![Build Status](https://travis-ci.org/pat/gutentag.png?branch=master)](https://travis-ci.org/pat/gutentag)
4
+ [![Code Climate](https://codeclimate.com/github/pat/gutentag.png)](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.1.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
 
@@ -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 => true
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
@@ -4,4 +4,7 @@ class Gutentag::Tagging < ActiveRecord::Base
4
4
 
5
5
  validates :taggable, :presence => true
6
6
  validates :tag, :presence => true
7
+ validates :tag_id, :uniqueness => {
8
+ :scope => [:taggable_id, :taggable_type]
9
+ }
7
10
  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.1.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
@@ -4,4 +4,5 @@ end
4
4
 
5
5
  require 'gutentag/active_record'
6
6
  require 'gutentag/engine'
7
+ require 'gutentag/tag_name'
7
8
  require 'gutentag/tag_names'
@@ -0,0 +1,17 @@
1
+ class Gutentag::TagName
2
+ def self.normalise(name)
3
+ new(name).to_s
4
+ end
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ end
9
+
10
+ def to_s
11
+ name.downcase
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :name
17
+ end
@@ -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
- tag = Gutentag::Tag.where(:name => name).first ||
31
- Gutentag::Tag.create(:name => name)
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
- taggable.tags << tag
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,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gutentag::TagName do
4
+ describe '.normalise' do
5
+ it "downcases the provided name" do
6
+ Gutentag::TagName.normalise('Tasty Pancakes').should == 'tasty pancakes'
7
+ end
8
+ end
9
+ 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.1.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 00:00:00.000000000 Z
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: []