flight_config 0.1.1 → 0.2.0

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