state_mate 0.0.5 → 0.0.6

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
  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