lowload 0.5.1 → 0.6.1

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: 4308f9be211e662f356659bccaa5e8c6a072e36b7a7cf2cac34d8b8f88bf0a61
4
- data.tar.gz: 065a035f71f16424c0dd2d98c2c95f60cdec86cab2863ecab8cceb3f6f9e8248
3
+ metadata.gz: e0a6b7db4c15bb23eea273eb0a2d63bfed6d182b03a9964eb4c8adabc947fe93
4
+ data.tar.gz: 4ae8e96140a5f5560204aa7aac365167dd500068e7cef9415ef4b919b8271239
5
5
  SHA512:
6
- metadata.gz: 5c8adc7cee6f2486e83c5adfae2d7aedcf33a6d6ceb26cbe052b2326c2d659de8cc58ccf54f4e572143ce11ed5d079585e7fb9f8c8886f8399fd248f245bd31a
7
- data.tar.gz: 579e6eddfd6e92b6d426f10214cab3571670126d0b64c9cc2f112b9af09f72cca5c383ef10821bbc1fe917c279ebbb06b99ee8ec3e683e6ad01d09906f3a9664
6
+ metadata.gz: 1aee43560dc727fd1093d933805dee8f6fb8f9d466a60c594cd4c6635f007f77830c143d05827efe412b48430705636d556ee8ab554d8973e73e19f04a944df4
7
+ data.tar.gz: 88c59f0e36586302d1457b00926afe013fbdef453815a38b56ef1cf0b15c74edd38d081e6d263403d5f062d0a9b47b44af635da504bebf63709f040f6f0d9570
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LowLoad
4
+ class Adapter
5
+ EXTENSIONS = []
6
+
7
+ def metadata(file_path:) = nil
8
+
9
+ def preload(file_path:) = nil
10
+
11
+ def evaluate(file_path:, top_level_binding: nil) = nil
12
+
13
+ def url_path(file_path:) = nil
14
+ end
15
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'lowkey'
5
+ require_relative 'adapter'
6
+
7
+ module LowLoad
8
+ class MarkdownAdapter < Adapter
9
+ EXTENSIONS = ['md', 'rd', 'markdown', 'raindown']
10
+
11
+ def metadata(file_path:)
12
+ line_numbers = []
13
+ yaml_lines = []
14
+
15
+ File.foreach(file_path).with_index do |line, index|
16
+ if line.strip == '---'
17
+ line_numbers << index + 1
18
+ break if line_numbers.count > 1
19
+ elsif line_numbers.count > 0
20
+ yaml_lines << line
21
+ end
22
+ end
23
+
24
+ # TODO: Test that this returns an empty hash when no yaml lines found.
25
+ YAML.safe_load(yaml_lines.join, symbolize_names: true)
26
+ end
27
+
28
+ def url_path(file_path:)
29
+ # Remove "_number-" and segments beginning with "_".
30
+ url_path = file_path.split('/').map do |segment|
31
+ next segment.sub(/^_\d\W/, '') if segment.sub(/^_\d\W/, '') != segment
32
+ next nil if segment.sub(/^_/, '') != segment
33
+
34
+ segment
35
+ end.compact.join('/')
36
+
37
+ EXTENSIONS.each do |extension|
38
+ url_path.delete_suffix!(".#{extension}")
39
+ end
40
+
41
+ url_path
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lowkey'
4
+ require_relative 'adapter'
5
+ require_relative '../loader'
6
+
7
+ def top_level_binding
8
+ binding
9
+ end
10
+
11
+ module LowLoad
12
+ class RBXAdapter < Adapter
13
+ EXTENSIONS = ['rbx']
14
+
15
+ # Map all definitions and dependencies.
16
+ def metadata(file_path:)
17
+ Lowkey.load(file_path)
18
+
19
+ { file_path: }
20
+ end
21
+
22
+ # Then autoload all dependencies for those files.
23
+ def preload(file_path:)
24
+ Loader.add_autoloads(file_proxy: Lowkey[file_path])
25
+ end
26
+
27
+ # Now we can load the file into Ruby.
28
+ def evaluate(file_path:)
29
+ # 1. "File Load" phase.
30
+ file_proxy = Lowkey[file_path] || Lowkey.load(file_path)
31
+ templates = wrap_render_methods(file_proxy:)
32
+
33
+ # 2. "Class Load" phase.
34
+ # Not a security risk because "eval" is equivalent to "load" or "require_relative" in this context.
35
+ eval(file_proxy.export, top_level_binding, file_proxy.file_path, 0) # rubocop:disable Security/Eval
36
+
37
+ # 3. "Runtime" phase (before low nodes are rendered).
38
+ # Templates only exist if antlers gem has been required by another gem (such as LowNode) and the template contains antlers syntax.
39
+ templates.each do |namespace, method_template|
40
+ klass = Object.const_get(namespace)
41
+ method, template = method_template
42
+ # TODO: If params contain "**props" or similar then send that, so that LowNode can replicate it.
43
+ params = method.params.map(&:name)
44
+
45
+ next unless supports_templates?(klass) || raise(UnsupportedTemplate, "Template for '#{namespace}' identified but not implemented")
46
+
47
+ # TODO: Make template engine configurable.
48
+ klass.build_template(template:, params:, engine: Antlers, namespace:)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def supports_templates?(klass)
55
+ klass.respond_to?(:render) && klass.respond_to?(:template) && klass.respond_to?(:build_template)
56
+ end
57
+
58
+ def wrap_render_methods(file_proxy:)
59
+ # GOAL: Templates could be organised by method and template rendering engine.
60
+ templates = {}
61
+
62
+ file_proxy.definitions.each_value do |class_proxy|
63
+ render_method = class_proxy.respond_to?(:instance_methods) ? class_proxy.instance_methods[:render] : nil
64
+
65
+ next unless render_method
66
+
67
+ render_method_body = render_method.body.export
68
+
69
+ if defined?(Antlers) && ['{', '<{'].any? { |needle| render_method_body.include?(needle) }
70
+ templates[class_proxy.namespace] = [render_method, render_method_body]
71
+ end
72
+
73
+ # Intended to target HTML or Antlers tags. Use HEREDOC "<<~" in a plain Ruby (".rb") file, not RBX.
74
+ render_method.body.wrap(prefix: '%q{', suffix: '}') if render_method_body.strip.start_with?('<')
75
+ end
76
+
77
+ templates
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lowkey'
4
+ require_relative 'adapter'
5
+ require_relative '../loader'
6
+
7
+ module LowLoad
8
+ class RubyAdapter < Adapter
9
+ EXTENSIONS = ['rb']
10
+
11
+ # Map all definitions and dependencies.
12
+ def metadata(file_path:)
13
+ Lowkey.load(file_path)
14
+
15
+ { file_path: }
16
+ end
17
+
18
+ # Then autoload all dependencies for those files.
19
+ def preload(file_path:)
20
+ Loader.add_autoloads(file_proxy: Lowkey[file_path])
21
+ end
22
+
23
+ # Now we can load the file into Ruby.
24
+ def evaluate(file_path:)
25
+ load(file_path)
26
+ end
27
+ end
28
+ end
data/lib/lowload.rb CHANGED
@@ -1,99 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'antlers'
4
- require 'lowkey'
5
-
3
+ require_relative 'adapters/markdown_adapter'
4
+ require_relative 'adapters/rbx_adapter'
5
+ require_relative 'adapters/ruby_adapter'
6
6
  require_relative 'loader'
7
-
8
- def top_level_binding
9
- binding
10
- end
7
+ require_relative 'metadata'
11
8
 
12
9
  module LowLoad
10
+ class UnsupportedFileType < StandardError; end
13
11
  class UnsupportedTemplate < StandardError; end
14
12
 
15
13
  class << self
16
- # Files are mapped, autoloaded, then loaded into Ruby in 3 separate stages.
14
+ ADAPTERS = [MarkdownAdapter.new, RBXAdapter.new, RubyAdapter.new]
15
+
17
16
  def dirload(path, pwd = Dir.pwd)
18
17
  absolute_path = File.expand_path(path, pwd)
19
18
  file_paths = Dir["#{absolute_path}/**/*"].filter { !File.directory?(it) }
20
19
 
21
- # Map all definitions and dependencies first.
22
- file_paths.each do |file_path|
23
- Lowkey.load(file_path)
20
+ file_path_adapters = file_paths.each_with_object({}) do |file_path, hash|
21
+ hash[file_path] = find_adapter(file_path:)
24
22
  end
25
23
 
26
- # Then autoload all dependencies for those files.
27
- file_paths.each do |file_path| # rubocop:disable Style/CombinableLoops
28
- Loader.add_autoloads(file_proxy: Lowkey[file_path])
29
- end
24
+ metadata = Metadata.new
25
+ metadata.process(file_path_adapters:)
26
+ step(:preload, file_path_adapters:)
27
+ step(:evaluate, file_path_adapters:)
30
28
 
31
- # Now we can load the files into Ruby.
32
- file_paths.each do |file_path| # rubocop:disable Style/CombinableLoops
33
- lowload(file_path)
34
- end
29
+ metadata
35
30
  end
36
31
 
37
- # Dependencies must first be loaded by dirload() or required by the file.
38
32
  def lowload(file_path)
39
- case File.extname(file_path).delete_prefix('.')
40
- when 'rb'
41
- load(file_path)
42
- when 'rbx'
43
- load_rbx(file_path)
44
- end
45
- end
46
-
47
- private
48
-
49
- def load_rbx(file_path)
50
- # 1. "File Load" phase.
51
- file_proxy = Lowkey[file_path] || Lowkey.load(file_path)
52
- templates = wrap_render_methods(file_proxy:)
53
-
54
- # 2. "Class Load" phase.
55
- # Not a security risk because "eval" is equivalent to "load" or "require_relative" in this context.
56
- eval(file_proxy.export, top_level_binding, file_proxy.file_path, 0) # rubocop:disable Security/Eval
33
+ adapter = find_adapter(file_path:)
57
34
 
58
- # 3. "Runtime" phase (before LowNode instances are rendered).
59
- # Templates only exist if antlers gem has been required by another gem (such as LowNode) and the template contains antlers syntax.
60
- templates.each do |namespace, method_template|
61
- klass = const_get(namespace)
62
- method, template = method_template
63
- # TODO: If params contain "**props" or similar then send that, so that LowNode can replicate it.
64
- params = method.params.map(&:name)
35
+ raise(UnsupportedFileType, "Could not load #{file_path}") if adapter.nil?
65
36
 
66
- next unless supports_templates?(klass) || raise(UnsupportedTemplate, "Template for '#{namespace}' identified but not implemented")
67
-
68
- # TODO: Make template engine configurable.
69
- klass.build_template(template:, params:, engine: Antlers, namespace:)
70
- end
37
+ adapter.evaluate(file_path:)
71
38
  end
72
39
 
73
- def supports_templates?(klass)
74
- klass.respond_to?(:render) && klass.respond_to?(:template) && klass.respond_to?(:build_template)
75
- end
76
-
77
- def wrap_render_methods(file_proxy:)
78
- # GOAL: Templates could be organised by method and template rendering engine.
79
- templates = {}
80
-
81
- file_proxy.definitions.each_value do |class_proxy|
82
- render_method = class_proxy.respond_to?(:instance_methods) ? class_proxy.instance_methods[:render] : nil
83
-
84
- next unless render_method
85
-
86
- render_method_body = render_method.body.export
87
-
88
- if defined?(Antlers) && ['{', '<{'].any? { |needle| render_method_body.include?(needle) }
89
- templates[class_proxy.namespace] = [render_method, render_method_body]
90
- end
91
-
92
- # Intended to target HTML or Antlers tags. Use HEREDOC "<<~" in a plain Ruby (".rb") file, not RBX.
93
- render_method.body.wrap(prefix: '%q{', suffix: '}') if render_method_body.strip.start_with?('<')
40
+ def step(step, file_path_adapters:)
41
+ file_path_adapters.each do |file_path, adapter|
42
+ adapter&.send(step, file_path:)
94
43
  end
44
+ end
95
45
 
96
- templates
46
+ def find_adapter(file_path:)
47
+ extension = File.extname(file_path).delete_prefix('.')
48
+ ADAPTERS.find { |adapter| adapter.class::EXTENSIONS.include?(extension) }
97
49
  end
98
50
  end
99
51
  end
data/lib/metadata.rb ADDED
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'adapters/markdown_adapter'
4
+
5
+ module LowLoad
6
+ class Metadata
7
+ EXTENSIONS = [
8
+ *::LowLoad::MarkdownAdapter::EXTENSIONS,
9
+ *::LowLoad::RBXAdapter::EXTENSIONS,
10
+ *::LowLoad::RubyAdapter::EXTENSIONS,
11
+ ]
12
+
13
+ attr_accessor :loaded_paths, :missed_paths, :file_types, :url_paths, :tags
14
+
15
+ def initialize
16
+ @loaded_paths = []
17
+ @missed_paths = []
18
+
19
+ @file_types = EXTENSIONS.each_with_object({}) { |extension, hash| hash[extension] = [] }
20
+ @url_paths = {}
21
+ @tags = {}
22
+ end
23
+
24
+ def process(file_path_adapters:)
25
+ file_path_adapters.each do |file_path, adapter|
26
+ if (metadata = adapter&.metadata(file_path:))
27
+ append(file_path:, adapter:, metadata:)
28
+ else
29
+ @missed_paths << file_path
30
+ end
31
+ end
32
+ end
33
+
34
+ def append(file_path:, adapter:, metadata:)
35
+ @loaded_paths << file_path
36
+ @file_types[File.extname(file_path).delete_prefix('.')] << file_path
37
+ @url_paths[adapter.url_path(file_path:)] = file_path
38
+
39
+ metadata[:tags]&.each do |tag|
40
+ @tags[tag] ||= []
41
+ @tags[tag] = file_path
42
+ end
43
+ end
44
+ end
45
+ end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LowLoad
4
- VERSION = '0.5.1'
4
+ VERSION = '0.6.1'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lowload
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - maedi
@@ -31,8 +31,13 @@ executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
+ - lib/adapters/adapter.rb
35
+ - lib/adapters/markdown_adapter.rb
36
+ - lib/adapters/rbx_adapter.rb
37
+ - lib/adapters/ruby_adapter.rb
34
38
  - lib/loader.rb
35
39
  - lib/lowload.rb
40
+ - lib/metadata.rb
36
41
  - lib/version.rb
37
42
  homepage: https://github.com/low-rb/lowload
38
43
  licenses: []
@@ -53,7 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
58
  - !ruby/object:Gem::Version
54
59
  version: '0'
55
60
  requirements: []
56
- rubygems_version: 4.0.10
61
+ rubygems_version: 4.0.6
57
62
  specification_version: 4
58
63
  summary: An autoloader without namespace and folder structure conventions
59
64
  test_files: []