ztodo 1.0.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.
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ coverage
5
+ doc/
6
+ lib/bundler/man
7
+ pkg/*
8
+ rdoc
9
+ spec/reports
10
+ tmp/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ztodo.gemspec
4
+ gemspec
5
+
6
+
7
+ gem 'rspec'
8
+ gem 'colorize'
data/Gemfile.lock ADDED
@@ -0,0 +1,26 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ztodo (1.0.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ colorize (0.5.8)
10
+ diff-lcs (1.1.3)
11
+ rspec (2.12.0)
12
+ rspec-core (~> 2.12.0)
13
+ rspec-expectations (~> 2.12.0)
14
+ rspec-mocks (~> 2.12.0)
15
+ rspec-core (2.12.0)
16
+ rspec-expectations (2.12.0)
17
+ diff-lcs (~> 1.1.3)
18
+ rspec-mocks (2.12.0)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ colorize
25
+ rspec
26
+ ztodo!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Andreas Sotnik <andreas.sotnik@gmail.com>
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # ztodo
2
+
3
+ Simple Console Task Manager.
4
+
5
+ ## Installation
6
+
7
+ Install gem:
8
+
9
+ $ gem install ztodo
10
+
11
+ ## Basic Usage
12
+
13
+ Initializing ztodo project in current folder:
14
+
15
+ $ ztodo init
16
+
17
+ List of uncompleted tasks (or all tasks in project):
18
+
19
+ $ ztodo list [all]
20
+
21
+ Add task to project (%key% determines task in project, unique identifier):
22
+
23
+ $ ztodo add
24
+
25
+ Modify task:
26
+
27
+ $ ztodo modify %key%
28
+
29
+ Remove task:
30
+
31
+ $ ztodo remove %key%
32
+
33
+ You may call ztodo without parameters for getting help and information about current project:
34
+
35
+ $ ztodo
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+ task :default => :spec
data/bin/ztodo.rb ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ztodo'
3
+
4
+ c = Ztodo::Controller.new
5
+ c.execute! ARGV
@@ -0,0 +1,158 @@
1
+ require 'colorize'
2
+ =begin
3
+ Class for processing user commands.
4
+ This class uses Ztodo::Project and Ztodo::Converter classes
5
+ =end
6
+ class Ztodo::Controller
7
+
8
+ def initialize
9
+ @proj = Ztodo::Project.new
10
+ @conv = Ztodo::Converter.new
11
+ end
12
+
13
+ # Execute necessary command
14
+ def execute! args
15
+ return cmd_help if args.empty?
16
+
17
+ handler = args[0]
18
+
19
+ if methods.include? ('cmd_'+handler).to_sym
20
+ args.slice! 0
21
+ method(('cmd_'+handler).to_sym).call args
22
+ else
23
+ cmd_help
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ # Print help message
30
+ def cmd_help params=[]
31
+ puts 'ztodo - Simple Command Line Task Manager'.light_blue
32
+ puts 'Usage:'
33
+
34
+ puts '> ztodo'.green
35
+ puts '> ztodo help'.green
36
+ puts '> ztodo init'.yellow
37
+ puts '> ztodo init hard'.yellow
38
+ puts '> ztodo list'.green
39
+ puts '> ztodo list all'.green
40
+ puts '> ztodo add'.yellow
41
+ puts '> ztodo add %task-key%'.yellow
42
+ puts '> ztodo modify %task-key%'.yellow
43
+ puts '> ztodo remove %task-key%'.yellow
44
+ end
45
+
46
+ def cmd_init params=[]
47
+ if File.exists?(Dir.pwd+'/.ztodo') && params[0] != 'hard'
48
+ puts "ztodo project already existed. Run 'ztodo init hard' for re-init".red
49
+ return
50
+ end
51
+ @proj.init!
52
+ puts 'ztodo project initialized.'.green
53
+ end
54
+
55
+ def format_task task
56
+ puts task[:key].to_s+' '+ @conv.colored_priority(task[:priority]) +
57
+ ' ('+(task[:done] ? 'done'.green : 'undone'.yellow)+')'
58
+ puts task[:description].to_s.light_blue
59
+ puts ''
60
+ end
61
+
62
+ def cmd_list params=[]
63
+ load_or_die
64
+ show_all_tasks = params[0] == 'all'
65
+
66
+ tasks = @proj.tasks(show_all_tasks)
67
+ puts ''
68
+ orig = []
69
+ tasks.each do |key, task|
70
+ ind = (task[:done] ? 5 : 0) - task[:priority] + 1
71
+ orig[ind] = [] if orig[ind].nil?
72
+ orig[ind].push task
73
+ end
74
+
75
+ orig.each do |task_list|
76
+ if task_list.class == Array
77
+ task_list.each do |task|
78
+ format_task task
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ def cmd_add params=[]
85
+ load_or_die
86
+ task = @proj.create
87
+ original_key = params.join(' ')
88
+ unless original_key.empty?
89
+ task[:key] = original_key.to_sym
90
+ end
91
+ File.open(Dir.pwd+'/.ztodo-tmp', 'w+') {|f| f.write(@conv.hash_to_str task) }
92
+
93
+ exit unless system("editor .ztodo-tmp")
94
+
95
+ File.open(Dir.pwd+'/.ztodo-tmp') { |f|
96
+ task = @conv.str_to_hash f.read
97
+
98
+ begin
99
+ @proj.add task
100
+ rescue Exception, e
101
+ puts 'Task with such key already exists. Postfix added'.yellow
102
+ task[:key] += Random.rand(1000).to_s
103
+ @proj.add task
104
+ end
105
+ @proj.save!
106
+ }
107
+
108
+ File.delete Dir.pwd+'/.ztodo-tmp'
109
+ end
110
+
111
+ def cmd_modify params=[]
112
+ load_or_die
113
+ original_key = params.join(' ').to_sym
114
+ task = @proj.tasks(true)[original_key]
115
+ if task.nil?
116
+ puts 'Invalid task key'.red
117
+ exit
118
+ end
119
+ File.open(Dir.pwd+'/.ztodo-tmp', 'w+') {|f| f.write(@conv.hash_to_str task) }
120
+
121
+ exit unless system("editor .ztodo-tmp")
122
+
123
+ File.open(Dir.pwd+'/.ztodo-tmp') { |f|
124
+ task = @conv.str_to_hash f.read
125
+
126
+ begin
127
+ @proj.modify original_key, task
128
+ rescue Exception, e
129
+ puts 'Task with such key already exists. Postfix added'.yellow
130
+ task[:key] += Random.rand(1000).to_s
131
+ @proj.modify original_key, task
132
+ end
133
+ @proj.save!
134
+ }
135
+
136
+ File.delete Dir.pwd+'/.ztodo-tmp'
137
+ end
138
+
139
+ def cmd_remove params=[]
140
+ load_or_die
141
+ begin
142
+ @proj.remove params.join(' ').to_sym
143
+ rescue Exception, e
144
+ puts 'Invalid task key'.red
145
+ end
146
+ @proj.save!
147
+ end
148
+
149
+ def load_or_die
150
+ unless File.exists? Dir.pwd+'/.ztodo'
151
+ puts 'ztodo project is not initialized! Run "ztodo init"'.red
152
+ exit
153
+ end
154
+
155
+ @proj.load!
156
+ end
157
+
158
+ end
@@ -0,0 +1,106 @@
1
+ =begin
2
+ Class for converting task hashes to strings and vice versa
3
+ =end
4
+ class Ztodo::Converter
5
+
6
+ # Convert string representation of task to hash
7
+ def str_to_hash str
8
+ out = {}
9
+ data = str.split("\n")
10
+ out[:key] = parse_key_line data[0]
11
+ out[:priority] = parse_priority_line data[1]
12
+ out[:done] = parse_completeness_line data[2]
13
+ out[:description] = extract_description data.clone
14
+ out
15
+ end
16
+
17
+ # Convert hash representation of task to string
18
+ def hash_to_str hash
19
+ out = ''
20
+ out += hash[:key].to_s+" # unique identifier of task\n"
21
+ out += format_priority(hash[:priority])+" # priority: 'low', 'normal' or 'high'\n"
22
+ out += (hash[:done] ? 'done' : 'undone' )+" # task completeness: 'done' or 'undone'\n"
23
+ out += hash[:description].to_s
24
+ out
25
+ end
26
+
27
+ def colored_priority int_val
28
+ require 'colorize'
29
+ if int_val == Ztodo::Project::LOW
30
+ 'low'.green
31
+ elsif int_val == Ztodo::Project::NORMAL
32
+ 'normal'.yellow
33
+ elsif int_val == Ztodo::Project::HIGH
34
+ 'high'.red
35
+ else
36
+ raise Exception.new('Priority should be -1, 0 or 1. Given: '+int_val)
37
+ end
38
+ end
39
+
40
+ protected
41
+
42
+ # Format priority integer value as string
43
+ def format_priority int_val
44
+ if int_val == Ztodo::Project::LOW
45
+ 'low'
46
+ elsif int_val == Ztodo::Project::NORMAL
47
+ 'normal'
48
+ elsif int_val == Ztodo::Project::HIGH
49
+ 'high'
50
+ else
51
+ raise Exception.new('Priority should be -1, 0 or 1. Given: '+int_val)
52
+ end
53
+ end
54
+
55
+ # Parse line with unique identifier
56
+ def parse_key_line line
57
+ line = clear_line line
58
+ if line.nil? || line.empty?
59
+ 'task'+Random.rand(1000).to_s
60
+ else
61
+ line
62
+ end
63
+ end
64
+
65
+ # Parse line with priority data
66
+ def parse_priority_line line
67
+ return Ztodo::Project::NORMAL if line.nil? || line.empty?
68
+ line = clear_line line
69
+
70
+ if line == 'low'
71
+ Ztodo::Project::LOW
72
+ elsif line == 'high'
73
+ Ztodo::Project::HIGH
74
+ else # 'normal' and any other option
75
+ Ztodo::Project::NORMAL
76
+ end
77
+ end
78
+
79
+ # Parse line with completeness data
80
+ def parse_completeness_line line
81
+ line = clear_line line
82
+ return line == 'done'
83
+ end
84
+
85
+
86
+ # Return line without commented part
87
+ def clear_line line
88
+ return nil if line.nil?
89
+
90
+ if line.index('#').nil?
91
+ res = line
92
+ else
93
+ res = line[0,line.index('#')]
94
+ end
95
+ res.strip!
96
+ return res
97
+ end
98
+
99
+ # Return description data
100
+ def extract_description splitted_data
101
+ return '' if splitted_data.size<=3
102
+ 3.times { splitted_data.slice! 0 }
103
+ splitted_data.join "\n"
104
+ end
105
+
106
+ end
@@ -0,0 +1,73 @@
1
+ require 'yaml'
2
+
3
+ =begin
4
+ Represent Project object.
5
+ =end
6
+ class Ztodo::Project
7
+
8
+ attr_reader :loaded
9
+
10
+ LOW = -1
11
+ NORMAL = 0
12
+ HIGH = 1
13
+
14
+ def initialize
15
+ @loaded = false
16
+ @data = {}
17
+ end
18
+
19
+ # Init project file (.ztodo) in current directory
20
+ def init!
21
+ @data = {}
22
+ save!
23
+ end
24
+
25
+ # load project from file in current directory
26
+ def load!
27
+ raise Exception.new('ztodo project file is not exists') unless File.exists?(Dir.pwd+'/.ztodo')
28
+
29
+ @data = YAML::load(File.open(Dir.pwd+'/.ztodo'))
30
+ @loaded = true
31
+ end
32
+
33
+ # Save project to file in current directory
34
+ def save!
35
+ File.open(Dir.pwd + '/.ztodo', 'w+') { |f| f.write(@data.to_yaml) }
36
+ @loaded = true
37
+ end
38
+
39
+ # Return all tasks
40
+ def tasks show_all = FALSE
41
+ return @data.reject {|k,v| v[:done]} if !show_all
42
+ @data.clone
43
+ end
44
+
45
+ # Add task
46
+ # Raise en exception if key is already used
47
+ def add task
48
+ raise Exception.new('Such key already exists') if @data.include?(task[:key].to_sym)
49
+ @data[task[:key].to_sym] = task
50
+ end
51
+
52
+ # Modify task
53
+ # Raise en exception if there is no task with such key
54
+ def modify key, task
55
+ raise Exception.new('No task with such key') unless @data.include?(key.to_sym)
56
+ @data.delete key
57
+ add task
58
+ end
59
+
60
+ # Remove task
61
+ # Raise en exception if there is no task with such key
62
+ def remove key
63
+ raise Exception.new('No task with such key') unless @data.include?(key.to_sym)
64
+ @data.delete key
65
+ end
66
+
67
+ # Create task (but not add it to data)
68
+ def create
69
+ {:key=>'key', :description=>'put description here',
70
+ :done=>false, :priority=> Ztodo::Project::NORMAL}
71
+ end
72
+
73
+ end
@@ -0,0 +1,3 @@
1
+ module Ztodo
2
+ VERSION = "1.0.0"
3
+ end
data/lib/ztodo.rb ADDED
@@ -0,0 +1,10 @@
1
+ require "ztodo/version"
2
+ require "ztodo/controller"
3
+ require "ztodo/project"
4
+ require "ztodo/converter"
5
+
6
+ =begin
7
+ Module for Console Task Manager
8
+ =end
9
+ module Ztodo
10
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ztodo::Converter do
4
+
5
+ before(:all) { @conv = Ztodo::Converter.new }
6
+
7
+ it 'can convert string to task hash' do
8
+ input = "test_title # unique identifier of task\n" +
9
+ "low # priority: 'low', 'normal' or 'high'\n" +
10
+ "done # task completeness: 'done' or 'undone'\n"+
11
+ "Here will be task\n" +
12
+ "Maybe in two lines"
13
+
14
+ output = {:key=>'test_title', :priority=>Ztodo::Project::LOW,
15
+ :description=>"Here will be task\nMaybe in two lines", :done=>true}
16
+
17
+ out = @conv.str_to_hash input
18
+ out.should eq(output)
19
+ end
20
+
21
+ it 'can convert task hash to string' do
22
+ input = {:key=>'my_task', :priority=>Ztodo::Project::HIGH,
23
+ :description=>"Task description\nwith two lines", :done=>true}
24
+
25
+ output = "my_task # unique identifier of task\n" +
26
+ "high # priority: 'low', 'normal' or 'high'\n" +
27
+ "done # task completeness: 'done' or 'undone'\n"+
28
+ "Task description\nwith two lines"
29
+
30
+ out = @conv.hash_to_str input
31
+ out.should eq(output)
32
+ end
33
+
34
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ztodo::Project do
4
+
5
+ it 'has methods: init, save and load' do
6
+ methods = [:init!, :save!, :load!]
7
+ methods.each { |method_name| Ztodo::Project.methods.include?(method_name) }
8
+ end
9
+
10
+ it 'can add tasks' do
11
+ p = Ztodo::Project.new
12
+ task = {:key=>'task_A', :priority=>Ztodo::Project::NORMAL,
13
+ :description=>"description for A task", :done=>false}
14
+ p.add task
15
+ tasks = p.tasks
16
+ tasks[:task_A].should eq(task)
17
+ end
18
+
19
+ it 'can modify tasks' do
20
+ p = Ztodo::Project.new
21
+ task = {:key=>'task_A', :priority=>Ztodo::Project::NORMAL,
22
+ :description=>"description for A task", :done=>false}
23
+ task2 = {:key=>'task_B', :priority=>Ztodo::Project::HIGH,
24
+ :description=>"description for B task", :done=>true}
25
+ p.add task
26
+ p.modify :task_A, task2
27
+
28
+ tasks = p.tasks true
29
+ tasks[:task_A].should eq(nil)
30
+ tasks[:task_B].should eq(task2)
31
+ end
32
+
33
+ it 'can delete tasks' do
34
+ p = Ztodo::Project.new
35
+ task = {:key=>'task_A', :priority=>Ztodo::Project::NORMAL,
36
+ :description=>"description for A task", :done=>false}
37
+ p.add task
38
+ p.tasks(true)[:task_A].should eq(task)
39
+ p.remove :task_A
40
+ p.tasks(true)[:task_A].should eq(nil)
41
+ end
42
+
43
+ it 'can list uncompleted tasks' do
44
+ p = Ztodo::Project.new
45
+
46
+ task = {:key=>'task_A', :priority=>Ztodo::Project::NORMAL,
47
+ :description=>"description for A task", :done=>false}
48
+ task2 = {:key=>'task_B', :priority=>Ztodo::Project::NORMAL,
49
+ :description=>"description for B task", :done=>true}
50
+ p.add task
51
+ p.add task2
52
+
53
+ tasks = p.tasks
54
+ tasks[:task_B].should eq(nil)
55
+ tasks[:task_A].should eq(task)
56
+
57
+ tasks = p.tasks true
58
+ tasks.size.should eq(2)
59
+ end
60
+
61
+ end
@@ -0,0 +1,27 @@
1
+ require 'ztodo'
2
+
3
+ RSpec.configure do |config|
4
+ config.treat_symbols_as_metadata_keys_with_true_values = true
5
+ config.run_all_when_everything_filtered = true
6
+ config.filter_run :focus
7
+
8
+ config.order = 'random'
9
+
10
+ config.before(:all) do
11
+ if Dir.exists?(Dir.pwd + '/tmp')
12
+ @old_dir = Dir.pwd
13
+ Dir.chdir (Dir.pwd + '/tmp')
14
+ end
15
+ end
16
+
17
+ config.after(:all) do
18
+ unless Dir.pwd == @old_dir
19
+ Dir.chdir @old_dir
20
+ end
21
+ end
22
+
23
+ config.after(:each) do
24
+ File.delete(Dir.pwd+'/.todo') if File.exists?(Dir.pwd+'/.todo')
25
+ end
26
+
27
+ end
data/tmp/.gitkeep ADDED
File without changes
data/ztodo.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ztodo/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "ztodo"
8
+ gem.version = Ztodo::VERSION
9
+ gem.authors = ["Andreas Sotnik"]
10
+ gem.email = ["andreas.sotnik@gmail.com"]
11
+ gem.description = 'Simple Console Task Manager. Check http://zenturio.me/pages/ztodo for further information.'
12
+ gem.summary = 'Simple Console Task Manager'
13
+ gem.homepage = "http://zenturio.me/pages/ztodo"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency('colorize')
21
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ztodo
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andreas Sotnik
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: colorize
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Simple Console Task Manager. Check http://zenturio.me/pages/ztodo for
31
+ further information.
32
+ email:
33
+ - andreas.sotnik@gmail.com
34
+ executables:
35
+ - ztodo.rb
36
+ extensions: []
37
+ extra_rdoc_files: []
38
+ files:
39
+ - .gitignore
40
+ - .rspec
41
+ - Gemfile
42
+ - Gemfile.lock
43
+ - LICENSE.txt
44
+ - README.md
45
+ - Rakefile
46
+ - bin/ztodo.rb
47
+ - lib/ztodo.rb
48
+ - lib/ztodo/controller.rb
49
+ - lib/ztodo/converter.rb
50
+ - lib/ztodo/project.rb
51
+ - lib/ztodo/version.rb
52
+ - spec/converter_spec.rb
53
+ - spec/project_spec.rb
54
+ - spec/spec_helper.rb
55
+ - tmp/.gitkeep
56
+ - ztodo.gemspec
57
+ homepage: http://zenturio.me/pages/ztodo
58
+ licenses: []
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 1.8.24
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Simple Console Task Manager
81
+ test_files:
82
+ - spec/converter_spec.rb
83
+ - spec/project_spec.rb
84
+ - spec/spec_helper.rb