labelized 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'activesupport', '3.0.7'
4
+
5
+ group :test do
6
+ gem 'rails'
7
+ end
8
+
9
+ group :development do
10
+ gem "bundler"
11
+ gem "jeweler"
12
+ gem 'guard'
13
+ gem 'growl'
14
+ gem "rcov"
15
+ gem "rspec"
16
+ gem 'sqlite3'
17
+ end
@@ -0,0 +1,105 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.7)
6
+ actionpack (= 3.0.7)
7
+ mail (~> 2.2.15)
8
+ actionpack (3.0.7)
9
+ activemodel (= 3.0.7)
10
+ activesupport (= 3.0.7)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.5.0)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.14)
16
+ rack-test (~> 0.5.7)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.7)
19
+ activesupport (= 3.0.7)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.5.0)
22
+ activerecord (3.0.7)
23
+ activemodel (= 3.0.7)
24
+ activesupport (= 3.0.7)
25
+ arel (~> 2.0.2)
26
+ tzinfo (~> 0.3.23)
27
+ activeresource (3.0.7)
28
+ activemodel (= 3.0.7)
29
+ activesupport (= 3.0.7)
30
+ activesupport (3.0.7)
31
+ arel (2.0.9)
32
+ builder (2.1.2)
33
+ configuration (1.2.0)
34
+ diff-lcs (1.1.2)
35
+ erubis (2.6.6)
36
+ abstract (>= 1.0.0)
37
+ git (1.2.5)
38
+ growl (1.0.3)
39
+ guard (0.3.0)
40
+ open_gem (~> 1.4.2)
41
+ thor (~> 0.14.6)
42
+ i18n (0.5.0)
43
+ jeweler (1.6.2)
44
+ bundler (~> 1.0)
45
+ git (>= 1.2.5)
46
+ rake
47
+ launchy (0.3.7)
48
+ configuration (>= 0.0.5)
49
+ rake (>= 0.8.1)
50
+ mail (2.2.15)
51
+ activesupport (>= 2.3.6)
52
+ i18n (>= 0.4.0)
53
+ mime-types (~> 1.16)
54
+ treetop (~> 1.4.8)
55
+ mime-types (1.16)
56
+ open_gem (1.4.2)
57
+ launchy (~> 0.3.5)
58
+ polyglot (0.3.1)
59
+ rack (1.2.2)
60
+ rack-mount (0.6.14)
61
+ rack (>= 1.0.0)
62
+ rack-test (0.5.7)
63
+ rack (>= 1.0)
64
+ rails (3.0.7)
65
+ actionmailer (= 3.0.7)
66
+ actionpack (= 3.0.7)
67
+ activerecord (= 3.0.7)
68
+ activeresource (= 3.0.7)
69
+ activesupport (= 3.0.7)
70
+ bundler (~> 1.0)
71
+ railties (= 3.0.7)
72
+ railties (3.0.7)
73
+ actionpack (= 3.0.7)
74
+ activesupport (= 3.0.7)
75
+ rake (>= 0.8.7)
76
+ thor (~> 0.14.4)
77
+ rake (0.9.2)
78
+ rcov (0.9.9)
79
+ rspec (2.5.0)
80
+ rspec-core (~> 2.5.0)
81
+ rspec-expectations (~> 2.5.0)
82
+ rspec-mocks (~> 2.5.0)
83
+ rspec-core (2.5.1)
84
+ rspec-expectations (2.5.0)
85
+ diff-lcs (~> 1.1.2)
86
+ rspec-mocks (2.5.0)
87
+ sqlite3 (1.3.3)
88
+ thor (0.14.6)
89
+ treetop (1.4.9)
90
+ polyglot (>= 0.3.1)
91
+ tzinfo (0.3.25)
92
+
93
+ PLATFORMS
94
+ ruby
95
+
96
+ DEPENDENCIES
97
+ activesupport (= 3.0.7)
98
+ bundler
99
+ growl
100
+ guard
101
+ jeweler
102
+ rails
103
+ rcov
104
+ rspec
105
+ sqlite3
@@ -0,0 +1,9 @@
1
+ guard 'rspec', :cli => '--color --format nested --fail-fast', :all_on_start => false, :all_after_pass => false, :notification => true, :keep_failed => false do
2
+ watch(/^spec\/(.*)_spec.rb/)
3
+ watch(/^lib\/(.*)\.rb/) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch(/^spec\/spec_helper.rb/) { "spec" }
5
+ #watch(/^app\/(.*)\.rb/) { |m| "spec/#{m[1]}_spec.rb" }
6
+ # watch(/^config\/routes.rb/) { "spec/routing" }
7
+ # watch(/^app\/controllers\/application_controller.rb/) { "spec/controllers" }
8
+ # watch(/^spec\/factories.rb/) { "spec/models" }
9
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Peter T. Brown
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ = labelized
2
+ A simple Ruby library for labeling things. Similar to and inspired by acts_as_taggable_on.
3
+
4
+ == Usage
5
+ Passing a term in the singular allows only a single label. Plural terms allow multiple labels.
6
+ The optional scope allows for multiple instances of the same tag, isolated by the scope; i.e. different capitalizations
7
+
8
+ class Label
9
+ labelized :scope => :community_id
10
+ end
11
+
12
+ class Thing
13
+ labelized [:kind, :keywords, :tracks]
14
+ end
15
+
16
+ thing = Thing.new
17
+
18
+ thing.kind
19
+ thing.kind = ['this', 'that'] # throws Exeption
20
+ thing.kind = 'this' # OK!
21
+
22
+ thing.kind # 'this'
23
+ thing.kind_list # 'this'
24
+
25
+ thing.keywords
26
+ thing.keywords = ['this', 'that']
27
+ thing.keywords = 'this, that'
28
+ thing.keywords # 'this, that'
29
+ thing.keyword_list # ['this', 'that']
30
+
31
+ thing.tracks
32
+
33
+ == Contributing to labelized
34
+
35
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
36
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
37
+ * Fork the project
38
+ * Start a feature/bugfix branch
39
+ * Commit and push until you are happy with your contribution
40
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
41
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
42
+
43
+ == Copyright
44
+
45
+ Copyright (c) 2011 Peter T. Brown. See LICENSE.txt for further details.
46
+
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "labelized"
18
+ gem.homepage = "http://github.com/flippyhead/labelized"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{A better tag library}
21
+ gem.description = %Q{A better tag library}
22
+ gem.email = "peter@pathable.com"
23
+ gem.authors = ["Peter T. Brown"]
24
+ end
25
+ Jeweler::RubygemsDotOrgTasks.new
26
+
27
+ require 'rspec/core'
28
+ require 'rspec/core/rake_task'
29
+ RSpec::Core::RakeTask.new(:spec) do |spec|
30
+ spec.pattern = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
34
+ spec.pattern = 'spec/**/*_spec.rb'
35
+ spec.rcov = true
36
+ end
37
+
38
+ task :default => :spec
39
+
40
+ # require 'rdoc/task'
41
+ # Rake::RDocTask.new do |rdoc|
42
+ # version = File.exist?('VERSION') ? File.read('VERSION') : ""
43
+ #
44
+ # rdoc.rdoc_dir = 'rdoc'
45
+ # rdoc.title = "labelized #{version}"
46
+ # rdoc.rdoc_files.include('README*')
47
+ # rdoc.rdoc_files.include('lib/**/*.rb')
48
+ # end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.6.0
@@ -0,0 +1,81 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{labelized}
8
+ s.version = "0.6.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Peter T. Brown"]
12
+ s.date = %q{2011-08-02}
13
+ s.description = %q{A better tag library}
14
+ s.email = %q{peter@pathable.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "Guardfile",
24
+ "LICENSE.txt",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "labelized.gemspec",
29
+ "lib/labelized.rb",
30
+ "lib/labelized/label_concern.rb",
31
+ "lib/labelized/label_list.rb",
32
+ "lib/labelized/label_set_concern.rb",
33
+ "lib/labelized/labeling_concern.rb",
34
+ "lib/labelized/labelized_concern.rb",
35
+ "lib/labelized/support.rb",
36
+ "spec/db/config.yml",
37
+ "spec/db/create_models.rb",
38
+ "spec/labelized/label_concern_spec.rb",
39
+ "spec/labelized/labelized_concern_spec.rb",
40
+ "spec/spec_helper.rb"
41
+ ]
42
+ s.homepage = %q{http://github.com/flippyhead/labelized}
43
+ s.licenses = ["MIT"]
44
+ s.require_paths = ["lib"]
45
+ s.rubygems_version = %q{1.6.2}
46
+ s.summary = %q{A better tag library}
47
+
48
+ if s.respond_to? :specification_version then
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
52
+ s.add_runtime_dependency(%q<activesupport>, ["= 3.0.7"])
53
+ s.add_development_dependency(%q<bundler>, [">= 0"])
54
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
55
+ s.add_development_dependency(%q<guard>, [">= 0"])
56
+ s.add_development_dependency(%q<growl>, [">= 0"])
57
+ s.add_development_dependency(%q<rcov>, [">= 0"])
58
+ s.add_development_dependency(%q<rspec>, [">= 0"])
59
+ s.add_development_dependency(%q<sqlite3>, [">= 0"])
60
+ else
61
+ s.add_dependency(%q<activesupport>, ["= 3.0.7"])
62
+ s.add_dependency(%q<bundler>, [">= 0"])
63
+ s.add_dependency(%q<jeweler>, [">= 0"])
64
+ s.add_dependency(%q<guard>, [">= 0"])
65
+ s.add_dependency(%q<growl>, [">= 0"])
66
+ s.add_dependency(%q<rcov>, [">= 0"])
67
+ s.add_dependency(%q<rspec>, [">= 0"])
68
+ s.add_dependency(%q<sqlite3>, [">= 0"])
69
+ end
70
+ else
71
+ s.add_dependency(%q<activesupport>, ["= 3.0.7"])
72
+ s.add_dependency(%q<bundler>, [">= 0"])
73
+ s.add_dependency(%q<jeweler>, [">= 0"])
74
+ s.add_dependency(%q<guard>, [">= 0"])
75
+ s.add_dependency(%q<growl>, [">= 0"])
76
+ s.add_dependency(%q<rcov>, [">= 0"])
77
+ s.add_dependency(%q<rspec>, [">= 0"])
78
+ s.add_dependency(%q<sqlite3>, [">= 0"])
79
+ end
80
+ end
81
+
@@ -0,0 +1,18 @@
1
+ require 'active_support'
2
+ require 'active_record'
3
+
4
+ require 'labelized/support'
5
+ require 'labelized/label_concern'
6
+ require 'labelized/labeling_concern'
7
+ require 'labelized/label_set_concern'
8
+ require 'labelized/labelized_concern'
9
+
10
+
11
+ # if defined?(ActiveRecord::Base)
12
+ # ActiveRecord::Base.extend ActsAsTaggableOn::Taggable
13
+ # ActiveRecord::Base.send :include, ActsAsTaggableOn::Tagger
14
+ # end
15
+ #
16
+ # if defined?(ActionView::Base)
17
+ # ActionView::Base.send :include, ActsAsTaggableOn::TagsHelper
18
+ # end
@@ -0,0 +1,42 @@
1
+ module Labelized
2
+ module LabelConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_many :labelings, :dependent => :destroy
7
+ belongs_to :label_set
8
+
9
+ extend Support
10
+ end
11
+
12
+ module ClassMethods
13
+ def labelized(params)
14
+ setup_labelized params
15
+
16
+ def find_or_build_by_list(labels, labeled, label_set_name = nil)
17
+ label_set_class = (labelized_options[:label_set_class_name] || 'LabelSet').constantize
18
+ existing_labels = self.label_scope(labeled).where(:name => labels)
19
+
20
+ unless label_set_name.blank?
21
+ label_set_name.strip! # ooh lah lah
22
+ label_set = label_set_class.label_scope(labeled).find_or_initialize_by_name(label_set_name)
23
+ end
24
+
25
+ new_label_names = labels.reject do |name|
26
+ existing_labels.any? do |label|
27
+ name.downcase == label.name.downcase
28
+ end
29
+ end
30
+
31
+ new_labels = new_label_names.map do |name|
32
+ name.strip!
33
+ self.label_scope(labeled).build(:name => name, :label_set => label_set)
34
+ end
35
+
36
+ existing_labels + new_labels
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,96 @@
1
+ module Labelized
2
+ class LabelList < Array
3
+ cattr_accessor :delimiter
4
+ self.delimiter = ','
5
+
6
+ attr_accessor :owner
7
+
8
+ def initialize(*args)
9
+ add(*args)
10
+ end
11
+
12
+ ##
13
+ # Returns a new TagList using the given tag string.
14
+ #
15
+ # Example:
16
+ # tag_list = TagList.from("One , Two, Three")
17
+ # tag_list # ["One", "Two", "Three"]
18
+ def self.from(string)
19
+ glue = delimiter.ends_with?(" ") ? delimiter : "#{delimiter} "
20
+ string = string.join(glue) if string.respond_to?(:join)
21
+
22
+ new.tap do |tag_list|
23
+ string = string.to_s.dup
24
+
25
+ # Parse the quoted tags
26
+ string.gsub!(/(\A|#{delimiter})\s*"(.*?)"\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
27
+ string.gsub!(/(\A|#{delimiter})\s*'(.*?)'\s*(#{delimiter}\s*|\z)/) { tag_list << $2; $3 }
28
+
29
+ tag_list.add(string.split(delimiter))
30
+ end
31
+ end
32
+
33
+ ##
34
+ # Add tags to the tag_list. Duplicate or blank tags will be ignored.
35
+ # Use the <tt>:parse</tt> option to add an unparsed tag string.
36
+ #
37
+ # Example:
38
+ # tag_list.add("Fun", "Happy")
39
+ # tag_list.add("Fun, Happy", :parse => true)
40
+ def add(*names)
41
+ extract_and_apply_options!(names)
42
+ concat(names)
43
+ clean!
44
+ self
45
+ end
46
+
47
+ ##
48
+ # Remove specific tags from the tag_list.
49
+ # Use the <tt>:parse</tt> option to add an unparsed tag string.
50
+ #
51
+ # Example:
52
+ # tag_list.remove("Sad", "Lonely")
53
+ # tag_list.remove("Sad, Lonely", :parse => true)
54
+ def remove(*names)
55
+ extract_and_apply_options!(names)
56
+ delete_if { |name| names.include?(name) }
57
+ self
58
+ end
59
+
60
+ ##
61
+ # Transform the tag_list into a tag string suitable for edting in a form.
62
+ # The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
63
+ #
64
+ # Example:
65
+ # tag_list = TagList.new("Round", "Square,Cube")
66
+ # tag_list.to_s # 'Round, "Square,Cube"'
67
+ def to_s
68
+ tags = frozen? ? self.dup : self
69
+ tags.send(:clean!)
70
+
71
+ tags.map do |name|
72
+ name.include?(delimiter) ? "\"#{name}\"" : name
73
+ end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
74
+ end
75
+
76
+ private
77
+
78
+ # Remove whitespace, duplicates, and blanks.
79
+ def clean!
80
+ reject!(&:blank?)
81
+ map!(&:strip)
82
+ uniq!
83
+ end
84
+
85
+ def extract_and_apply_options!(args)
86
+ options = args.last.is_a?(Hash) ? args.pop : {}
87
+ options.assert_valid_keys :parse
88
+
89
+ if options[:parse]
90
+ args.map! { |a| self.class.from(a) }
91
+ end
92
+
93
+ args.flatten!
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,19 @@
1
+ module Labelized
2
+ module LabelSetConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_many :labelings, :dependent => :destroy
7
+ has_many :labels #, :through => :labelings
8
+ validates_presence_of :name
9
+
10
+ extend Support
11
+ end
12
+
13
+ module ClassMethods
14
+ def labelized(params)
15
+ setup_labelized params
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ module Labelized
2
+ module LabelingConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ belongs_to :label
7
+ belongs_to :labeled, :polymorphic => true
8
+ validates_presence_of :label_id, :labeled_id, :labeled_type
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,68 @@
1
+ module Labelized
2
+ module LabelizedConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ extend Support
7
+ end
8
+
9
+ module InstanceMethods
10
+ private
11
+
12
+ def cache_label_set(label_set_name, labels)
13
+ instance_variable_set label_cache_name(label_set_name), labels
14
+ end
15
+
16
+ def cache_label_get(label_set_name)
17
+ instance_variable_get label_cache_name(label_set_name)
18
+ end
19
+
20
+ def label_cache_name(label_set_name)
21
+ "@_labelized_#{label_set_name}".to_sym
22
+ end
23
+
24
+ end
25
+
26
+ module ClassMethods
27
+ def labelized(*params)
28
+ setup_labelized params
29
+
30
+ # thing.kind
31
+ # thing.kind = ['this', 'that'] # throws Exeption
32
+ # thing.kind = 'this' # OK!
33
+ # thing.label 'this', :kind
34
+ # labelized [:kind, :keywords, :tracks], :scope => [:community_id], :label_class => Label, :labeling_class => Labeling
35
+ #
36
+ class_eval do
37
+ has_many :labelings, :as => :labeled, :dependent => :destroy
38
+ has_many :labels, :through => :labelings
39
+
40
+ define_method('label') do |labels, label_set_name = :label|
41
+ label_class = (labelized_options[:label_class_name] || 'Label').constantize
42
+
43
+ labels = [labels].flatten
44
+ return [] if labels.empty?
45
+
46
+ self.labels = cache_label_set(label_set_name, label_class.label_scope(self).find_or_build_by_list(labels, self, label_set_name.to_s))
47
+ end
48
+
49
+ define_method('label_for') do |label_set_name|
50
+ label_set_class = (labelized_options[:label_set_class_name] || 'LabelSet').constantize
51
+ label_set = label_set_class.label_scope(self).find_or_initialize_by_name(label_set_name)
52
+ cache_label_get(label_set_name) || self.labels.where(:label_set_id => label_set.id)
53
+ end
54
+
55
+ labelized_label_set_names.map(&:to_s).each do |label_set_name|
56
+ define_method "#{label_set_name}=" do |labels|
57
+ label labels, label_set_name
58
+ end
59
+
60
+ define_method "#{label_set_name}" do
61
+ label_for label_set_name
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,42 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+
3
+ module Labelized
4
+ module Support
5
+ def self.singular?(str)
6
+ str.to_s.pluralize != str.to_s && str.to_s.singularize == str.to_s
7
+ end
8
+
9
+ def self.extract_params(*params)
10
+ labels = params.to_a.flatten.compact
11
+ options = labels[-1].kind_of?(Hash) ? labels.slice!(-1) : {}
12
+ labels = labels.map(&:to_sym)
13
+ [labels, options]
14
+ end
15
+
16
+ def setup_labelized(params)
17
+ label_sets, options = Support.extract_params(params)
18
+ scopes = [options[:scope]].flatten
19
+
20
+ self.setup_labelized_options(options)
21
+ self.setup_labelized_label_set_names(label_sets)
22
+ self.setup_label_scope(scopes)
23
+ end
24
+
25
+ def setup_label_scope(scopes)
26
+ self.class_eval do
27
+ scope :label_scope, lambda {|labeled| where(*scopes.collect{|s| {s => labeled.send(s)}}.flatten)}
28
+ end
29
+ end
30
+
31
+ def setup_labelized_options(options)
32
+ write_inheritable_attribute(:labelized_options, options)
33
+ class_inheritable_reader(:labelized_options)
34
+ end
35
+
36
+ def setup_labelized_label_set_names(names)
37
+ write_inheritable_attribute(:labelized_label_set_names, names)
38
+ class_inheritable_reader(:labelized_label_set_names)
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,2 @@
1
+ adapter: sqlite3
2
+ database: ":memory:"
@@ -0,0 +1,9 @@
1
+ require 'active_support'
2
+
3
+ class CreateModels < ActiveRecord::Migration
4
+ def self.up
5
+ create_table :things do |t|
6
+ t.string :name
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,45 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ class Label < ActiveRecord::Base
4
+ include Labelized::LabelConcern
5
+
6
+ belongs_to :root
7
+ labelized :scope => :root_id
8
+ end
9
+
10
+ class Root < ActiveRecord::Base
11
+ has_many :things
12
+ end
13
+
14
+ class Thing < ActiveRecord::Base
15
+ include Labelized::LabelizedConcern
16
+ belongs_to :root
17
+ labelized [:tags]
18
+ end
19
+
20
+
21
+ describe Labelized::LabelConcern do
22
+ let(:root){Root.create(:name => "Root One")}
23
+ let(:thing){Thing.new(:root => root)}
24
+
25
+ subject{Label.new(:root => root)}
26
+
27
+ it{should_not be_nil}
28
+
29
+ it 'should build from scope' do
30
+ Label.label_scope(thing).build.root.should == root
31
+ end
32
+
33
+ it 'should create from scope' do
34
+ Label.label_scope(thing).create.root.should == root
35
+ end
36
+
37
+ context 'when created' do
38
+ before do
39
+ subject.save!
40
+ end
41
+
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,103 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ class Labeling < ActiveRecord::Base
4
+ include Labelized::LabelingConcern
5
+ end
6
+
7
+ class LabelSet < ActiveRecord::Base
8
+ include Labelized::LabelSetConcern
9
+
10
+ belongs_to :root
11
+ labelized :scope => :root_id
12
+ end
13
+
14
+ class Label < ActiveRecord::Base
15
+ include Labelized::LabelConcern
16
+
17
+ belongs_to :root
18
+ labelized :scope => :root_id
19
+ validates_presence_of :root_id
20
+ end
21
+
22
+ class Root < ActiveRecord::Base
23
+ has_many :things
24
+ end
25
+
26
+ class Thing < ActiveRecord::Base
27
+ include Labelized::LabelizedConcern
28
+ belongs_to :root
29
+ labelized :tags
30
+ end
31
+
32
+ # thing.tags # [<label id=1>]
33
+ # label.label_set
34
+ # thing.labels # [<label id=1>]
35
+ # thing.labelings
36
+
37
+ describe Labelized::LabelizedConcern do
38
+ let!(:root){Root.create(:name => "Root One")}
39
+ let!(:label_set){LabelSet.create(:name => 'tags', :root_id => root.id)}
40
+ let(:thing){Thing.new(:root_id => root.id, :tags => ['tag one'])}
41
+ subject{thing}
42
+
43
+ it{should_not be_nil}
44
+ it{should respond_to(:tags)}
45
+ it{should respond_to(:tags=)}
46
+ it{should have(1).tag}
47
+
48
+ it{should respond_to(:labels)}
49
+ it{should respond_to(:labelings)}
50
+
51
+ its(:tags){should be_present}
52
+
53
+ context 'the cached tags' do
54
+ subject{thing.tags.first}
55
+
56
+ it{should be_instance_of Label}
57
+ its(:name){should == 'tag one'}
58
+ end
59
+
60
+ context 'setting labels dynamically' do
61
+ before do
62
+ thing.label('tag one', :tags)
63
+ end
64
+
65
+ its(:tags){should be_present}
66
+ specify{subject.label_for(:tags).should be_present}
67
+ end
68
+
69
+ context 'the result of setting one label' do
70
+ subject{thing.tags= ['tag one']}
71
+
72
+ it{should be_instance_of Array}
73
+ it{should include('tag one')}
74
+ its(:size){should == 1}
75
+ end
76
+
77
+ context 'after saved' do
78
+ before do
79
+ thing.save
80
+ end
81
+
82
+ its(:tags){should respond_to(:each)} # collection-like thing
83
+
84
+ context 'each label' do
85
+ subject{thing.labels.first}
86
+
87
+ it{should_not be_new_record}
88
+ its(:name){should == 'tag one'}
89
+ its(:root){should == root}
90
+ its(:label_set){should == label_set}
91
+ end
92
+
93
+ context 'exiting labels' do
94
+ it 'should re-use existing labels' do
95
+ expect {
96
+ thing.tags = ['tag one', 'tag two']
97
+ thing.save
98
+ }.to change{Label.count}.by(1)
99
+ end
100
+ end
101
+ end
102
+
103
+ end
@@ -0,0 +1,67 @@
1
+ # require 'rubygems'
2
+ # require 'bundler'
3
+ #
4
+ # begin
5
+ # Bundler.setup(:default, :development)
6
+ # rescue Bundler::BundlerError => e
7
+ # $stderr.puts e.message
8
+ # $stderr.puts "Run `bundle install` to install missing gems"
9
+ # exit e.status_code
10
+ # end
11
+ #
12
+ # require 'spec'
13
+ #
14
+ # $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
15
+ # $LOAD_PATH.unshift(File.dirname(__FILE__))
16
+ #
17
+ # require 'labelized'
18
+
19
+
20
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
21
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
22
+ require 'rspec'
23
+ require 'labelized'
24
+
25
+ # Requires supporting files with custom matchers and macros, etc,
26
+ # in ./support/ and its subdirectories.
27
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
28
+
29
+ RSpec.configure do |config|
30
+ end
31
+
32
+ require 'active_record'
33
+
34
+ dbconf = YAML::load(File.open(File.expand_path(File.dirname(__FILE__) + '/db/config.yml')))
35
+ ActiveRecord::Base.establish_connection(dbconf)
36
+
37
+ # do a quick pseudo migration. This should only get executed on the first run
38
+ ActiveRecord::Base.connection.create_table(:things) do |t|
39
+ t.column :name, :string
40
+ t.column :root_id, :integer
41
+ end
42
+
43
+ ActiveRecord::Base.connection.create_table(:roots) do |t|
44
+ t.column :name, :string
45
+ end
46
+
47
+ ActiveRecord::Base.connection.create_table(:labels) do |t|
48
+ t.column :name, :string
49
+ t.column :root_id, :integer
50
+ t.column :label_set_id, :integer
51
+ end
52
+
53
+ ActiveRecord::Base.connection.create_table(:labelings) do |t|
54
+ t.column :label_id, :integer
55
+ t.column :labeled_id, :integer
56
+ t.column :labeled_type, :string
57
+ end
58
+
59
+ ActiveRecord::Base.connection.create_table(:label_sets) do |t|
60
+ t.column :name, :string
61
+ t.column :root_id, :integer
62
+ end
63
+
64
+ # ActiveRecord::Base.logger = Logger.new(File.open('log/database.log', 'a'))
65
+ # MigrationsXX.migrate(:up)
66
+
67
+
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: labelized
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.6.0
6
+ platform: ruby
7
+ authors:
8
+ - Peter T. Brown
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-08-02 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - "="
22
+ - !ruby/object:Gem::Version
23
+ version: 3.0.7
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: jeweler
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: guard
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: growl
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: rcov
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: rspec
84
+ requirement: &id007 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: "0"
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: sqlite3
95
+ requirement: &id008 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: "0"
101
+ type: :development
102
+ prerelease: false
103
+ version_requirements: *id008
104
+ description: A better tag library
105
+ email: peter@pathable.com
106
+ executables: []
107
+
108
+ extensions: []
109
+
110
+ extra_rdoc_files:
111
+ - LICENSE.txt
112
+ - README.rdoc
113
+ files:
114
+ - .document
115
+ - Gemfile
116
+ - Gemfile.lock
117
+ - Guardfile
118
+ - LICENSE.txt
119
+ - README.rdoc
120
+ - Rakefile
121
+ - VERSION
122
+ - labelized.gemspec
123
+ - lib/labelized.rb
124
+ - lib/labelized/label_concern.rb
125
+ - lib/labelized/label_list.rb
126
+ - lib/labelized/label_set_concern.rb
127
+ - lib/labelized/labeling_concern.rb
128
+ - lib/labelized/labelized_concern.rb
129
+ - lib/labelized/support.rb
130
+ - spec/db/config.yml
131
+ - spec/db/create_models.rb
132
+ - spec/labelized/label_concern_spec.rb
133
+ - spec/labelized/labelized_concern_spec.rb
134
+ - spec/spec_helper.rb
135
+ has_rdoc: true
136
+ homepage: http://github.com/flippyhead/labelized
137
+ licenses:
138
+ - MIT
139
+ post_install_message:
140
+ rdoc_options: []
141
+
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ hash: 656861408803560129
150
+ segments:
151
+ - 0
152
+ version: "0"
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ none: false
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: "0"
159
+ requirements: []
160
+
161
+ rubyforge_project:
162
+ rubygems_version: 1.6.2
163
+ signing_key:
164
+ specification_version: 3
165
+ summary: A better tag library
166
+ test_files: []
167
+