cardigan 0.0.5 → 0.0.6

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/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