k_manager 0.0.13 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/Gemfile +18 -0
  4. data/Rakefile +2 -0
  5. data/docs/flow.drawio +16 -0
  6. data/exe/k_manager +20 -0
  7. data/k_manager.gemspec +6 -0
  8. data/lib/k_manager/area.rb +47 -0
  9. data/lib/k_manager/document_factory.rb +74 -0
  10. data/lib/k_manager/manager.rb +58 -0
  11. data/lib/k_manager/overview/dashboard.rb +187 -0
  12. data/lib/k_manager/overview/dump_json.rb +35 -0
  13. data/lib/k_manager/overview/models.rb +76 -0
  14. data/lib/k_manager/overview/queries.rb +53 -0
  15. data/lib/k_manager/resources/base_resource.rb +189 -52
  16. data/lib/k_manager/resources/file_resource.rb +80 -58
  17. data/lib/k_manager/resources/{ruby_file_resource.rb → file_resources/ruby_file_resource.rb} +0 -0
  18. data/lib/k_manager/resources/{unknown_file_resource.rb → file_resources/unknown_file_resource.rb} +0 -0
  19. data/lib/k_manager/resources/mem_resource.rb +17 -0
  20. data/lib/k_manager/resources/resource_document_factory.rb +119 -0
  21. data/lib/k_manager/resources/resource_factory.rb +28 -0
  22. data/lib/k_manager/resources/resource_manager.rb +216 -0
  23. data/lib/k_manager/resources/resource_set.rb +90 -0
  24. data/lib/k_manager/resources/web_resource.rb +113 -0
  25. data/lib/k_manager/version.rb +1 -1
  26. data/lib/k_manager/watcher.rb +75 -0
  27. data/lib/k_manager/{x_project.rb → x_resource_documents/x_project.rb} +0 -0
  28. data/lib/k_manager/{x_project_manager.rb → x_resource_documents/x_project_manager.rb} +0 -0
  29. data/lib/k_manager/{x_register.rb → x_resource_documents/x_register.rb} +0 -0
  30. data/lib/k_manager.rb +93 -20
  31. data/tasks/watch.rake +113 -0
  32. metadata +70 -24
  33. data/Assessment1.md +0 -127
  34. data/Assessment2.md +0 -88
  35. data/lib/k_manager/create_document.rb +0 -31
  36. data/lib/k_manager/documents/basic_document.rb +0 -21
  37. data/lib/k_manager/documents/builder_document.rb +0 -18
  38. data/lib/k_manager/documents/document_taggable.rb +0 -94
  39. data/lib/k_manager/documents/model_document.rb +0 -19
  40. data/lib/k_manager/project.rb +0 -50
  41. data/lib/k_manager/resources/csv_file_resource.rb +0 -27
  42. data/lib/k_manager/resources/factories/document_factory.rb +0 -52
  43. data/lib/k_manager/resources/json_file_resource.rb +0 -22
  44. data/lib/k_manager/resources/yaml_file_resource.rb +0 -21
@@ -5,6 +5,8 @@ module KManager
5
5
  # A resource represents text based content in the project. The content
6
6
  # maybe data, interpreted ruby code or a combination of the two.
7
7
  #
8
+ # Any non-binary file that is useful for processing.
9
+ #
8
10
  # Currently resources refer to file based content, but it is envisaged
9
11
  # that resources could come from a distributed source such as Gist,
10
12
  # WebService, or even FTP.
@@ -21,9 +23,21 @@ module KManager
21
23
  # generally only one document.
22
24
  class BaseResource
23
25
  include KLog::Logging
26
+ include KDoc::Guarded
27
+
28
+ ACTIONS = %i[load_content register_document load_document].freeze
24
29
 
30
+ class << self
31
+ def valid_action?(action)
32
+ ACTIONS.include?(action)
33
+ end
34
+ end
35
+
36
+ attr_reader :uri # https://ruby-doc.org/stdlib-2.6.1/libdoc/uri/rdoc/URI/Generic.html
37
+
38
+ # TODO: Refactor from status to state and extract to a State class
25
39
  # Status of the resource
26
- # - :initialized
40
+ # - :alive (i am alive, or instantiated)
27
41
  # - :content_loading
28
42
  # - :content_loaded
29
43
  # - :documents_registering
@@ -32,17 +46,25 @@ module KManager
32
46
  # - :documents_loaded
33
47
  attr_reader :status
34
48
 
35
- # Where is the source of content
49
+ # What content type does the underlying resource type generally contain
36
50
  #
37
- # Implement in child classes, examples: :file, :uri, :dynamic
38
- attr_reader :source
39
-
40
- # Where is the type of the
51
+ # Examples:
41
52
  #
42
- # Implement in child classes, examples: :csv, :json, :ruby, :dsl, :yaml
43
- attr_reader :type
53
+ # :csv - CSV text content
54
+ # :json - JSON text content
55
+ # :yaml - YAML text content
56
+ # :xml - XML text content
57
+ # :ruby - Ruby code file of unknown capability
58
+ # :ruby_dsl - Ruby code holding some type of known DSL such as a KDoc
59
+ # DISCUSS: should this subtype be delegated to an attribute on a responsible class
60
+ attr_reader :content_type
44
61
 
45
- attr_reader :project
62
+ # TODO: Write Test
63
+ # Area is an option property what will only be set when working with Area
64
+ attr_accessor :area
65
+
66
+ # Optional namespace that the resource belongs to.
67
+ attr_reader :namespace
46
68
 
47
69
  # Content of resource, use read content to load this property
48
70
  attr_reader :content
@@ -60,19 +82,22 @@ module KManager
60
82
  #
61
83
  # @param [Hash] **opts Options for initializing the resource
62
84
  # @option opts [Project] :project attach the resource to a project
63
- def initialize(**opts)
64
- @status = :initialized
65
- @source = :unknown
66
- @type = :unknown
85
+ # NAMESPACE can probably be taken from file set relative path
86
+ def initialize(uri, **opts)
87
+ self.uri = uri
88
+
89
+ @status = :alive
90
+ @namespace = value_remove(opts, :namespace)
91
+ @content_type = @content_type || value_remove(opts, :content_type) || infer_content_type || default_content_type
92
+ @content = value_remove(opts, :content)
67
93
 
68
- attach_project(opts[:project]) if opts[:project]
94
+ # attach_project(opts[:project]) if opts[:project]
69
95
  @documents = []
70
96
  end
71
97
 
72
- def attach_project(project)
73
- @project = project
74
- @project.add_resource(self)
75
- self
98
+ def source_path
99
+ # Expectation that uri is of type URI::HTTP or URI::HTTPS
100
+ uri.to_s
76
101
  end
77
102
 
78
103
  def document
@@ -85,15 +110,31 @@ module KManager
85
110
  # - :load_content for loading text content
86
111
  # - :register_document for registering 1 or more documents (name and namespace) against the resource
87
112
  # - :load_document for parsing the content into a document
113
+ # rubocop:disable Metrics/CyclomaticComplexity
88
114
  def fire_action(action)
89
- if action == :load_content && @status == :initialized
90
- load_content_action
91
- elsif action == :register_document && @status == :content_loaded
92
- register_document_action
93
- elsif action == :load_document && @status == :documents_registered
94
- load_document_action
115
+ # TODO: Write test for valid
116
+ return unless valid?
117
+
118
+ case action
119
+ when :load_content
120
+ load_content_action if alive?
121
+ when :register_document
122
+ register_document_action if content_loaded?
123
+ when :load_document
124
+ load_document_action if documents_registered?
95
125
  else
96
- puts 'unknown'
126
+ log.warn "Action: '#{action}' is invalid for status: '#{status}'"
127
+ end
128
+ end
129
+ # rubocop:enable Metrics/CyclomaticComplexity
130
+
131
+ def fire_next_action
132
+ if alive?
133
+ fire_action(:load_content)
134
+ elsif content_loaded?
135
+ fire_action(:register_document)
136
+ elsif documents_registered?
137
+ fire_action(:load_document)
97
138
  end
98
139
  end
99
140
 
@@ -106,60 +147,156 @@ module KManager
106
147
  end
107
148
 
108
149
  def load_content
109
- log.warn 'you need to implement load_content'
150
+ # log.warn 'you need to implement load_content'
110
151
  end
111
152
 
112
153
  def register_document
113
- log.warn 'you need to implement register_document'
154
+ # log.warn 'you need to implement register_document'
155
+ KManager::Resources::ResourceDocumentFactory.create_documents(self)
114
156
  end
115
157
 
116
- # This might be better off in a factory method
117
- # Klue.basic
118
- def create_document
119
- KManager::Documents::BasicDocument.new(
158
+ # rubocop:disable Lint/RescueException
159
+ def load_document
160
+ # log.warn 'you need to implement register_document'
161
+ documents.each(&:execute_block)
162
+ rescue Exception => e
163
+ guard(e.message)
164
+ debug
165
+ log.exception(e, style: :short)
166
+ end
167
+ # rubocop:enable Lint/RescueException
168
+
169
+ # This is when you need a simple container
170
+ def new_document(data)
171
+ document = KDoc::Container.new(
120
172
  key: infer_key,
121
- type: type,
122
- namespace: '',
123
- resource: self
173
+ type: content_type,
174
+ namespace: namespace,
175
+ default_data_type: data.class,
176
+ data: data
124
177
  )
178
+ attach_document(document)
125
179
  end
126
180
 
127
- # TODO: Unit Test
128
- def attach_document(document, change_resource_type: nil)
129
- @type = change_resource_type if change_resource_type
181
+ def attach_document(document, change_content_type: nil)
182
+ @content_type = change_content_type if change_content_type
130
183
 
131
- add_document(document)
184
+ document.owner = self
185
+ @documents << document
186
+ document
132
187
  end
133
188
 
134
- def load_document
135
- log.warn 'you need to implement load_document'
189
+ def scheme
190
+ uri&.scheme&.to_sym || default_scheme
191
+ end
192
+
193
+ def host
194
+ uri&.host
195
+ end
196
+
197
+ # What schema does the underlying resource connect with by default
198
+ #
199
+ # Examples:
200
+ #
201
+ # :file
202
+ # :web (http: https: fpt:)
203
+ # :mem - some type of memory structure
204
+ def default_scheme
205
+ :unknown
206
+ end
207
+
208
+ # Optionally overridden, this is the case with FileResource
209
+ def infer_content_type
210
+ nil
211
+ end
212
+
213
+ def default_content_type
214
+ :unknown
215
+ end
216
+
217
+ def alive?
218
+ @status == :alive
219
+ end
220
+
221
+ def content_loaded?
222
+ @status == :content_loaded
223
+ end
224
+
225
+ def documents_registered?
226
+ @status == :documents_registered
227
+ end
228
+
229
+ def documents_loaded?
230
+ @status == :documents_loaded
231
+ end
232
+
233
+ # Setting the URI can be overridden by WebResource and FileResource
234
+ def uri=(uri)
235
+ return if uri.nil?
236
+
237
+ @uri = URI(uri) if uri.is_a?(String)
238
+ @uri = uri if uri.is_a?(URI)
239
+
240
+ # log.kv 'uri type', uri.class
241
+ # It might be useful to have a Resource Specific Guard being called to warn if the wrong URI type is inferred from here
242
+ # supported URI::Class (Generic, File, HTTP, HTTPS)
136
243
  end
137
244
 
138
245
  # rubocop:disable Metrics/AbcSize
139
- def debug
140
- log.section_heading('resource')
141
- log.kv 'source' , source , 15
142
- log.kv 'type' , type , 15
143
- log.kv 'status' , status , 15
246
+ def attribute_values(prefix = nil)
247
+ result = {}
248
+ result["#{prefix}id".to_sym] = object_id
249
+ result["#{prefix}key".to_sym] = infer_key
250
+ result["#{prefix}namespace".to_sym] = namespace
251
+ result["#{prefix}status".to_sym] = status
252
+ result["#{prefix}source".to_sym] = source_path
253
+ result["#{prefix}content_type".to_sym] = content_type
254
+ result["#{prefix}content".to_sym] = content
255
+ result["#{prefix}document_count".to_sym] = documents.length
256
+ result["#{prefix}errors".to_sym] = error_hash
257
+ result["#{prefix}valid".to_sym] = valid?
258
+ result["#{prefix}scheme".to_sym] = scheme
259
+ result["#{prefix}host".to_sym] = host
260
+ result
261
+ end
262
+ # rubocop:enable Metrics/AbcSize
263
+
264
+ # rubocop:disable Metrics/AbcSize
265
+ def debug(heading = 'resource')
266
+ width = 20
267
+ log.section_heading(heading)
268
+ log.kv 'area' , area.name , width if area
269
+ log.kv 'area namespace' , area.namespace , width if area
270
+ log.kv 'scheme' , scheme , width
271
+ log.kv 'host' , host , width
272
+ log.kv 'source_path' , source_path , width
273
+ log.kv 'content_type' , content_type , width
274
+ log.kv 'status' , status , width
275
+ log.kv 'content' , content.nil? ? '' : content[0..100].gsub("\n", '\n') , width
276
+ log.kv 'documents' , documents.length , width
277
+
278
+ yield if block_given?
279
+
280
+ log_any_messages
281
+
282
+ # log.kv 'infer_key', infer_key , width
144
283
  # log.kv 'project' , project
145
- log.kv 'content' , content.nil? ? '' : content[0..100].gsub("\n", '\n') , 15
146
- log.kv 'documents', documents.length , 15
147
284
 
148
285
  documents.each(&:debug)
286
+ nil
149
287
  end
150
288
  # rubocop:enable Metrics/AbcSize
151
289
 
152
290
  private
153
291
 
154
- def add_document(document)
155
- # First document in list goes into .document
156
- @documents << document
157
- document
292
+ def value_remove(opts, key)
293
+ return opts.delete(key) if opts.key?(key)
294
+
295
+ nil
158
296
  end
159
297
 
160
298
  def load_content_action
161
299
  @status = :content_loading
162
- @content = nil
163
300
  load_content
164
301
  @status = :content_loaded
165
302
  end
@@ -2,91 +2,113 @@
2
2
 
3
3
  module KManager
4
4
  module Resources
5
- require 'handlebars/helpers/string_formatting/dasherize'
6
-
7
- # A file resource represents context that is loaded via a file.
5
+ # A file resource represents content that is loaded via a file.
8
6
  #
9
7
  # File resources have the benefit that file watchers can watch them
10
- # locally and reload these resources on change.
8
+ # locally and reload when these resources on change.
11
9
  class FileResource < KManager::Resources::BaseResource
12
10
  include KLog::Logging
13
11
 
14
- # Full path to file
12
+ KNOWN_EXTENSIONS = {
13
+ '.rb' => :ruby,
14
+ '.csv' => :csv,
15
+ '.json' => :json,
16
+ '.yaml' => :yaml
17
+ }.freeze
18
+
19
+ def initialize(uri, **opts)
20
+ warn('URI::File type expected for File Resource') unless uri.is_a?(URI::File)
21
+ super(uri, **opts)
22
+ log_any_messages unless valid?
23
+ end
24
+
25
+ # Infer key is the file name without the extension stored in dash-case
26
+ def infer_key
27
+ file_name = Pathname.new(source_path).basename.sub_ext('').to_s
28
+ Handlebars::Helpers::StringFormatting::Snake.new.parse(file_name)
29
+ end
30
+
31
+ def default_scheme
32
+ :file
33
+ end
34
+
35
+ # Currently in base
36
+ # def register_document
37
+ # KManager::Resources::ResourceDocumentFactory.create_documents(self)
38
+ # end
39
+
40
+ def attribute_values(prefix = nil)
41
+ result = super(prefix)
42
+ result["#{prefix}path".to_sym] = resource_path
43
+ result["#{prefix}relative_path".to_sym] = resource_relative_path
44
+ result["#{prefix}exist".to_sym] = resource_valid?
45
+ result
46
+ end
47
+
48
+ def debug
49
+ super do
50
+ log.kv 'infer_key' , infer_key , 20
51
+ log.kv 'file' , source_path , 20
52
+ log.kv 'resource_path' , resource_path , 20
53
+ log.kv 'resource_valid?' , resource_valid? , 20
54
+ end
55
+ end
56
+
57
+ # Source path - aka Full path to file
15
58
  #
16
59
  # example: /Users/davidcruwys/dev/kgems/k_dsl/spec/factories/dsls/common-auth/admin_user.rb
17
- attr_reader :file
60
+ def source_path
61
+ uri.path
62
+ end
18
63
 
19
- def initialize(**opts)
20
- super(**opts)
21
- @source = :file
22
- @file = opts[:file]
64
+ # TODO: Write tests
65
+ def recreate(resource)
66
+ raise 'Recreate only works for resources of the same type' unless resource.is_a?(self.class)
23
67
 
24
- guard
68
+ # area: resource.area,
69
+ # content_type: resource.content_type,
70
+ opts = {
71
+ namespace: resource.namespace
72
+ }
73
+
74
+ resource.class.new(resource.uri, **opts)
25
75
  end
26
76
 
27
- class << self
28
- def instance(**opts)
29
- file = opts[:file]
30
-
31
- extension = ::File.extname(file).downcase
32
-
33
- case extension
34
- when '.rb'
35
- KManager::Resources::RubyFileResource.new(**opts)
36
- when '.csv'
37
- KManager::Resources::CsvFileResource.new(**opts)
38
- when '.json'
39
- KManager::Resources::JsonFileResource.new(**opts)
40
- when '.yaml'
41
- KManager::Resources::YamlFileResource.new(**opts)
42
- else
43
- KManager::Resources::UnknownFileResource.new(**opts)
44
- end
45
- end
77
+ def resource_path
78
+ @resource_path ||= absolute_path(source_path)
46
79
  end
47
80
 
48
- # Infer key is the file name without the extension stored in dash-case
49
- def infer_key
50
- file_name = Pathname.new(@file).basename.sub_ext('').to_s
51
- Handlebars::Helpers::StringFormatting::Dasherize.new.parse(file_name)
81
+ def resource_relative_path(from_path = Dir.pwd)
82
+ Pathname.new(resource_path).relative_path_from(from_path).to_s
52
83
  end
53
84
 
85
+ def resource_valid?
86
+ File.exist?(resource_path)
87
+ end
88
+
89
+ private
90
+
54
91
  def load_content
55
- if File.exist?(file)
92
+ if resource_valid?
56
93
  begin
57
- @content = File.read(file)
94
+ @content = File.read(resource_path)
58
95
  rescue StandardError => e
59
96
  log.error e
60
97
  end
61
98
  else
62
- log.error "Source file not found: #{file}"
99
+ guard("Source file not found: #{resource_path}")
63
100
  end
64
101
  end
65
102
 
66
- def register_document
67
- attach_document(create_document)
68
- end
103
+ def absolute_path(path)
104
+ pn = Pathname(path)
69
105
 
70
- # rubocop:disable Metrics/AbcSize
71
- def debug
72
- log.section_heading('resource')
73
- log.kv 'source' , source , 15
74
- log.kv 'file' , file , 15
75
- log.kv 'type' , type , 15
76
- log.kv 'infer_key', infer_key , 15
77
- log.kv 'status' , status , 15
78
- # log.kv 'project' , project
79
- log.kv 'content' , content.nil? ? '' : content[0..100].gsub("\n", '\n') , 15
80
- log.kv 'documents', documents.length , 15
81
-
82
- documents.each(&:debug)
106
+ pn.exist? ? pn.realpath.to_s : File.expand_path(path)
83
107
  end
84
- # rubocop:enable Metrics/AbcSize
85
-
86
- private
87
108
 
88
- def guard
89
- raise KType::Error, 'File resource requires a file option' if @file.nil? || @file == ''
109
+ def infer_content_type
110
+ extension = ::File.extname(source_path).downcase
111
+ KNOWN_EXTENSIONS[extension]
90
112
  end
91
113
  end
92
114
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module KManager
6
+ module Resources
7
+ require 'handlebars/helpers/string_formatting/dasherize'
8
+
9
+ # A memory resource represents content that is generated programmatically and just stored in memory.
10
+ class MemResource < KManager::Resources::BaseResource
11
+ def initialize(**opts)
12
+ fake_uri = URI.parse("mem://#{SecureRandom.alphanumeric(4)}")
13
+ super(fake_uri, **opts)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KManager
4
+ module Resources
5
+ # Create documents based on the target resource
6
+ #
7
+ # This factory will lock the resource and then create
8
+ # new documents based on the content of the resource.
9
+ #
10
+ # In the case of a ruby resource, this factory will evaluate the ruby
11
+ # code dynamically and any ruby file with standard document DSL's will
12
+ # create additional documents using KManager::DocumentFactory
13
+ class ResourceDocumentFactory
14
+ # TODO: The original system always created 1 document, so need to consider if 0-more should become 1-more
15
+ class << self
16
+ include KLog::Logging
17
+ # Build 0-more documents and attach them to the resource.
18
+ #
19
+ # The resource is stored in the KManager.current_resource context
20
+ # and wrapped by a Mutex so that any self registering documents can
21
+ # figure out which resource to register themselves against
22
+ def create_documents(target_resource)
23
+ KManager.for_resource(target_resource) do |resource|
24
+ process_resource(resource)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ # Create 0-Many documents and attach to the resource
31
+ #
32
+ # @param [BaseResource] resource The resource that created and is thus owns the document
33
+ # @param [Symbol] content_type Type of content, %i[csv json yaml ruby unknown]
34
+ # @param [String] key is the unique resource key
35
+ # @param [String] content Resource content as a raw string, examples could be CSV, JSON, YAML, RUBY or some other text content
36
+ def process_resource(resource)
37
+ case resource.content_type
38
+ when :csv
39
+ resource.new_document(process_csv(resource.content))
40
+ when :json
41
+ resource.new_document(process_json(resource.content))
42
+ when :yaml
43
+ resource.new_document(process_yaml(resource.content))
44
+ when :ruby
45
+ process_ruby(resource)
46
+ :ruby
47
+ else
48
+ :unknown
49
+ end
50
+ end
51
+
52
+ def process_csv(content)
53
+ rows = []
54
+
55
+ CSV.parse(content, headers: true, header_converters: :symbol) do |row|
56
+ rows << row.to_h
57
+ end
58
+ rows
59
+ rescue StandardError => e
60
+ log.exception(e, style: :message)
61
+ []
62
+ end
63
+
64
+ def process_json(content)
65
+ JSON.parse(content)
66
+ rescue StandardError => e
67
+ log.exception(e, style: :message)
68
+ {}
69
+ end
70
+
71
+ def process_yaml(content)
72
+ YAML.safe_load(content)
73
+ rescue StandardError => e
74
+ log.exception(e, style: :message)
75
+ {}
76
+ end
77
+
78
+ # rubocop:disable Lint/RescueException
79
+ def process_ruby(resource)
80
+ # puts content
81
+ # KManager::Manager.current_resource
82
+ # KDsl.target_resource = self
83
+
84
+ Object.class_eval resource.content, resource.resource_path
85
+
86
+ # # Only DSL's will add new resource_documents
87
+ # if documents.length > 0
88
+ # resource.resource_type = KDsl::Resources::Resource::TYPE_RUBY_DSL
89
+ # end
90
+ rescue Exception => e
91
+ # Report the error but still add the document so that you can see
92
+ # it in the ResourceDocument list, it will be marked as Error
93
+ # resource.error = ex
94
+ resource.guard(e.message)
95
+ resource.debug
96
+ log.exception(e, style: :short)
97
+
98
+ # L.exception resource.error
99
+
100
+ # KDsl.target_resource = nil
101
+
102
+ # # A regular ruby file would not add resource_documents
103
+ # # so create one manually
104
+ # add_document(new_document) if documents.length === 0
105
+ end
106
+ # rubocop:enable Lint/RescueException
107
+
108
+ # # TEST REQUIRED
109
+ # def add_document(document)
110
+ # # project.register_dsl(document)
111
+ # project.add_resource_document(self, document)
112
+ # document.resource = self
113
+ # documents << document
114
+ # document
115
+ # end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KManager
4
+ module Resources
5
+ class ResourceFactory
6
+ # TODO: Write tests
7
+
8
+ # Create a resource based on a resource URI.
9
+ #
10
+ # Resources may be of type File or Web
11
+ #
12
+ # The resource content_type should be passed in when it cannot be inferred (e.g. WebResource)
13
+ # for a FileResource this option is optional and will be inferred from the file extension.
14
+ def instance(resource_uri, **opts)
15
+ scheme = resource_uri.scheme.to_sym
16
+
17
+ case scheme
18
+ when :file
19
+ FileResource.new(resource_uri, **opts)
20
+ when :http, :https
21
+ WebResource.new(resource_uri, **opts)
22
+ else
23
+ raise 'Unknown schema'
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end