dirload 0.6.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 96c8cec00fefea226ccd40b45b876c674ab1c58782033134d42b202c3b4fe27e
4
+ data.tar.gz: 52bd411227f4d22d78162e0a4a90eb1c23a80d9b63ce434d88e274eb173b4dec
5
+ SHA512:
6
+ metadata.gz: 70624c6d69e6a945a3e1cb18c55483ca07724cc683641dfe6ff1a3d2b733c5079c3294e1e4dada1c1612be38c7292cd064882cc0d8efb79eddcf52b2ebf74d73
7
+ data.tar.gz: 86c1ea5c9d70fe629351f2b07a6f1441c3bef6ff71ac6b7df2d8350e417c2dbebe09eb9f2025fe8d01aa56b01c268682d364e3ba9d3383fdec286da7b8a9e97b
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Lint/UnusedMethodArgument
4
+
5
+ module Dirload
6
+ class Adapter
7
+ EXTENSIONS = [].freeze
8
+
9
+ def mapload(file_path:) = nil
10
+
11
+ def preload(file_path:) = nil
12
+
13
+ def evaluate(file_path:, top_level_binding: nil) = nil
14
+ end
15
+ end
16
+
17
+ # rubocop:enable Lint/UnusedMethodArgument
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'lowkey'
5
+ require_relative 'adapter'
6
+
7
+ module Dirload
8
+ class MarkdownAdapter < Adapter
9
+ EXTENSIONS = %w[md rd markdown raindown].freeze
10
+ end
11
+ end
@@ -0,0 +1,78 @@
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 Dirload
12
+ class RBXAdapter < Adapter
13
+ EXTENSIONS = ['rbx'].freeze
14
+
15
+ # Map all definitions and dependencies.
16
+ def mapload(file_path:)
17
+ Lowkey.load(file_path)
18
+ end
19
+
20
+ # Then autoload all dependencies for those files.
21
+ def preload(file_path:)
22
+ Loader.add_autoloads(file_proxy: Lowkey[file_path])
23
+ end
24
+
25
+ # Now we can load the file into Ruby.
26
+ def evaluate(file_path:)
27
+ # 1. "File Load" phase.
28
+ file_proxy = Lowkey[file_path] || Lowkey.load(file_path)
29
+ templates = wrap_render_methods(file_proxy:)
30
+
31
+ # 2. "Class Load" phase.
32
+ # Not a security risk because "eval" is equivalent to "load" or "require_relative" in this context.
33
+ eval(file_proxy.export, top_level_binding, file_proxy.file_path, 0) # rubocop:disable Security/Eval
34
+
35
+ # 3. "Runtime" phase (before low nodes are rendered).
36
+ # Templates only exist if antlers gem has been required by another gem (like LowNode) and the template contains antlers syntax.
37
+ templates.each do |namespace, method_template|
38
+ klass = Object.const_get(namespace)
39
+ method, template = method_template
40
+ # TODO: If params contain "**props" or similar then send that, so that LowNode can replicate it.
41
+ params = method.params.map(&:name)
42
+
43
+ next unless supports_templates?(klass) || raise(UnsupportedTemplate, "Template for '#{namespace}' identified but not implemented")
44
+
45
+ # TODO: Make template engine configurable.
46
+ klass.build_template(template:, params:, engine: Antlers, namespace:)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def supports_templates?(klass)
53
+ klass.respond_to?(:render) && klass.respond_to?(:template) && klass.respond_to?(:build_template)
54
+ end
55
+
56
+ def wrap_render_methods(file_proxy:)
57
+ # GOAL: Templates could be organised by method and template rendering engine.
58
+ templates = {}
59
+
60
+ file_proxy.definitions.each_value do |class_proxy|
61
+ render_method = class_proxy.respond_to?(:instance_methods) ? class_proxy.instance_methods[:render] : nil
62
+
63
+ next unless render_method
64
+
65
+ render_method_body = render_method.body.export
66
+
67
+ if defined?(Antlers) && ['{', '<{'].any? { |needle| render_method_body.include?(needle) }
68
+ templates[class_proxy.namespace] = [render_method, render_method_body]
69
+ end
70
+
71
+ # Intended to target HTML or Antlers tags. Use HEREDOC "<<~" in a plain Ruby (".rb") file, not RBX.
72
+ render_method.body.wrap(prefix: '%q{', suffix: '}') if render_method_body.strip.start_with?('<')
73
+ end
74
+
75
+ templates
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lowkey'
4
+ require_relative 'adapter'
5
+ require_relative '../loader'
6
+
7
+ module Dirload
8
+ class RubyAdapter < Adapter
9
+ EXTENSIONS = ['rb'].freeze
10
+
11
+ # Map all definitions and dependencies.
12
+ def mapload(file_path:)
13
+ Lowkey.load(file_path)
14
+ end
15
+
16
+ # Then autoload all dependencies for those files.
17
+ def preload(file_path:)
18
+ Loader.add_autoloads(file_proxy: Lowkey[file_path])
19
+ end
20
+
21
+ # Now we can load the file into Ruby.
22
+ def evaluate(file_path:)
23
+ load(file_path)
24
+ end
25
+ end
26
+ end
data/lib/dirload.rb ADDED
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'adapters/markdown_adapter'
4
+ require_relative 'adapters/rbx_adapter'
5
+ require_relative 'adapters/ruby_adapter'
6
+ require_relative 'loader'
7
+ require_relative 'metadata'
8
+
9
+ module Dirload
10
+ class UnsupportedFileType < StandardError; end
11
+ class UnsupportedTemplate < StandardError; end
12
+
13
+ ADAPTERS = [MarkdownAdapter.new, RBXAdapter.new, RubyAdapter.new].freeze
14
+
15
+ def dirload(path, pwd = Dir.pwd, error: false) # rubocop:disable Metrics/AbcSize
16
+ absolute_path = File.expand_path(path, pwd)
17
+
18
+ return adapter_load(file_path: absolute_path) unless File.directory?(absolute_path)
19
+
20
+ loaded_paths = {}
21
+ missed_paths = []
22
+ file_types = {}
23
+
24
+ file_paths(absolute_path:).each do |file_path|
25
+ if (adapter = find_adapter(file_path:))
26
+ loaded_paths[file_path] = adapter
27
+ else
28
+ missed_paths << adapter
29
+ end
30
+
31
+ file_types[extension(file_path:)] ||= []
32
+ file_types[extension(file_path:)] << file_path
33
+ end
34
+
35
+ step(:mapload, loaded_paths:)
36
+ step(:preload, loaded_paths:)
37
+ step(:evaluate, loaded_paths:)
38
+
39
+ Metadata.new(loaded_paths:, missed_paths:, file_types:)
40
+ end
41
+
42
+ private
43
+
44
+ def file_paths(absolute_path:)
45
+ Dir["#{absolute_path}/**/*"].filter { !File.directory?(it) }
46
+ end
47
+
48
+ def adapter_load(file_path:)
49
+ adapter = find_adapter(file_path:)
50
+
51
+ raise(UnsupportedFileType, "Could not load #{file_path}") if adapter.nil?
52
+
53
+ adapter.evaluate(file_path:)
54
+ end
55
+
56
+ def step(step, loaded_paths:)
57
+ loaded_paths.each do |file_path, adapter|
58
+ adapter&.send(step, file_path:)
59
+ end
60
+ end
61
+
62
+ def find_adapter(file_path:)
63
+ ADAPTERS.find { |adapter| adapter.class::EXTENSIONS.include?(extension(file_path:)) }
64
+ end
65
+
66
+ def extension(file_path:)
67
+ File.extname(file_path).delete_prefix('.')
68
+ end
69
+ end
70
+
71
+ Kernel.send(:include, Dirload)
data/lib/loader.rb ADDED
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dirload
4
+ class Loader
5
+ class MissingDependencyError < StandardError; end
6
+
7
+ class << self
8
+ def add_autoloads(file_proxy:)
9
+ autoloads = []
10
+
11
+ file_proxy.dependencies.each do |namespace_with_dependency|
12
+ *namespace, dependency = namespace_with_dependency.split('::')
13
+
14
+ dependency_proxies = find_dependency_proxies(namespace:, dependency:)
15
+
16
+ current_namespace = Object
17
+ namespace.each do |module_name|
18
+ current_namespace.const_set(module_name, Module.new) unless current_namespace.const_defined?(module_name)
19
+ current_namespace = current_namespace.const_get(module_name)
20
+ end
21
+
22
+ dependency_proxies.each do |dependency_proxy|
23
+ autoloads << { origin: file_proxy.file_path, current_namespace:, dependency:, file_path: dependency_proxy.file_path }
24
+ current_namespace.autoload(dependency.to_sym, dependency_proxy.file_path)
25
+ end
26
+ end
27
+
28
+ autoloads
29
+ end
30
+
31
+ def find_dependency_proxies(namespace:, dependency:)
32
+ return Lowkey[dependency] || raise(MissingDependencyError) if namespace.empty?
33
+
34
+ namespace_with_dependency = [namespace, dependency].join('::')
35
+ return Lowkey[namespace_with_dependency] if Lowkey[namespace_with_dependency]
36
+
37
+ namespace.pop
38
+ find_dependency_proxies(namespace:, dependency:)
39
+ rescue MissingDependencyError => e
40
+ raise(MissingDependencyError, "Couldn't autoload #{dependency}. Require it manually?") unless const_get(dependency)
41
+
42
+ []
43
+ end
44
+ end
45
+ end
46
+ end
data/lib/metadata.rb ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'adapters/markdown_adapter'
4
+
5
+ module Dirload
6
+ class Metadata
7
+ EXTENSIONS = [
8
+ *::Dirload::MarkdownAdapter::EXTENSIONS,
9
+ *::Dirload::RBXAdapter::EXTENSIONS,
10
+ *::Dirload::RubyAdapter::EXTENSIONS
11
+ ].freeze
12
+
13
+ attr_accessor :loaded_paths, :missed_paths, :file_types
14
+
15
+ def initialize(loaded_paths:, missed_paths:, file_types:)
16
+ @loaded_paths = loaded_paths
17
+ @missed_paths = missed_paths
18
+ @file_types = file_types
19
+ end
20
+ end
21
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dirload
4
+ VERSION = '0.6.2'
5
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dirload
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.2
5
+ platform: ruby
6
+ authors:
7
+ - maedi
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: lowkey
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ description: An autoloader that lets you use any module namespace with any folder
27
+ structure and mix in manual requires.
28
+ email:
29
+ - maediprichard@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
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
38
+ - lib/dirload.rb
39
+ - lib/loader.rb
40
+ - lib/metadata.rb
41
+ - lib/version.rb
42
+ homepage: https://github.com/low-rb/dirload
43
+ licenses: []
44
+ metadata:
45
+ homepage_uri: https://github.com/low-rb/dirload
46
+ source_code_uri: https://github.com/low-rb/dirload/src/branch/main
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 3.3.0
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubygems_version: 4.0.6
62
+ specification_version: 4
63
+ summary: An autoloader without namespace and folder structure conventions
64
+ test_files: []