new 0.1.1 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +13 -5
  2. data/bin/new +0 -2
  3. data/lib/new.rb +125 -52
  4. data/lib/new/cli.rb +514 -67
  5. data/lib/new/source.rb +110 -0
  6. data/lib/new/task.rb +67 -24
  7. data/lib/new/validation.rb +191 -0
  8. metadata +75 -132
  9. data/.gitignore +0 -6
  10. data/.new +0 -23
  11. data/.rspec +0 -2
  12. data/Gemfile +0 -16
  13. data/Gemfile.lock +0 -82
  14. data/Guardfile +0 -16
  15. data/LICENSE.txt +0 -22
  16. data/README.md +0 -128
  17. data/lib/new/core.rb +0 -7
  18. data/lib/new/dsl.rb +0 -42
  19. data/lib/new/interpolate.rb +0 -106
  20. data/lib/new/project.rb +0 -34
  21. data/lib/new/template.rb +0 -67
  22. data/lib/new/version.rb +0 -54
  23. data/spec/fixtures/custom/.new +0 -16
  24. data/spec/fixtures/custom/tasks/custom_bar_task/custom_bar_task.rb +0 -3
  25. data/spec/fixtures/custom/templates/custom_bar_template/.new +0 -3
  26. data/spec/fixtures/custom/templates/custom_bar_template/custom_bar.txt +0 -0
  27. data/spec/fixtures/project/.new +0 -4
  28. data/spec/fixtures/project/.new_cli_release_spec +0 -2
  29. data/spec/fixtures/tasks/custom_bar_task/custom_bar_task.rb +0 -1
  30. data/spec/fixtures/tasks/foo_task/Gemfile +0 -5
  31. data/spec/fixtures/tasks/foo_task/foo_task.rb +0 -7
  32. data/spec/fixtures/templates/custom_bar_template/.gitkeep +0 -0
  33. data/spec/fixtures/templates/foo_template/.new +0 -1
  34. data/spec/fixtures/templates/foo_template/[FOO.BAR].txt.erb +0 -1
  35. data/spec/fixtures/templates/foo_template/nested_[FOO.BAR]/foo.txt.erb +0 -1
  36. data/spec/lib/new/cli_spec.rb +0 -107
  37. data/spec/lib/new/interpolate_spec.rb +0 -43
  38. data/spec/lib/new/project_spec.rb +0 -33
  39. data/spec/lib/new/task_spec.rb +0 -39
  40. data/spec/lib/new/template_spec.rb +0 -59
  41. data/spec/lib/new/version_spec.rb +0 -26
  42. data/spec/lib/new_spec.rb +0 -19
  43. data/spec/spec_helper.rb +0 -46
  44. data/tasks/gem/.gemspec.erb +0 -4
  45. data/tasks/gem/README.md +0 -36
  46. data/tasks/gem/gem.rb +0 -170
  47. data/tasks/gem/gem_spec.rb +0 -138
  48. data/templates/js/.bowerrc +0 -3
  49. data/templates/js/.gitignore +0 -3
  50. data/templates/js/.new.erb +0 -11
  51. data/templates/js/CHANGELOG.md +0 -3
  52. data/templates/js/Gemfile +0 -2
  53. data/templates/js/Guardfile +0 -7
  54. data/templates/js/LICENSE-MIT.erb +0 -22
  55. data/templates/js/README.md.erb +0 -41
  56. data/templates/js/bower.json.erb +0 -11
  57. data/templates/js/demo/[PROJECT.FILENAME]_demo.coffee +0 -0
  58. data/templates/js/demo/[PROJECT.FILENAME]_demo.sass +0 -0
  59. data/templates/js/demo/index.html.erb +0 -12
  60. data/templates/js/lib/README.md +0 -2
  61. data/templates/js/package.json +0 -5
  62. data/templates/js/spec/[PROJECT.FILENAME]_spec.coffee.erb +0 -1
  63. data/templates/js/spec/[PROJECT.FILENAME]_spec.sass +0 -0
  64. data/templates/js/spec/index.html.erb +0 -35
  65. data/templates/js/src/[PROJECT.FILENAME].coffee.erb +0 -18
  66. data/templates/js/src/[PROJECT.FILENAME].sass +0 -0
  67. data/templates/js/testem.yml +0 -23
  68. data/templates/js/yuyi_menu +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 49de7a525567990df4a73895129d9016fd6cb025
4
- data.tar.gz: a7187f5f73de9b3ccc126306e8243c8ad3377cde
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZjNlNjUyMWY0NjY3ZjBjZThkZTM0MjQwODU1YTllYTNhNjYwZDBlZA==
5
+ data.tar.gz: !binary |-
6
+ NmQ4NmQ4NTUwZWU3ZTAwNmFkZTJkMjUzOTYwN2Y0M2FjMDc2MzExZg==
5
7
  SHA512:
6
- metadata.gz: d2ee8c15e70e71526ba0681e8d81686e06c80eab051470938e26d89d3dbe6d37ea64ed996bb3da5baf065a48df299aadfddd7b80af946726112b21358d9f8221
7
- data.tar.gz: f2784493ad68df62dc988f31d98b5045c1fdebf39b8f1442f3d975903d1d9470e9de1ca43444bb335d9d25d1e3f870da53a7c07c75848e852a802fcc02a01f76
8
+ metadata.gz: !binary |-
9
+ NmE3NjBmODFiNDMxODkzNTFmYTAzMjA5YTlhMzEwZWQ4NDU4MmI0MzhiOTJm
10
+ NzczOGVjOWY2Yzk2NzQ5NTI5YThhMGJkZDBiN2U3MzJkNTA4MDFjYTQ1N2Fk
11
+ NjM2MzZkZDFhYTUwZDQ3MzFiY2RlZjZjMGQwZmZhZGE4MDlmM2Q=
12
+ data.tar.gz: !binary |-
13
+ MzlhMWQ4OWFjYmNhMTQ5YzYxMWNiM2I4OGVlNDA3ZmRkNzViMzM5ODU4ZTYz
14
+ MTU0NmIyMGU3YTgwNmFlOGVlNTQ0M2Y5YTA1ZGZlMmU2YmZkODcyMWQ4YzRl
15
+ MDI2NzE4ODRlZDJjZDFiNDI1NWY0NjIzMzcxZjZmMDc2ZDFjNDc=
data/bin/new CHANGED
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
  $: << File.dirname(__FILE__) + '/../lib'
3
-
4
3
  require 'new'
5
-
6
4
  New::Cli.start ARGV
data/lib/new.rb CHANGED
@@ -1,71 +1,144 @@
1
+ require 'active_support/core_ext/hash/deep_merge'
1
2
  require 'yaml'
2
3
 
4
+ # Allow true/false to respond to Boolean class
5
+ module Boolean; end
6
+ class TrueClass; include Boolean; end
7
+ class FalseClass; include Boolean; end
8
+
3
9
  class New
4
- VERSION = '0.0.1'
5
- DEFAULT_DIR = File.expand_path('../..', __FILE__)
6
- CUSTOM_DIR = File.expand_path('~/.new')
7
- TASKS_DIR_NAME = 'tasks'
8
- TEMPLATES_DIR_NAME = 'templates'
9
- CONFIG_FILE = '.new'
10
-
11
- # List all the available tasks
12
- #
13
- def self.tasks
14
- custom_tasks | default_tasks
15
- end
10
+ require 'new/validation'
11
+
12
+ require 'new/cli'
13
+ require 'new/source'
14
+ require 'new/task'
15
+
16
+ HOME_DIRECTORY = ENV['HOME']
17
+ PROJECT_DIRECTORY = Dir.pwd
18
+ NEWFILE_NAME = 'Newfile'
16
19
 
17
- # List all the available templates
18
20
  #
19
- def self.templates
20
- custom_templates | default_templates
21
- end
21
+ # CLASS METHODS
22
+ #
23
+ class << self
24
+ @@cli = false
25
+ @@verbose = false
26
+ @@new_object = {
27
+ :sources => {
28
+ :default => 'brewster1134/new-tasks'
29
+ }
30
+ }
22
31
 
23
- def self.default_templates
24
- @default_templates ||= get_list TEMPLATES_DIR_NAME, :default
25
- end
32
+ # access the current new object
33
+ def new_object; @@new_object; end
26
34
 
27
- def self.custom_templates
28
- @custom_templates ||= get_list TEMPLATES_DIR_NAME, :custom
29
- end
35
+ # set cli to true when initialized via cli
36
+ def set_cli; @@cli = true; end
30
37
 
31
- def self.default_tasks
32
- @default_tasks ||= get_list TASKS_DIR_NAME, :default
33
- end
38
+ # set verbose to true when set via cli
39
+ def set_verbose; @@verbose = true; end
40
+ def verbose; @@verbose; end
34
41
 
35
- def self.custom_tasks
36
- @custom_tasks ||= get_list TASKS_DIR_NAME, :custom
37
- end
42
+ # Load Newfile in home & project directory
43
+ #
44
+ def load_newfiles
45
+ load_newfile File.join(HOME_DIRECTORY, NEWFILE_NAME)
46
+ load_newfile File.join(PROJECT_DIRECTORY, NEWFILE_NAME)
47
+ end
48
+
49
+ # Merge symbolized hash data into global new object
50
+ #
51
+ # @param hash [Hash] A hash to be merged into existing data
52
+ #
53
+ # @return [Hash] New merged data
54
+ #
55
+ def new_object= hash
56
+ @@new_object.deep_merge! hash.deep_symbolize_keys
57
+ end
58
+
59
+ private
60
+
61
+ # Load newfile contents into the global new object
62
+ #
63
+ # @param newfile_path [String] Path to a valid Newfile
64
+ #
65
+ # @return [Hash] Hash of Newfile yaml contents
66
+ #
67
+ def load_newfile newfile_path
68
+ # check file exists
69
+ return false unless File.file? newfile_path
38
70
 
39
- def self.custom_config
40
- @custom_config ||= YAML.load(File.open(File.join(CUSTOM_DIR, CONFIG_FILE))).deep_symbolize_keys! rescue {}
71
+ # load Newfile yaml into global new_object hash
72
+ self.new_object = YAML.load File.read newfile_path
73
+ end
41
74
  end
42
75
 
43
76
  private
44
77
 
45
- def self.get_list dir, filter
46
- case filter
47
- when :default
48
- Dir[File.join(DEFAULT_DIR, dir, '**')]
49
- when :custom
50
- Dir[File.join(CUSTOM_DIR, dir, '**')]
51
- end.map{ |d| File.basename(d).to_sym }
52
- end
53
- end
78
+ def initialize version, changelog, *skip_tasks
79
+ # load newfiles and sources
80
+ New.load_newfiles unless @@cli
81
+ New::Source.load_sources
54
82
 
55
- # core
56
- require 'new/core'
83
+ # update options with new attributes
84
+ @@new_object[:version] = version
85
+ @@new_object[:changelog] = changelog
57
86
 
58
- # modules
59
- require 'new/dsl'
60
- require 'new/interpolate'
61
- require 'new/version'
87
+ # create new options to pass to task
88
+ new_options = @@new_object.dup
89
+ new_options.delete(:sources)
90
+ new_options.delete(:tasks)
62
91
 
63
- # classes
64
- require 'new/cli'
65
- require 'new/project'
66
- require 'new/template'
67
- require 'new/task'
92
+ new_tasks = []
93
+ @@new_object[:tasks].each do |task_name, task_options|
94
+ # skip tasks
95
+ next if skip_tasks.include? task_name.to_s
68
96
 
69
- class New
70
- extend New::Dsl
97
+ S.ay "Preparing `#{task_name}`", :header
98
+
99
+ # dupe task options
100
+ new_task_options = task_options ? task_options.dup : {}
101
+
102
+ # lookup and add task to array
103
+ task = New::Source.find_task task_name, new_task_options.delete(:source)
104
+ new_tasks << task
105
+
106
+ # add task options
107
+ new_options[:task_options] = new_task_options
108
+
109
+ # set options
110
+ task.options = new_options.dup
111
+
112
+ # validate task before running anything
113
+ S.ay 'Validating Options: ', :highlight_key
114
+ task.validate
115
+ S.ay 'OK', :highlight_value
116
+
117
+ # verify tasks
118
+ S.ay 'Verifying Task Dependencies: ', :highlight_key
119
+ task.verify
120
+ S.ay 'OK', :highlight_value
121
+ S.ay
122
+ end
123
+
124
+ # write new Newfile with new version
125
+ new_newfile = YAML.load(File.read(File.join(PROJECT_DIRECTORY, NEWFILE_NAME)))
126
+ new_newfile['version'] = version
127
+ File.open File.join(PROJECT_DIRECTORY, NEWFILE_NAME), 'w+' do |f|
128
+ f.write new_newfile.to_yaml
129
+ end
130
+
131
+ new_tasks.each do |task|
132
+ S.ay "Running `#{task.name}`", :header
133
+ task.run
134
+ end
135
+
136
+ # release summary
137
+ S.ay
138
+ S.ay 'Version ', :newline => false
139
+ S.ay "#{@@new_object[:version]}", :preset => :header, :newline => false
140
+ S.ay ' of ', :newline => false
141
+ S.ay "#{@@new_object[:name]}", :preset => :header, :newline => false
142
+ S.ay ' successfully released!'
143
+ end
71
144
  end
data/lib/new/cli.rb CHANGED
@@ -1,94 +1,541 @@
1
+ require 'active_support/core_ext/hash/reverse_merge'
2
+ require 'active_support/core_ext/hash/keys'
3
+ require 'cli_miami'
4
+ require 'listen'
5
+ require 'pp'
6
+ require 'semantic'
1
7
  require 'thor'
2
8
  require 'yaml'
3
9
 
10
+ CliMiami.set_preset :error, :color => :red
11
+ CliMiami.set_preset :fine_print, :color => :cyan
12
+ CliMiami.set_preset :header, :color => :green
13
+ CliMiami.set_preset :highlight_key, :indent => 2, :newline => false, :padding => 30, :justify => :rjust
14
+ CliMiami.set_preset :highlight_value, :color => :blue, :style => :bright, :indent => 1
15
+ CliMiami.set_preset :instruction, :color => :yellow, :indent => 2
16
+ CliMiami.set_preset :list_item, :indent => 2
17
+ CliMiami.set_preset :prompt, :color => :yellow, :style => :bold
18
+
19
+ # create a horizontal line
20
+ HR = -> {S.ay('-' * 32 , :header)}
21
+
4
22
  class New::Cli < Thor
5
- desc '[TEMPLATE] [NAME]', "Create a new project with a given template (#{New.templates.join(' ')})"
6
- # option :name, required: true
7
- def method_missing method, *args
8
- if New.templates.include? method.to_sym
9
- # Split args that look like options (i.e start with - or --) into a separate array
10
- positional_args, opts = Thor::Options.split args
11
-
12
- # extract name from args
13
- name = positional_args[0]
14
- raise Thor::RequiredArgumentMissingError unless name
15
-
16
- # Add all options here
17
- # Make sure to include required options up above as well so they show in the help menu
18
- parser = Thor::Options.new(
19
- # name: Thor::Option.new(:name, required: true, type: :string)
20
- )
21
-
22
- # The options hash is frozen in #initialize so you need to merge and re-assign
23
- self.options = options.merge(parser.parse(opts)).freeze
24
-
25
- # Dispatch the command
26
- project method, name
27
- else
28
- super
23
+ desc 'init', 'Create a Newfile for your project'
24
+ option :name, :type => :string, :aliases => ['-n'], :default => '', :desc => 'Your project name'
25
+ option :version, :type => :string, :aliases => ['-v'], :default => '', :desc => 'Your project\'s current version'
26
+ option :tasks, :type => :array, :aliases => ['-t'], :default => [], :desc => 'Tasks to run when releasing your project'
27
+ def init
28
+ New.load_newfiles
29
+ New::Source.load_sources
30
+
31
+ # initialize empty newfile object
32
+ newfile_object = {
33
+ :tasks => {}
34
+ }
35
+
36
+ # get valid name
37
+ name = @options['name']
38
+ until !name.empty?
39
+ name = A.sk 'Project Name:', :prompt
40
+ end
41
+ newfile_object[:name] = name
42
+
43
+ # get valid version
44
+ version = Semantic::Version.new(@options['version']) rescue nil
45
+ until !version.to_s.empty?
46
+ begin
47
+ response = A.sk 'Current Project Version:', :prompt
48
+ version = Semantic::Version.new response
49
+ rescue
50
+ S.ay "`#{response}` is not a valid semantic version (e.g. 1.2.3)", :error
51
+ end
52
+ end
53
+ newfile_object[:version] = version.to_s
54
+
55
+ # get tasks
56
+ tasks_list = []
57
+ @options['tasks'].each do |task|
58
+ tasks_list << New::Source.find_task(task)
59
+ end
60
+
61
+ # remove any empty tasks in case the user specified an invalid task
62
+ tasks_list.compact!
63
+
64
+ # if no tasks are specified, show all available tasks
65
+ if tasks_list.empty?
66
+ S.ay
67
+ self.tasks :show_source => true, :load_newfiles => false, :load_sources => false
68
+
69
+ S.ay 'Add multiple tasks by pressing ENTER after each one', :instruction
70
+ S.ay 'Enter tasks in the order you want them to run', :instruction
71
+ S.ay 'Enter both the source and the task (e.g. source#task)', :instruction
72
+ S.ay 'Enter an empty value to finish', :instruction
73
+
74
+ added_task = nil
75
+ until added_task == '' && !tasks_list.empty?
76
+ S.ay
77
+ added_task = A.sk 'Add a task:', :prompt
78
+
79
+ # if a task is entered, verify it exists
80
+ unless added_task.empty?
81
+
82
+ # find task
83
+ task = New::Source.find_task added_task
84
+
85
+ # add task to array
86
+ if task
87
+ tasks_list << task
88
+ end
89
+ end
90
+ end
91
+
92
+ S.ay
93
+ end
94
+
95
+ # output the summary so far (no task options entered yet)
96
+ padding = 10
97
+ S.ay 'Name:', :preset => :highlight_key, :padding => padding
98
+ S.ay name, :preset => :highlight_value
99
+ S.ay 'Version:', :preset => :highlight_key, :padding => padding
100
+ S.ay version.to_s, :preset => :highlight_value
101
+ S.ay 'Tasks:', :preset => :highlight_key, :padding => padding
102
+
103
+ # output first task without a padding so it looks nicer
104
+ tasks_dup = tasks_list.dup
105
+ first_task = tasks_dup.shift
106
+ S.ay "#{first_task.source.name}##{first_task.name}", :highlight_value
107
+ tasks_dup.each do |task|
108
+ S.ay "#{task.source.name}##{task.name}", :preset => :highlight_value, :indent => padding + 3
109
+ end
110
+ S.ay
111
+
112
+ tasks_list.each do |task|
113
+ HR.call
114
+ S.ay 'OK, now lets set options for', :highlight_key
115
+ S.ay task.name.to_s.upcase, :highlight_value
116
+ HR.call
117
+ S.ay
118
+
119
+ newfile_object[:tasks][task.name] = {}
120
+ task.class_options.each do |option_name, option_settings|
121
+ S.ay option_name.to_s, :preset => :highlight_key, :padding => 0
122
+ S.ay option_settings[:description], :highlight_value
123
+
124
+ # show default
125
+ default = option_settings[:default]
126
+ if default && !option_settings[:required]
127
+ default = case default
128
+ when Array then default.join(', ')
129
+ when Hash then default.keys.join(', ')
130
+ else default.to_s
131
+ end
132
+
133
+ S.ay "default: #{default}", :preset => :fine_print, :indent => option_name.length + 3
134
+ end
135
+
136
+ # GET USER INPUT FOR ARRAY TYPE
137
+ #
138
+ option_type = option_settings[:type]
139
+ case
140
+
141
+ # collect array option type values
142
+ when option_type == Array
143
+ # cast type onto all user input values (default is String)
144
+ klass = option_settings[:validation] || String
145
+
146
+ # collect array elements from the user
147
+ option_value = nil
148
+ until option_value
149
+ begin
150
+ option_value = get_array_from_user(klass)
151
+ option_value = New::Task.validate_option(option_name, option_settings, option_value)
152
+ rescue
153
+ option_value = nil
154
+ end
155
+ end
156
+
157
+ # collect hash option type values
158
+ when option_type == Hash
159
+ # loop through the expected keys from the validation and get users input
160
+ option_value = nil
161
+ until option_value
162
+ begin
163
+ option_value = get_hash_from_user(option_settings[:validation])
164
+ option_value = New::Task.validate_option(option_name, option_settings, option_value)
165
+ rescue
166
+ option_value = nil
167
+ end
168
+ end
169
+
170
+ # collect non array/hash option type value
171
+ else
172
+ option_value = nil
173
+ until option_value
174
+ A.sk '', :newline => false, :preset => :prompt do |response|
175
+ option_value = New::Task.validate_option(option_name, option_settings, response) rescue nil
176
+ end
177
+ end
178
+ end
179
+
180
+ newfile_object[:tasks][task.name][option_name] = option_value
181
+ S.ay
182
+ end
183
+ end
184
+
185
+ # write project Newfile
186
+ project_newfile = File.join New::PROJECT_DIRECTORY, New::NEWFILE_NAME
187
+ File.open project_newfile, 'w+' do |f|
188
+ f.write newfile_object.deep_stringify_keys.to_yaml
29
189
  end
190
+
191
+ # Success Message
192
+ S.ay "A `#{'Newfile'.green}` was successfully created for your project `#{name.to_s.green}`"
193
+ S.ay 'Open the file to verify the values are correct, and make any neccessary modifications.'
194
+ S.ay "You are now ready to run `#{'new release'.green}` to release your software into the wild!"
195
+ S.ay
30
196
  end
31
197
 
32
- desc 'init', 'Set up your home directory folder for storing custom templates and default configuration'
33
- def init
34
- if Dir.exists? New::CUSTOM_DIR
35
- New.say 'Home folder already exists.', type: :warn
36
- else
37
- # create folders
38
- New.say 'Creating home folder.', type: :success
39
- FileUtils.mkdir_p File.join(New::CUSTOM_DIR, New::TASKS_DIR_NAME)
40
- FileUtils.mkdir_p File.join(New::CUSTOM_DIR, New::TEMPLATES_DIR_NAME)
41
-
42
- # create config file
43
- New.say 'Creating default configuration file.', type: :success
44
- File.open File.join(New::CUSTOM_DIR, New::CONFIG_FILE), 'w' do |f|
45
- f.write New::Template::CUSTOM_CONFIG_TEMPLATE.deep_stringify_keys.to_yaml
198
+ desc 'tasks', 'List all available tasks'
199
+ option :sources, :type => :boolean, :aliases => ['-s'], :default => false, :desc => 'Show/Hide task sources'
200
+ def tasks args = {}
201
+ # merge into default options
202
+ options = {
203
+ :show_source => (@options['sources'] || false),
204
+ :load_newfiles => true,
205
+ :load_sources => true
206
+ }.merge(args)
207
+
208
+ New.load_newfiles if options[:load_newfiles]
209
+ if options[:load_sources]
210
+ S.ay
211
+ S.ay 'Fetching sources...', :header
212
+ S.ay "Use the #{'green'.green} value for defining task sources in your Newfile", :indent => 2 if options[:show_source]
213
+ S.ay
214
+ New::Source.load_sources
215
+ end
216
+
217
+ New::Source.sources.each do |source_name, source|
218
+ # determine the widest task & add some padding
219
+ longest_task_length = source.tasks.keys.map(&:length).max
220
+
221
+ S.ay source_name.to_s, :indent => 2, :newline => false, :style => :underline
222
+ S.ay source.path, :highlight_value
223
+
224
+ source.tasks.each do |task_name, task|
225
+ if options[:show_source]
226
+ padding = longest_task_length + source_name.to_s.length + 2
227
+ S.ay "#{source_name}##{task_name}", :preset => :header, :newline => false, :indent => 2, :padding => padding, :justify => :ljust
228
+ else
229
+ padding = longest_task_length + 2
230
+ S.ay task_name.to_s, :preset => :header, :newline => false, :indent => 2, :padding => padding, :justify => :ljust
231
+ end
232
+ S.ay task.description, :indent => 2
46
233
  end
47
234
 
48
- New.say "Edit `#{File.join(New::CUSTOM_DIR, New::CONFIG_FILE)}` with your custom configuration details.", type: :warn
235
+ S.ay
49
236
  end
50
237
  end
51
238
 
52
- desc 'release', 'Release your new code (Run from within a project directory!)'
239
+ desc 'release', 'Release a new version of your project'
240
+ option :verbose, :type => :boolean, :aliases => ['-v'], :default => false, :desc => 'Verbose mode'
241
+ option :skip, :type => :array, :aliases => ['-s'], :default => [], :desc => 'Tasks to skip for this release'
53
242
  def release
54
- project_config_file = File.join(Dir.pwd, New::CONFIG_FILE)
55
- raise unless File.exists? project_config_file
243
+ New.set_verbose if @options['verbose']
244
+ New.set_cli
245
+ New.load_newfiles
56
246
 
57
- # get project config file
58
- project_config = YAML.load(File.open(project_config_file)).deep_symbolize_keys!
247
+ # request the version to bump
248
+ S.ay
249
+ S.ay 'Releasing a new version of: ', :highlight_key
250
+ S.ay New.new_object[:name], :highlight_value
251
+ S.ay 'What do you want to bump: ', :highlight_key
252
+ S.ay "[#{'Mmp'.green}] (#{'M'.green}ajor / #{'m'.green}inor / #{'p'.green}atch)", :preset => :highlight_value, :color => :white
253
+ version = Semantic::Version.new New.new_object[:version]
254
+ version_bump_part = nil
255
+ until version_bump_part
256
+ S.ay 'Current Version:', :highlight_key
257
+ A.sk version.to_s, :highlight_value do |response|
258
+ version_bump_part = case response
259
+ when 'M'
260
+ version.major += 1
261
+ version.minor = 0
262
+ version.patch = 0
263
+ when 'm'
264
+ version.minor += 1
265
+ version.patch = 0
266
+ when 'p'
267
+ version.patch += 1
268
+ else
269
+ S.ay 'You must choose from [Mmp]', :error
270
+ nil
271
+ end
272
+ end
273
+ end
274
+ S.ay 'New Version: ', :highlight_key
275
+ S.ay version.to_s, :highlight_value
276
+ S.ay
59
277
 
60
- # extract tasks from configuration
61
- tasks = (project_config[:tasks] || {}).keys.map(&:to_sym)
278
+ # collect a list of changes in this version
279
+ changelog = get_changelog_from_user
280
+ S.ay
62
281
 
63
- tasks.each do |task|
64
- # require custom task and initialize it
65
- begin
66
- if New.custom_tasks.include? task
67
- require "#{New::CUSTOM_DIR}/#{New::TASKS_DIR_NAME}/#{task}/#{task}"
68
- else
69
- require "#{New::DEFAULT_DIR}/#{New::TASKS_DIR_NAME}/#{task}/#{task}"
282
+ # show tasks
283
+ S.ay "#{'Running Tasks:'.green} (in order)"
284
+ skip_tasks = @options['skip'].map(&:to_sym)
285
+ New.new_object[:tasks].keys.each do |task|
286
+ if skip_tasks.include?(task)
287
+ S.ay "#{task.to_s} (skipped)", :preset => :fine_print, :indent => 4
288
+ else
289
+ S.ay task.to_s, :indent => 2
290
+ end
291
+ end
292
+ S.ay
293
+
294
+ New.new version.to_s, changelog, @options['skip']
295
+ end
296
+
297
+ desc 'version', 'Show the current version'
298
+ def version
299
+ New.load_newfiles
300
+ S.ay New.new_object[:name], :highlight_key
301
+ S.ay New.new_object[:version], :highlight_value
302
+ end
303
+
304
+ desc 'test', 'Run task tests from sources'
305
+ option :watch, :type => :boolean, :aliases => ['-w'], :desc => 'Watch local tasks for changes and run tests'
306
+ option :source, :type => :string, :aliases => ['-s'], :desc => 'Source name'
307
+ option :task, :type => :string, :aliases => ['-t'], :desc => 'Task name'
308
+ def test
309
+ spec_paths = []
310
+ watch_dirs = []
311
+
312
+ New.load_newfiles
313
+ New::Source.load_sources
314
+
315
+ # create a hash with a single source if one is passed
316
+ sources = if @options['source']
317
+ source_name = @options['source'].to_sym
318
+ source_hash = {}
319
+ source_hash[source_name] = New::Source.sources[source_name]
320
+ source_hash
321
+ else
322
+ New::Source.sources
323
+ end
324
+
325
+ # collect specs to run/watch
326
+ sources.each do |source_name, source|
327
+ next unless source
328
+
329
+ # create a hash with a single task if one is passed
330
+ tasks = @options['task'] ? [source.tasks[options['task'].to_sym]] : source.tasks
331
+ tasks = if @options['task']
332
+ task_name = @options['task'].to_sym
333
+ task_hash = {}
334
+ task_hash[task_name] = source.tasks[task_name]
335
+ task_hash
336
+ else
337
+ source.tasks
338
+ end
339
+
340
+ tasks.each do |task_name, task|
341
+ next unless task.path
342
+
343
+ spec_path = File.join(File.dirname(task.path), "#{task_name}_task_spec.rb")
344
+
345
+ # if the source/task has a spec file, and the source is local, watch the task directory for changes and run the spec whenever anything changes
346
+ if File.file?(spec_path) && File.directory?(source.path)
347
+ # find task directory in original path, not the sourcerer tmp directory
348
+ original_task_dir_path = File.dirname(Dir[File.join(source.path, '**', File.basename(task.path))][0])
349
+
350
+ watch_dirs << original_task_dir_path
351
+ spec_paths << spec_path
70
352
  end
71
- rescue LoadError
72
- New.say "No task '#{task}' found!", type: :fail
73
- next
74
353
  end
75
- New.say ">>> Initializing #{task.to_s.humanize} Task...", type: :warn
76
- "New::Task::#{task.to_s.classify}".constantize.new project_config
354
+ end
355
+
356
+ # run tests
357
+ if !spec_paths.empty?
358
+ # S.ay "Running tests for `#{task_name}` task in `#{source_name}` source...", :warn
359
+ Kernel::system "bundle exec rspec #{spec_paths.join(' ')}"
360
+ end
361
+
362
+ # watch tests
363
+ # if watch files are found, start a listener to run the spec
364
+ if @options['watch'] && !watch_dirs.empty?
365
+ listener = Listen.to *watch_dirs do |modified, added, removed|
366
+ all = modified + added + removed
367
+
368
+ # find sibling spec file from modified file
369
+ spec_path = all.collect{ |file| Dir[File.join(File.dirname(file), '*_spec.rb')] }.flatten.first
370
+
371
+ Kernel::system "bundle exec rspec #{spec_path}"
372
+ end
373
+ listener.start
374
+ Kernel::sleep
77
375
  end
78
376
  end
79
377
 
80
- private
378
+ no_commands do
379
+ def get_changelog_from_user
380
+ S.ay 'Lets add some items to the changelog', :header
381
+ S.ay 'Add multiple entries by pressing ENTER after each one', :instruction
382
+ S.ay 'Enter an empty value to finish', :instruction
81
383
 
82
- def project template, name
83
- if Dir.exists? File.join(Dir.pwd, name)
84
- New.say "A project named #{name} already exists...", type: :warn
85
- New.say " Overwrite it? [Yn]", type: :warn
86
- exit if STDIN.gets.chomp! != 'Y'
384
+ user_changelog = []
385
+ user_response = nil
87
386
 
88
- # Delete existing project
89
- FileUtils.rm_rf File.join(Dir.pwd, name)
387
+ # add entries to the changelog until an empty string is entered
388
+ until user_response == '' && !user_changelog.empty?
389
+ A.sk user_changelog.compact.join("\n"), :prompt do |response|
390
+ if response.empty?
391
+ user_response = ''
392
+ next
393
+ end
394
+
395
+ user_changelog << user_response = response
396
+ end
397
+ end
398
+
399
+ user_changelog
90
400
  end
91
401
 
92
- New::Project.new template, name
402
+ def get_array_from_user validation = String
403
+ # start to build the array of user values
404
+ user_array = []
405
+
406
+ # convert array to hash of Strings
407
+ if validation.is_a? Array
408
+ validation_hash = {}
409
+ validation.each do |v|
410
+ validation_hash[v.to_sym] = String
411
+ end
412
+ validation = validation_hash
413
+ end
414
+
415
+ if validation.is_a? Hash
416
+ S.ay 'We need to collect a list of complex objects', :header
417
+
418
+ user_response = nil
419
+ until user_response == 'n'
420
+ S.ay "#{user_array.length} objects created: ", :indent => 2, :newline => false
421
+ S.ay user_array.join(', '), :highlight_value
422
+ A.sk 'Press ENTER to create another object. Enter `n` to stop.', :prompt do |response|
423
+ if response == 'n'
424
+ user_response = 'n'
425
+ next
426
+ end
427
+
428
+ user_response = get_hash_from_user(validation, false)
429
+ user_array << user_response
430
+ end
431
+ end
432
+
433
+ else
434
+ S.ay "We need to collect a list of #{validation}s", :header
435
+ S.ay 'Add multiple values by pressing ENTER after each one', :instruction
436
+ S.ay 'Enter an empty value to finish', :instruction
437
+
438
+ # add to the array until an empty string is entered
439
+ user_response = nil
440
+ until user_response == ''
441
+ A.sk user_array.compact.join(', '), :prompt do |response|
442
+
443
+ # if the option is valid, pass through the empty string to end entering values
444
+ if response.empty?
445
+ user_response = ''
446
+ next
447
+ end
448
+
449
+ user_response = New::Task.validate_class(response, validation) rescue nil
450
+ user_array << user_response
451
+ end
452
+ end
453
+ end
454
+
455
+ return user_array.compact
456
+ end
457
+
458
+ def get_hash_from_user validation = {}, allow_custom_keys = true
459
+ # start to build the hash of user values
460
+ user_hash = {}
461
+
462
+ # convert array to hash of Strings
463
+ if validation.is_a? Array
464
+ validation_hash = {}
465
+ validation.each do |v|
466
+ validation_hash[v.to_sym] = String
467
+ end
468
+ validation = validation_hash
469
+ end
470
+
471
+ # get user values for required validation keys
472
+ validation.each do |key, klass|
473
+ S.ay 'We need to collect some required values', :header
474
+
475
+ user_response = nil
476
+ until user_response
477
+
478
+ # do not allow nested arrays/hashes
479
+ # these should be declared as their own option
480
+ if klass == Array || klass == Hash
481
+ S.ay 'Hash options cannot have nested Arrays or Hashes. They should be declared as their own option.', :error
482
+ exit
483
+ end
484
+
485
+ A.sk "Enter a VALUE for `#{key}`", :prompt do |response|
486
+ # make sure validation keys have a value
487
+ if response == ''
488
+ user_response = nil
489
+ next
490
+ end
491
+
492
+ begin
493
+ user_response = New::Task.validate_class(response, klass)
494
+ user_hash[key] = user_response
495
+ S.ay user_hash
496
+ rescue
497
+ user_response = nil
498
+ end
499
+ end
500
+ end
501
+ end
502
+
503
+ if allow_custom_keys
504
+ # Allow users to enter custom keys AND values
505
+ S.ay 'You can add custom keys & values if you want', :header
506
+ S.ay 'Add multiple key/value pairs by pressing ENTER after each one', :instruction
507
+ S.ay 'Enter an empty key to finish', :instruction
508
+
509
+ user_key_response = nil
510
+ until user_key_response == ''
511
+ A.sk 'Enter a KEY name', :prompt do |key_response|
512
+ # exit loop if user is done entering info
513
+ if key_response == ''
514
+ user_key_response = ''
515
+ next
516
+ end
517
+
518
+ user_value_response = nil
519
+ until user_value_response
520
+ A.sk "Enter a VALUE for `#{key_response}`", :prompt do |value_response|
521
+ # make sure value exists for user created key
522
+ if value_response == ''
523
+ user_value_response = nil
524
+ next
525
+ end
526
+
527
+ # create key/value pair
528
+ user_hash[key_response.to_sym] = user_value_response = value_response
529
+ S.ay user_hash
530
+ end
531
+ end
532
+ end
533
+ end
534
+ end
535
+
536
+ return user_hash
537
+ end
93
538
  end
539
+
540
+ default_task :release
94
541
  end