tagutils 0.1.1 → 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.
data/Manifest.txt CHANGED
@@ -4,9 +4,12 @@ README.md
4
4
  Rakefile
5
5
  lib/tagutils.rb
6
6
  lib/tagutils/active_record.rb
7
+ lib/tagutils/models/categorization.rb
8
+ lib/tagutils/models/category.rb
7
9
  lib/tagutils/models/tag.rb
8
10
  lib/tagutils/models/tagging.rb
9
- lib/tagutils/schema.rb
11
+ lib/tagutils/schema_categories.rb
12
+ lib/tagutils/schema_tags.rb
10
13
  lib/tagutils/version.rb
11
14
  test/helper.rb
12
15
  test/test_models.rb
data/README.md CHANGED
@@ -10,21 +10,63 @@ tagutils gems - tag utilities (tag, taggings, tag list, etc.)
10
10
 
11
11
  ## Usage
12
12
 
13
+ ### Schema / Tables
14
+
15
+ Use `TagDb.create` to build the `tags` and `taggings` tables
16
+ and `CategoryDb.create` to build the `categories` and `categorizations` tables.
17
+ Example:
18
+
13
19
  ~~~
14
- class Country < ActiveRecord::Base
20
+ # ...
21
+ TagDb.create
22
+ # ...
23
+ CategoryDb.create
24
+ # ...
25
+ ~~~
26
+
27
+
28
+ ### Models
29
+
30
+ Add the associations yourself with the standard `has_many` class macro:
31
+
32
+ ~~~
33
+ class Movie < ActiveRecord::Base
34
+ # ...
15
35
  has_many :taggings, class_name: 'TagDb::Model::Tagging', :as => :taggable
16
36
  has_many :tags, class_name: 'TagDb::Model::Tag', :through => :taggings
37
+ # ...
38
+ has_many :categorizations, class_name: 'CategoryDb::Model::Categorizations', :as => :categorizable
39
+ has_many :categories, class_name: 'CategoryDb::Model::Category', :through => :categorizations
40
+ # ...
17
41
  end
18
42
  ~~~
19
43
 
20
- or
44
+ or use the built-in class macro shortcuts:
21
45
 
22
46
  ~~~
23
- class Country < ActiveRecord::Base
47
+ class Movie < ActiveRecord::Base
48
+ # ...
24
49
  has_many_tags
50
+ # ...
51
+ has_many_categories
52
+ # ...
25
53
  end
26
54
  ~~~
27
55
 
56
+ The `has_many_tags` also adds the following methods:
57
+
58
+ ~~~
59
+ Movie.with_tag( 'doc' )
60
+ # e.g. scope :with_tag, ->(tag_key){ joins(:tags).where('tags.key' => tag_key) }
61
+ ~~~
62
+
63
+ The `has_many_categories` also adds the following methods:
64
+
65
+ ~~~
66
+ Movie.with_category( 'doc' )
67
+ # e.g. scope :with_category, ->(category_key){ joins(:categories).where('categories.key' => category_key) }
68
+ ~~~
69
+
28
70
 
29
71
  ## Real World Usage
30
72
 
@@ -186,6 +228,21 @@ create_table :categoryz3_child_items do |t|
186
228
  end
187
229
  ~~~
188
230
 
231
+ - [categorizable](https://github.com/boof/categorizable)
232
+
233
+ ~~~
234
+ create_table :categories do |t|
235
+ t.string :name, null: false
236
+ t.timestamps
237
+ end
238
+
239
+ create_table :categorizations do |t|
240
+ t.references :category, null: false
241
+ t.references :categorizable, null: false, :polymorphic: true
242
+ t.timestamps
243
+ end
244
+ ~~~
245
+
189
246
  - [acts_as_category](https://github.com/wuwx/acts_as_category)
190
247
 
191
248
 
data/lib/tagutils.rb CHANGED
@@ -9,12 +9,16 @@ require 'logutils'
9
9
 
10
10
  require 'tagutils/version' # let it always go first
11
11
 
12
- require 'tagutils/schema'
12
+ require 'tagutils/schema_tags'
13
13
  require 'tagutils/models/tag'
14
14
  require 'tagutils/models/tagging'
15
15
 
16
+ require 'tagutils/schema_categories'
17
+ require 'tagutils/models/category'
18
+ require 'tagutils/models/categorization'
16
19
 
17
- require 'tagutils/active_record' # -- adds has_many_tags macro
20
+
21
+ require 'tagutils/active_record' # -- adds has_many_tags, has_many_categories class macros
18
22
 
19
23
 
20
24
  module TagUtils
@@ -58,5 +62,39 @@ module TagDb
58
62
  end # module TagDb
59
63
 
60
64
 
65
+ module CategoryDb
66
+ #####
67
+ # add convenience module alias in plural
68
+ # e.g. lets you use include CategoryDb::Models
69
+ Models = Model
70
+
71
+ def self.create
72
+ CreateDb.new.up
73
+ ## WorldDb::Model::Prop.create!( key: 'db.schema.world.version', value: VERSION )
74
+ end
75
+
76
+ # delete ALL records (use with care!)
77
+ def self.delete!
78
+ puts '*** deleting category/categorization table records/data...'
79
+ Model::Categorization.delete_all
80
+ Model::Category.delete_all
81
+ end
82
+
83
+ def self.tables
84
+ puts "#{Model::Category.count} categories"
85
+ puts "#{Model::Categorization.count} categorizations"
86
+ end
87
+ end # module CategoryDb
88
+
89
+ CatDb = CategoryDb # for conveniene add alias for CatDb
90
+
91
+
92
+ ####
93
+ # use shared/common module/namespace ?
94
+ # e.g.
95
+ # - ClassificationDb, ClassiDb ??
96
+ # - TaxonomyDb, TaxonDb, TaxyDb ???
97
+ # - TopicDb, KeywordDb ?? -- why? why not??
98
+
61
99
 
62
100
  puts TagUtils.banner # say hello
@@ -1,14 +1,48 @@
1
1
 
2
+ module TagDb
3
+ module ClassMacros
4
+ def has_many_tags( opts={} )
5
+ puts " [TagDb.has_many_tags] adding taggings n tags has_many assocs to model >#{name}<"
2
6
 
3
- module ActiveRecord
4
- class Base
5
-
6
- def self.has_many_tags( opts={} )
7
- puts " [TagUtils.has_many_tags] adding taggings n tags has_many assocs to model >#{name}<"
8
7
  has_many :taggings, class_name: 'TagDb::Model::Tagging', :as => :taggable
9
8
  has_many :tags, class_name: 'TagDb::Model::Tag', :through => :taggings
9
+
10
+ ### check: use tag_name instead of tag_key ???
11
+ scope :with_tag, ->(tag_key){ joins(:tags).where('tags.key' => tag_key) }
10
12
  end
13
+
14
+ ## same as scope above
15
+ ## def with_tag( tag_key )
16
+ ## joins(:tags).where( 'tags.key' => tag_key )
17
+ ## end
18
+ end
19
+ end
20
+
21
+
22
+ module CategoryDb
23
+ module ClassMacros
24
+ def has_many_categories( opts={} )
25
+ puts " [CategoryDb.has_many_categories] adding categorizations n category has_many assocs to model >#{name}<"
26
+
27
+ has_many :categorizations, class_name: 'CategoryDb::Model::Categorization', :as => :categorizable
28
+ has_many :categories, class_name: 'CategoryDb::Model::Category', :through => :categorizations
11
29
 
30
+ ### check: use category_name instead of category_key ???
31
+ scope :with_category, ->(category_key){ joins(:categories).where('categories.key' => category_key) }
32
+ end
33
+
34
+ ## same as scope above
35
+ ## def with_category( category_key )
36
+ ## joins(:categories).where( 'categories.key' => category_key )
37
+ ## end
38
+ end
39
+ end
40
+
41
+
42
+ module ActiveRecord
43
+ class Base
44
+ extend TagDb::ClassMacros
45
+ extend CategoryDb::ClassMacros
12
46
  end # class Base
13
47
  end # module ActiveRecord
14
48
 
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ module CategoryDb
4
+ module Model
5
+
6
+
7
+ class Categorization < ActiveRecord::Base
8
+
9
+ belongs_to :category
10
+ belongs_to :categorizable, :polymorphic => true
11
+
12
+ ## validates :category, presence: true
13
+ ## validates :categorizable, presence: true
14
+
15
+ end # class Categorization
16
+
17
+
18
+ end # module CategoryDb
19
+ end # module Model
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ module CategoryDb
4
+ module Model
5
+
6
+ class Category < ActiveRecord::Base
7
+
8
+ self.table_name = 'categories'
9
+
10
+ has_many :categorizations
11
+
12
+ ###
13
+ ## todo/fix: add validate for key - what constraints to use ??
14
+ #
15
+ ## nb: only allow spaces and underscore inbetween;
16
+ ## do NOT allow digit as first char for now
17
+ # validates :key, :format => { :with => /^([a-z]|[a-z][a-z0-9_ ]*[a-z0-9])$/,
18
+ # :message => 'expected one or more lowercase letters a-z or 0-9 digits or space or underscore' }
19
+
20
+ scope :by_key, -> { order( 'key desc' ) }
21
+ scope :by_name, -> { order( 'name desc' ) }
22
+
23
+ before_save :on_before_save
24
+
25
+ def on_before_save
26
+ # replace space with dash e.g. north america becomes north-america and so on
27
+ self.slug = key.gsub( ' ', '-' )
28
+ end
29
+
30
+
31
+ ############################################################################
32
+ # alias for name (remove! add depreciated api call) ?? why? why not??
33
+
34
+ def title() name; end
35
+ def title=(value) self.name = value; end
36
+
37
+ scope :by_title, -> { order( 'name desc' ) } ### depreciated ??? - use by_name
38
+
39
+
40
+ end # class Category
41
+
42
+
43
+ end # module CategoryDb
44
+ end # module Model
@@ -12,9 +12,9 @@ class Tag < ActiveRecord::Base
12
12
  validates :key, :format => { :with => /^([a-z]|[a-z][a-z0-9_ ]*[a-z0-9])$/,
13
13
  :message => 'expected one or more lowercase letters a-z or 0-9 digits or space or underscore' }
14
14
 
15
- scope :by_key, order( 'key desc' )
16
- scope :by_name, order( 'name desc' )
17
- scope :top, where( 'grade=1' )
15
+ scope :by_key, -> { order( 'key desc' ) }
16
+ scope :by_name, -> { order( 'name desc' ) }
17
+ scope :top, -> { where( 'grade=1' ) }
18
18
 
19
19
 
20
20
  before_save :on_before_save
@@ -34,7 +34,7 @@ class Tag < ActiveRecord::Base
34
34
  def title() name; end
35
35
  def title=(value) self.name = value; end
36
36
 
37
- scope :by_title, order( 'name desc' ) ### depreciated ??? - use by_name
37
+ scope :by_title, -> { order( 'name desc' ) } ### depreciated ??? - use by_name
38
38
 
39
39
 
40
40
  end # class Tag
@@ -9,6 +9,9 @@ class Tagging < ActiveRecord::Base
9
9
  belongs_to :tag
10
10
  belongs_to :taggable, :polymorphic => true
11
11
 
12
+ ## validates :tag, presence: true
13
+ ## validates :taggable, presence: true
14
+
12
15
  end # class Tagging
13
16
 
14
17
 
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ module CategoryDb
4
+
5
+ class CreateDb < ActiveRecord::Migration
6
+
7
+ def up
8
+
9
+ create_table :categories do |t|
10
+ t.string :key, null: false
11
+ t.string :slug, null: false
12
+ t.string :name, null: false
13
+ t.references :parent
14
+ # todo: use only t.datetime :created_at (do we get ar magic? is updated used/needed??)
15
+ t.timestamps
16
+ end
17
+
18
+ add_index :categories, :key, unique: true
19
+
20
+ create_table :categorizations do |t|
21
+ t.references :category, null: false
22
+ t.references :categorizable, null: false, polymorphic: true
23
+
24
+ # todo: use only t.datetime :created_at (do we get ar magic? is updated used/needed??)
25
+ t.timestamps
26
+ end
27
+
28
+ ###
29
+ # note: use name prop
30
+ # too avoid error index name too long (e.g. sqlite requires <64 chars)
31
+
32
+ add_index :categorizations, :category_id, name: 'categorizations_cats_idx'
33
+ add_index :categorizations, [:categorizable_id, :categorizable_type], name: 'categorizations_idx'
34
+ add_index :categorizations, [:categorizable_id, :categorizable_type, :category_id], unique: true, name: 'categorizations_unique_idx'
35
+
36
+ end # method up
37
+
38
+
39
+ def down
40
+ raise ActiveRecord::IrreversibleMigration
41
+ end
42
+
43
+ end # class CreateDb
44
+
45
+ end # module CategoryDb
@@ -12,7 +12,8 @@ create_table :tags do |t|
12
12
  t.string :name # todo: make required? -- note: was title formerly
13
13
  t.integer :grade, null: false, default: 1 # grade/tier e.g. 1/2/3 for now
14
14
 
15
- ## todo: add parent or similar for hierachy (for tag stacks/packs)
15
+ # parent used for hierachy (e.g. lets you use tag stacks/packs etc.)
16
+ t.references :parent
16
17
 
17
18
  # todo: use only t.datetime :created_at (do we get ar magic? is updated used/needed??)
18
19
  t.timestamps
@@ -1,4 +1,4 @@
1
1
 
2
2
  module TagUtils
3
- VERSION = '0.1.1'
3
+ VERSION = '0.2.0'
4
4
  end
data/test/helper.rb CHANGED
@@ -27,14 +27,19 @@ require 'logutils/db' # NB: explict require required for LogDb (not automatic)
27
27
 
28
28
 
29
29
 
30
- Tag = TagDb::Model::Tag
31
- Tagging = TagDb::Model::Tagging
30
+ Tag = TagDb::Model::Tag
31
+ Tagging = TagDb::Model::Tagging
32
+
33
+ Category = CategoryDb::Model::Category
34
+ Categorization = CategoryDb::Model::Categorization
32
35
 
33
36
 
34
37
  class Movie < ActiveRecord::Base
35
38
  has_many_tags
39
+ has_many_categories
36
40
  end
37
41
 
42
+
38
43
  class CreateMovieDb < ActiveRecord::Migration
39
44
 
40
45
  def up
@@ -74,6 +79,7 @@ def setup_in_memory_db
74
79
 
75
80
  LogDb.create
76
81
  TagDb.create
82
+ CategoryDb.create
77
83
  CreateMovieDb.new.up
78
84
 
79
85
  end
data/test/test_models.rb CHANGED
@@ -6,29 +6,80 @@ class TestModels < MiniTest::Unit::TestCase
6
6
 
7
7
  def setup
8
8
  TagDb.delete!
9
+ CategoryDb.delete!
10
+
11
+ Movie.delete_all
9
12
  end
10
13
 
11
14
  def test_count
12
15
  assert_equal 0, Tag.count
13
16
  assert_equal 0, Tagging.count
17
+
18
+ assert_equal 0, Category.count
19
+ assert_equal 0, Categorization.count
20
+
14
21
  assert_equal 0, Movie.count
15
22
  end
16
23
 
24
+
25
+ def test_with
26
+ tag = Tag.create!( key: 'key', name: 'name' )
27
+ cat = Category.create!( key: 'key', name: 'name' )
28
+
29
+ ## add 1st movie
30
+ movie = Movie.create!( key: 'key', name: 'name' )
31
+
32
+ movie.tags << tag
33
+ movie.categories << cat
34
+
35
+ assert_equal 1, Movie.with_tag( 'key' ).count
36
+ assert_equal 0, Movie.with_tag( 'xxx' ).count
37
+
38
+ assert_equal 1, Movie.joins(:categories).count
39
+ assert_equal 1, Movie.joins(:categories).where( 'categories.key' => 'key' ).count
40
+
41
+ assert_equal 1, Movie.with_category( 'key' ).count
42
+ assert_equal 0, Movie.with_category( 'xxx' ).count
43
+
44
+ ## add another (2nd) movie
45
+ movie2 = Movie.create!( key: 'key2', name: 'name2' )
46
+
47
+ movie2.tags << tag
48
+ movie2.categories << cat
49
+
50
+ assert_equal 2, Movie.with_tag( 'key' ).count
51
+ assert_equal 0, Movie.with_tag( 'xxx' ).count
52
+
53
+ assert_equal 2, Movie.with_category( 'key' ).count
54
+ assert_equal 0, Movie.with_category( 'xxx' ).count
55
+ end
56
+
57
+
17
58
  def test_assocs
18
59
  tag = Tag.new( key: 'key', name: 'name' )
19
60
  assert_equal 0, tag.taggings.count
20
61
 
62
+ cat = Category.new( key: 'key', name: 'name' )
63
+ assert_equal 0, cat.categorizations.count
64
+
21
65
  movie = Movie.new( key: 'key', name: 'name' )
22
66
  assert_equal 0, movie.taggings.count
23
67
  assert_equal 0, movie.tags.count
68
+
69
+ assert_equal 0, movie.categorizations.count
70
+ assert_equal 0, movie.categories.count
24
71
 
25
72
  tag.save!
73
+ cat.save!
26
74
  movie.save!
27
75
 
28
76
  assert_equal 0, tag.taggings.count
77
+ assert_equal 0, cat.categorizations.count
29
78
 
30
79
  assert_equal 0, movie.taggings.count
31
80
  assert_equal 0, movie.tags.count
81
+ assert_equal 0, movie.categorizations.count
82
+ assert_equal 0, movie.categories.count
32
83
 
33
84
  ## note: add tag; op << will auto-save
34
85
  movie.tags << tag
@@ -36,6 +87,13 @@ class TestModels < MiniTest::Unit::TestCase
36
87
  assert_equal 1, tag.taggings.count
37
88
  assert_equal 1, movie.taggings.count
38
89
  assert_equal 1, movie.tags.count
90
+
91
+ ## note: add tag; op << will auto-save
92
+ movie.categories << cat
93
+
94
+ assert_equal 1, cat.categorizations.count
95
+ assert_equal 1, movie.categorizations.count
96
+ assert_equal 1, movie.categories.count
39
97
  end
40
98
 
41
99
  end # class TestModels
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tagutils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-08 00:00:00.000000000 Z
12
+ date: 2014-03-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
16
- requirement: &72493260 !ruby/object:Gem::Requirement
16
+ requirement: &85193010 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *72493260
24
+ version_requirements: *85193010
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: logutils
27
- requirement: &72492220 !ruby/object:Gem::Requirement
27
+ requirement: &85192530 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0.6'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *72492220
35
+ version_requirements: *85192530
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rdoc
38
- requirement: &72582430 !ruby/object:Gem::Requirement
38
+ requirement: &85191800 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '3.10'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *72582430
46
+ version_requirements: *85191800
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: hoe
49
- requirement: &72582120 !ruby/object:Gem::Requirement
49
+ requirement: &85191060 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '3.3'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *72582120
57
+ version_requirements: *85191060
58
58
  description: tagutils - tag utilities (tag, taggings, tag list, etc.)
59
59
  email: openmundi@googlegroups.com
60
60
  executables: []
@@ -68,9 +68,12 @@ files:
68
68
  - Rakefile
69
69
  - lib/tagutils.rb
70
70
  - lib/tagutils/active_record.rb
71
+ - lib/tagutils/models/categorization.rb
72
+ - lib/tagutils/models/category.rb
71
73
  - lib/tagutils/models/tag.rb
72
74
  - lib/tagutils/models/tagging.rb
73
- - lib/tagutils/schema.rb
75
+ - lib/tagutils/schema_categories.rb
76
+ - lib/tagutils/schema_tags.rb
74
77
  - lib/tagutils/version.rb
75
78
  - test/helper.rb
76
79
  - test/test_models.rb