razor-client 0.14.0 → 0.15.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.
Files changed (48) hide show
  1. data/NEWS.md +18 -0
  2. data/bin/razor +9 -4
  3. data/lib/razor/cli.rb +10 -0
  4. data/lib/razor/cli/document.rb +59 -0
  5. data/lib/razor/cli/format.rb +70 -20
  6. data/lib/razor/cli/navigate.rb +114 -25
  7. data/lib/razor/cli/parse.rb +79 -10
  8. data/lib/razor/cli/transforms.rb +39 -0
  9. data/lib/razor/cli/version.rb +46 -0
  10. data/lib/razor/cli/views.rb +25 -0
  11. data/lib/razor/cli/views.yaml +189 -0
  12. data/spec/cli/format_spec.rb +77 -0
  13. data/spec/cli/navigate_spec.rb +102 -2
  14. data/spec/cli/parse_spec.rb +20 -0
  15. data/spec/fixtures/vcr/Razor_CLI_Navigate/argument_formatting/should_allow_spaces.yml +966 -0
  16. data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_--help_command_.yml +99 -0
  17. data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_-h_command_.yml +99 -0
  18. data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_command_--help_.yml +99 -0
  19. data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_command_-h_.yml +99 -0
  20. data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_command_help_.yml +99 -0
  21. data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_help_command_.yml +99 -0
  22. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_authentication/should_preserve_that_across_navigation.yml +9 -42
  23. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_authentication/should_supply_that_to_the_API_service.yml +5 -5
  24. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_invalid_parameter/should_fail_with_bad_JSON.yml +228 -0
  25. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_invalid_parameter/should_fail_with_malformed_argument.yml +120 -0
  26. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_array/should_merge_an_array_into_an_existing_array.yml +2006 -0
  27. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_array/should_merge_the_arguments_as_an_array.yml +2006 -0
  28. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_array/should_merge_the_arguments_into_an_existing_array.yml +2006 -0
  29. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_construct_a_json_object.yml +234 -0
  30. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_fail_with_mixed_types_array_then_hash_.yml +228 -0
  31. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_fail_with_mixed_types_hash_then_array_.yml +164 -0
  32. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_no_parameters/should_fail_with_bad_JSON.yml +36 -0
  33. data/spec/fixtures/vcr/Razor_CLI_Parse/_new/_help/should_print_a_list_of_known_endpoints.yml +5 -5
  34. data/spec/spec_helper.rb +12 -1
  35. data/spec/testing.md +16 -0
  36. data/spec/version_spec.rb +8 -0
  37. metadata +74 -66
  38. data/.gitignore +0 -7
  39. data/.yardopts +0 -2
  40. data/Gemfile +0 -35
  41. data/Gemfile.lock +0 -53
  42. data/Rakefile +0 -37
  43. data/lib/razor/cli/navigate.yaml +0 -28
  44. data/razor-client.gemspec +0 -32
  45. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_a_single_item_path/.yml +0 -69
  46. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_an_invalid_path/.yml +0 -36
  47. data/spec/fixtures/vcr/Razor_CLI_Navigate/with_no_path/.yml +0 -36
  48. data/spec/fixtures/vcr/Razor_CLI_Parse/_new/_help/.yml +0 -36
data/NEWS.md ADDED
@@ -0,0 +1,18 @@
1
+ # Razor Client Release Notes
2
+
3
+ ## 0.15.0 - 2014-05-22
4
+
5
+ Usability of the client has been greatly enhanced:
6
+
7
+ * Tabular views of most collections: things like 'razor nodes' now display
8
+ a table of results with important details about each node.
9
+ * Get help on commands via `razor help COMMAND`
10
+ * Output now includes hints on how to get more details on the things displayed
11
+ * No need to enter JSON on the command line for most commands (all but
12
+ create-tag)
13
+ + arrays can now be entered by repeating the same option, e.g. `razor
14
+ create-tag --name ... --tag t1 --tag t2`
15
+ + broker configuration is set using `razor create-broker --name
16
+ .. --configuration var1=value1 --configuration var2=value2 ...`
17
+ * Clearer error message when server responds with 'Unauthorized'
18
+ (RAZOR-175)
data/bin/razor CHANGED
@@ -27,7 +27,12 @@ rescue OptionParser::InvalidOption => e
27
27
  die e.message + "\nTry 'razor --help' for more information"
28
28
  end
29
29
 
30
- if parse.show_help?
30
+ if parse.show_version?
31
+ puts parse.version
32
+ exit 0
33
+ end
34
+
35
+ if parse.show_help? and not parse.show_command_help?
31
36
  puts parse.help
32
37
  exit 0
33
38
  end
@@ -35,9 +40,7 @@ end
35
40
  begin
36
41
  document = parse.navigate.get_document
37
42
  url = parse.navigate.last_url
38
- puts "From #{url}:\n\n#{format_document document}\n\n"
39
- rescue Razor::CLI::Error => e
40
- die "#{e}\n#{parse.help}\n\n"
43
+ puts "From #{url}:\n\n#{format_document document, parse}\n\n"
41
44
  rescue SocketError, Errno::ECONNREFUSED => e
42
45
  puts "Error: Could not connect to the server at #{parse.api_url}"
43
46
  puts " #{e}\n"
@@ -58,4 +61,6 @@ rescue RestClient::Exception => e
58
61
  puts r.body
59
62
  end
60
63
  die
64
+ rescue StandardError => e
65
+ die "#{e.message}\nTry 'razor --help' for more information\n\n"
61
66
  end
@@ -26,9 +26,19 @@ module Razor
26
26
  end
27
27
  end
28
28
 
29
+ class VersionCompatibilityError < Error
30
+ def initialize(reason)
31
+ super "Server version is not compatible with client version: #{reason}"
32
+ end
33
+ end
34
+
29
35
  end
30
36
  end
31
37
 
38
+ require_relative 'cli/version'
32
39
  require_relative 'cli/navigate'
33
40
  require_relative 'cli/parse'
34
41
  require_relative 'cli/format'
42
+ require_relative 'cli/document'
43
+ require_relative 'cli/views'
44
+ require_relative 'cli/transforms'
@@ -0,0 +1,59 @@
1
+ require 'forwardable'
2
+
3
+ module Razor::CLI
4
+ class Document
5
+ extend Forwardable
6
+ attr_reader 'spec', 'items', 'format_view', 'original_items'
7
+ def initialize(doc, format_type)
8
+ if doc['spec'].is_a?(Array)
9
+ @spec, @remaining_navigation = doc['spec']
10
+ else
11
+ @spec = doc['spec']
12
+ end
13
+ @command = doc['command']
14
+ if doc.has_key?('items')
15
+ @type = :list
16
+ else
17
+ @type = :single
18
+ end
19
+ @items = doc['items'] || Array[doc]
20
+ @format_view = Razor::CLI::Views.find_formatting(@spec, format_type, @remaining_navigation)
21
+
22
+ # Untransformed and unordered for displaying nested views.
23
+ @original_items = @items
24
+ @items = hide_or_transform_elements!(items, format_view)
25
+ end
26
+
27
+ def is_list?
28
+ @type == :list
29
+ end
30
+
31
+ private
32
+ # This method:
33
+ # - rearranges columns per Razor::CLI::Views.
34
+ # - hides columns per Razor::CLI::Views.
35
+ # - transforms data using both Razor::CLI::Views and its `TRANSFORMS`.
36
+ def hide_or_transform_elements!(items, format_view)
37
+ if format_view.has_key?('+show')
38
+ items.map do |item|
39
+ Hash[
40
+ format_view['+show'].map do |item_format_spec|
41
+ # Allow both '+column' as overrides.
42
+ item_spec = (item_format_spec[1] or {})
43
+ item_label = item_format_spec[0]
44
+ item_column = (item_spec['+column'] or item_label)
45
+ value = Razor::CLI::Views.transform(item[item_column], item_spec['+format'])
46
+
47
+ [item_label, value]
48
+ end
49
+ ].tap do |hash|
50
+ # Re-add the special 'command' key and value if the key isn't already there.
51
+ hash['command'] = @command if @command and not hash.has_key?('command')
52
+ end
53
+ end
54
+ else
55
+ items
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,8 +1,10 @@
1
+ require 'forwardable'
1
2
  require 'terminal-table'
2
3
 
3
4
  module Razor::CLI
4
5
  module Format
5
- PriorityKeys = %w[ id name ]
6
+ extend Forwardable
7
+ PriorityKeys = %w[ id name spec ]
6
8
  SpecNames = {
7
9
  "/spec/object/policy" => "Policy",
8
10
  "/spec/object/tag" => "Tag",
@@ -16,12 +18,41 @@ module Razor::CLI
16
18
  spec
17
19
  end
18
20
 
19
- def format_document(doc)
20
- case doc
21
- when Array then format_objects(doc)
22
- when Hash then format_object(doc)
23
- else doc.to_s
24
- end.chomp
21
+ def format_document(doc, parse = nil)
22
+ format = parse.format
23
+ arguments = parse.args
24
+ doc = Razor::CLI::Document.new(doc, format)
25
+
26
+ return "There are no items for this query." if doc.items.empty?
27
+ return format_objects(doc.items).chomp if parse.show_command_help?
28
+
29
+ case (doc.format_view['+layout'] or 'list')
30
+ when 'list'
31
+ format_objects(doc.items) + String(additional_details(doc, arguments)).chomp
32
+ when 'table'
33
+ case doc.items
34
+ when Array then
35
+ get_table(doc.items, doc.format_view) + String(additional_details(doc, arguments))
36
+ else doc.to_s
37
+ end
38
+ else
39
+ raise ArgumentError, "Unrecognized view format #{doc.format_view['+layout']}"
40
+ end
41
+ end
42
+
43
+ private
44
+ def get_table(doc, formatting)
45
+ # Use the formatting if it exists, otherwise build from the data.
46
+ headings = (formatting['+show'] and formatting['+show'].keys or [])
47
+ Terminal::Table.new do |t|
48
+ t.rows = doc.map do |page|
49
+ page.map do |item|
50
+ headings << item[0] unless headings.include? item[0]
51
+ item[1]
52
+ end
53
+ end
54
+ t.headings = headings
55
+ end.to_s
25
56
  end
26
57
 
27
58
  # We assume that all collections are homogenous
@@ -31,21 +62,16 @@ module Razor::CLI
31
62
  end.join "\n\n"
32
63
  end
33
64
 
34
- def format_reference_object(ref, indent = 0)
35
- output = ' '* indent + "#{ref['name']} => #{ref['id'].to_s.ljust 4}"
36
- end
37
-
38
-
39
65
  def format_object(object, indent = 0)
40
- if object.keys == ["id", "name"]
41
- format_reference_object(object, indent)
66
+ if object.has_key?('help') and object.has_key?('name')
67
+ object['help']['full']
42
68
  else
43
69
  format_default_object(object, indent)
44
70
  end
45
71
  end
46
72
 
47
73
  def format_default_object(object, indent = 0 )
48
- fields = (PriorityKeys & object.keys) + (object.keys - PriorityKeys)
74
+ fields = display_fields(object)
49
75
  key_indent = indent + fields.map {|f| f.length}.max
50
76
  output = ""
51
77
  fields.map do |f|
@@ -59,11 +85,13 @@ module Razor::CLI
59
85
  "\n" + format_object(value, key_indent + 4).rstrip
60
86
  end
61
87
  when Array
62
- if value.all? { |v| v.is_a?(String) }
63
- "[" + value.map(&:inspect).join(",") + "]"
64
- else
65
- "[\n" + format_objects(value, key_indent + 6) + ("\n"+' '*(key_indent+4)+"]")
66
- end
88
+ if value.all? { |v| v.is_a?(String) }
89
+ "[" + value.map(&:to_s).join(",") + "]"
90
+ else
91
+ "[\n" + format_objects(value, key_indent + 6) + ("\n"+' '*(key_indent+4)+"]")
92
+ end
93
+ when String
94
+ value
67
95
  else
68
96
  case f
69
97
  when "spec" then "\"#{Format.spec_name(value)}\""
@@ -72,5 +100,27 @@ module Razor::CLI
72
100
  end
73
101
  end.join "\n"
74
102
  end
103
+
104
+ def display_fields(object)
105
+ keys = object.respond_to?(:keys) ? object.keys : []
106
+ (PriorityKeys & keys) + (keys - PriorityKeys) - ['+spec']
107
+ end
108
+
109
+ def additional_details(doc, arguments)
110
+ objects = doc.original_items
111
+ # If every element has the 'name' key, it has nested elements.
112
+ if doc.is_list? and objects.all? { |it| it.is_a?(Hash) && it.has_key?('name')}
113
+ "\n\nQuery an entry by including its name, e.g. `razor #{arguments.join(' ')} #{objects.first['name']}`"
114
+ elsif objects.any?
115
+ object = objects.first
116
+ fields = display_fields(object) - PriorityKeys
117
+ list = fields.select do |f|
118
+ object[f].is_a?(Hash) or object[f].is_a?(Array)
119
+ end.sort
120
+ if list.any?
121
+ "\n\nQuery additional details via: `razor #{arguments.join(' ')} [#{list.join(', ')}]`"
122
+ end
123
+ end
124
+ end
75
125
  end
76
126
  end
@@ -1,9 +1,11 @@
1
1
  require 'rest-client'
2
2
  require 'multi_json'
3
3
  require 'yaml'
4
+ require 'forwardable'
4
5
 
5
6
  module Razor::CLI
6
7
  class Navigate
8
+ extend Forwardable
7
9
 
8
10
  def initialize(parse, segments)
9
11
  @parse = parse
@@ -28,6 +30,10 @@ module Razor::CLI
28
30
  entrypoint["commands"]
29
31
  end
30
32
 
33
+ def server_version
34
+ entrypoint.has_key?('version') and entrypoint['version']['server'] or 'Unknown'
35
+ end
36
+
31
37
  def query?
32
38
  collections.any? { |coll| coll["name"] == @segments.first }
33
39
  end
@@ -40,6 +46,8 @@ module Razor::CLI
40
46
  !! command(@segments.first)
41
47
  end
42
48
 
49
+ def_delegator '@parse', 'show_command_help?'
50
+
43
51
  def get_document
44
52
  if @segments.empty?
45
53
  entrypoint
@@ -48,15 +56,18 @@ module Razor::CLI
48
56
  while @segments.any?
49
57
  move_to @segments.shift
50
58
  end
59
+
60
+ # Get the next level if it's a list of objects.
61
+ if @doc.is_a?(Hash) and @doc['items'].is_a?(Array)
62
+ @doc['items'] = @doc['items'].map do |item|
63
+ item.is_a?(Hash) && item.has_key?('id') ? json_get(item['id']) : item
64
+ end
65
+ end
51
66
  @doc
52
67
  elsif command?
53
68
  # @todo lutter 2013-08-16: None of this has any tests, and error
54
69
  # handling is heinous at best
55
70
  cmd, body = extract_command
56
- if body.empty?
57
- raise Razor::CLI::Error,
58
- "No arguments for command (did you forget --json ?)"
59
- end
60
71
  # Ensure that we copy authentication data from our previous URL.
61
72
  url = cmd["id"]
62
73
  if @doc_url
@@ -64,7 +75,18 @@ module Razor::CLI
64
75
  url.userinfo = @doc_url.userinfo
65
76
  end
66
77
 
67
- json_post(url, body)
78
+ if show_command_help?
79
+ json_get(url)
80
+ else
81
+ if body.empty?
82
+ raise Razor::CLI::Error,
83
+ "No arguments for command (did you forget --json ?)"
84
+ end
85
+ result = json_post(url, body)
86
+ # Get actual object from the id.
87
+ result = result.merge(json_get(result['id'])) if result['id']
88
+ result
89
+ end
68
90
  else
69
91
  raise NavigationError.new(@doc_url, @segments, @doc)
70
92
  end
@@ -72,12 +94,36 @@ module Razor::CLI
72
94
 
73
95
  def extract_command
74
96
  cmd = command(@segments.shift)
97
+ @cmd_url = cmd['id']
75
98
  body = {}
76
99
  until @segments.empty?
77
- if @segments.shift =~ /\A--([a-z-]+)(=(\S+))?\Z/
100
+ argument = @segments.shift
101
+ if argument =~ /\A--([a-z-]+)(=(.+))?\Z/
102
+ # `--arg=value` or `--arg value`
78
103
  arg, value = [$1, $3]
79
104
  value = @segments.shift if value.nil? && @segments[0] !~ /^--/
80
- body[arg] = convert_arg(cmd["name"], arg, value)
105
+ if value =~ /\A([a-zA-Z._-]+)=(\S+)?\z/
106
+ # `--arg name=value`
107
+ unless body[arg].nil? or body[arg].is_a?(Hash)
108
+ # Error: `--arg value --arg name=value`
109
+ raise ArgumentError, "Cannot handle mixed types for argument #{arg}"
110
+ end
111
+ # Do not convert, assume the above is the conversion.
112
+ body[arg] = (body[arg].nil? ? {} : body[arg]).merge($1 => $2)
113
+ elsif body[arg].is_a?(Hash)
114
+ # Error: `--arg name=value --arg value`
115
+ raise ArgumentError, "Cannot handle mixed types for argument #{arg}"
116
+ else
117
+ value = convert_arg(cmd["name"], arg, value)
118
+ if body[arg].nil?
119
+ body[arg] = value
120
+ else
121
+ # Either/both `body[arg]` or/and `value` might be an array at this point.
122
+ body[arg] = Array(body[arg]) + Array(value)
123
+ end
124
+ end
125
+ else
126
+ raise ArgumentError, "Unexpected argument #{argument}"
81
127
  end
82
128
  end
83
129
 
@@ -89,15 +135,16 @@ module Razor::CLI
89
135
  raise Razor::CLI::Error, "File #{body["json"]} not found"
90
136
  rescue Errno::EACCES
91
137
  raise Razor::CLI::Error,
92
- "Permission to read file #{body["json"]} denied"
138
+ "Permission to read file #{body["json"]} denied"
93
139
  end
94
140
  [cmd, body]
95
141
  end
96
142
 
97
143
  def move_to(key)
98
- key = key.to_i if key.to_i.to_s == key
99
144
  if @doc.is_a? Array
100
145
  obj = @doc.find {|x| x.is_a?(Hash) and x["name"] == key }
146
+ elsif @doc.is_a?(Hash) && @doc['items'].is_a?(Array)
147
+ obj = @doc['items'].find {|x| x.is_a?(Hash) and x["name"] == key }
101
148
  elsif @doc.is_a? Hash
102
149
  obj = @doc[key]
103
150
  end
@@ -112,13 +159,27 @@ module Razor::CLI
112
159
  end
113
160
 
114
161
  @doc = json_get(url)
115
- # strip the wrapper around collections
116
- if @doc.is_a? Hash and @doc["items"].is_a? Array
117
- @doc = @doc["items"]
118
- end
119
162
  @doc_url = url
120
- elsif obj.is_a? Hash
163
+ elsif obj.is_a?(Hash) && obj['spec']
121
164
  @doc = obj
165
+ elsif obj.is_a?(Hash)
166
+ # No spec string; use parent's and remember extra navigation.
167
+ if @doc['+spec'].is_a?(Array)
168
+ # Something's been added.
169
+ @doc['+spec'] << key
170
+ elsif @doc['+spec'].nil? || @doc['+spec'].is_a?(String)
171
+ @doc['+spec'] = [@doc['spec'], key]
172
+ end
173
+ @doc = obj.merge({'+spec' => @doc['+spec']})
174
+ elsif obj.is_a?(Array)
175
+ # No spec string; use parent's and remember extra navigation.
176
+ if @doc['+spec'].is_a?(Array)
177
+ # Something's already been added.
178
+ @doc['+spec'] << key
179
+ elsif @doc['+spec'].nil? || @doc['+spec'].is_a?(String)
180
+ @doc['+spec'] = [@doc['spec'], key]
181
+ end
182
+ @doc = {'+spec' => @doc['+spec'], 'items' => obj}
122
183
  else
123
184
  @doc = nil
124
185
  end
@@ -133,7 +194,7 @@ module Razor::CLI
133
194
  def json_get(url, headers = {})
134
195
  response = get(url,headers.merge(:accept => :json))
135
196
  unless response.headers[:content_type] =~ /application\/json/
136
- raise "Received content type #{response.headers[:content_type]}"
197
+ raise "Received content type #{response.headers[:content_type]}"
137
198
  end
138
199
  MultiJson.load(response.body)
139
200
  end
@@ -152,25 +213,53 @@ module Razor::CLI
152
213
  end
153
214
 
154
215
  private
155
- def self.annotations
156
- @@annotations ||=
157
- YAML::load_file(File::join(File::dirname(__FILE__), "navigate.yaml"))
216
+ def cmd_schema(cmd_name)
217
+ cmd = json_get(@cmd_url)
218
+ cmd['schema'] or raise VersionCompatibilityError, 'Server must supply the expected datatypes for command arguments'
158
219
  end
159
220
 
160
- def self.arg_type(cmd_name, arg_name)
161
- cmd = annotations["commands"][cmd_name]
162
- cmd && cmd["args"][arg_name]
221
+ def arg_type(cmd_name, arg_name)
222
+ cmd = cmd_schema(cmd_name)
223
+ cmd && cmd[arg_name] && cmd[arg_name]['type'] or nil
163
224
  end
164
225
 
165
226
  def convert_arg(cmd_name, arg_name, value)
166
227
  value = nil if value == "null"
167
- case self.class.arg_type(cmd_name, arg_name)
168
- when "json"
169
- MultiJson::load(value)
228
+
229
+ argument_type = arg_type(cmd_name, arg_name)
230
+
231
+ # This might be helpful, since there's no other method for debug-level logging on the client.
232
+ puts "Formatting argument #{arg_name} with value #{value} as #{argument_type}\n" if @parse.dump_response?
233
+
234
+ case argument_type
235
+ when "array"
236
+ # 'array' datatype arguments will never fail. At worst, they'll be wrapped in an array.
237
+ begin
238
+ MultiJson::load(value)
239
+ rescue MultiJson::LoadError => _
240
+ Array(value)
241
+ end
242
+ when "object"
243
+ begin
244
+ MultiJson::load(value)
245
+ rescue MultiJson::LoadError => error
246
+ raise ArgumentError, "Invalid JSON for argument '#{arg_name}': #{error.message}"
247
+ end
170
248
  when "boolean"
171
249
  ["true", nil].include?(value)
172
- else
250
+ when "number"
251
+ begin
252
+ Integer(value)
253
+ rescue ArgumentError
254
+ raise ArgumentError, "Invalid integer for argument '#{arg_name}': #{value}"
255
+ end
256
+ when "null"
257
+ raise ArgumentError, "Expected nothing for argument '#{arg_name}', but was: '#{value}'" unless value.nil?
258
+ nil
259
+ when "string", nil # `nil` for 'might be an alias, send as-is'
173
260
  value
261
+ else
262
+ raise Razor::CLI::Error, "Unexpected datatype '#{argument_type}' for argument #{arg_name}"
174
263
  end
175
264
  end
176
265
  end