tag 0.1.0 → 0.2.0

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