Class: Opto::Option

Inherits:
Object
  • Object
show all
Defined in:
lib/opto/option.rb

Overview

What is an option? It’s like a variable that has a value, which can be validated or manipulated on creation. The value can be resolved from a number of origins, such as an environment variable or random string generator.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Option

Initialize an instance of Opto::Option

Examples:

Create an option

Opto::Option.new(
  name: 'cat_name',
  type: 'string',
  label: 'Name of your Cat',
  required: true,
  description: 'Enter a name for your cat',
  from:
    env: 'CAT_NAME'
  only_if:
    pet: 'cat'
  min_length: 2
  max_length: 20
)

Create a random string

Opto::Option.new(
  name: 'random_string',
  type: :string,
  from:
    random_string:
      length: 20
      charset: ascii_printable
)

Parameters:

  • options (Hash) (defaults to: {})

    @option [String] :name Option name @option [String,Symbol] :type Option type, such as :integer, :string, :boolean, :enum @option [String] :label A label for this field, to be used in for example an interactive prompt @option [String] :description Same as label, but more detailed @option [*] :default Default value for option @option [String,Symbol,Array<String,Symbol,Hash>,Hash] :from Resolver origins @option [String,Symbol,Array<String,Symbol,Hash>,Hash] :to Setter targets @option [String,Symbol,Array<String,Symbol,Hash>,Hash] :skip_if Conditionals that define if this option should be skipped @option [String,Symbol,Array<String,Symbol,Hash>,Hash] :only_if Conditionals that define if this option should be included @option [Opto::Group] :group Parent group reference @option […] Type definition options, such as { min_length: 3, strip: true }



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
# File 'lib/opto/option.rb', line 68

def initialize(options = {})
  opts           = options.dup

  @group         = opts.delete(:group)
  if @group && @group.defaults
    opts = @group.defaults.reject{|k,_| [:from, :to].include?(k)}.merge(opts)
  end

  @name          = opts.delete(:name).to_s

  type           = opts.delete(:type)
  @type          = type.to_s.snakecase unless type.nil?

  @label         = opts.delete(:label) || @name
  @description   = opts.delete(:description)
  @default       = opts.delete(:default)
  val            = opts.delete(:value)
  @skip_if       = opts.delete(:skip_if)
  @only_if       = opts.delete(:only_if)
  @from          = normalize_from_to(opts.delete(:from))
  @to            = normalize_from_to(opts.delete(:to))
  @type_options  = opts

  set_initial(val) if val
  deep_merge_defaults
end

Instance Attribute Details

#defaultObject

Returns the value of attribute default.



21
22
23
# File 'lib/opto/option.rb', line 21

def default
  @default
end

#descriptionObject

Returns the value of attribute description.



19
20
21
# File 'lib/opto/option.rb', line 19

def description
  @description
end

#fromObject (readonly)

Returns the value of attribute from.



22
23
24
# File 'lib/opto/option.rb', line 22

def from
  @from
end

#groupObject (readonly)

Returns the value of attribute group.



24
25
26
# File 'lib/opto/option.rb', line 24

def group
  @group
end

#initial_valueObject (readonly)

Returns the value of attribute initial_value.



27
28
29
# File 'lib/opto/option.rb', line 27

def initial_value
  @initial_value
end

#labelObject

Returns the value of attribute label.



18
19
20
# File 'lib/opto/option.rb', line 18

def label
  @label
end

#nameObject

Returns the value of attribute name.



17
18
19
# File 'lib/opto/option.rb', line 17

def name
  @name
end

#only_ifObject (readonly)

Returns the value of attribute only_if.



26
27
28
# File 'lib/opto/option.rb', line 26

def only_if
  @only_if
end

#requiredObject

Returns the value of attribute required.



20
21
22
# File 'lib/opto/option.rb', line 20

def required
  @required
end

#skip_ifObject (readonly)

Returns the value of attribute skip_if.



25
26
27
# File 'lib/opto/option.rb', line 25

def skip_if
  @skip_if
end

#toObject (readonly)

Returns the value of attribute to.



23
24
25
# File 'lib/opto/option.rb', line 23

def to
  @to
end

#typeObject

Returns the value of attribute type.



16
17
18
# File 'lib/opto/option.rb', line 16

def type
  @type
end

#type_optionsObject (readonly)

Returns the value of attribute type_options.



28
29
30
# File 'lib/opto/option.rb', line 28

def type_options
  @type_options
end

Instance Method Details

#deep_merge_defaultsObject



95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/opto/option.rb', line 95

def deep_merge_defaults
  return nil unless group && group.defaults
  if group.defaults[:from]
    normalize_from_to(group.defaults[:from]).each do |k,v|
      from[k] ||= v
    end
  end
  if group.defaults[:to]
    normalize_from_to(group.defaults[:to]).each do |k,v|
      to[k] ||= v
    end
  end
end

#errorsHash

Validation errors

Returns:

  • (Hash)


240
241
242
# File 'lib/opto/option.rb', line 240

def errors
  handler.errors
end

#handlerOpto::Type

Access the Opto::Type handler for this option

Returns:



166
167
168
169
170
# File 'lib/opto/option.rb', line 166

def handler
  @handler ||= Type.for(type).new(type_options)
rescue StandardError => ex
  raise ex, "#{name}: #{ex.message}"
end

#normalize_from_to(inputs) ⇒ Object



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/opto/option.rb', line 244

def normalize_from_to(inputs)
  case inputs
  when ::Array
    case inputs.first
    when String, Symbol
      inputs.each_with_object({}) { |o, hash| hash[o.to_s.snakecase.to_sym] = name }
    when Hash
      inputs.each_with_object({}) { |o, hash| o.each { |k,v| hash[k.to_s.snakecase.to_sym] = v } }
    when NilClass
      {}
    else
      raise TypeError, "Invalid format #{inputs.inspect}"
    end
  when Hash
    inputs.each_with_object({}) { |(k, v), hash| hash[k.to_s.snakecase.to_sym] = v }
  when String, Symbol
    { inputs.to_s.snakecase.to_sym => name }
  when NilClass
    {}
  else
    raise TypeError, "Invalid format #{inputs.inspect}"
  end
end

#outputObject

Run setters



215
216
217
218
219
220
221
222
223
224
225
# File 'lib/opto/option.rb', line 215

def output
  setters.each do |setter|
    begin
      setter.respond_to?(:before) && setter.before(self)
      setter.set(value)
      setter.respond_to?(:after)  && setter.after(self)
    rescue StandardError => ex
      raise ex, "Setter '#{setter.target}' for '#{name}' : #{ex.message}"
    end
  end
end

#required?Boolean

True if this field is defined as required: true

Returns:

  • (Boolean)


193
194
195
# File 'lib/opto/option.rb', line 193

def required?
  handler.required?
end

#resolveObject

Run resolvers

Raises:

  • (TypeError, ArgumentError)


199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/opto/option.rb', line 199

def resolve
  resolvers.each do |resolver|
    begin
      result = resolver.try_resolve
    rescue StandardError => ex
      raise ex, "Resolver '#{resolver.origin}' for '#{name}' : #{ex.message}"
    end
    unless result.nil?
      @origin = resolver.origin
      return result
    end
  end
  nil
end

#resolversArray<Opto::Resolver>

Accessor to defined resolvers for this option.

Returns:



183
184
185
# File 'lib/opto/option.rb', line 183

def resolvers
  @resolvers ||= from.merge(default: self).map { |origin, hint| Resolver.for(origin).new(hint, self) }
end

#set(value) ⇒ Object Also known as: value=

Set option value. Also aliased as #value=

Parameters:

  • value


132
133
134
135
136
# File 'lib/opto/option.rb', line 132

def set(value)
  @value = handler.sanitize(value)
  validate
  @value
end

#settersObject



187
188
189
# File 'lib/opto/option.rb', line 187

def setters
  @setters ||= to.map { |target, hint| Setter.for(target).new(hint, self) }
end

#skip?Boolean

Returns true if this field should not be processed because of the conditionals

Returns:

  • (Boolean)


142
143
144
145
146
147
# File 'lib/opto/option.rb', line 142

def skip?
  return false if group.nil?
  return true if group.any_true?(skip_if)
  return true unless group.all_true?(only_if)
  false
end

#to_h(with_errors: false, with_value: true) ⇒ Hash

Hash representation of Opto::Option. Can be passed back to Opto::Option.new

Parameters:

  • with_errors (Boolean) (defaults to: false)

    Include possible validation errors hash

  • with_value (Boolean) (defaults to: true)

    Include current value

Returns:

  • (Hash)


113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/opto/option.rb', line 113

def to_h(with_errors: false, with_value: true)
  hash = {
    name: name,
    label: label,
    type: type,
    description: description,
    default: default,
    from: from.reject { |k,_| k == :default},
    to: to
  }.merge(type_options).reject { |_,v| v.nil? }
  hash[:skip_if] = skip_if if skip_if
  hash[:only_if] = only_if if only_if
  hash[:errors]  = errors  if with_errors
  hash[:value]   = value   if with_value
  hash
end

#true?Boolean

Returns:

  • (Boolean)


234
235
236
# File 'lib/opto/option.rb', line 234

def true?
  handler.truthy?(value)
end

#valid?Boolean

True if value is valid

Returns:

  • (Boolean)


229
230
231
232
# File 'lib/opto/option.rb', line 229

def valid?
  return true if skip?
  handler.valid?(value)
end

#validateObject

Run validators

Raises:

  • (TypeError, ArgumentError)


158
159
160
161
162
# File 'lib/opto/option.rb', line 158

def validate
  handler.validate(@value)
rescue StandardError => ex
  raise ex, "Validation for #{name} : #{ex.message}"
end

#valueObject

The value of this option. Will try to run resolvers.

Returns:

  • option_value



174
175
176
177
178
179
# File 'lib/opto/option.rb', line 174

def value
  return @value unless @value.nil?
  return nil if skip?
  set(resolve)
  @value
end

#value_of(option_name) ⇒ Object

Get a value of another Opto::Group member

Parameters:

  • option_name (String)


151
152
153
154
# File 'lib/opto/option.rb', line 151

def value_of(option_name)
  return value if option_name == self.name
  group.nil? ? nil : group.value_of(option_name)
end