flight_config 0.1.1 → 0.2.0

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
  SHA256:
3
- metadata.gz: '081d7e1e6de4db075516f7c0c6e56014960a1a67a40d10038ca934039c2ef7ad'
4
- data.tar.gz: ed51d2999f19411b130f1f5acc41b58f056efc2bbad05783d1eec00b95c38d62
3
+ metadata.gz: f8c36bdbe520695cb9d379d6038699c3529fb1fb8938af728fa50452ec0414ae
4
+ data.tar.gz: 58bf550037f38ede9db72516a5a70afd1b2fa1a47f1be8e7a3d7b95144d9d768
5
5
  SHA512:
6
- metadata.gz: cd304a5753c64707df7e0fd4c107489bac75378db3c731db249deeba0262053fc0a05c7aee9e3bd1df8704897b46183c3a8be33661e41e731b29881679a4ecbe
7
- data.tar.gz: 4635f2ee905c961830fa2c393db15c8978afbd24d2131d20734c8df967abb842a7dd04781e551a75d97f1f42e56ab0ea5949c7cc47d93591c102a0142ff88c24
6
+ metadata.gz: bf01421b86b937cfd4df7180398a81578723391cd26ac0f54e8c98d7ad741cc1315ff23f257460cb6e7474c652285e67cc5ffabf739fb1d3fda6e826847d6d21
7
+ data.tar.gz: 17768019155bd3e1621f0d597c89010b1403fe11f280ccec08f9e27c5685bb0435b0dbfdbc888b59bfcc84772146ebb153c2f624330505979d7c3b61b1bdbdc0
@@ -0,0 +1,89 @@
1
+ #==============================================================================
2
+ # Copyright (C) 2019-present Alces Flight Ltd.
3
+ #
4
+ # This file is part of FlightConfig.
5
+ #
6
+ # This program and the accompanying materials are made available under
7
+ # the terms of the Eclipse Public License 2.0 which is available at
8
+ # <https://www.eclipse.org/legal/epl-2.0>, or alternative license
9
+ # terms made available by Alces Flight Ltd - please direct inquiries
10
+ # about licensing to licensing@alces-flight.com.
11
+ #
12
+ # FlightConfig is distributed in the hope that it will be useful, but
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
14
+ # IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS
15
+ # OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
16
+ # PARTICULAR PURPOSE. See the Eclipse Public License 2.0 for more
17
+ # details.
18
+ #
19
+ # You should have received a copy of the Eclipse Public License 2.0
20
+ # along with FlightConfig. If not, see:
21
+ #
22
+ # https://opensource.org/licenses/EPL-2.0
23
+ #
24
+ # For more information on FlightConfig, please visit:
25
+ # https://github.com/openflighthpc/flight_config
26
+ #==============================================================================
27
+
28
+ # NOTE: This is compatibility layer between the accessor and TTY::Config.
29
+ # TTY::Config isn't a good match with FlightConfig as they both try and preform
30
+ # the file handling. Instead a Hashie type object should be used.
31
+ #
32
+ # To facilitate the transition, the accessors use the standard [] and []= methods
33
+ # as these will be defined on most hashie objects. TTY::Config does not implement
34
+ # them however. Hence the need for the compatibility layer
35
+
36
+ module FlightConfig
37
+ module TTYConfigAccessor
38
+ def [](key)
39
+ fetch(key)
40
+ end
41
+
42
+ def []=(key, value)
43
+ if value.nil?
44
+ delete(key)
45
+ else
46
+ set(key, value: value)
47
+ end
48
+ end
49
+ end
50
+
51
+ TTY::Config.include(TTYConfigAccessor)
52
+ end
53
+
54
+ module FlightConfig
55
+ module Accessor
56
+ def self.included(base)
57
+ base.extend(ClassMethods)
58
+ end
59
+
60
+ module ClassMethods
61
+ def data_accessor(key)
62
+ data_reader(key)
63
+ data_writer(key)
64
+ end
65
+
66
+ def data_reader(key, &b)
67
+ self.define_method(key) do
68
+ raw = __data__[key]
69
+ b ? instance_exec(raw, &b) : raw
70
+ end
71
+ end
72
+
73
+ def data_writer(key, &b)
74
+ self.define_method("#{key}=") do |raw|
75
+ __data__[key] = b ? instance_exec(raw, &b) : raw
76
+ end
77
+ end
78
+
79
+ def define_input_methods_from_path_parameters
80
+ self.method(:path)
81
+ .parameters
82
+ .select { |type, _| type == :req }
83
+ .each_with_index do |(_, arg), idx|
84
+ define_method(arg) { __inputs__[idx] }
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -24,6 +24,7 @@
24
24
  # For more information on FlightConfig, please visit:
25
25
  # https://github.com/openflighthpc/flight_config
26
26
  #==============================================================================
27
+
27
28
  require 'flight_config/exceptions'
28
29
  require 'flight_config/log'
29
30
  require 'flight_config/patches/tty_config'
@@ -43,7 +44,8 @@ module FlightConfig
43
44
 
44
45
  # Deprecated: The mode can be switched directly on the object
45
46
  def self.read(obj)
46
- obj.__data__set_read_mode
47
+ obj.instance_variable_set(:@__read_mode__, true)
48
+ obj.instance_variable_set(:@__data__, nil)
47
49
  obj.__data__
48
50
  end
49
51
 
@@ -76,12 +78,32 @@ module FlightConfig
76
78
  end
77
79
  end
78
80
 
79
- attr_reader :__data__mode
81
+ module ClassMethods
82
+ # NOTE: The _path method acts as adaptation layer to the two ways the path
83
+ # could be defined: on the class or instance. Moving forward, the path method
84
+ # should be defined on the class
85
+ # NOTE: DO NOT USE THIS METHOD PUBLICLY. Define a `path` method instead
86
+ def _path(*args)
87
+ if self.respond_to?(:path)
88
+ self.path(*args)
89
+ else
90
+ msg = <<~WARN.gsub("\n", ' ').chomp
91
+ FlightConfig Deprecation: #{self}.path is not defined. Falling back to
92
+ the instance method
93
+ WARN
94
+ Log.warn msg
95
+ $stderr.puts msg
96
+ self.new(*args).path
97
+ end
98
+ end
80
99
 
81
- def __data__initialize(_tty_config)
82
- end
100
+ # *READ ME*: Hack Alart
101
+ # NOTE: Override this method in your class, and it should just work
102
+ # TODO: Eventually replace the _path method with this
103
+ # def path(*_args)
104
+ # raise NotImplementedError
105
+ # end
83
106
 
84
- module ClassMethods
85
107
  def allow_missing_read(fetch: false)
86
108
  if fetch
87
109
  @allow_missing_read ||= false
@@ -90,48 +112,62 @@ module FlightConfig
90
112
  end
91
113
  end
92
114
 
93
- def new__data__
94
- TTY::Config.new
115
+ def data_core(klass = nil, &b)
116
+ @data_core ||= -> do
117
+ obj = (klass || TTY::Config).new
118
+ b ? b.call(obj) : obj
119
+ end
120
+ @data_core.call
95
121
  end
96
122
  end
97
123
 
98
- def __data__read(tty_config)
99
- if File.exists?(path)
100
- Core.log(self, 'read')
101
- str = File.read(path)
102
- yaml_h = (str == Core::PLACEHOLDER ? nil : YAML.load(File.read(path)))
103
- return unless yaml_h
104
- tty_config.merge(yaml_h)
105
- elsif self.class.allow_missing_read(fetch: true)
106
- Core.log(self, 'missing (skip read)')
107
- else
108
- raise MissingFile, "The file does not exist: #{path}"
109
- end
124
+ attr_reader :__inputs__, :__read_mode__
125
+
126
+ def initialize(*input_args, registry: nil, read_mode: nil)
127
+ @__inputs__ = input_args
128
+ # TODO: Make this @path = self.class.path(*__inputs__)
129
+ self.path # Ensure the path can be established with the __inputs__
130
+ @__registry__ = registry
131
+ @__read_mode__ = read_mode
110
132
  end
111
133
 
112
- def __data__set_read_mode
113
- # Do not call '__data__' directly as this skips the initialize block
114
- if @__data__
115
- raise BadModeError, <<~ERROR.chomp
116
- The read mode can not be changed after __data__ has been set
117
- ERROR
118
- else
119
- @__data__mode = :read
120
- end
134
+ def __registry__
135
+ @__registry__ ||= FlightConfig::Registry.new
136
+ end
137
+
138
+ def __data__initialize(_tty_config)
121
139
  end
122
140
 
123
141
  def __data__
124
- @__data__ ||= self.class.new__data__.tap do |core|
125
- if __data__mode == :read
126
- __data__read(core)
142
+ @__data__ ||= self.class.data_core.tap do |core|
143
+ if __read_mode__ && File.exists?(path)
144
+ Core.log(self, 'read')
145
+ str = File.read(path)
146
+ yaml_h = (str == Core::PLACEHOLDER ? nil : YAML.load(str))
147
+ core.merge(yaml_h) if yaml_h
148
+ elsif __read_mode__ && self.class.allow_missing_read(fetch: true)
149
+ Core.log(self, 'missing (skip read)')
150
+ elsif __read_mode__
151
+ raise MissingFile, "The file does not exist: #{path}"
127
152
  else
128
153
  __data__initialize(core)
129
154
  end
130
155
  end
131
156
  end
132
157
 
158
+ # TODO: Eventually remove the error section as all the configs will have a
159
+ # class path method
160
+ # TODO: Set the path in initialize
133
161
  def path
134
- raise NotImplementedError
162
+ @path ||= begin
163
+ if self.class.respond_to?(:path)
164
+ self.class.path(*__inputs__)
165
+ else
166
+ raise FlightConfigError, <<~ERROR.chomp
167
+ #{self.class}.path has not been defined!
168
+ ERROR
169
+ end
170
+ end
135
171
  end
136
172
  end
137
173
  end
@@ -42,11 +42,10 @@ module FlightConfig
42
42
 
43
43
  module ClassMethods
44
44
  def delete(*a)
45
- new!(*a) do |config|
45
+ new!(*a, read_mode: true) do |config|
46
46
  Deleter.delete_error_if_missing(config)
47
47
  Core.log(config, 'delete')
48
48
  Core.lock(config) do
49
- config.__data__set_read_mode
50
49
  config.__data__
51
50
  if block_given? && !(yield config)
52
51
  Core.log(config, 'delete (failed)')
@@ -29,11 +29,12 @@ require 'flight_config/reader'
29
29
  module FlightConfig
30
30
  module Globber
31
31
  class Matcher
32
- attr_reader :klass, :arity
32
+ attr_reader :klass, :arity, :registry
33
33
 
34
- def initialize(klass, arity)
34
+ def initialize(klass, arity, registry)
35
35
  @klass = klass
36
36
  @arity = arity
37
+ @registry = (registry || FlightConfig::Registry.new)
37
38
  end
38
39
 
39
40
  def keys
@@ -50,7 +51,7 @@ module FlightConfig
50
51
  def read(path)
51
52
  data = regex.match(path)
52
53
  init_args = keys.map { |key| data[key] }
53
- klass.read(*init_args)
54
+ klass.read(*init_args, registry: registry)
54
55
  end
55
56
  end
56
57
 
@@ -59,8 +60,8 @@ module FlightConfig
59
60
  end
60
61
 
61
62
  module ClassMethods
62
- def glob_read(*a)
63
- matcher = Globber::Matcher.new(self, a.length)
63
+ def glob_read(*a, registry: nil)
64
+ matcher = Globber::Matcher.new(self, a.length, registry)
64
65
  glob_regex = self.new(*a).path
65
66
  Dir.glob(glob_regex)
66
67
  .map { |path| matcher.read(path) }
@@ -0,0 +1,54 @@
1
+ #==============================================================================
2
+ # Copyright (C) 2019-present Alces Flight Ltd.
3
+ #
4
+ # This file is part of FlightConfig.
5
+ #
6
+ # This program and the accompanying materials are made available under
7
+ # the terms of the Eclipse Public License 2.0 which is available at
8
+ # <https://www.eclipse.org/legal/epl-2.0>, or alternative license
9
+ # terms made available by Alces Flight Ltd - please direct inquiries
10
+ # about licensing to licensing@alces-flight.com.
11
+ #
12
+ # FlightConfig is distributed in the hope that it will be useful, but
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
14
+ # IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS
15
+ # OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
16
+ # PARTICULAR PURPOSE. See the Eclipse Public License 2.0 for more
17
+ # details.
18
+ #
19
+ # You should have received a copy of the Eclipse Public License 2.0
20
+ # along with FlightConfig. If not, see:
21
+ #
22
+ # https://opensource.org/licenses/EPL-2.0
23
+ #
24
+ # For more information on FlightConfig, please visit:
25
+ # https://github.com/openflighthpc/flight_config
26
+ #==============================================================================
27
+
28
+ module FlightConfig
29
+ module Links
30
+ def self.included(base)
31
+ base.extend(ClassMethods)
32
+ end
33
+
34
+ module ClassMethods
35
+ include Globber::ClassMethods
36
+
37
+ def define_link(key, klass, glob: false, &b)
38
+ links_class.define_method(key) do
39
+ args = config.instance_exec(&b)
40
+ method = (glob ? :glob_read : :read)
41
+ klass.public_send(method, *args, registry: config.__registry__)
42
+ end
43
+ end
44
+
45
+ def links_class
46
+ @links_class ||= Struct.new(:config)
47
+ end
48
+ end
49
+
50
+ def links
51
+ @links ||= self.class.links_class.new(self)
52
+ end
53
+ end
54
+ end
@@ -24,9 +24,26 @@
24
24
  # For more information on FlightConfig, please visit:
25
25
  # https://github.com/openflighthpc/flight_config
26
26
  #==============================================================================
27
+
27
28
  require 'flight_config/core'
28
29
 
29
30
  module FlightConfig
31
+ class Registry
32
+ def read(klass, *args)
33
+ class_hash = (cache[klass] ||= {})
34
+ arity_hash = (class_hash[args.length] ||= {})
35
+ last_arg = args.pop
36
+ last_hash = args.reduce(arity_hash) { |hash, arg| hash[arg] ||= {} }
37
+ last_hash[last_arg] ||= klass.new(*args, last_arg, registry: self, read_mode: true)
38
+ end
39
+
40
+ private
41
+
42
+ def cache
43
+ @cache ||= {}
44
+ end
45
+ end
46
+
30
47
  module Reader
31
48
  include Core
32
49
 
@@ -37,27 +54,19 @@ module FlightConfig
37
54
  module ClassMethods
38
55
  include Core::ClassMethods
39
56
 
40
- def new!(*a)
41
- new(*a).tap do |config|
57
+ def new!(*a, **h)
58
+ new(*a, **h).tap do |config|
42
59
  yield config if block_given?
43
60
  end
44
61
  end
45
62
 
46
- def read(*a)
47
- new!(*a) do |config|
48
- config.__data__set_read_mode
49
- config.__data__
50
- end
63
+ def read(*a, registry: nil)
64
+ (registry || Registry.new).read(self, *a)
51
65
  end
52
66
  alias_method :load, :read
53
67
 
54
68
  def read_or_new(*a)
55
- new!(*a) do |config|
56
- if File.exists?(config.path)
57
- config.__data__set_read_mode
58
- config.__data__
59
- end
60
- end
69
+ File.exists?(_path(*a)) ? read(*a) : new(*a)
61
70
  end
62
71
  end
63
72
  end
@@ -64,16 +64,15 @@ module FlightConfig
64
64
  include Reader::ClassMethods
65
65
 
66
66
  def update(*a, &b)
67
- new!(*a) do |config|
67
+ new!(*a, read_mode: true) do |config|
68
68
  Updater.update_error_if_missing(config)
69
- config.__data__set_read_mode
70
69
  Updater.create_or_update(config, action: 'update', &b)
71
70
  end
72
71
  end
73
72
 
74
73
  def create_or_update(*a, &b)
75
- new!(*a) do |config|
76
- config.__data__set_read_mode if File.exists?(config.path)
74
+ mode = File.exists?(_path(*a))
75
+ new!(*a, read_mode: mode) do |config|
77
76
  Updater.create_or_update(config, action: 'create_or_update', &b)
78
77
  end
79
78
  end
@@ -25,5 +25,5 @@
25
25
  # https://github.com/openflighthpc/flight_config
26
26
  #==============================================================================
27
27
  module FlightConfig
28
- VERSION = "0.1.1"
28
+ VERSION = "0.2.0"
29
29
  end
data/lib/flight_config.rb CHANGED
@@ -30,3 +30,5 @@ require 'flight_config/globber'
30
30
  require 'flight_config/log'
31
31
  require 'flight_config/reader'
32
32
  require 'flight_config/updater'
33
+ require 'flight_config/accessor'
34
+ require 'flight_config/links'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flight_config
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alces Flight Ltd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-19 00:00:00.000000000 Z
11
+ date: 2019-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tty-config
@@ -112,10 +112,12 @@ files:
112
112
  - Rakefile
113
113
  - flight_config.gemspec
114
114
  - lib/flight_config.rb
115
+ - lib/flight_config/accessor.rb
115
116
  - lib/flight_config/core.rb
116
117
  - lib/flight_config/deleter.rb
117
118
  - lib/flight_config/exceptions.rb
118
119
  - lib/flight_config/globber.rb
120
+ - lib/flight_config/links.rb
119
121
  - lib/flight_config/log.rb
120
122
  - lib/flight_config/patches/tty_config.rb
121
123
  - lib/flight_config/reader.rb