cardigan 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -40,11 +40,15 @@ The commands available are (with the awesome power of tab completion):
40
40
  * destroy <numbers> - deletes the specified cards (by index from the list)
41
41
  * set <key> <numbers> - prompts for a field value and sets it on all specified cards (by index from the list)
42
42
  * workflow - enters workflow editing mode
43
+ * count <grouping fields> - counts the number of cards for each combination of the grouping fields
44
+ * total <numeric field> <grouping fields> - calculates the total of the specified numeric field for each combination of the grouping fields
45
+ * export <filename> - writes all columns for current filtered cards to <filename>.csv
46
+ * import <filename> - imports all cards from <filename>.csv - cards will be updated with a matching id, otherwise new cards will be created.
43
47
 
44
48
  == Editing mode
45
49
 
46
50
  * quit or exit or ctrl-d - exit
47
- * show - dumps the values of all card field values
51
+ * list - dumps the values of all card field values
48
52
  * set <key> - creates a new field and prompts for the new value
49
53
  * now <status> - changes the status of the card to the given status (the tab completion will be populated with the valid subsequent statuses from the current status)
50
54
 
@@ -55,7 +59,7 @@ Workflow is pretty simple - it is just for convenience in specifiying the set of
55
59
  The only purpose is to populate tab completion for the 'now' command in edit mode for a card.
56
60
 
57
61
  * quit or exit or ctrl-d - exit
58
- * show - dumps the current workflow (statuses with their valid subsequent statuses)
62
+ * list - dumps the current workflow (statuses with their valid subsequent statuses)
59
63
  * create <status> - creates a new status
60
64
  * add <status> <statuses> - add the specified statuses as valid transitions from status
61
65
  * remove <status> <statuses> - remove the specified statuses as valid transitions from status
@@ -0,0 +1,23 @@
1
+ module Cardigan
2
+ class CardEditor
3
+ def initialize card, io
4
+ @card, @io = card, io
5
+ end
6
+
7
+ def set key, value
8
+ return if key == 'id'
9
+ value = '' unless value
10
+ if value.empty?
11
+ if @card[key]
12
+ @io.say "removing #{key} from '#{@card['id']}'"
13
+ @card.delete key
14
+ end
15
+ else
16
+ unless @card[key] == value
17
+ @io.say "changing #{key} from '#{@card[key]}' to '#{value}' for '#{@card['id']}'"
18
+ @card[key] = value
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,3 +1,5 @@
1
+ require 'cardigan/card_editor'
2
+
1
3
  module Cardigan
2
4
  module Command
3
5
  class BatchUpdateCards
@@ -9,13 +11,7 @@ module Cardigan
9
11
  key, *rest = text.scan(/\w+/)
10
12
  value = @io.ask("Enter the new value for #{key}")
11
13
  @repository.each_card_from_indices(rest.join(' ')) do |card|
12
- if value.empty?
13
- @io.say "removing #{key} from '#{card['name']}'"
14
- card.delete key
15
- else
16
- @io.say "setting #{key} to '#{value}' for '#{card['name']}'"
17
- card[key] = value
18
- end
14
+ Cardigan::CardEditor.new(card, @io).set key, value
19
15
  @repository.save card
20
16
  end
21
17
  end
@@ -0,0 +1,13 @@
1
+ module Cardigan
2
+ module Command
3
+ class ChangeStatus
4
+ def initialize entry
5
+ @entry = entry
6
+ end
7
+
8
+ def execute status
9
+ @entry['status'] = status
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ require 'cardigan/card_editor'
2
+
3
+ module Cardigan
4
+ module Command
5
+ class ChangeValue
6
+ def initialize entry, io
7
+ @entry, @io = entry, io
8
+ end
9
+
10
+ def execute key
11
+ Cardigan::CardEditor.new(@entry, @io).set key, @io.ask("Enter the new value for #{key}")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,8 +1,8 @@
1
1
  module Cardigan
2
2
  module Command
3
3
  class ClaimCards
4
- def initialize repository, io
5
- @repository, @io = repository, io
4
+ def initialize repository, io, name
5
+ @repository, @io, @name = repository, io, name
6
6
  end
7
7
 
8
8
  def execute numbers
@@ -0,0 +1,15 @@
1
+ class Cardigan::Command::CountCards
2
+ def initialize repository, io
3
+ @repository, @io = repository, io
4
+ end
5
+
6
+ def execute text
7
+ grouping_fields = text.scan(/\w+/)
8
+ counts = {}
9
+ @repository.cards.each do |card|
10
+ key = grouping_fields.map {|key| card[key] ? card[key] : ''}
11
+ counts[key] = counts[key] ? counts[key] + 1 : 1
12
+ end
13
+ counts.keys.sort.each {|key| @io.say "#{counts[key]} #{key.inspect}"}
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module Cardigan::Command::DisplayCards
2
+ def display_cards
3
+ cards = @repository.cards
4
+ if cards.empty?
5
+ @io.say "There are no cards to display"
6
+ return
7
+ end
8
+ formatter = Cardigan::TextReportFormatter.new @io
9
+ @repository.display_columns.each do |column|
10
+ formatter.add_column(column, @repository.max_field_length(column))
11
+ end
12
+ formatter.output cards
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ require 'csv'
2
+
3
+ class Cardigan::Command::ExportCards
4
+ def initialize repository
5
+ @repository = repository
6
+ end
7
+
8
+ def execute filename
9
+ filename += ".csv"
10
+ columns = @repository.columns
11
+ CSV.open(filename, 'w') do |writer|
12
+ writer << columns
13
+ @repository.each do |card|
14
+ writer << columns.map {|column| card[column] ? card[column] : ''}
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ require 'csv'
2
+
3
+ class Cardigan::Command::ImportCards
4
+ def initialize repository, io
5
+ @repository, @io = repository, io
6
+ end
7
+
8
+ def execute filename
9
+ filename += ".csv"
10
+ unless File.exist?(filename)
11
+ @io.say "#{filename} does not exist"
12
+ return
13
+ end
14
+ header, id_index = nil, nil
15
+ CSV.open(filename, 'r') do |row|
16
+ if header
17
+ card = @repository.load(row[id_index]) if id_index and row[id_index]
18
+ @io.say card ? "updating #{card['id']}" : "creating new card"
19
+ card = @repository.create unless card
20
+ editor = Cardigan::CardEditor.new(card, @io)
21
+ header.each_with_index do |field, index|
22
+ editor.set field, row[index]
23
+ end
24
+ @repository.save card
25
+ else
26
+ header = row
27
+ id_index = header.find_index('id')
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,20 +1,14 @@
1
1
  require 'cardigan/text_report_formatter'
2
+ require 'cardigan/command/display_cards'
2
3
 
3
- module Cardigan
4
- module Command
5
- class ListCards
6
- def initialize repository, io
7
- @repository, @io = repository, io
8
- end
4
+ class Cardigan::Command::ListCards
5
+ include Cardigan::Command::DisplayCards
9
6
 
10
- def execute text
11
- cards = @repository.cards
12
- formatter = TextReportFormatter.new @io
13
- @repository.display_columns.each do |column|
14
- formatter.add_column(column, @repository.max_field_length(column))
15
- end
16
- formatter.output cards
17
- end
18
- end
7
+ def initialize repository, io
8
+ @repository, @io = repository, io
9
+ end
10
+
11
+ def execute text
12
+ display_cards
19
13
  end
20
14
  end
@@ -0,0 +1,13 @@
1
+ module Cardigan
2
+ module Command
3
+ class ShowEntry
4
+ def initialize entry, io
5
+ @entry, @io = entry, io
6
+ end
7
+
8
+ def execute ignored=nil
9
+ @entry.keys.sort.each { |key| @io.say "#{key}: #{@entry[key].inspect}" }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,20 +1,19 @@
1
- module Cardigan
2
- module Command
3
- class SpecifyDisplayColumns
4
- def initialize repository, io
5
- @repository, @io = repository, io
6
- end
1
+ require 'cardigan/command/display_cards'
7
2
 
8
- def execute text
9
- if text
10
- @repository.display_columns = text.scan(/\w+/)
11
- else
12
- @io.say "current columns: #{@repository.display_columns.join(',')}"
13
- columns = Set.new
14
- @repository.cards.each {|card| columns += card.keys }
15
- @io.say "available columns: #{columns.sort.join(',')}"
16
- end
17
- end
3
+ class Cardigan::Command::SpecifyDisplayColumns
4
+ include Cardigan::Command::DisplayCards
5
+
6
+ def initialize repository, io
7
+ @repository, @io = repository, io
8
+ end
9
+
10
+ def execute text
11
+ if text
12
+ @repository.display_columns = text.scan(/\w+/)
13
+ display_cards
14
+ else
15
+ @io.say "current columns: #{@repository.display_columns.join(',')}"
16
+ @io.say "available columns: #{@repository.columns.sort.join(',')}"
18
17
  end
19
18
  end
20
19
  end
@@ -1,20 +1,19 @@
1
- module Cardigan
2
- module Command
3
- class SpecifySortColumns
4
- def initialize repository, io
5
- @repository, @io = repository, io
6
- end
1
+ require 'cardigan/command/display_cards'
7
2
 
8
- def execute text
9
- if text
10
- @repository.sort_columns = text.scan(/\w+/)
11
- else
12
- @io.say "current columns: #{@repository.sort_columns.join(',')}"
13
- columns = Set.new
14
- @repository.cards.each {|card| columns += card.keys }
15
- @io.say "available columns: #{columns.sort.join(',')}"
16
- end
17
- end
3
+ class Cardigan::Command::SpecifySortColumns
4
+ include Cardigan::Command::DisplayCards
5
+
6
+ def initialize repository, io
7
+ @repository, @io = repository, io
8
+ end
9
+
10
+ def execute text
11
+ if text
12
+ @repository.sort_columns = text.scan(/\w+/)
13
+ display_cards
14
+ else
15
+ @io.say "current columns: #{@repository.sort_columns.join(',')}"
16
+ @io.say "available columns: #{@repository.columns.sort.join(',')}"
18
17
  end
19
18
  end
20
19
  end
@@ -0,0 +1,16 @@
1
+ class Cardigan::Command::TotalCards
2
+ def initialize repository, io
3
+ @repository, @io = repository, io
4
+ end
5
+
6
+ def execute text
7
+ count_field, *grouping_fields = text.scan(/\w+/)
8
+ counts = {}
9
+ @repository.cards.each do |card|
10
+ key = grouping_fields.map {|key| card[key] ? card[key] : ''}
11
+ value = card[count_field].to_i
12
+ counts[key] = counts[key] ? counts[key] + value : value
13
+ end
14
+ counts.keys.sort.each {|key| @io.say "#{counts[key]} #{key.inspect}"}
15
+ end
16
+ end
@@ -1,7 +1,17 @@
1
1
  require 'readline'
2
+ require 'rubygems'
3
+ require 'active_support/inflector'
2
4
 
3
5
  module Cardigan
6
+ module Command
7
+ end
8
+
4
9
  module Context
10
+ def command name, *args
11
+ require "cardigan/command/#{name}"
12
+ Command.const_get(name.to_s.camelize).new(*args)
13
+ end
14
+
5
15
  def refresh
6
16
  commands = respond_to?(:refresh_commands) ? refresh_commands : []
7
17
  Readline.completion_proc = lambda do |text|
@@ -7,30 +7,16 @@ module Cardigan
7
7
  def initialize io, workflow_repository, entry
8
8
  @io, @workflow_repository, @entry = io, workflow_repository, entry
9
9
  @prompt_text = "#{File.expand_path('.').split('/').last.slice(0..0)}/#{entry['name']} > "
10
- @commands = {}
10
+ @commands = {
11
+ 'now' => command(:change_status, @entry),
12
+ 'set' => command(:change_value, @entry, @io),
13
+ 'list' => command(:show_entry, @entry, @io)
14
+ }
11
15
  end
12
16
 
13
17
  def refresh_commands
14
- commands = ['set', 'show']
15
18
  status = @entry['status'] || 'none'
16
- @workflow_repository.load[status].each do |s|
17
- commands << "now #{s}"
18
- end
19
- commands
20
- end
21
-
22
- def now_command status
23
- @entry['status'] = status
24
- end
25
-
26
- def set_command key
27
- @entry[key] = @io.ask("Enter the new value for #{key}")
28
- end
29
-
30
- def show_command ignored=nil
31
- @entry.keys.sort.each do |key|
32
- @io.say "#{key}: #{@entry[key]}"
33
- end
19
+ @workflow_repository.load[status].map { |s| "now #{s}" }
34
20
  end
35
21
  end
36
22
  end
@@ -6,7 +6,7 @@ module Cardigan
6
6
 
7
7
  extend Forwardable
8
8
 
9
- def_delegators :@repository, :refresh, :save, :destroy, :find_or_create
9
+ def_delegators :@repository, :refresh, :save, :destroy, :find_or_create, :columns, :load, :create
10
10
 
11
11
  def initialize repository, user, *columns
12
12
  @repository, @sort_columns, @display_columns, @user = repository, columns, columns, user
@@ -22,6 +22,10 @@ module Cardigan
22
22
  end
23
23
  end
24
24
 
25
+ def each
26
+ cards.each {|card| yield card }
27
+ end
28
+
25
29
  def max_field_length name
26
30
  cards.map {|card| card[name] ? card[name].length : 0 }.max
27
31
  end
@@ -1,3 +1,6 @@
1
+ require 'rubygems'
2
+ require 'uuidtools'
3
+ require 'set'
1
4
  require 'cardigan/directory'
2
5
 
3
6
  module Cardigan
@@ -26,10 +29,26 @@ module Cardigan
26
29
  end
27
30
 
28
31
  def find_or_create name
29
- card = find_card(name)
30
- card or { 'id' => UUIDTools::UUID.random_create.to_s,
31
- 'name' => name
32
- }
32
+ find_card(name) or create('name' => name)
33
+ end
34
+
35
+ def each
36
+ @cards.each {|card| yield card}
37
+ end
38
+
39
+ def columns
40
+ columns = Set.new
41
+ each {|card| columns += card.keys }
42
+ columns.to_a
43
+ end
44
+
45
+ def load id
46
+ file = "#{id}.card"
47
+ @directory.has_file?(file) ? @directory.load(file) : nil
48
+ end
49
+
50
+ def create hash={}
51
+ { 'id' => UUIDTools::UUID.random_create.to_s }.merge hash
33
52
  end
34
53
  end
35
54
  end
@@ -1,20 +1,5 @@
1
- require 'rubygems'
2
- require 'uuidtools'
3
- require 'set'
4
1
  require 'cardigan/context'
5
2
  require 'cardigan/filtered_repository'
6
- require 'cardigan/command/batch_update_cards'
7
- require 'cardigan/command/claim_cards'
8
- require 'cardigan/command/create_card'
9
- require 'cardigan/command/destroy_cards'
10
- require 'cardigan/command/filter_cards'
11
- require 'cardigan/command/list_cards'
12
- require 'cardigan/command/open_card'
13
- require 'cardigan/command/open_workflow'
14
- require 'cardigan/command/specify_display_columns'
15
- require 'cardigan/command/specify_sort_columns'
16
- require 'cardigan/command/unclaim_cards'
17
- require 'cardigan/command/unfilter_cards'
18
3
 
19
4
  module Cardigan
20
5
  class RootContext
@@ -24,18 +9,22 @@ module Cardigan
24
9
  @io, @repository, @name, @workflow_repository = io, FilteredRepository.new(repository, name, 'name'), name, workflow_repository
25
10
  @prompt_text = "#{File.expand_path('.').split('/').last} > "
26
11
  @commands = {
27
- 'claim' => Command::ClaimCards.new(@repository, @io),
28
- 'create' => Command::CreateCard.new(@repository),
29
- 'destroy' => Command::DestroyCards.new(@repository, @io),
30
- 'display' => Command::SpecifyDisplayColumns.new(@repository, @io),
31
- 'filter' => Command::FilterCards.new(@repository, @io),
32
- 'list' => Command::ListCards.new(@repository, @io),
33
- 'open' => Command::OpenCard.new(@repository, @workflow_repository, @io),
34
- 'set' => Command::BatchUpdateCards.new(@repository, @io),
35
- 'sort' => Command::SpecifySortColumns.new(@repository, @io),
36
- 'unclaim' => Command::UnclaimCards.new(@repository, @io),
37
- 'unfilter' => Command::UnfilterCards.new(@repository),
38
- 'workflow' => Command::OpenWorkflow.new(@workflow_repository, @io)
12
+ 'claim' => command(:claim_cards, @repository, @io, @name),
13
+ 'create' => command(:create_card, @repository),
14
+ 'destroy' => command(:destroy_cards, @repository, @io),
15
+ 'display' => command(:specify_display_columns, @repository, @io),
16
+ 'filter' => command(:filter_cards, @repository, @io),
17
+ 'list' => command(:list_cards, @repository, @io),
18
+ 'open' => command(:open_card, @repository, @workflow_repository, @io),
19
+ 'set' => command(:batch_update_cards, @repository, @io),
20
+ 'sort' => command(:specify_sort_columns, @repository, @io),
21
+ 'unclaim' => command(:unclaim_cards, @repository, @io),
22
+ 'unfilter' => command(:unfilter_cards, @repository),
23
+ 'workflow' => command(:open_workflow, @workflow_repository, @io),
24
+ 'count' => command(:count_cards, @repository, @io),
25
+ 'total' => command(:total_cards, @repository, @io),
26
+ 'export' => command(:export_cards, @repository),
27
+ 'import' => command(:import_cards, @repository, @io)
39
28
  }
40
29
  end
41
30
 
@@ -0,0 +1,3 @@
1
+ class Symbol
2
+ def
3
+ end
@@ -7,11 +7,13 @@ module Cardigan
7
7
  def initialize io, entry
8
8
  @io, @entry = io, entry
9
9
  @prompt_text = "#{File.expand_path('.').split('/').last.slice(0..0)}/workflow > "
10
- @commands = {}
10
+ @commands = {
11
+ 'list' => command(:show_entry, @entry, @io)
12
+ }
11
13
  end
12
14
 
13
15
  def refresh_commands
14
- ['create', 'add', 'remove', 'show']
16
+ ['create', 'add', 'remove']
15
17
  end
16
18
 
17
19
  def create_command key
@@ -27,11 +29,5 @@ module Cardigan
27
29
  name, *states = text.scan(/\w+/)
28
30
  @entry[name] -= states
29
31
  end
30
-
31
- def show_command ignored=nil
32
- @entry.keys.sort.each do |key|
33
- @io.say "#{key}: #{@entry[key].join(',')}"
34
- end
35
- end
36
32
  end
37
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cardigan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Ryall
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-03-14 00:00:00 +11:00
12
+ date: 2010-03-16 00:00:00 +11:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,6 +22,16 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 2.1.1
24
24
  version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: activesupport
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.5
34
+ version:
25
35
  description: |
26
36
  A command line utility that manages cards as individual files so that they can be added to a version control system
27
37
 
@@ -33,18 +43,27 @@ extensions: []
33
43
  extra_rdoc_files: []
34
44
 
35
45
  files:
46
+ - lib/cardigan/card_editor.rb
36
47
  - lib/cardigan/cli.rb
37
48
  - lib/cardigan/command/batch_update_cards.rb
49
+ - lib/cardigan/command/change_status.rb
50
+ - lib/cardigan/command/change_value.rb
38
51
  - lib/cardigan/command/claim_cards.rb
52
+ - lib/cardigan/command/count_cards.rb
39
53
  - lib/cardigan/command/create_card.rb
40
54
  - lib/cardigan/command/destroy_cards.rb
55
+ - lib/cardigan/command/display_cards.rb
56
+ - lib/cardigan/command/export_cards.rb
41
57
  - lib/cardigan/command/filter_cards.rb
58
+ - lib/cardigan/command/import_cards.rb
42
59
  - lib/cardigan/command/list_cards.rb
43
60
  - lib/cardigan/command/open_card.rb
44
61
  - lib/cardigan/command/open_workflow.rb
45
62
  - lib/cardigan/command/select_columns.rb
63
+ - lib/cardigan/command/show_entry.rb
46
64
  - lib/cardigan/command/specify_display_columns.rb
47
65
  - lib/cardigan/command/specify_sort_columns.rb
66
+ - lib/cardigan/command/total_cards.rb
48
67
  - lib/cardigan/command/unclaim_cards.rb
49
68
  - lib/cardigan/command/unfilter_cards.rb
50
69
  - lib/cardigan/context.rb
@@ -54,6 +73,7 @@ files:
54
73
  - lib/cardigan/io.rb
55
74
  - lib/cardigan/repository.rb
56
75
  - lib/cardigan/root_context.rb
76
+ - lib/cardigan/symbol_extensions.rb
57
77
  - lib/cardigan/text_report_formatter.rb
58
78
  - lib/cardigan/workflow_context.rb
59
79
  - lib/cardigan/workflow_repository.rb