Class: New::Cli

Inherits:
Thor
  • Object
show all
Defined in:
lib/new/cli.rb

Instance Method Summary collapse

Instance Method Details

#initObject



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/new/cli.rb', line 30

def init
  New.load_newfiles
  New::Source.load_sources

  # initialize empty newfile object
  newfile_object = {
    :tasks => {}
  }

  # get valid name
  name = @options['name']
  until !name.empty?
    name = A.sk 'Project Name:', :prompt
  end
  newfile_object[:name] = name

  # get valid version
  version = Semantic::Version.new(@options['version']) rescue nil
  until !version.to_s.empty?
    begin
      response = A.sk 'Current Project Version:', :prompt
      version = Semantic::Version.new response
    rescue
      S.ay "`#{response}` is not a valid semantic version (e.g. 1.2.3)", :error
    end
  end
  newfile_object[:version] = version.to_s

  # get tasks
  tasks_list = []
  @options['tasks'].each do |task|
    tasks_list << New::Source.find_task(task)
  end

  # remove any empty tasks in case the user specified an invalid task
  tasks_list.compact!

  # if no tasks are specified, show all available tasks
  if tasks_list.empty?
    S.ay
    self.tasks :show_source => true, :load_newfiles => false, :load_sources => false

    S.ay 'Add multiple tasks by pressing ENTER after each one', :instruction
    S.ay 'Enter tasks in the order you want them to run', :instruction
    S.ay 'Enter both the source and the task (e.g. source#task)', :instruction
    S.ay 'Enter an empty value to finish', :instruction

    added_task = nil
    until added_task == '' && !tasks_list.empty?
      S.ay
      added_task = A.sk 'Add a task:', :prompt

      # if a task is entered, verify it exists
      unless added_task.empty?

        # find task
        task = New::Source.find_task added_task

        # add task to array
        if task
          tasks_list << task
        end
      end
    end

    S.ay
  end

  # output the summary so far (no task options entered yet)
  padding = 10
  S.ay 'Name:', :preset => :highlight_key, :padding => padding
  S.ay name, :preset => :highlight_value
  S.ay 'Version:', :preset => :highlight_key, :padding => padding
  S.ay version.to_s, :preset => :highlight_value
  S.ay 'Tasks:', :preset => :highlight_key, :padding => padding

  # output first task without a padding so it looks nicer
  tasks_dup = tasks_list.dup
  first_task = tasks_dup.shift
  S.ay "#{first_task.source.name}##{first_task.name}", :highlight_value
  tasks_dup.each do |task|
    S.ay "#{task.source.name}##{task.name}", :preset => :highlight_value, :indent => padding + 3
  end
  S.ay

  tasks_list.each do |task|
    HR.call
    S.ay 'OK, now lets set options for', :highlight_key
    S.ay task.name.to_s.upcase, :highlight_value
    HR.call
    S.ay

    newfile_object[:tasks][task.name] = {}
    task.class_options.each do |option_name, option_settings|
      S.ay option_name.to_s, :preset => :highlight_key, :padding => 0
      S.ay option_settings[:description], :highlight_value

      # show default
      default = option_settings[:default]
      if default && !option_settings[:required]
        default = case default
        when Array then default.join(', ')
        when Hash then default.keys.join(', ')
        else default.to_s
        end

        S.ay "default: #{default}", :preset => :fine_print, :indent => option_name.length + 3
      end

      # GET USER INPUT FOR ARRAY TYPE
      #
      option_type = option_settings[:type]
      case

      # collect array option type values
      when option_type == Array
        # cast type onto all user input values (default is String)
        klass = option_settings[:validation] || String

        # collect array elements from the user
        option_value = nil
        until option_value
          begin
            option_value = get_array_from_user(klass)
            option_value = New::Task.validate_option(option_name, option_settings, option_value)
          rescue
            option_value = nil
          end
        end

      # collect hash option type values
      when option_type == Hash
        # loop through the expected keys from the validation and get users input
        option_value = nil
        until option_value
          begin
            option_value = get_hash_from_user(option_settings[:validation])
            option_value = New::Task.validate_option(option_name, option_settings, option_value)
          rescue
            option_value = nil
          end
        end

      # collect non array/hash option type value
      else
        option_value = nil
        until option_value
          A.sk '', :newline => false, :preset => :prompt do |response|
            option_value = New::Task.validate_option(option_name, option_settings, response) rescue nil
          end
        end
      end

      newfile_object[:tasks][task.name][option_name] = option_value
      S.ay
    end
  end

  # write project Newfile
  project_newfile = File.join New::PROJECT_DIRECTORY, New::NEWFILE_NAME
  File.open project_newfile, 'w+' do |f|
    f.write newfile_object.deep_stringify_keys.to_yaml
  end

  # Success Message
  S.ay "A `#{'Newfile'.green}` was successfully created for your project `#{name.to_s.green}`"
  S.ay 'Open the file to verify the values are correct, and make any neccessary modifications.'
  S.ay "You are now ready to run `#{'new release'.green}` to release your software into the wild!"
  S.ay
end

#releaseObject



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/new/cli.rb', line 277

def release
  New.set_cli
  New.set_verbose if @options['verbose']
  New.load_newfiles

  S.ay
  S.ay 'Releasing a new version of:', :highlight_key
  S.ay New.new_object[:name].to_s, :highlight_value

  version = Semantic::Version.new New.new_object[:version]
  S.ay 'Current Version:', :highlight_key
  S.ay version.to_s, :highlight_value

  if @options['bump']
    version = bump_version version, @options['bump']
  else
    S.ay 'What do you want to bump:', :highlight_key
    S.ay "[#{'Mmp'.green}] (#{'M'.green}ajor / #{'m'.green}inor / #{'p'.green}atch)", :preset => :highlight_value, :color => :white
    version_bump_part = nil
    until version_bump_part
      A.sk '', :prompt do |response|
        version = version_bump_part = bump_version version, response
      end
    end
  end

  S.ay 'New Version:', :highlight_key
  S.ay version.to_s, :highlight_value
  S.ay

  # collect a list of changes in this version
  changelog = get_changelog_from_user
  S.ay

  # show tasks
  S.ay "#{'Running Tasks:'.green} (in order)"
  skip_tasks = @options['skip'].map(&:to_sym)
  New.new_object[:tasks].keys.each do |task|
    if skip_tasks.include?(task)
      S.ay "#{task.to_s} (skipped)", :preset => :fine_print, :indent => 4
    else
      S.ay task.to_s, :indent => 2
    end
  end
  S.ay

  New.new version.to_s, changelog, @options['skip']
end

#tasks(args = {}) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/new/cli.rb', line 204

def tasks args = {}
  # merge into default options
  options = {
   :show_source => (@options['sources'] || false),
   :load_newfiles => true,
   :load_sources => true
  }.merge(args)

  New.load_newfiles if options[:load_newfiles]
  if options[:load_sources]
    S.ay 'Fetching sources...', :header
    S.ay "Use the #{'green'.green} value for defining task sources in your Newfile", :indent => 2 if options[:show_source]
    S.ay
    New::Source.load_sources
  end

  if @options['task']
    task = New::Source.find_task @options['task']
    S.ay task.name.to_s.upcase, :preset => :header, :style => :underline, :padding => 30, :justify => :center
    S.ay "source: #{task.source.name.to_s}", :color => :cyan, :padding => 30, :justify => :center
    S.ay

    max_option_length = task.class_options.keys.map(&:length).max
    task.class_options.each do |name, settings|
      S.ay name.to_s, :preset => :highlight_key, :padding => max_option_length, :style => :underline
      S.ay settings[:description], :highlight_value

      S.ay 'Type:', :preset => :highlight_key, :padding => max_option_length + 13
      S.ay "#{settings[:type] || 'String'}", :highlight_value

      if settings[:required]
        S.ay 'Required:', :preset => :highlight_key, :padding => max_option_length + 13
        S.ay 'true', :highlight_value
      elsif settings[:default]
        S.ay 'Default:', :preset => :highlight_key, :padding => max_option_length + 13
        S.ay settings[:default].to_s, :highlight_value
      end

      if settings[:validation]
        S.ay 'Validation:', :preset => :highlight_key, :padding => max_option_length + 13
        S.ay settings[:validation].to_s, :highlight_value
      end

      S.ay
    end
  else
    New::Source.sources.each do |source_name, source|
      # determine the widest task & add some padding
      longest_task_length = source.tasks.keys.map(&:length).max

      S.ay source_name.to_s, :indent => 2, :newline => false, :style => :underline
      S.ay source.path, :highlight_value

      source.tasks.each do |task_name, task|
        if options[:show_source]
          padding = longest_task_length + source_name.to_s.length + 2
          S.ay "#{source_name}##{task_name}", :preset => :header, :newline => false, :indent => 2, :padding => padding, :justify => :ljust
        else
          padding = longest_task_length + 2
          S.ay task_name.to_s, :preset => :header, :newline => false, :indent => 2, :padding => padding, :justify => :ljust
        end
        S.ay task.description, :indent => 2
      end

      S.ay
    end
  end
end

#testObject



337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/new/cli.rb', line 337

def test
  spec_paths = []
  watch_dirs = []

  New.load_newfiles
  New::Source.load_sources

  # create a hash with a single source if one is passed
  sources = if @options['source']
    source_name = @options['source'].to_sym
    source_hash = {}
    source_hash[source_name] = New::Source.sources[source_name]
    source_hash
  else
    New::Source.sources
  end

  # collect specs to run/watch
  sources.each do |source_name, source|
    next unless source

    # create a hash with a single task if one is passed
    tasks = @options['task'] ? [source.tasks[options['task'].to_sym]] : source.tasks
    tasks = if @options['task']
      task_name = @options['task'].to_sym
      task_hash = {}
      task_hash[task_name] = source.tasks[task_name]
      task_hash
    else
      source.tasks
    end

    tasks.each do |task_name, task|
      next unless task.path

      spec_path = File.join(File.dirname(task.path), "#{task_name}_task_spec.rb")

      # 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
      if File.file?(spec_path) && File.directory?(source.path)
        # find task directory in original path, not the sourcerer tmp directory
        original_task_dir_path = File.dirname(Dir[File.join(source.path, '**', File.basename(task.path))][0])

        watch_dirs << original_task_dir_path
        spec_paths << spec_path
      end
    end
  end

  # run tests
  if !spec_paths.empty?
    # S.ay "Running tests for `#{task_name}` task in `#{source_name}` source...", :warn
    Kernel::system "bundle exec rspec #{spec_paths.join(' ')}"
  end

  # watch tests
  # if watch files are found, start a listener to run the spec
  if @options['watch'] && !watch_dirs.empty?
    listener = Listen.to *watch_dirs do |modified, added, removed|
      all = modified + added + removed

      # find sibling spec file from modified file
      spec_path = all.collect{ |file| Dir[File.join(File.dirname(file), '*_spec.rb')] }.flatten.first

      Kernel::system "bundle exec rspec #{spec_path}"
    end
    listener.start
    Kernel::sleep
  end
end

#versionObject



327
328
329
330
331
# File 'lib/new/cli.rb', line 327

def version
  New.load_newfiles
  S.ay New.new_object[:name], :preset => :highlight_key, :padding => 0, :indent => 0
  S.ay New.new_object[:version], :highlight_value
end