cli-mastermind 0.7.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c548ef96d5f0b89113a1110cae2deae76be3c15fd4380727fe74c84bcda29766
4
- data.tar.gz: f189bc224bc60e7ee496b5704df921b3cfd0d37ecb831990889c2666305628b8
3
+ metadata.gz: cd49841f87237f2a7223cd748c61772244232e84cdf46112241e291e73f1a311
4
+ data.tar.gz: '0197b2452f75c1c1c7ac50d84621a9dfef1635d04781c500fb4ca8516f2b65e5'
5
5
  SHA512:
6
- metadata.gz: cc898bb8b3ade138634ebd11fbcd6ab830c60e874528759acbbf9c95ba42b2db7a7d66551d34de8f379a74842bdf2850c2e81860cd66310cddcab86a49ce591c
7
- data.tar.gz: b2f01d7795e3b1ec527d1d3e74ab53b8f9fe250c4e647c7af004b2569b92a463ba11254d136eceb14fe06ea6c8963dd857ffbfc58075bec00a3d5e7963d0eb58
6
+ metadata.gz: b3f195e375e769c105e335c29ab33102db1b2ad76b963f6f463f9c77e0d25a6b79bced05eb44cc86eccaa8d32701236d10b73854310955f8f50742cbeb76a7ac
7
+ data.tar.gz: a5e649e5427b3bea7e52e6cb1a12fa3e563d5e54fbf405623c2d2cdb58147c89dab6043d8fabb3351bf70c13174b0c4c5827383cf75fea0270665847c4a2a3a0
data/README.md CHANGED
@@ -26,11 +26,11 @@ See [Writing Masterplans][writing-masterplans] for more on their structure and
26
26
  semantics.
27
27
 
28
28
  Mastermind makes up for the lack of flexibility in its configuration with full
29
- flexibility in its planfiles. Which brings us to...
29
+ flexibility in its plan files. Which brings us to...
30
30
 
31
31
  ## Extensibility
32
32
 
33
- Mastermind is designed from the outset to provide a means of extending its planfile
33
+ Mastermind is designed from the outset to provide a means of extending its plan file
34
34
  formats through custom `Loader`s. In fact, Mastermind's own `PlanfileLoader` is
35
35
  the first of such loaders. You can specify your own file extensions and provide
36
36
  your own loaders as needed.
@@ -41,14 +41,14 @@ about in the details!
41
41
 
42
42
  Obviously, it'd be a bit difficult to write your plan files in an entirely separate
43
43
  language, but there's nothing stopping you from delegating actions to another
44
- executable or even writing some C code to call into something else alltogether.
44
+ executable or even writing some C code to call into something else altogether.
45
45
 
46
- If you are writing your plans in Ruby, Mastermind provides `CLI::Mastermind::Plan::Interface`
46
+ If you are writing your plans in Ruby, Mastermind provides `CLI::Mastermind::Plan`
47
47
  which you can include in your plans to provide the basic `Plan` interface.
48
48
 
49
49
  ## Minimal Dependencies
50
50
 
51
- Mastermind only has one dependency, Shopify's excelent [cli-ui project][cli-ui].
51
+ Mastermind only has one dependency, Shopify's excellent [cli-ui project][cli-ui].
52
52
  Mastermind doesn't require that you load it in your Gemfile or add anything to
53
53
  your project's configuration files.
54
54
 
@@ -18,7 +18,7 @@ module CLI
18
18
  class << self
19
19
  # Expose the configuration loaded during +execute+.
20
20
  def configuration
21
- @config
21
+ @config ||= spinner('Loading configuration') { Configuration.new @base_path }
22
22
  end
23
23
 
24
24
  # Allows utilities wrapping Mastermind to specify that only plans under a
@@ -41,15 +41,11 @@ module CLI
41
41
  enable_ui if @arguments.display_ui?
42
42
 
43
43
  frame('Mastermind') do
44
- @config = spinner('Loading configuration') { Configuration.new @base_path }
45
-
46
44
  if @arguments.dump_config?
47
45
  do_print_configuration
48
46
  exit 0
49
47
  end
50
48
 
51
- @plans = spinner('Loading plans') { @config.load_plans }
52
-
53
49
  if @arguments.display_plans?
54
50
  do_filtered_plan_display
55
51
  exit 0
@@ -67,22 +63,61 @@ module CLI
67
63
  end
68
64
  end
69
65
 
66
+ # Look up a specific plan by its name
67
+ #
68
+ # Because plans also implement this method in a compatible way, there are
69
+ # three ways this method could be used:
70
+ #
71
+ # 1. List of arguments
72
+ # * Mastermind['name', 'of', 'plans']
73
+ #
74
+ # 2. Space separated string
75
+ # * Mastermind['name of plans']
76
+ #
77
+ # 3. Hash-like access
78
+ # * Mastermind['name']['of']['plans']
79
+ #
80
+ # All will provide the same plan.
81
+ #
82
+ # ---
83
+ #
84
+ # GOTCHA: Be careful if your plan name includes a space!
85
+ #
86
+ # While it's entirely valid to have a plan name that inlcudes a space, you
87
+ # should avoid them if you plan to look up your plan using this method.
88
+ #
89
+ def [](*plan_stack)
90
+ # Allow for a single space-separated string
91
+ if plan_stack.size == 1 and plan_stack.first.is_a?(String)
92
+ plan_stack = plan_stack.first.split(' ')
93
+ end
94
+
95
+ plan_stack.compact.reduce(plans) do |plan, plan_name|
96
+ plan[plan_name]
97
+ end
98
+ end
99
+
70
100
  private
71
101
 
102
+ def plans
103
+ @plans ||= spinner('Loading plans') { Loader.load_all configuration.plan_files }
104
+ end
105
+
72
106
  def do_print_configuration
73
107
  frame('Configuration') do
74
108
  fade_code = CLI::UI::Color.new(90, '').code
75
109
  puts stylize("{{?}} #{fade_code}Values starting with {{*}} #{fade_code}were lazy loaded.#{CLI::UI::Color::RESET.code}")
76
110
  print "\n"
77
- @config.instance_variables.each do |attribute|
78
- value = @config.instance_variable_get(attribute)
111
+
112
+ configuration.instance_variables.each do |attribute|
113
+ value = configuration.instance_variable_get(attribute)
79
114
 
80
115
  name = attribute.to_s.sub(/^@/, '')
81
116
 
82
117
  if value.respond_to? :call
83
118
  if @arguments.resolve_callable_attributes?
84
119
  value = begin
85
- @config.send(name)
120
+ configuration.send(name)
86
121
  rescue => e
87
122
  "UNABLE TO LOAD: #{e.message}"
88
123
  end
@@ -105,7 +140,7 @@ module CLI
105
140
  def do_filtered_plan_display
106
141
  filter_plans @arguments.pattern
107
142
 
108
- unless @plans.empty?
143
+ unless plans.empty?
109
144
  frame('Plans') do
110
145
  puts build_display_string
111
146
  end
@@ -114,7 +149,7 @@ module CLI
114
149
  end
115
150
  end
116
151
 
117
- def build_display_string(plans=@plans, prefix='')
152
+ def build_display_string(plans=self.plans, prefix='')
118
153
  fade_code = CLI::UI::Color.new(90, '').code
119
154
  reset = CLI::UI::Color::RESET.code
120
155
 
@@ -147,7 +182,7 @@ module CLI
147
182
  display_string.gsub(/\n{3,}/, "\n\n")
148
183
  end
149
184
 
150
- def filter_plans(pattern, plans=@plans)
185
+ def filter_plans(pattern, plans=self.plans)
151
186
  plans.keep_if do |name, plan|
152
187
  # Don't display plans without a description or children
153
188
  next false unless plan.has_children? or plan.description
@@ -161,13 +196,13 @@ module CLI
161
196
  end
162
197
 
163
198
  def process_plan_names
164
- @arguments.do_command_expansion!(@config)
199
+ @arguments.do_command_expansion!(configuration)
165
200
 
166
201
  @arguments.insert_base_plan!(@base_plan) unless @base_plan.nil?
167
202
 
168
203
  @plan_stack = []
169
204
 
170
- @selected_plan = @plans if @arguments.has_additional_plan_names?
205
+ @selected_plan = plans if @arguments.has_additional_plan_names?
171
206
 
172
207
  while @arguments.has_additional_plan_names?
173
208
  plan_name = @arguments.get_next_plan_name
@@ -184,7 +219,7 @@ module CLI
184
219
  end
185
220
 
186
221
  def do_interactive_plan_selection
187
- options = (@selected_plan&.children || @plans).map { |k,v| [titleize(k.to_s), v] }.to_h
222
+ options = (@selected_plan&.children || plans).map { |k,v| [titleize(k.to_s), v] }.to_h
188
223
 
189
224
  @selected_plan = select("Select a plan under #{@plan_stack.join('/')}", options: options)
190
225
  @plan_stack << titleize(@selected_plan.name)
@@ -195,7 +230,7 @@ module CLI
195
230
  end
196
231
 
197
232
  def user_is_sure?
198
- !@arguments.ask? or !@config.ask? or confirm("Execute plan #{@plan_stack.join('/')}?")
233
+ !@arguments.ask? or !configuration.ask? or confirm("Execute plan #{@plan_stack.join('/')}?")
199
234
  end
200
235
 
201
236
  def execute_plan!
@@ -24,7 +24,7 @@ module CLI
24
24
  # Path to the top-level masterplan
25
25
  MASTER_PLAN = File.join(Dir.home, PLANFILE)
26
26
 
27
- attr_reader :plans
27
+ attr_reader :plan_files
28
28
 
29
29
  # Adds an arbitrary attribute given by +attribute+ to the configuration class
30
30
  def self.add_attribute(attribute)
@@ -73,31 +73,6 @@ module CLI
73
73
  @plan_files.merge(allowed_plans)
74
74
  end
75
75
 
76
- # Loads all plan files added using +add_plans+
77
- # @see Plan.load
78
- def load_plans
79
- @plans = Loader.load_all(@plan_files)
80
- end
81
-
82
- def execute_plan(*plan_stack, arguments: nil)
83
- if plan_stack.size == 1
84
- case plan_stack.first
85
- when Array
86
- plan_stack = plan_stack.first
87
- when String
88
- plan_stack = plan_stack.first.split(' ')
89
- end
90
- end
91
-
92
- plan = @plans
93
-
94
- plan_stack.each do |plan_name|
95
- plan = plan[plan_name]
96
- end
97
-
98
- plan.call(arguments)
99
- end
100
-
101
76
  # Loads a masterplan using the DSL, if it exists and hasn't been loaded already
102
77
  def load_masterplan filename
103
78
  if File.exists? filename and !@loaded_masterplans.include? filename
@@ -126,6 +101,12 @@ module CLI
126
101
 
127
102
  private
128
103
 
104
+ def method_missing(symbol, *args)
105
+ super
106
+ rescue NoMethodError
107
+ raise MissingConfigurationError, symbol
108
+ end
109
+
129
110
  # Walks up the file tree looking for masterplans.
130
111
  def lookup_and_load_masterplans
131
112
  load_masterplan File.join(Dir.pwd, PLANFILE)
@@ -17,5 +17,11 @@ module CLI
17
17
  super "#{message}: #{directory} does not exist or is not a directory"
18
18
  end
19
19
  end
20
+
21
+ class MissingConfigurationError < Error
22
+ def initialize(attribute)
23
+ super "#{attribute} has not been defined. Call `configure :#{attribute}[, value]` in a `#{Configuration::PLANFILE}` to set it."
24
+ end
25
+ end
20
26
  end
21
27
  end
@@ -48,6 +48,7 @@ module CLI
48
48
  def call(options=nil)
49
49
  raise NotImplementedError
50
50
  end
51
+ alias_method :execute, :call
51
52
 
52
53
  def add_alias(alias_to)
53
54
  config.define_alias(alias_to.to_s, name)
@@ -6,8 +6,8 @@ module CLI
6
6
  end
7
7
 
8
8
  module VERSION
9
- RELEASE = 0
10
- MAJOR = 7
9
+ RELEASE = 1
10
+ MAJOR = 0
11
11
  MINOR = 0
12
12
  PATCH = nil
13
13
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cli-mastermind
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Hall
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-03-08 00:00:00.000000000 Z
11
+ date: 2019-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cli-ui
@@ -103,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
103
  - !ruby/object:Gem::Version
104
104
  version: '0'
105
105
  requirements: []
106
- rubygems_version: 3.0.2
106
+ rubygems_version: 3.0.4
107
107
  signing_key:
108
108
  specification_version: 4
109
109
  summary: Mastermind is a framework for constructing command line toolboxes.