state_mate 0.0.5 → 0.0.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2a4c8ab3b912af529324511e027680f26fef9266
4
- data.tar.gz: 19900ae7fbd3aca5d28530833af4417b3018f5a6
3
+ metadata.gz: 8a688ee124e85b425b1448494e3a59bb5244097d
4
+ data.tar.gz: e3f8f643717a325cf6743c4f690cbf4f2bc45fef
5
5
  SHA512:
6
- metadata.gz: 725018a48c59f60532de69717380def4d25a7c1d93a14a3496f539877cf52c2dc30eb4885325f2e87fc373256afbe61f224219c3221d9661d81749f8138b069b
7
- data.tar.gz: 9ebc8e8dfaf989dfaf951881bfbc608b1d2a35e7451764643fa5f1d8a219f12592c27856961c434299e3f158633e71592021eebba9fcec1a2bc36001e46a63e6
6
+ metadata.gz: 55925c3df961ce4eef5df8d88e5f4e48a53173bce138321df7f1ac9dabe54a04cefc3143821c5c7ee683806cbe388fdc4fc3bcf19b73d98b8c4c5da6a410b0c2
7
+ data.tar.gz: aec931eefee2567705e5a604bdde89be85967bf8c3c0d6f0bf7910275ebce938298ce005ab5b4e30e49ba7759783dc405698805abe1a3ce4c3119d4c9087df84
@@ -9,10 +9,12 @@ require 'CFPropertyList'
9
9
 
10
10
  require 'cmds'
11
11
 
12
- module StateMate; end
13
- module StateMate::Adapters; end
12
+ require 'state_mate'
14
13
 
15
14
  module StateMate::Adapters::Defaults
15
+ include StateMate::Adapters
16
+ register 'defaults'
17
+
16
18
  # constants
17
19
  # ========
18
20
 
@@ -43,28 +45,20 @@ module StateMate::Adapters::Defaults
43
45
  # `nil`.
44
46
  #
45
47
  # @param options [Hash]
46
- # @option options [Boolean] 'current_host' if true, the read will be done
48
+ # @option options [Boolean] :current_host if true, the read will be done
47
49
  # for the domain's "current host" plist file (using the `-currentHost`
48
50
  # option when calling the system's `defaults` command).
49
- #
50
- # note that the key is a {String} and not a {Symbol}.
51
51
  #
52
52
  # @return our Ruby representation of the value, or `nil` if it's not found.
53
53
  #
54
54
  def self.read key, options = {}
55
- if options.key? :current_host
56
- raise ArgumentError.new NRSER.squish <<-END
57
- current_host option key must be a string, not a symbol.
58
- END
59
- end
60
-
61
55
  options = {
62
- 'current_host' => false,
56
+ current_host: false,
63
57
  }.merge options
64
58
 
65
59
  domain, key_segs = parse_key key
66
60
 
67
- value = read_defaults domain, options['current_host']
61
+ value = read_defaults domain, options[:current_host]
68
62
 
69
63
  key_segs.each do |seg|
70
64
  value = if (value.is_a?(Hash) && value.key?(seg))
@@ -93,23 +87,15 @@ module StateMate::Adapters::Defaults
93
87
  # key.
94
88
  #
95
89
  # @param options [Hash]
96
- # @option options [Boolean] 'current_host' if true, the read will be done
90
+ # @option options [Boolean] :current_host if true, the read will be done
97
91
  # for the domain's "current host" plist file (using the `-currentHost`
98
92
  # option when calling the system's `defaults` command).
99
- #
100
- # note that the key is a {String} and not a {Symbol}.
101
93
  #
102
94
  # @return nil
103
95
  #
104
96
  def self.write key, value, options = {}
105
- if options.key? :current_host
106
- raise ArgumentError.new NRSER.squish <<-END
107
- current_host option key must be a string, not a symbol.
108
- END
109
- end
110
-
111
97
  options = {
112
- 'current_host' => false,
98
+ current_host: false,
113
99
  }.merge options
114
100
 
115
101
  domain, key_segs = parse_key key
@@ -119,12 +105,12 @@ module StateMate::Adapters::Defaults
119
105
  key_segs[0],
120
106
  key_segs.drop(1),
121
107
  value,
122
- options['current_host']
108
+ options[:current_host]
123
109
  else
124
110
  basic_write domain,
125
111
  key_segs[0],
126
112
  value,
127
- options['current_host']
113
+ options[:current_host]
128
114
  end
129
115
 
130
116
  nil
@@ -588,7 +574,7 @@ module StateMate::Adapters::Defaults
588
574
  # @return nil
589
575
  #
590
576
  def self.deep_write domain, key, deep_segs, value, current_host
591
- root = read [domain, key], 'current_host' => current_host
577
+ root = read [domain, key], current_host: current_host
592
578
  # handle the root not being there
593
579
  root = {} unless root.is_a? Hash
594
580
  hash_deep_write! root, deep_segs, value
@@ -1,10 +1,11 @@
1
1
  require 'cmds'
2
2
 
3
- module StateMate; end
4
- module StateMate::Adapters; end
3
+ require 'state_mate'
5
4
 
6
5
  # adapter to set global git config options
7
6
  module StateMate::Adapters::GitConfig
7
+ include StateMate::Adapters
8
+ register 'git_config'
8
9
 
9
10
  # @api adapter
10
11
  #
@@ -4,6 +4,9 @@ require 'state_mate'
4
4
  require 'state_mate/adapters/defaults'
5
5
 
6
6
  module StateMate::Adapters::JSON
7
+ include StateMate::Adapters
8
+ register 'json'
9
+
7
10
  def self.parse_key key
8
11
  # use the same key seperation as Defaults
9
12
  StateMate::Adapters::Defaults.parse_key key
@@ -14,7 +17,7 @@ module StateMate::Adapters::JSON
14
17
 
15
18
  contents = File.read(File.expand_path(filepath))
16
19
 
17
- value = JSON.load contents
20
+ value = ::JSON.load contents
18
21
 
19
22
  key_segs.each do |seg|
20
23
  value = if (value.is_a?(Hash) && value.key?(seg))
@@ -49,9 +52,9 @@ module StateMate::Adapters::JSON
49
52
  end
50
53
 
51
54
  content = if options['pretty']
52
- JSON.pretty_generate new_root
55
+ ::JSON.pretty_generate new_root
53
56
  else
54
- JSON.dump new_root
57
+ ::JSON.dump new_root
55
58
  end
56
59
 
57
60
  File.open(filepath, 'w') do |f|
@@ -15,6 +15,8 @@ using NRSER
15
15
  # 46
16
16
 
17
17
  module StateMate::Adapters::LaunchD
18
+ include StateMate::Adapters
19
+ register 'launchd'
18
20
 
19
21
  EXE = '/bin/launchctl'
20
22
 
@@ -1,12 +1,14 @@
1
1
  require 'cmds'
2
2
  require 'nrser'
3
3
 
4
- using NRSER
4
+ require 'state_mate'
5
5
 
6
- module StateMate; end
7
- module StateMate::Adapters; end
6
+ using NRSER
8
7
 
9
8
  module StateMate::Adapters::NVRAM
9
+ include StateMate::Adapters
10
+ register 'nvram'
11
+
10
12
  def self.read key, options = {}
11
13
  result = Cmds "nvram %{key}", key: key
12
14
 
@@ -1,13 +1,16 @@
1
+ require 'pp'
2
+
1
3
  require 'cmds'
2
4
  require 'nrser'
3
- require 'pp'
4
5
 
5
- using NRSER
6
+ require 'state_mate'
6
7
 
7
- module StateMate; end
8
- module StateMate::Adapters; end
8
+ using NRSER
9
9
 
10
10
  module StateMate::Adapters::PMSet
11
+ include StateMate::Adapters
12
+ register 'pmset'
13
+
11
14
  # whitelist of modes we handle mapped to their `pmset` flag
12
15
  #
13
16
  # there is also a UPS mode, but i don't know what it looks like
@@ -1,10 +1,11 @@
1
1
  require 'cmds'
2
2
 
3
- module StateMate; end
4
- module StateMate::Adapters; end
3
+ require 'state_mate'
5
4
 
6
5
  # adapter to set global git config options
7
6
  module StateMate::Adapters::SCUtil
7
+ include StateMate::Adapters
8
+ register 'scutil'
8
9
 
9
10
  # @api adapter
10
11
  #
@@ -5,12 +5,14 @@ require 'CFPropertyList'
5
5
  require 'nrser'
6
6
  require 'nrser/exec'
7
7
 
8
- using NRSER
8
+ require 'state_mate'
9
9
 
10
- module StateMate; end;
11
- module StateMate::Adapters; end
10
+ using NRSER
12
11
 
13
12
  module StateMate::Adapters::TimeMachine
13
+ include StateMate::Adapters
14
+ register 'time_machine'
15
+
14
16
  EXE = '/usr/bin/tmutil'
15
17
  PLIST_PATH = '/Library/Preferences/com.apple.TimeMachine.plist'
16
18
 
@@ -0,0 +1,52 @@
1
+ require 'pp'
2
+
3
+ module StateMate; end
4
+
5
+ module StateMate::Adapters
6
+ API_METHOD_NAMES = [:read, :write]
7
+
8
+ @@index = {}
9
+
10
+ module IncludeClassMethods
11
+ def register name
12
+ StateMate::Adapters.register name, self
13
+ end
14
+ end
15
+
16
+ def self.included base
17
+ base.extend IncludeClassMethods
18
+ end
19
+
20
+ def self.register name, obj
21
+ unless name.is_a? String
22
+ raise StateMate::Error::TypeError.new name, "name must be a String"
23
+ end
24
+
25
+ @@index[name] = obj
26
+ end
27
+
28
+ def self.get name
29
+ # return it if it's already loaded
30
+ return @@index[name] if @@index.key? name
31
+
32
+ # try to require it
33
+ begin
34
+ require "state_mate/adapters/#{ name }"
35
+ rescue LoadError => e
36
+ end
37
+
38
+ unless @@index.key? name
39
+ raise StateMate::Error::AdapterNotFoundError.new NRSER.dedent <<-END
40
+ adapter #{ name.inspect } was not found.
41
+
42
+ registered adapters:
43
+
44
+ #{ @@index.pretty_inspect }
45
+
46
+ END
47
+ end
48
+
49
+ @@index[name]
50
+ end
51
+
52
+ end # Adapters
@@ -0,0 +1,28 @@
1
+ module StateMate
2
+ module Error
3
+ class StateMateError < StandardError; end
4
+
5
+ class ExecutionError < StateMateError; end
6
+
7
+ class WriteError < ExecutionError; end
8
+
9
+ # raised when an erros is encountered running a sync method on an adapter
10
+ #(set, unset, array_contains, array_missing)
11
+ class ValueSyncError < ExecutionError; end
12
+
13
+ class TypeError < ::TypeError
14
+ attr_accessor :value
15
+
16
+ def initialize value, msg
17
+ @value = value
18
+ super "#{ msg }, found #{ value.inspect }"
19
+ end
20
+ end
21
+
22
+ class AdapterNotFoundError < StateMateError; end
23
+
24
+ # raised when the current structre of a value prevents the desired sync
25
+ # operation with the given options.
26
+ class StructureConflictError < StateMateError; end
27
+ end # Error
28
+ end # StateMate
@@ -1,3 +1,3 @@
1
1
  module StateMate
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
data/lib/state_mate.rb CHANGED
@@ -5,33 +5,18 @@ require 'nrser/refinements'
5
5
  using NRSER
6
6
 
7
7
  require "state_mate/version"
8
+ require "state_mate/error"
9
+ require "state_mate/adapters"
8
10
 
9
11
  module StateMate
10
12
 
11
13
  DIRECTIVES = Set.new [
12
- 'set',
13
- 'unset',
14
- 'array_contains',
15
- 'array_missing',
14
+ :set,
15
+ :unset,
16
+ :array_contains,
17
+ :array_missing,
16
18
  ]
17
19
 
18
- module Error
19
- class ExecutionError < StandardError; end
20
-
21
- class WriteError < ExecutionError; end
22
-
23
- class ValueChangeError < ExecutionError; end
24
-
25
- class TypeError < ::TypeError
26
- attr_accessor :value
27
-
28
- def initialize value, msg
29
- @value = value
30
- super "#{ msg }, found #{ value.inspect }"
31
- end
32
- end
33
- end # Error
34
-
35
20
  class StateSet
36
21
  attr_accessor :spec
37
22
  attr_reader :states,
@@ -60,7 +45,7 @@ module StateMate
60
45
  end
61
46
 
62
47
  spec.each do |adapter_name, states|
63
- adapter = StateMate.get_adapter adapter_name
48
+ adapter = StateMate::Adapters.get adapter_name
64
49
 
65
50
  states = case states
66
51
  when Hash
@@ -98,13 +83,16 @@ module StateMate
98
83
  end
99
84
 
100
85
  state.each do |k, v|
101
- if k == 'key'
86
+ # normalize to symbols
87
+ k = k.to_sym if k.is_a? String
88
+
89
+ if k == :key
102
90
  key = v
103
- elsif k == 'options'
91
+ elsif k == :options
104
92
  # pass, dealt with above
105
93
  elsif DIRECTIVES.include? k
106
94
  directives << [k, v]
107
- elsif k == 'type'
95
+ elsif k == :type
108
96
  type_name = v
109
97
  else
110
98
  # any other keys are set as options
@@ -181,7 +169,11 @@ module StateMate
181
169
  test_method = StateMate.method "#{ state.directive }?"
182
170
 
183
171
  # find out if the state is in sync
184
- in_sync = test_method.call state.key, read_value, state.value, state.adapter
172
+ in_sync = test_method.call state.key,
173
+ read_value,
174
+ state.value,
175
+ state.adapter,
176
+ state.options
185
177
 
186
178
  # add to the list of changes to be made for states that are
187
179
  # out of sync
@@ -205,13 +197,13 @@ module StateMate
205
197
  state.options
206
198
  rescue Exception => e
207
199
  @new_value_error = e
208
- raise Error::ValueChangeError.new binding.erb <<-BLOCK
200
+ raise Error::ValueSyncError.new binding.erb <<-BLOCK
209
201
  an error occured when changing a values:
210
202
 
211
203
  <%= @new_value_error.format %>
212
204
 
213
205
  no changes were attempted to the system, so there is no rollback
214
- neessicary.
206
+ necessary.
215
207
  BLOCK
216
208
  end
217
209
  # change successful, store the new value along-side the state
@@ -293,39 +285,64 @@ module StateMate
293
285
  # as strings.
294
286
  #
295
287
  # @param type_name [String] the 'name' of the type to cast to.
296
-
288
+ # @param value the value to cast.
289
+ #
297
290
  def self.cast type_name, value
298
291
  case type_name
299
292
  when 'string', 'str'
300
293
  value.to_s
301
294
  when 'integer', 'int'
302
- value.to_i
295
+ case value
296
+ when Fixnum
297
+ value
298
+ when true
299
+ 1
300
+ when false
301
+ 0
302
+ when String
303
+ if value =~ /\A[-+]?[0-9]*\Z/
304
+ value.to_i
305
+ elsif value.downcase == 'true'
306
+ 1
307
+ elsif value.downcase == 'false'
308
+ 0
309
+ else
310
+ raise ArgumentError.new "can't cast to integer: #{ value.inspect }"
311
+ end
312
+ else
313
+ raise TypeError.new "can't cast type to integer: #{ value.inspect }"
314
+ end
303
315
  when 'float'
304
- value.to_f
316
+ case value
317
+ when Float
318
+ value
319
+ when Fixnum
320
+ value.to_f
321
+ when String
322
+ if value =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
323
+ value.to_f
324
+ else
325
+ raise ArgumentError.new "can't cast to float: #{ value.inspect }"
326
+ end
327
+ else
328
+ raise TypeError.new "can't cast type to float: #{ value.inspect }"
329
+ end
305
330
  when 'boolean', 'bool'
306
- if value.to_s.downcase == 'true'
307
- true
308
- elsif value.to_s.downcase == 'false'
331
+ case value
332
+ when true, false
333
+ value
334
+ when 0, '0', 'False', 'false', 'FALSE'
309
335
  false
310
- else
311
- raise ArgumentError.new "can't cast to boolean: #{ value.inspect }"
336
+ when 1, '1', 'True', 'true', 'TRUE'
337
+ true
338
+ else
339
+ raise ArgumentError.new "can't cast type to boolean: #{ value.inspect }"
312
340
  end
313
341
  else
314
342
  raise ArgumentError.new "bad type name: #{ type_name.inspect }"
315
343
  end
316
344
  end
317
345
 
318
- def self.get_adapter adapter_name
319
- begin
320
- require "state_mate/adapters/#{ adapter_name }"
321
- StateMate::Adapters.constants.find {|sym|
322
- sym.to_s.downcase == adapter_name.gsub('_', '')
323
- }.pipe {|sym| StateMate::Adapters.const_get sym}
324
- rescue Exception => e
325
- raise "can't find adapter #{ adapter_name.inspect }: #{ e }"
326
- end
327
- end
328
-
329
346
  def self.execute spec
330
347
  StateSet.from_spec(spec).execute
331
348
  end
@@ -338,7 +355,7 @@ module StateMate
338
355
  end
339
356
  end
340
357
 
341
- def self.set? key, current, value, adapter
358
+ def self.set? key, current, value, adapter, options
342
359
  values_equal? current, value, adapter
343
360
  end
344
361
 
@@ -347,7 +364,7 @@ module StateMate
347
364
  value
348
365
  end
349
366
 
350
- def self.unset? key, current, value, adapter
367
+ def self.unset? key, current, value, adapter, options
351
368
  current.nil?
352
369
  end
353
370
 
@@ -357,7 +374,7 @@ module StateMate
357
374
  nil
358
375
  end
359
376
 
360
- def self.array_contains? key, current, value, adapter
377
+ def self.array_contains? key, current, value, adapter, options
361
378
  current.is_a?(Array) && current.any? {|v|
362
379
  values_equal? v, value, adapter
363
380
  }
@@ -366,14 +383,22 @@ module StateMate
366
383
  def self.array_contains key, current, value, options
367
384
  case current
368
385
  when Array
369
- current + [value]
386
+ # this is just to make the function consistent, so it doesn't add another
387
+ # copy of value if it's there... in practice StateMate should not
388
+ # call {.array_contains} if the value is alreay in the array
389
+ # (that's what {.array_contains?} tests for)
390
+ if current.include? value
391
+ current
392
+ else
393
+ current + [value]
394
+ end
370
395
 
371
396
  when nil
372
397
  # it needs to be created
373
- if options[:create]
398
+ if options[:create] || options[:clobber]
374
399
  [value]
375
400
  else
376
- raise <<-BLOCK.unblock
401
+ raise Error::StructureConflictError.new <<-BLOCK.unblock
377
402
  can not ensure #{ key.inspect } contains #{ value.inspect } because
378
403
  the key does not exist and options[:create] is not true.
379
404
  BLOCK
@@ -387,18 +412,34 @@ module StateMate
387
412
  if options[:clobber]
388
413
  [value]
389
414
  else
390
- raise <<-BLOCK.unblock
415
+ raise Error::StructureConflictError.new <<-BLOCK.unblock
391
416
  can not ensure #{ key.inspect } contains #{ value.inspect } because
392
417
  the value is #{ current.inspect } and options[:clobber] is not true.
393
418
  BLOCK
394
419
  end
395
420
  end # case current
396
421
  end # array_contians
397
-
398
- def self.array_missing? key, current, value, adapter
399
- current.is_a?(Array) && !current.any? {|v|
400
- values_equal? v, value, adapter
401
- }
422
+
423
+ # @param options [Hash]
424
+ # @option options [Boolean] :unset_ok if true, the value being unset is
425
+ # acceptible. many plist files will simply omit the key rather than
426
+ # store an empty array in the case that an array value is empty,
427
+ # and setting these to an empty array when all we want to do is make
428
+ # sure that *if it is there, it doesn't contain the value* seems
429
+ # pointless.
430
+ def self.array_missing? key, current, value, adapter, options
431
+ case current
432
+ when nil
433
+ if options[:unset_ok]
434
+ true
435
+ else
436
+ false
437
+ end
438
+ when Array
439
+ !current.any? {|v| values_equal? v, value, adapter}
440
+ else
441
+ false
442
+ end
402
443
  end
403
444
 
404
445
  def self.array_missing key, current, value, options
@@ -407,14 +448,20 @@ module StateMate
407
448
  current - [value]
408
449
 
409
450
  when nil
410
- # there is no value, only option is to create a new empty array there
411
- if options[:create]
412
- []
451
+ # if we're ok with the value being unset (`nil` to us here), then
452
+ # we're done
453
+ if options[:unset_ok]
454
+ nil
413
455
  else
414
- raise <<-BLOCK.unblock
415
- can not ensure #{ key.inspect } missing #{ value.inspect } because
416
- the key does not exist and options[:create] is not true.
417
- BLOCK
456
+ # there is no value, only option is to create a new empty array there
457
+ if options[:create] || options[:clobber]
458
+ []
459
+ else
460
+ raise Error::StructureConflictError.new <<-BLOCK.unblock
461
+ can not ensure #{ key.inspect } missing #{ value.inspect } because
462
+ the key does not exist and options[:create] is not true.
463
+ BLOCK
464
+ end
418
465
  end
419
466
 
420
467
  else
@@ -424,7 +471,7 @@ module StateMate
424
471
  if options[:clobber]
425
472
  []
426
473
  else
427
- raise <<-BLOCK.unblock
474
+ raise Error::StructureConflictError.new <<-BLOCK.unblock
428
475
  can not ensure #{ key.inspect } missing #{ value.inspect } because
429
476
  the value is #{ current.inspect } and options[:clobber] is not true.
430
477
  BLOCK
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: state_mate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - nrser
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-10-19 00:00:00.000000000 Z
11
+ date: 2015-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -167,6 +167,7 @@ files:
167
167
  - ansible/library/state
168
168
  - bin/console
169
169
  - lib/state_mate.rb
170
+ - lib/state_mate/adapters.rb
170
171
  - lib/state_mate/adapters/defaults.rb
171
172
  - lib/state_mate/adapters/git_config.rb
172
173
  - lib/state_mate/adapters/json.rb
@@ -175,6 +176,7 @@ files:
175
176
  - lib/state_mate/adapters/pmset.rb
176
177
  - lib/state_mate/adapters/scutil.rb
177
178
  - lib/state_mate/adapters/time_machine.rb
179
+ - lib/state_mate/error.rb
178
180
  - lib/state_mate/version.rb
179
181
  - notes/state-set-steps.md
180
182
  - state_mate.gemspec