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.
@@ -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,5 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: ":memory:"
4
+ pool: 5
5
+ timeout: 5000
@@ -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
@@ -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