tagutils 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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