k_manager 0.0.13 → 0.0.22

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