Module: StateMate

Defined in:
lib/state_mate.rb,
lib/state_mate/error.rb,
lib/state_mate/version.rb,
lib/state_mate/adapters.rb,
lib/state_mate/state_set.rb

Overview

Declarations

Defined Under Namespace

Modules: Adapters, Error Classes: StateSet

Constant Summary collapse

DIRECTIVES =
Set.new [
  # ensures that the key is set to the provided value
  :set,
  
  # ensures that the key is absent
  :unset,
  
  # ensures that the key is an array containing the provided items.
  # 
  # if the key is missing and `create` or `clobber` options evaluate true, it
  # will be created to have exactly the provided items. otherwise it will
  # fail.
  # 
  # if the value is not an array and `clobber` option evaluates true it will
  # be replaced with an array of exactly the provided items. otherwise it
  # will fail.
  # 
  :array_contains,
  
  # ensures that the value is an array that is missing the provided items.
  # 
  # if the current value is:
  # 
  # -   missing/nil/null:
  #     -   if the `unset_ok` option evaluates **true**:
  #         -   it will validate as a correct state and no action will be
  #             taken.
  #         -   **NOTE: this is the ONLY case where the action succeeds
  #             and the value IS NOT an array afterwards**.
  #     -   if the `unset_ok` option evaluates **false** (default):
  #         -   if the `create` or `clobber` options evaluate **true**:
  #             -   the value will be set to an empty array.
  #         -   otherwise:
  #             -   fails.
  # -   something else that's not an array:
  #     -   if the `clobber` option evaluates **true**:
  #         -   value will be set to an empty array.
  #     -   if the `clobber` option evaluates **false** (default):
  #         -   fails.
  :array_missing,
  
  # initializes a value - setting it only if it is missing/nil
  :init,
]
VERSION =
'0.1.1'

Class Method Summary collapse

Class Method Details

.array_contains(key, current, value, options) ⇒ Object



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
# File 'lib/state_mate.rb', line 216

def self.array_contains key, current, value, options
  case current
  when Array
    # this is just to make the function consistent, so it doesn't add another
    # copy of value if it's there... in practice StateMate should not
    # call {.array_contains} if the value is already in the array
    # (that's what {.array_contains?} tests for)
    if current.include? value
      current
    else
      current + [value]
    end

  when nil
    # it needs to be created
    if options[:create] || options[:clobber]
      [value]
    else
      raise Error::StructureConflictError.new <<-BLOCK.unblock
        can not ensure #{ key.inspect } contains #{ value.inspect } because
        the key does not exist and options[:create] is not true.
      BLOCK
    end

  else
    # there is something there, but it's not an array. out only option
    # to achieve the declared state is to replace it with a new array
    # where value is the only element, but we don't want to do that unless
    # we've been told to clobber
    if options[:clobber]
      [value]
    else
      raise Error::StructureConflictError.new <<-BLOCK.unblock
        can not ensure #{ key.inspect } contains #{ value.inspect } because
        the value is #{ current.inspect } and options[:clobber] is not true.
      BLOCK
    end
  end # case current
end

.array_contains?(key, current, value, adapter, options) ⇒ Boolean

Returns:

  • (Boolean)


210
211
212
213
214
# File 'lib/state_mate.rb', line 210

def self.array_contains? key, current, value, adapter, options
  current.is_a?(Array) && current.any? {|v|
    values_equal? v, value, adapter
  }
end

.array_missing(key, current, value, options) ⇒ Object



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
# File 'lib/state_mate.rb', line 278

def self.array_missing key, current, value, options
  case current
  when Array
    current - [value]

  when nil
    # if we're ok with the value being unset (`nil` to us here), then
    # we're done
    if options[:unset_ok]
      nil
    else
      # there is no value, only option is to create a new empty array there
      if options[:create] || options[:clobber]
        []
      else
        raise Error::StructureConflictError.new <<-BLOCK.unblock
          can not ensure #{ key.inspect } missing #{ value.inspect } because
          the key does not exist and options[:create] is not true.
        BLOCK
      end
    end

  else
    # there is something there, but it's not an array. out only option
    # to achieve the declared state is to replace it with a new empty array
    # but we don't want to do that unless we've been told to clobber
    if options[:clobber]
      []
    else
      raise Error::StructureConflictError.new <<-BLOCK.unblock
        can not ensure #{ key.inspect } missing #{ value.inspect } because
        the value is #{ current.inspect } and options[:clobber] is not true.
      BLOCK
    end
  end # case current
end

.array_missing?(key, current, value, adapter, options) ⇒ Boolean

Parameters:

  • options (Hash)

Options Hash (options):

  • :unset_ok (Boolean)

    if true, the value being unset is acceptable. many plist files will simply omit the key rather than store an empty array in the case that an array value is empty, and setting these to an empty array when all we want to do is make sure that if it is there, it doesn't contain the value seems pointless.

Returns:

  • (Boolean)


263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/state_mate.rb', line 263

def self.array_missing? key, current, value, adapter, options
  case current
  when nil
    if options[:unset_ok]
      true
    else
      false
    end
  when Array
    !current.any? {|v| values_equal? v, value, adapter}
  else
    false
  end
end

.cast(type_name, value) ⇒ Object

pure

casts a value to a type, or raises an error if not possible. this is useful because Ansible in particular likes to pass things as strings.

Parameters:

  • type_name (String)

    the 'name' of the type to cast to.

  • value

    the value to cast.



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
# File 'lib/state_mate.rb', line 123

def self.cast type_name, value
  case type_name
  when 'string', 'str'
    value.to_s
  when 'integer', 'int'
    case value
    when Fixnum
      value
    when true
      1
    when false
      0
    when String
      if value =~ /\A[-+]?[0-9]*\Z/
        value.to_i
      elsif value.downcase == 'true'
        1
      elsif value.downcase == 'false'
        0
      else
        raise ArgumentError.new "can't cast to integer: #{ value.inspect }"
      end
    else
      raise TypeError.new "can't cast type to integer: #{ value.inspect }"
    end
  when 'float'
    case value
    when Float
      value
    when Fixnum
      value.to_f
    when String
      if value =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
        value.to_f
      else
        raise ArgumentError.new "can't cast to float: #{ value.inspect }"
      end
    else
      raise TypeError.new "can't cast type to float: #{ value.inspect }"
    end
  when 'boolean', 'bool'
    case value
    when true, false
      value
    when 0, '0', 'False', 'false', 'FALSE'
      false
    when 1, '1', 'True', 'true', 'TRUE'
      true
    else
      raise ArgumentError.new "can't cast type to boolean: #{ value.inspect }"
    end
  else
    raise ArgumentError.new "bad type name: #{ type_name.inspect }"
  end
end

.debug(*messages) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/state_mate.rb', line 94

def self.debug *messages
  return unless @debug
  
  @debug_file ||= File.open('./state_mate.debug.log', @debug_mode)
  
  messages.each_with_index do |message, index|
    if index == 0
      @debug_file.write 'DEBUG '
    end
    
    if message.is_a? String
      @debug_file.puts message
    else
      @debug_file.puts
      PP.pp(message, @debug_file)
    end
  end
end

.debug=(mode) ⇒ Object

turns debug on or off

Parameters:

  • mode (Boolean|String)

    if a string, enables and sets the debug file mode (use 'a' or 'w'). if a boolean, sets enabled.



87
88
89
90
91
92
# File 'lib/state_mate.rb', line 87

def self.debug= mode
  if mode.is_a? String
    @debug_mode = mode
  end
  @debug = !!mode
end

.execute(spec) ⇒ Object



179
180
181
# File 'lib/state_mate.rb', line 179

def self.execute spec
  StateSet.from_spec(spec).execute
end

.init(key, current, value, options) ⇒ Object

when a value needs to be initialized it is simply set to the value.



321
322
323
# File 'lib/state_mate.rb', line 321

def self.init key, current, value, options
  return value
end

.init?(key, current, value, adapter, options) ⇒ Boolean

the value is initialized if it's currently not nil

Returns:

  • (Boolean)


316
317
318
# File 'lib/state_mate.rb', line 316

def self.init? key, current, value, adapter, options
  return !current.nil?
end

.set(key, current, value, options) ⇒ Object



195
196
197
198
# File 'lib/state_mate.rb', line 195

def self.set key, current, value, options
  # TODO: handle options
  value
end

.set?(key, current, value, adapter, options) ⇒ Boolean

Returns:

  • (Boolean)


191
192
193
# File 'lib/state_mate.rb', line 191

def self.set? key, current, value, adapter, options
  values_equal? current, value, adapter
end

.unset(key, current, value, options) ⇒ Object



204
205
206
207
208
# File 'lib/state_mate.rb', line 204

def self.unset key, current, value, options
  # TODO: handle options
  raise "value most be nil to unset" unless value.nil?
  nil
end

.unset?(key, current, value, adapter, options) ⇒ Boolean

Returns:

  • (Boolean)


200
201
202
# File 'lib/state_mate.rb', line 200

def self.unset? key, current, value, adapter, options
  current.nil?
end

.values_equal?(current, desired, adapter) ⇒ Boolean

Returns:

  • (Boolean)


183
184
185
186
187
188
189
# File 'lib/state_mate.rb', line 183

def self.values_equal? current, desired, adapter
  if adapter.respond_to? :values_equal?
    adapter.values_equal? current, desired
  else
    current == desired
  end
end