tag 0.1.0 → 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.
@@ -1,2 +1,5 @@
1
- = 0.1.0
1
+ == 0.2.0
2
+ * Add models (tagging for multiple objects or apps)
3
+
4
+ == 0.1.0
2
5
  * Something works!
data/README.md CHANGED
@@ -8,13 +8,28 @@ In making tags a first class nix citizen, perhaps they will see the light of day
8
8
  Examples
9
9
  ========
10
10
 
11
- $ tag add horse animal
12
- $ tag add cat animal
13
- $ tag list animal
11
+ Let's start with tagging animals:
12
+
13
+ $ tag add horse -t fast strong
14
+ $ tag add cat -t fast independent
15
+ $ tag add dog -t loving
16
+ $ tag list fast
14
17
  horse
15
18
  cat
16
19
 
17
- # TODO
20
+ Tired of animals, let's tag cities. To avoid interfering with
21
+ the animals list, we'll use a cities model:
22
+
23
+ $ tag add nyc -t fast fun -m cities
24
+ $ tag add boston -t educated clean -m cities
25
+ $ tag add paris -t awesome delicious
26
+ $ tag list fast -m cities
27
+ nyc
28
+ # to avoid typing '-m cities' for every command
29
+ $ export TAG_MODEL=cities
30
+
31
+ Since models are isolated from one another, third party commandline apps can
32
+ use tag for their own private tagging purposes.
18
33
 
19
34
  Motivation
20
35
  ==========
@@ -29,7 +44,6 @@ Contributing
29
44
  Todo
30
45
  ====
31
46
 
32
- * Tests!
33
47
  * Several more tag actions
34
48
  * Description and time fields
35
49
  * Make storage agnostic
data/lib/tag.rb CHANGED
@@ -1,88 +1,17 @@
1
- require 'yaml'
2
- require 'thor'
1
+ require 'tag/runner'
2
+ require 'fileutils'
3
+ require 'tag/store'
3
4
 
4
5
  module Tag
5
- VERSION = '0.1.0'
6
+ VERSION = '0.2.0'
6
7
 
7
- class Store
8
- class << self; attr_accessor :file; end
9
- self.file = Dir.home + '/.tagrc'
10
-
11
- def initialize
12
- @file = self.class.file
13
- @hash = File.exists?(@file) ? YAML.load_file(@file) : {}
14
- end
15
-
16
- def save
17
- File.open(@file, 'w') {|f| f.write(@hash.to_yaml) }
18
- end
19
-
20
- def tag(item, tag)
21
- @hash[tag] ||= []
22
- @hash[tag] << item
23
- save
24
- end
25
-
26
- def remove_tag(item, tag)
27
- @hash[tag] ||= []
28
- @hash[tag].delete item
29
- save
30
- end
31
-
32
- def list(tag)
33
- @hash[tag] ||= []
34
- @hash[tag]
35
- end
36
-
37
- def list_tags
38
- @hash.keys.sort
39
- end
40
-
41
- def tree
42
- list_tags.map {|tag|
43
- ["#{tag}", list(tag).map {|t| " #{t}" }]
44
- }.flatten
45
- end
46
-
47
- def delete_tags(*tags)
48
- tags.each {|tag| @hash.delete(tag) }
49
- save
50
- end
8
+ def self.store(model = nil)
9
+ @store ||= Store.new(model)
51
10
  end
52
11
 
53
- def self.store
54
- @store ||= Store.new
55
- end
56
-
57
- class Runner < Thor
58
- desc "add ITEM TAG", 'tag an item'
59
- def add(item, tag)
60
- Tag.store.tag(item, tag)
61
- end
62
-
63
- desc "rm ITEM TAG", 'remove tag from item'
64
- def rm(item, tag)
65
- Tag.store.remove_tag(item, tag)
66
- end
67
-
68
- desc "list TAG", 'list items by tag'
69
- def list(tag)
70
- puts Tag.store.list(tag)
71
- end
72
-
73
- method_option :rm, :type => :boolean, :desc => 'remove tags'
74
- desc "tags", 'list or remove tags'
75
- def tags(*args)
76
- if options[:rm]
77
- Tag.store.delete_tags(*args)
78
- else
79
- puts Tag.store.list_tags
80
- end
81
- end
82
-
83
- desc "tree", 'print tags with their items underneath them'
84
- def tree
85
- puts Tag.store.tree
12
+ def self.home
13
+ @home ||= (ENV['TAG_HOME'] || File.join(Dir.home, '.tag')).tap do |dir|
14
+ FileUtils.mkdir_p(dir)
86
15
  end
87
16
  end
88
17
  end
@@ -0,0 +1,51 @@
1
+ require 'thor'
2
+
3
+ module Tag
4
+ class Runner < Thor
5
+ def self.model_option
6
+ method_option :model, :type => :string, :aliases => '-m'
7
+ end
8
+
9
+ model_option
10
+ method_option :tags, :type => :array, :required => true, :aliases => '-t'
11
+ desc "add *ITEMS -t *TAGS", 'add tag(s) to item(s)'
12
+ def add(*items)
13
+ Tag.store(options[:model]).multi_tag(items, options[:tags])
14
+ end
15
+
16
+ model_option
17
+ method_option :tags, :type => :array, :required => true, :aliases => '-t'
18
+ desc "rm *ITEMS -t *TAGS", 'remove tag(s) from item(s)'
19
+ def rm(*items)
20
+ Tag.store(options[:model]).multi_remove_tag(items, options[:tags])
21
+ end
22
+
23
+ model_option
24
+ desc "list TAG", 'list items by tag'
25
+ def list(tag)
26
+ puts Tag.store(options[:model]).list(tag)
27
+ end
28
+
29
+ model_option
30
+ method_option :rm, :type => :boolean, :desc => 'remove tags'
31
+ desc "tags", 'list or remove tags'
32
+ def tags(*args)
33
+ if options[:rm]
34
+ Tag.store(options[:model]).delete_tags(*args)
35
+ else
36
+ puts Tag.store(options[:model]).list_tags
37
+ end
38
+ end
39
+
40
+ model_option
41
+ desc "tree", 'print tags with their items underneath them'
42
+ def tree
43
+ puts Tag.store(options[:model]).tree
44
+ end
45
+
46
+ desc "models", 'list models'
47
+ def models
48
+ puts Tag::Store.models
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,68 @@
1
+ require 'yaml'
2
+
3
+ module Tag
4
+ class Store
5
+
6
+ def self.models
7
+ Dir["#{Tag.home}/*.yml"].map {|file| file[%r{([^/]+).yml$}, 1] }.sort
8
+ end
9
+
10
+ def initialize(model = nil)
11
+ model ||= ENV['TAG_MODEL'] || 'default'
12
+ @file = File.join(Tag.home, "#{model}.yml")
13
+ @hash = File.exists?(@file) ? YAML.load_file(@file) : {}
14
+ end
15
+
16
+ def save
17
+ File.open(@file, 'w') {|f| f.write(@hash.to_yaml) }
18
+ end
19
+
20
+ def tag(item, tag)
21
+ @hash[tag] ||= []
22
+ @hash[tag] << item
23
+ end
24
+
25
+ def multi_tag(items, tags)
26
+ tags.each do |tag|
27
+ items.each do |item|
28
+ tag(item, tag)
29
+ end
30
+ end
31
+ save
32
+ end
33
+
34
+ def multi_remove_tag(items, tags)
35
+ tags.each do |tag|
36
+ items.each do |item|
37
+ remove_tag(item, tag)
38
+ end
39
+ end
40
+ save
41
+ end
42
+
43
+ def remove_tag(item, tag)
44
+ @hash[tag] ||= []
45
+ @hash[tag].delete item
46
+ end
47
+
48
+ def list(tag)
49
+ @hash[tag] ||= []
50
+ @hash[tag]
51
+ end
52
+
53
+ def list_tags
54
+ @hash.keys.sort
55
+ end
56
+
57
+ def tree
58
+ list_tags.map {|tag|
59
+ ["#{tag}", list(tag).map {|t| " #{t}" }]
60
+ }.flatten
61
+ end
62
+
63
+ def delete_tags(*tags)
64
+ tags.each {|tag| @hash.delete(tag) }
65
+ save
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,25 @@
1
+ gem 'minitest' unless ENV['NO_RUBYGEMS']
2
+ require 'minitest/autorun'
3
+ require 'tag'
4
+ require 'open3'
5
+ require 'fileutils'
6
+
7
+ ENV['TAG_HOME'] = File.dirname(__FILE__) + '/.tag'
8
+
9
+ module TestHelpers
10
+ attr_reader :stdout, :stderr, :process
11
+
12
+ def tag(cmd)
13
+ args = cmd.split(/\s+/)
14
+ args.unshift File.dirname(__FILE__) + '/../bin/tag'
15
+ args.unshift({'RUBYLIB' => "#{File.dirname(__FILE__)}/../lib:" +
16
+ ENV['RUBYLIB']})
17
+ @stdout, @stderr, @process = Open3.capture3(*args)
18
+ end
19
+ end
20
+
21
+ class MiniTest::Unit::TestCase
22
+ include TestHelpers
23
+ end
24
+
25
+ MiniTest::Unit.after_tests { FileUtils.rm_rf(ENV['TAG_HOME']) }
@@ -0,0 +1,122 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/helper')
2
+
3
+ describe Tag::Runner do
4
+ def tagged(tag)
5
+ tag "list #{tag}"
6
+ stdout.split("\n")
7
+ end
8
+
9
+ before { FileUtils.rm_rf(ENV['TAG_HOME']) }
10
+
11
+ describe "add" do
12
+ it "adds a tag" do
13
+ tag 'add feynman -t physicist'
14
+ tagged('physicist').must_equal ["feynman"]
15
+ end
16
+
17
+ it "adds two tags" do
18
+ tag 'add feynman -t physicist teacher'
19
+ tagged('physicist').must_equal ["feynman"]
20
+ tagged('physicist').must_equal ["feynman"]
21
+ end
22
+
23
+ it "adds a tag to two items" do
24
+ tag 'add feynman fermi -t physicist'
25
+ tagged('physicist').must_equal %w{feynman fermi}
26
+ end
27
+
28
+ it "fails if no tag given" do
29
+ tag 'add um'
30
+ stderr.must_include 'required option'
31
+ end
32
+
33
+ it "with model option adds to a different model" do
34
+ tag 'add feynman -t physicist -m physics'
35
+ tagged('physicist -m physics').must_equal ['feynman']
36
+ tagged('physicist').must_equal []
37
+ end
38
+
39
+ it "with $TAG_MODEL adds to a different model" do
40
+ ENV['TAG_MODEL'] = 'physica'
41
+ tag 'add feynman -t physicist'
42
+ tagged('physicist').must_equal ['feynman']
43
+ tagged('physicist -m default').must_equal []
44
+ ENV['TAG_MODEL'] = nil
45
+ end
46
+ end
47
+
48
+ describe "rm" do
49
+ it "removes a tag" do
50
+ tag 'add newton -t physicist fig'
51
+ tag 'rm newton -t fig'
52
+ tagged('physicist').must_equal ['newton']
53
+ tagged('fig').must_equal []
54
+ end
55
+
56
+ it "removes two tags" do
57
+ tag 'add newton -t physicist fig sir'
58
+ tag 'rm newton -t fig sir'
59
+ tagged('physicist').must_equal ['newton']
60
+ tagged('fig').must_equal []
61
+ tagged('sir').must_equal []
62
+ end
63
+
64
+ it "removes two items from a tag" do
65
+ tag 'add newton -t physicist fig'
66
+ tag 'add tree -t fig'
67
+ tag 'rm newton tree -t fig'
68
+ tagged('fig').must_equal []
69
+ end
70
+
71
+ it "fails if no tag given" do
72
+ tag 'rm um'
73
+ stderr.must_include 'required option'
74
+ end
75
+
76
+ it "with model option removes a tag from other model" do
77
+ tag 'add newton -t physicist fig -m physics'
78
+ tag 'rm newton -t fig -m physics'
79
+ tagged('physicist -m physics').must_equal ['newton']
80
+ tagged('physicist').must_equal []
81
+ end
82
+ end
83
+
84
+ describe "tags" do
85
+ it "by default lists all tags" do
86
+ tag 'add fermi -t italian german irish'
87
+ tag 'tags'
88
+ stdout.split("\n").must_equal %w{german irish italian}
89
+ end
90
+
91
+ it "with model option lists all tags for another model" do
92
+ tag 'add fermi -t italian german irish -m physicist'
93
+ tag 'tags -m physicist'
94
+ stdout.split("\n").must_equal %w{german irish italian}
95
+ tag 'tags'
96
+ stdout.split("\n").must_equal []
97
+ end
98
+
99
+ it "with rm option deletes tags" do
100
+ tag 'add fermi -t italian german irish'
101
+ tag 'add davinci -t italian german irish'
102
+ tag 'tags german irish --rm'
103
+ tagged('german').must_equal []
104
+ tagged('irish').must_equal []
105
+ end
106
+ end
107
+
108
+ it "tree prints tree" do
109
+ tag 'add child -t parent1 parent2'
110
+ tag 'tree'
111
+ stdout.chomp.must_equal [
112
+ 'parent1', ' child', 'parent2', ' child'
113
+ ].join("\n")
114
+ end
115
+
116
+ it "models prints list of models" do
117
+ tag 'add feynman -t funny -m physicist'
118
+ tag 'add cat -t independent'
119
+ tag 'models'
120
+ stdout.split("\n").must_equal ['default', 'physicist']
121
+ end
122
+ end
metadata CHANGED
@@ -1,40 +1,44 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: tag
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
4
5
  prerelease:
5
- version: 0.1.0
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Gabriel Horner
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-11-12 00:00:00 -05:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
12
+ date: 2011-12-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
17
15
  name: thor
18
- prerelease: false
19
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2152084180 !ruby/object:Gem::Requirement
20
17
  none: false
21
- requirements:
18
+ requirements:
22
19
  - - ~>
23
- - !ruby/object:Gem::Version
20
+ - !ruby/object:Gem::Version
24
21
  version: 0.14.6
25
22
  type: :runtime
26
- version_requirements: *id001
27
- description: This project lets you tag anything from the commandline. The `tag` executable provides a consistent way to add, remove and modify tags for tagged items. The goal is to make tagging dead simple and usable by other commandline apps. In making tags a first class nix citizen, perhaps they will see the light of day.
23
+ prerelease: false
24
+ version_requirements: *2152084180
25
+ description: This project lets you tag anything from the commandline. The `tag` executable
26
+ provides a consistent way to add, remove and modify tags for tagged items. The goal
27
+ is to make tagging dead simple and usable by other commandline apps. In making tags
28
+ a first class nix citizen, perhaps they will see the light of day.
28
29
  email: gabriel.horner@gmail.com
29
- executables:
30
+ executables:
30
31
  - tag
31
32
  extensions: []
32
-
33
- extra_rdoc_files:
33
+ extra_rdoc_files:
34
34
  - README.md
35
35
  - LICENSE.txt
36
- files:
36
+ files:
37
+ - lib/tag/runner.rb
38
+ - lib/tag/store.rb
37
39
  - lib/tag.rb
40
+ - spec/helper.rb
41
+ - spec/runner_spec.rb
38
42
  - bin/tag
39
43
  - LICENSE.txt
40
44
  - CHANGELOG.rdoc
@@ -42,33 +46,29 @@ files:
42
46
  - deps.rip
43
47
  - Rakefile
44
48
  - .gemspec
45
- has_rdoc: true
46
49
  homepage: http://github.com/cldwalker/tag
47
- licenses:
50
+ licenses:
48
51
  - MIT
49
52
  post_install_message:
50
53
  rdoc_options: []
51
-
52
- require_paths:
54
+ require_paths:
53
55
  - lib
54
- required_ruby_version: !ruby/object:Gem::Requirement
56
+ required_ruby_version: !ruby/object:Gem::Requirement
55
57
  none: false
56
- requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- version: "0"
60
- required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
63
  none: false
62
- requirements:
63
- - - ">="
64
- - !ruby/object:Gem::Version
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
65
67
  version: 1.3.6
66
68
  requirements: []
67
-
68
69
  rubyforge_project:
69
- rubygems_version: 1.6.2
70
+ rubygems_version: 1.8.10
70
71
  signing_key:
71
72
  specification_version: 3
72
73
  summary: tag anything from the commandline
73
74
  test_files: []
74
-