spraypaint 1.0.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.
- data/.gitignore +4 -0
- data/CHANGELOG +4 -0
- data/MIT-LICENSE +20 -0
- data/README +17 -0
- data/Rakefile +21 -0
- data/TODO +7 -0
- data/VERSION.yml +4 -0
- data/about.yml +10 -0
- data/generators/spraypaint_migration/spraypaint_migration_generator.rb +21 -0
- data/generators/spraypaint_migration/templates/spraypaint_migration.rb +25 -0
- data/lib/spraypaint.rb +59 -0
- data/lib/spraypaint/behaviour.rb +11 -0
- data/lib/spraypaint/behaviour/discovery.rb +57 -0
- data/lib/spraypaint/behaviour/manipulation.rb +71 -0
- data/lib/spraypaint/behaviour/persistence.rb +24 -0
- data/lib/spraypaint/default_sanitizer.rb +17 -0
- data/lib/spraypaint/model/tag.rb +15 -0
- data/lib/spraypaint/model/tagging.rb +11 -0
- data/lib/spraypaint/sanitizer.rb +11 -0
- data/rails/init.rb +3 -0
- data/spraypaint.gemspec +78 -0
- data/test/config/boot.rb +110 -0
- data/test/config/database.yml +5 -0
- data/test/config/environment.rb +27 -0
- data/test/config/environments/test.rb +0 -0
- data/test/db/schema.rb +40 -0
- data/test/spec/default_sanitizer_spec.rb +26 -0
- data/test/spec/models/tag_spec.rb +20 -0
- data/test/spec/models/tagging_spec.rb +49 -0
- data/test/spec/spec_helper.rb +50 -0
- data/test/spec/spraypaint_spec.rb +284 -0
- data/vendor/penknife/lib/penknife.rb +2 -0
- data/vendor/penknife/lib/penknife/rake.rb +41 -0
- data/vendor/penknife/lib/penknife/rake/plugin_tasks.rb +79 -0
- metadata +96 -0
data/test/config/boot.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Don't change this file!
|
2
|
+
# Configure your app in config/environment.rb and config/environments/*.rb
|
3
|
+
|
4
|
+
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
|
5
|
+
|
6
|
+
module Rails
|
7
|
+
class << self
|
8
|
+
def boot!
|
9
|
+
unless booted?
|
10
|
+
preinitialize
|
11
|
+
pick_boot.run
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def booted?
|
16
|
+
defined? Rails::Initializer
|
17
|
+
end
|
18
|
+
|
19
|
+
def pick_boot
|
20
|
+
(vendor_rails? ? VendorBoot : GemBoot).new
|
21
|
+
end
|
22
|
+
|
23
|
+
def vendor_rails?
|
24
|
+
File.exist?("#{RAILS_ROOT}/vendor/rails")
|
25
|
+
end
|
26
|
+
|
27
|
+
def preinitialize
|
28
|
+
load(preinitializer_path) if File.exist?(preinitializer_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def preinitializer_path
|
32
|
+
"#{RAILS_ROOT}/config/preinitializer.rb"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Boot
|
37
|
+
def run
|
38
|
+
load_initializer
|
39
|
+
Rails::Initializer.run(:set_load_path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class VendorBoot < Boot
|
44
|
+
def load_initializer
|
45
|
+
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
|
46
|
+
Rails::Initializer.run(:install_gem_spec_stubs)
|
47
|
+
Rails::GemDependency.add_frozen_gem_path
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class GemBoot < Boot
|
52
|
+
def load_initializer
|
53
|
+
self.class.load_rubygems
|
54
|
+
load_rails_gem
|
55
|
+
require 'initializer'
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_rails_gem
|
59
|
+
if version = self.class.gem_version
|
60
|
+
gem 'rails', version
|
61
|
+
else
|
62
|
+
gem 'rails'
|
63
|
+
end
|
64
|
+
rescue Gem::LoadError => load_error
|
65
|
+
$stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
|
66
|
+
exit 1
|
67
|
+
end
|
68
|
+
|
69
|
+
class << self
|
70
|
+
def rubygems_version
|
71
|
+
Gem::RubyGemsVersion rescue nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def gem_version
|
75
|
+
if defined? RAILS_GEM_VERSION
|
76
|
+
RAILS_GEM_VERSION
|
77
|
+
elsif ENV.include?('RAILS_GEM_VERSION')
|
78
|
+
ENV['RAILS_GEM_VERSION']
|
79
|
+
else
|
80
|
+
parse_gem_version(read_environment_rb)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def load_rubygems
|
85
|
+
require 'rubygems'
|
86
|
+
min_version = '1.3.1'
|
87
|
+
unless rubygems_version >= min_version
|
88
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
|
89
|
+
exit 1
|
90
|
+
end
|
91
|
+
|
92
|
+
rescue LoadError
|
93
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
|
94
|
+
exit 1
|
95
|
+
end
|
96
|
+
|
97
|
+
def parse_gem_version(text)
|
98
|
+
$1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
def read_environment_rb
|
103
|
+
File.read("#{RAILS_ROOT}/config/environment.rb")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# All that for this:
|
110
|
+
Rails.boot!
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Simplified environment file, only meant for testing spraypaint
|
2
|
+
|
3
|
+
# Specifies gem version of Rails to use when vendor/rails is not present
|
4
|
+
RAILS_GEM_VERSION = '2.3.4' unless defined? RAILS_GEM_VERSION
|
5
|
+
|
6
|
+
# Bootstrap the Rails environment, frameworks, and default configuration
|
7
|
+
require File.join(File.dirname(__FILE__), 'boot')
|
8
|
+
|
9
|
+
# This plugin locator will only find the plugin we wish to test
|
10
|
+
|
11
|
+
class SinglePluginLocator < Rails::Plugin::FileSystemLocator
|
12
|
+
def plugins
|
13
|
+
if plugin = create_plugin(File.expand_path(File.join(File.dirname(__FILE__), '..', '..')))
|
14
|
+
[plugin]
|
15
|
+
else
|
16
|
+
raise "Plugin to be tested couldn't be found"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Rails::Initializer.run do |config|
|
22
|
+
# Change the plugin path to the grandparent folder, where the plugin to be tested actually resides
|
23
|
+
config.plugin_locators = [SinglePluginLocator]
|
24
|
+
|
25
|
+
# Spraypaint only touches ActiveRecord, so don't bother loading any other frameworks
|
26
|
+
config.frameworks = [:active_record]
|
27
|
+
end
|
File without changes
|
data/test/db/schema.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead of editing this file,
|
2
|
+
# please use the migrations feature of Active Record to incrementally modify your database, and
|
3
|
+
# then regenerate this schema definition.
|
4
|
+
#
|
5
|
+
# Note that this schema.rb definition is the authoritative source for your database schema. If you need
|
6
|
+
# to create the application database on another system, you should be using db:schema:load, not running
|
7
|
+
# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
8
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
9
|
+
#
|
10
|
+
# It's strongly recommended to check this file into your version control system.
|
11
|
+
|
12
|
+
ActiveRecord::Schema.define(:version => 20090323171649) do
|
13
|
+
create_table 'spraypaint_tags', :force => true do |t|
|
14
|
+
t.string 'name', :null => false
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table 'spraypaint_taggings', :force => true do |t|
|
18
|
+
t.integer 'tag_id', :null => false
|
19
|
+
t.integer 'target_id', :null => false
|
20
|
+
t.string 'target_type', :null => false
|
21
|
+
t.integer 'owner_id'
|
22
|
+
t.string 'owner_type'
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table 'books', :force => true do |t|
|
26
|
+
t.string 'name', :null => false
|
27
|
+
t.string 'author'
|
28
|
+
t.string 'type'
|
29
|
+
end
|
30
|
+
|
31
|
+
create_table 'films', :force => true do |t|
|
32
|
+
t.string 'name', :null => false
|
33
|
+
t.string 'director'
|
34
|
+
t.string 'tag_string'
|
35
|
+
end
|
36
|
+
|
37
|
+
create_table 'accounts', :force => true do |t|
|
38
|
+
t.string 'login', :null => false
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe Spraypaint::DefaultSanitizer do
|
4
|
+
describe '(with no arguments passed to constructor)' do
|
5
|
+
before(:each) do
|
6
|
+
@it = Spraypaint::DefaultSanitizer.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should strip surrounding whitespace from tag" do
|
10
|
+
@it.sanitize_tag(" hello ").should == "hello"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should convert accented characters to their non-accented forms" do
|
14
|
+
@it.sanitize_tag("fåçêtîous").should == "facetious"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should remove all non-allowed characters" do
|
18
|
+
@it.sanitize_tag("a*b^c@d!e%f$g").should == "abcdefg"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should leave passed in tag unaltered" do
|
22
|
+
@it.sanitize_tag(value = " hello ")
|
23
|
+
value.should == " hello "
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
describe Spraypaint::Model::Tag do
|
4
|
+
before(:each) do
|
5
|
+
@class = Spraypaint::Model::Tag
|
6
|
+
@it = Spraypaint::Model::Tag.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "(in general)" do
|
10
|
+
it "should be valid with a name" do
|
11
|
+
@it.name = 'name'
|
12
|
+
@it.should be_valid
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should not be valid without a name" do
|
16
|
+
@it.name = nil
|
17
|
+
@it.should_not be_valid
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
describe Spraypaint::Model::Tagging do
|
4
|
+
before(:each) do
|
5
|
+
@class = Spraypaint::Model::Tagging
|
6
|
+
@it = Spraypaint::Model::Tagging.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "(in general)" do
|
10
|
+
before(:each) do
|
11
|
+
build_model :books do
|
12
|
+
string :name
|
13
|
+
end
|
14
|
+
|
15
|
+
@it.tag = Spraypaint::Model::Tag.create :name => 'a'
|
16
|
+
@it.target = Book.create :name => 'b'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should be valid with a tag and a target" do
|
20
|
+
@it.should be_valid
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should not be valid without a tag" do
|
24
|
+
@it.tag = nil
|
25
|
+
@it.should_not be_valid
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should not be valid without a target" do
|
29
|
+
@it.target = nil
|
30
|
+
@it.should_not be_valid
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should allow an association with a tag" do
|
34
|
+
@tag = Spraypaint::Model::Tag.create! :name => 'tag'
|
35
|
+
@it.tag = @tag
|
36
|
+
@it.save!
|
37
|
+
@reloaded = @class.find(@it.id)
|
38
|
+
@reloaded.tag.should == @tag
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should allow an association with a target" do
|
42
|
+
@target = Book.create! :name => 'tag'
|
43
|
+
@it.target = @target
|
44
|
+
@it.save!
|
45
|
+
@reloaded = @class.find(@it.id)
|
46
|
+
@reloaded.target.should == @target
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
ENV["RAILS_ENV"] = "test"
|
2
|
+
RAILS_ENV = "test"
|
3
|
+
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../config/environment.rb'))
|
5
|
+
require File.expand_path(File.dirname(__FILE__) + '/../db/schema')
|
6
|
+
|
7
|
+
module ActsAsFu
|
8
|
+
def build_model(name, options={}, &block)
|
9
|
+
klass_name = name.to_s.classify
|
10
|
+
super_class = options[:superclass] || ActiveRecord::Base
|
11
|
+
contained = options[:contained] || Object
|
12
|
+
|
13
|
+
contained.send(:remove_const, klass_name) rescue nil
|
14
|
+
klass = Class.new(super_class)
|
15
|
+
contained.const_set(klass_name, klass)
|
16
|
+
|
17
|
+
# table_name isn't available until after the class is created.
|
18
|
+
if super_class == ActiveRecord::Base
|
19
|
+
ActiveRecord::Base.connection.create_table(klass.table_name, :force => true) { }
|
20
|
+
end
|
21
|
+
|
22
|
+
model_eval(klass, &block)
|
23
|
+
klass
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def model_eval(klass, &block)
|
29
|
+
class << klass
|
30
|
+
def method_missing_with_columns(sym, *args, &block)
|
31
|
+
ActiveRecord::Base.connection.change_table(table_name) do |t|
|
32
|
+
t.send(sym, *args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
alias_method_chain :method_missing, :columns
|
37
|
+
end
|
38
|
+
|
39
|
+
klass.class_eval(&block) if block_given?
|
40
|
+
|
41
|
+
class << klass
|
42
|
+
alias_method :method_missing, :method_missing_without_columns
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
Spec::Runner.configure do |config|
|
49
|
+
include ActsAsFu
|
50
|
+
end
|
@@ -0,0 +1,284 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe Spraypaint::Behaviour do
|
4
|
+
before(:each) do
|
5
|
+
build_model :books do
|
6
|
+
string :name
|
7
|
+
string :type
|
8
|
+
|
9
|
+
tag_with_spraypaint
|
10
|
+
end
|
11
|
+
|
12
|
+
build_model :non_fiction_books, :superclass => Book
|
13
|
+
build_model :novel, :superclass => Book
|
14
|
+
|
15
|
+
build_model :films do
|
16
|
+
string :name
|
17
|
+
string :director
|
18
|
+
|
19
|
+
tag_with_spraypaint
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "(records initialized with no tags)" do
|
24
|
+
before(:each) do
|
25
|
+
@it = Film.new :name => 'No Country For Old Men'
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return an empty array for tags" do
|
29
|
+
@it.tags.should == []
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return false for tags_changed?" do
|
33
|
+
@it.tags_changed?.should be_false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "(records initialized with tags passed into constructor)" do
|
38
|
+
before(:each) do
|
39
|
+
@tags = ['some', 'tags', 'to', 'set']
|
40
|
+
@it = Film.new(:tags => @tags)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should return tags passed to constructor" do
|
44
|
+
@it.tags.should == @tags
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should return true for tags_changed?" do
|
48
|
+
@it.tags_changed?.should be_true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "(records with no tags)" do
|
53
|
+
before(:each) do
|
54
|
+
@it = Film.new :name => 'No Country For Old Men'
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should save successfully" do
|
58
|
+
@it.save
|
59
|
+
@it.id.should_not be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should return an empty array of tags after reloading" do
|
63
|
+
@it.save!
|
64
|
+
@it = @it.class.find(@it.id)
|
65
|
+
@it.tags.should == []
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "(records with tags)" do
|
70
|
+
before(:each) do
|
71
|
+
@tags = ['mccarthy', 'coen brothers']
|
72
|
+
@it = Film.new :name => 'No Country For Old Men', :tags => @tags
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should save successfully" do
|
76
|
+
@it.save
|
77
|
+
@it.id.should_not be_nil
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should return original tags after reloading" do
|
81
|
+
@it.save!
|
82
|
+
@it = @it.class.find(@it.id)
|
83
|
+
@it.tags.should == @tags
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should retain tag order after saving and reloading" do
|
87
|
+
Film.create! :name => 'Dummy Film', :tags => ['a', 'b', 'c', 'd']
|
88
|
+
@it.tags = ['z', 'a', 'd', 'b']
|
89
|
+
@it.save!
|
90
|
+
@it = @it.class.find(@it.id)
|
91
|
+
@it.tags.should == ['z', 'a', 'd', 'b']
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "(setting tags using #tags=)" do
|
96
|
+
before(:each) do
|
97
|
+
@tags = ['some', 'tags', 'to', 'set']
|
98
|
+
@it = Film.new
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should set tags to empty array if set to nil" do
|
102
|
+
@it.tags = @tags
|
103
|
+
@it.tags = nil
|
104
|
+
@it.tags.should == []
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should remove duplicate tags" do
|
108
|
+
@it.tags = ['duplicate', 'duplicate', 'original']
|
109
|
+
@it.tags.should == ['duplicate', 'original']
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should remove nil tags" do
|
113
|
+
@it.tags = [nil, 'by', 'mouth']
|
114
|
+
@it.tags.should == ['by', 'mouth']
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should remove zero-length tags" do
|
118
|
+
@it.tags = ['', 'clean', '']
|
119
|
+
@it.tags.should == ['clean']
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should sanitize tags" do
|
123
|
+
@it.class.tag_sanitizer.should_receive(:sanitize_tag).with('unsanitized').and_return('sanitized')
|
124
|
+
@it.tags = ['unsanitized']
|
125
|
+
@it.tags.should == ['sanitized']
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should remove duplicates that result from sanitization" do
|
129
|
+
@it.class.tag_sanitizer.stub!(:sanitize_tag).and_return('clean')
|
130
|
+
@it.tags = ['unclean', 'dirty', 'filthy']
|
131
|
+
@it.tags.should == ['clean']
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should remove zero-length tags that result from sanitization" do
|
135
|
+
@it.class.tag_sanitizer.stub!(:sanitize_tag).and_return(nil)
|
136
|
+
@it.tags = ['unclean', 'dirty', 'filthy']
|
137
|
+
@it.tags.should == []
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should remove nil tags that result from sanitization" do
|
141
|
+
@it.class.tag_sanitizer.stub!(:sanitize_tag).and_return(nil)
|
142
|
+
@it.tags = ['unclean', 'dirty', 'filthy']
|
143
|
+
@it.tags.should == []
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "(setting tags using #tag_string=)" do
|
148
|
+
before(:each) do
|
149
|
+
@it = Film.new :name => 'Film 5'
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should split string up by commas" do
|
153
|
+
@it.tag_string = "some, tags, go, here"
|
154
|
+
@it.tags.should == ['some', 'tags', 'go', 'here']
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should remove superfluous whitespace surrounding tags" do
|
158
|
+
@it.tag_string = "some , tags, go , here"
|
159
|
+
@it.tags.should == ['some', 'tags', 'go', 'here']
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should set tags to empty array if set to nil" do
|
163
|
+
@it.tag_string = nil
|
164
|
+
@it.tags.should == []
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "(reading tag_string on records with a tag_string db column)" do
|
169
|
+
before(:each) do
|
170
|
+
@it = Film.new :name => 'Film 5'
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should read tag_string direct from db" do
|
174
|
+
@it.tag_string = "some , tags, go , here"
|
175
|
+
@it.save!
|
176
|
+
@it = Film.find(@it.id)
|
177
|
+
@it.tag_string = "some , tags, go , here"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
describe "(reading tag_string on records where there is no tag_string db column)" do
|
183
|
+
before(:each) do
|
184
|
+
@it = Book.new :name => 'Book 5'
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should reconstitue tag string from tags" do
|
188
|
+
@it.tag_string = "some , tags, go , here"
|
189
|
+
@it.save!
|
190
|
+
@it = Book.find(@it.id)
|
191
|
+
@it.tag_string = "some, tags, go, here"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe "(finding tags associated with a class of records)" do
|
196
|
+
before(:each) do
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should return set of tags associated with individual records" do
|
200
|
+
Film.create! :name => 'No Country For Old Men', :tags => ['coen brothers', 'josh brolin']
|
201
|
+
Film.create! :name => 'W', :tags => ['oliver stone', 'josh brolin']
|
202
|
+
Film.tags.sort.should == ['coen brothers', 'josh brolin', 'oliver stone']
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should limit found tags by class of record" do
|
206
|
+
Film.create! :name => 'No Country For Old Men', :tags => ['coen brothers', 'mccarthy']
|
207
|
+
Book.create! :name => 'No Country For Old Men', :tags => ['mccarthy', 'novel']
|
208
|
+
Film.tags.should == ['coen brothers', 'mccarthy']
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should limit tags found using STI subclasses" do
|
212
|
+
NonFictionBook.create! :name => 'The Wealth Of Nations', :tags => ['economics', 'adam smith']
|
213
|
+
Novel.create! :name => 'The Road', :tags => ['post apocalyptic', 'mccarthy']
|
214
|
+
Book.tags.should == ['adam smith', 'economics', 'mccarthy', 'post apocalyptic']
|
215
|
+
Novel.tags.should == ['mccarthy', 'post apocalyptic']
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should respect scoped queries" do
|
219
|
+
Film.named_scope 'directed_by', lambda {|director|
|
220
|
+
{:conditions => {:director => director}}
|
221
|
+
}
|
222
|
+
|
223
|
+
Film.create! :name => 'Goodfellas', :director => 'Scorcese', :tags => ['mafia', 'gangster']
|
224
|
+
Film.create! :name => 'The Great Dictator', :director => 'Chaplin', :tags => ['hitler', 'satire']
|
225
|
+
Film.create! :name => 'Raging Bull', :director => 'Scorcese', :tags => ['boxing']
|
226
|
+
|
227
|
+
Film.directed_by('Scorcese').tags.sort.should == ['boxing', 'gangster', 'mafia']
|
228
|
+
Film.directed_by('Chaplin').tags.sort.should == ['hitler', 'satire']
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should return tags in decending order of frequency" do
|
232
|
+
Film.create! :name => 'Superman', :tags => ['first']
|
233
|
+
Film.create! :name => 'Superman 2', :tags => ['second']
|
234
|
+
Film.create! :name => 'Police Academy 2', :tags => ['second']
|
235
|
+
Film.create! :name => 'Rocky 3', :tags => ['third']
|
236
|
+
Film.create! :name => 'Naked Gun 33 1/3', :tags => ['third']
|
237
|
+
Film.create! :name => 'Spiderman 3', :tags => ['third']
|
238
|
+
|
239
|
+
Film.tags.should == ['third', 'second', 'first']
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should return tags alphabetically if frequency is the same" do
|
243
|
+
Film.create! :name => 'No Country For Old Men', :tags => ['coen brothers']
|
244
|
+
Film.create! :name => 'W', :tags => ['oliver stone']
|
245
|
+
Film.create! :name => 'The Great Dictator', :tags => ['charlie chaplin']
|
246
|
+
Film.tags.should == ['charlie chaplin', 'coen brothers', 'oliver stone']
|
247
|
+
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should respect limit and offset on the returned tags" do
|
251
|
+
Film.create! :name => 'No Country For Old Men', :tags => ['coen brothers', 'josh brolin']
|
252
|
+
Film.create! :name => 'W', :tags => ['oliver stone', 'josh brolin']
|
253
|
+
Film.tags(:limit => 1).should == ['josh brolin']
|
254
|
+
Film.tags(:limit => 1, :offset => 1).should == ['coen brothers']
|
255
|
+
end
|
256
|
+
|
257
|
+
it "should include the frequency as an attribute on returned tags" do
|
258
|
+
Film.create! :name => 'No Country For Old Men', :tags => ['coen brothers', 'josh brolin']
|
259
|
+
Film.create! :name => 'W', :tags => ['oliver stone', 'josh brolin']
|
260
|
+
Film.tags.first.frequency.should == 2
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe "(finding records via their tags)" do
|
265
|
+
it "should return records matching given tag" do
|
266
|
+
no_country = Film.create! :name => 'No Country For Old Men', :tags => ['coen brothers', 'josh brolin']
|
267
|
+
w = Film.create! :name => 'W', :tags => ['oliver stone', 'josh brolin']
|
268
|
+
Film.tagged_with('oliver stone').should == [w]
|
269
|
+
end
|
270
|
+
|
271
|
+
it "should return records matching all given tags" do
|
272
|
+
no_country = Film.create! :name => 'No Country For Old Men', :tags => ['coen brothers', 'josh brolin']
|
273
|
+
w = Film.create! :name => 'W', :tags => ['oliver stone', 'josh brolin']
|
274
|
+
Film.tagged_with('oliver stone', 'josh brolin').should == [w]
|
275
|
+
Film.tagged_with('oliver stone', 'coen brothers').should == []
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should sanitize tags before searching" do
|
279
|
+
Film.tag_sanitizer.stub!(:sanitize_tag).and_return('clean')
|
280
|
+
Film.create! :name => 'No Country For Old Men', :tags => ['unclean']
|
281
|
+
Film.tagged_with('dirty').size.should == 1
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|