modulation 0.27 → 0.28
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 +4 -4
- data/CHANGELOG.md +7 -1
- data/README.md +62 -26
- data/bin/mdl +6 -10
- data/lib/modulation/builder.rb +57 -0
- data/lib/modulation/core.rb +5 -59
- data/lib/modulation/ext.rb +5 -5
- data/lib/modulation/packer.rb +107 -0
- data/lib/modulation/paths.rb +28 -5
- data/lib/modulation/version.rb +1 -1
- metadata +3 -3
- data/lib/modulation/packing.rb +0 -89
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 630df5ec1eef61abafa86781e8f73c892dcf01c738bc02b695e767586208c728
|
4
|
+
data.tar.gz: 4f98dbd921afc4c3e0f33d82d19083e56cfacffee7192bf0ad621e638b5cf6a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 726ac2711660e422fac0caeeb41828d4fafea79c3ef6da7abb24ea0160990248b2c1830b4c69f278b2f798a0038bd9c2b361dc8152bd33f726bda4f67eb71fca
|
7
|
+
data.tar.gz: 4ccfd538a57c5a8183409e42e04d9a5df14f22068d2900b269841690fbec878473c1b52ed84e8e2ec1b87736a4f915463d829670731f80e732fe1dee8068cc26
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
+
0.28 2019-08-23
|
2
|
+
---------------
|
3
|
+
|
4
|
+
* Implement tagged sources
|
5
|
+
* Improve application packer
|
6
|
+
|
1
7
|
0.27 2019-08-21
|
2
8
|
---------------
|
3
9
|
|
4
|
-
* Add initial packing functionality
|
10
|
+
* Add initial packing functionality
|
5
11
|
|
6
12
|
0.26 2019-08-20
|
7
13
|
---------------
|
data/README.md
CHANGED
@@ -25,6 +25,28 @@ a functional style, minimizing boilerplate code.
|
|
25
25
|
> Modulation, it is not intended as a comprehensive solution for using
|
26
26
|
> third-party libraries.
|
27
27
|
|
28
|
+
## Features
|
29
|
+
|
30
|
+
- Provides complete isolation of each module: constant definitions in one file
|
31
|
+
do not leak into another.
|
32
|
+
- Enforces explicit exporting and importing of methods, classes, modules and
|
33
|
+
constants.
|
34
|
+
- Supports circular dependencies.
|
35
|
+
- Supports [default exports](#default-exports) for modules exporting a single
|
36
|
+
class or value.
|
37
|
+
- Modules can be [lazy loaded](#lazy-loading) to improve start up time and
|
38
|
+
memory consumption.
|
39
|
+
- Modules can be [reloaded](#reloading-modules) at runtime without breaking your
|
40
|
+
code in wierd ways.
|
41
|
+
- Allows [mocking of dependencies](#mocking-dependencies) for testing purposes.
|
42
|
+
- Can be used to [write gems](#writing-gems-using-modulation).
|
43
|
+
- Module dependencies can be [introspected](#dependency-introspection).
|
44
|
+
- Facilitates [unit-testing](#unit-testing-modules) of private methods and
|
45
|
+
constants.
|
46
|
+
- Can load all source files in directory [at once](#importing-all-source-files-in-a-directory).
|
47
|
+
- Packs entire applications [into a single
|
48
|
+
file](#packing-applications-with-modulation).
|
49
|
+
|
28
50
|
## Rationale
|
29
51
|
|
30
52
|
You're probably asking yourself "what the ****?" , but when your Ruby app grows
|
@@ -65,33 +87,12 @@ are hidden unless explicitly exported, and the global namespace remains
|
|
65
87
|
clutter-free. All dependencies between source files are explicit, visible, and
|
66
88
|
easy to understand.
|
67
89
|
|
68
|
-
## Features
|
69
|
-
|
70
|
-
- Provides complete isolation of each module: constant definitions in one file
|
71
|
-
do not leak into another.
|
72
|
-
- Enforces explicit exporting and importing of methods, classes, modules and
|
73
|
-
constants.
|
74
|
-
- Supports circular dependencies.
|
75
|
-
- Supports [default exports](#default-exports) for modules exporting a single
|
76
|
-
class or value.
|
77
|
-
- Modules can be [lazy loaded](#lazy-loading) to improve start up time and
|
78
|
-
memory consumption.
|
79
|
-
- Modules can be [reloaded](#reloading-modules) at runtime without breaking your
|
80
|
-
code in wierd ways.
|
81
|
-
- Allows [mocking of dependencies](#mocking-dependencies) for testing purposes.
|
82
|
-
- Can be used to [write gems](#writing-gems-using-modulation).
|
83
|
-
- Module dependencies can be [introspected](#dependency-introspection).
|
84
|
-
- Facilitates [unit-testing](#unit-testing-modules) of private methods and
|
85
|
-
constants.
|
86
|
-
- Can load all source files in directory [at once](#importing-all-source-files-in-a-directory).
|
87
|
-
- Pack entire applications [into a single file](#packing-applications-with-modulation).
|
88
|
-
|
89
90
|
## Installing Modulation
|
90
91
|
|
91
|
-
You can install the Modulation
|
92
|
+
You can install the Modulation using `gem install`, or add it to your `Gemfile`:
|
92
93
|
|
93
|
-
```
|
94
|
-
|
94
|
+
```ruby
|
95
|
+
gem 'modulation'
|
95
96
|
```
|
96
97
|
|
97
98
|
## Organizing your code with Modulation
|
@@ -198,8 +199,18 @@ User = import('./models')::User
|
|
198
199
|
user = User.new(...)
|
199
200
|
```
|
200
201
|
|
201
|
-
|
202
|
-
|
202
|
+
### Using tags to designate common subdirectories
|
203
|
+
|
204
|
+
Normally, module paths are always relative to the file calling the `#import`
|
205
|
+
method, just like `#require_relative`. This can become a problem once you start
|
206
|
+
moving your source files around. In addition, in applications where your source
|
207
|
+
files are arranged in multiple directories, it can quickly become tedious to do
|
208
|
+
stuff like `Post = import('../models/post')`.
|
209
|
+
|
210
|
+
Modulation provides an alternative to relative paths in the form of tagged
|
211
|
+
sources. A tagged source is simply a path associated with a label. For example,
|
212
|
+
an application may tag `lib/models` simply as `@models`. Once tags are defined,
|
213
|
+
they can be used when importing files, e.g. `import('@models/post')`.
|
203
214
|
|
204
215
|
### Importing all source files in a directory
|
205
216
|
|
@@ -484,6 +495,31 @@ settings = import('settings')
|
|
484
495
|
settings = settings.__reload!
|
485
496
|
```
|
486
497
|
|
498
|
+
### Retaining state between reloads
|
499
|
+
|
500
|
+
Before a module is reloaded, all of its methods and constants are removed. In
|
501
|
+
some cases, a module might need to retain state across reloads. You can do this
|
502
|
+
by simply using instance variables:
|
503
|
+
|
504
|
+
```ruby
|
505
|
+
export :value, :inc
|
506
|
+
|
507
|
+
@counter ||= 0
|
508
|
+
|
509
|
+
def value
|
510
|
+
@counter
|
511
|
+
end
|
512
|
+
|
513
|
+
def incr
|
514
|
+
@counter += 1
|
515
|
+
end
|
516
|
+
```
|
517
|
+
|
518
|
+
Care must be taken not to reassign values outside of methods, as this will
|
519
|
+
overwrite any value retained in the instance variable. To assign initial values,
|
520
|
+
use the `||=` operator as in the example above. See also the
|
521
|
+
[reload example](examples/reload).
|
522
|
+
|
487
523
|
## Dependency introspection
|
488
524
|
|
489
525
|
Modulation allows runtime introspection of dependencies between modules. You can
|
data/bin/mdl
CHANGED
@@ -10,6 +10,10 @@ def self.run
|
|
10
10
|
mod = import(File.expand_path(fn))
|
11
11
|
mod.send(method) if method
|
12
12
|
end
|
13
|
+
rescue StandardError => e
|
14
|
+
backtrace = e.backtrace.reject { |l| l =~ /^(#{Modulation::DIR})|(bin\/mdl)/ }
|
15
|
+
e.set_backtrace(backtrace)
|
16
|
+
raise e
|
13
17
|
end
|
14
18
|
|
15
19
|
def collect_deps(fn, paths)
|
@@ -31,16 +35,8 @@ def self.deps
|
|
31
35
|
end
|
32
36
|
|
33
37
|
def self.pack
|
34
|
-
|
35
|
-
ARGV
|
36
|
-
fn, method = (arg =~ /^([^\:]+)\:(.+)$/) ? [$1, $2.to_sym] : [arg, :main]
|
37
|
-
mod = import(File.expand_path(fn))
|
38
|
-
paths << File.expand_path(fn)
|
39
|
-
mod.__traverse_dependencies { |m| paths << m.__module_info[:location] }
|
40
|
-
end
|
41
|
-
|
42
|
-
require 'modulation/packing'
|
43
|
-
STDOUT << Modulation::Packing.pack(paths, hide_filenames: true)
|
38
|
+
require 'modulation/packer'
|
39
|
+
STDOUT << Modulation::Packer.pack(ARGV, hide_filenames: true)
|
44
40
|
end
|
45
41
|
|
46
42
|
cmd = ARGV.shift
|
data/lib/modulation/builder.rb
CHANGED
@@ -100,6 +100,63 @@ module Modulation
|
|
100
100
|
mod.__exported_symbols.clear
|
101
101
|
mod.__reset_dependencies
|
102
102
|
end
|
103
|
+
|
104
|
+
# Adds all or part of a module's methods to a target object
|
105
|
+
# If no symbols are given, all methods are added
|
106
|
+
# @param mod [Module] imported module
|
107
|
+
# @param target [Object] object to add methods to
|
108
|
+
# @param symbols [Array<Symbol>] list of methods to add
|
109
|
+
# @return [void]
|
110
|
+
def add_module_methods(mod, target, *symbols)
|
111
|
+
methods = mod.singleton_class.instance_methods(false)
|
112
|
+
unless symbols.empty?
|
113
|
+
symbols.select! { |s| s =~ /^[a-z]/ }
|
114
|
+
methods = filter_exported_symbols(methods, symbols)
|
115
|
+
end
|
116
|
+
methods.each do |sym|
|
117
|
+
target.send(:define_method, sym, &mod.method(sym))
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Adds all or part of a module's constants to a target object
|
122
|
+
# If no symbols are given, all constants are added
|
123
|
+
# @param mod [Module] imported module
|
124
|
+
# @param target [Object] object to add constants to
|
125
|
+
# @param symbols [Array<Symbol>] list of constants to add
|
126
|
+
# @return [void]
|
127
|
+
def add_module_constants(mod, target, *symbols)
|
128
|
+
exported = mod.__module_info[:exported_symbols]
|
129
|
+
unless symbols.empty?
|
130
|
+
symbols.select! { |s| s =~ /^[A-Z]/ }
|
131
|
+
exported = filter_exported_symbols(exported, symbols)
|
132
|
+
end
|
133
|
+
mod.singleton_class.constants(false).each do |sym|
|
134
|
+
next unless exported.include?(sym)
|
135
|
+
|
136
|
+
target.const_set(sym, mod.singleton_class.const_get(sym))
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def filter_exported_symbols(exported, requested)
|
141
|
+
not_exported = requested - exported
|
142
|
+
unless not_exported.empty?
|
143
|
+
raise NameError, "symbol #{not_exported.first.inspect} not exported"
|
144
|
+
end
|
145
|
+
|
146
|
+
exported & requested
|
147
|
+
end
|
148
|
+
|
149
|
+
# Defines a const_missing method used for auto-importing on a given object
|
150
|
+
# @param receiver [Object] object to receive the const_missing method call
|
151
|
+
# @param auto_import_hash [Hash] a hash mapping constant names to a source
|
152
|
+
# file and a caller location
|
153
|
+
# @return [void]
|
154
|
+
def define_auto_import_const_missing_method(receiver, auto_import_hash)
|
155
|
+
receiver.singleton_class.define_method(:const_missing) do |sym|
|
156
|
+
(path, caller_location) = auto_import_hash[sym]
|
157
|
+
path ? const_set(sym, import(path, caller_location)) : super(sym)
|
158
|
+
end
|
159
|
+
end
|
103
160
|
end
|
104
161
|
end
|
105
162
|
end
|
data/lib/modulation/core.rb
CHANGED
@@ -32,8 +32,7 @@ module Modulation
|
|
32
32
|
# @param caller_location [String] caller location
|
33
33
|
# @return [Module] loaded module object
|
34
34
|
def import(path, caller_location = caller(1..1).first)
|
35
|
-
abs_path = Paths.
|
36
|
-
Paths.lookup_gem_path(path)
|
35
|
+
abs_path = Paths.process(path, caller_location)
|
37
36
|
|
38
37
|
case abs_path
|
39
38
|
when String
|
@@ -71,63 +70,6 @@ module Modulation
|
|
71
70
|
end
|
72
71
|
end
|
73
72
|
|
74
|
-
# Adds all or part of a module's methods to a target object
|
75
|
-
# If no symbols are given, all methods are added
|
76
|
-
# @param mod [Module] imported module
|
77
|
-
# @param target [Object] object to add methods to
|
78
|
-
# @param symbols [Array<Symbol>] list of methods to add
|
79
|
-
# @return [void]
|
80
|
-
def add_module_methods(mod, target, *symbols)
|
81
|
-
methods = mod.singleton_class.instance_methods(false)
|
82
|
-
unless symbols.empty?
|
83
|
-
symbols.select! { |s| s =~ /^[a-z]/ }
|
84
|
-
methods = filter_exported_symbols(methods, symbols)
|
85
|
-
end
|
86
|
-
methods.each do |sym|
|
87
|
-
target.send(:define_method, sym, &mod.method(sym))
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# Adds all or part of a module's constants to a target object
|
92
|
-
# If no symbols are given, all constants are added
|
93
|
-
# @param mod [Module] imported module
|
94
|
-
# @param target [Object] object to add constants to
|
95
|
-
# @param symbols [Array<Symbol>] list of constants to add
|
96
|
-
# @return [void]
|
97
|
-
def add_module_constants(mod, target, *symbols)
|
98
|
-
exported = mod.__module_info[:exported_symbols]
|
99
|
-
unless symbols.empty?
|
100
|
-
symbols.select! { |s| s =~ /^[A-Z]/ }
|
101
|
-
exported = filter_exported_symbols(exported, symbols)
|
102
|
-
end
|
103
|
-
mod.singleton_class.constants(false).each do |sym|
|
104
|
-
next unless exported.include?(sym)
|
105
|
-
|
106
|
-
target.const_set(sym, mod.singleton_class.const_get(sym))
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def filter_exported_symbols(exported, requested)
|
111
|
-
not_exported = requested - exported
|
112
|
-
unless not_exported.empty?
|
113
|
-
raise NameError, "symbol #{not_exported.first.inspect} not exported"
|
114
|
-
end
|
115
|
-
|
116
|
-
exported & requested
|
117
|
-
end
|
118
|
-
|
119
|
-
# Defines a const_missing method used for auto-importing on a given object
|
120
|
-
# @param receiver [Object] object to receive the const_missing method call
|
121
|
-
# @param auto_import_hash [Hash] a hash mapping constant names to a source
|
122
|
-
# file and a caller location
|
123
|
-
# @return [void]
|
124
|
-
def define_auto_import_const_missing_method(receiver, auto_import_hash)
|
125
|
-
receiver.singleton_class.define_method(:const_missing) do |sym|
|
126
|
-
(path, caller_location) = auto_import_hash[sym]
|
127
|
-
path ? const_set(sym, import(path, caller_location)) : super(sym)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
73
|
# Creates a new module from a source file
|
132
74
|
# @param path [String] source file name
|
133
75
|
# @return [Module] module
|
@@ -180,6 +122,10 @@ module Modulation
|
|
180
122
|
ensure
|
181
123
|
@loaded_modules[path] = old_module if block_given?
|
182
124
|
end
|
125
|
+
|
126
|
+
def add_tags(tags)
|
127
|
+
Paths.add_tags(tags, caller(1..1).first)
|
128
|
+
end
|
183
129
|
end
|
184
130
|
end
|
185
131
|
|
data/lib/modulation/ext.rb
CHANGED
@@ -45,7 +45,7 @@ class Module
|
|
45
45
|
|
46
46
|
def setup_auto_import_registry
|
47
47
|
@__auto_import_registry = {}
|
48
|
-
Modulation.define_auto_import_const_missing_method(
|
48
|
+
Modulation::Builder.define_auto_import_const_missing_method(
|
49
49
|
self,
|
50
50
|
@__auto_import_registry
|
51
51
|
)
|
@@ -56,8 +56,8 @@ class Module
|
|
56
56
|
# @return [void]
|
57
57
|
def extend_from(path)
|
58
58
|
mod = import(path, caller(1..1).first)
|
59
|
-
Modulation.add_module_methods(mod, self.class)
|
60
|
-
Modulation.add_module_constants(mod, self)
|
59
|
+
Modulation::Builder.add_module_methods(mod, self.class)
|
60
|
+
Modulation::Builder.add_module_constants(mod, self)
|
61
61
|
end
|
62
62
|
|
63
63
|
# Includes exported methods from the given file name in the receiver
|
@@ -67,8 +67,8 @@ class Module
|
|
67
67
|
# @return [void]
|
68
68
|
def include_from(path, *symbols)
|
69
69
|
mod = import(path, caller(1..1).first)
|
70
|
-
Modulation.add_module_methods(mod, self, *symbols)
|
71
|
-
Modulation.add_module_constants(mod, self, *symbols)
|
70
|
+
Modulation::Builder.add_module_methods(mod, self, *symbols)
|
71
|
+
Modulation::Builder.add_module_constants(mod, self, *symbols)
|
72
72
|
end
|
73
73
|
|
74
74
|
# Aliases the given method only if the alias does not exist, implementing in
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../modulation'
|
4
|
+
require_relative '../modulation/version'
|
5
|
+
require 'zlib'
|
6
|
+
|
7
|
+
module Modulation
|
8
|
+
# Implements packing functionality
|
9
|
+
module Packer
|
10
|
+
BOOTSTRAP_CODE = <<~SRC.encode('ASCII-8BIT').chomp
|
11
|
+
# encoding: ASCII-8BIT
|
12
|
+
require 'bundler/inline'
|
13
|
+
|
14
|
+
gemfile do
|
15
|
+
source 'https://rubygems.org'
|
16
|
+
gem 'modulation', '~> %<modulation_version>s'
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'modulation/packer'
|
20
|
+
Modulation::Bootstrap.setup(DATA, %<dictionary>s)
|
21
|
+
import(%<entry_point>s).send(:main)
|
22
|
+
__END__
|
23
|
+
%<data>s
|
24
|
+
SRC
|
25
|
+
|
26
|
+
def self.pack(paths, _options = {})
|
27
|
+
paths = [paths] unless paths.is_a?(Array)
|
28
|
+
deps = collect_dependencies(paths)
|
29
|
+
entry_point_filename = File.expand_path(paths.first)
|
30
|
+
dictionary, data = generate_packed_data(deps)
|
31
|
+
generate_bootstrap(dictionary, data, entry_point_filename)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.collect_dependencies(paths)
|
35
|
+
paths.each_with_object([]) do |fn, deps|
|
36
|
+
mod = import(File.expand_path(fn))
|
37
|
+
deps << File.expand_path(fn)
|
38
|
+
mod.__traverse_dependencies { |m| deps << m.__module_info[:location] }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.generate_packed_data(deps)
|
43
|
+
files = deps.each_with_object({}) do |path, dict|
|
44
|
+
dict[path] = IO.read(path)
|
45
|
+
end
|
46
|
+
# files[INLINE_GEMFILE_PATH] = generate_gemfile
|
47
|
+
pack_files(files)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.pack_files(files)
|
51
|
+
data = (+'').encode('ASCII-8BIT')
|
52
|
+
last_offset = 0
|
53
|
+
dictionary = files.each_with_object({}) do |(path, content), dict|
|
54
|
+
zipped = Zlib::Deflate.deflate(content)
|
55
|
+
size = zipped.bytesize
|
56
|
+
|
57
|
+
data << zipped
|
58
|
+
dict[path] = [last_offset, size]
|
59
|
+
last_offset += size
|
60
|
+
end
|
61
|
+
[dictionary, data]
|
62
|
+
end
|
63
|
+
|
64
|
+
# def self.generate_gemfile
|
65
|
+
# format(INLINE_GEMFILE_CODE)
|
66
|
+
# end
|
67
|
+
|
68
|
+
def self.generate_bootstrap(dictionary, data, entry_point)
|
69
|
+
format(BOOTSTRAP_CODE, modulation_version: Modulation::VERSION,
|
70
|
+
dictionary: dictionary.inspect,
|
71
|
+
entry_point: entry_point.inspect,
|
72
|
+
data: data)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Packed app bootstrapping code
|
77
|
+
module Bootstrap
|
78
|
+
def self.setup(data, dictionary)
|
79
|
+
patch_builder
|
80
|
+
@data = data
|
81
|
+
@data_offset = data.pos
|
82
|
+
@dictionary = dictionary
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.patch_builder
|
86
|
+
class << Modulation::Builder
|
87
|
+
alias_method :orig_make, :make
|
88
|
+
def make(info)
|
89
|
+
if Modulation::Bootstrap.find(info[:location])
|
90
|
+
info[:source] = Modulation::Bootstrap.read(info[:location])
|
91
|
+
end
|
92
|
+
orig_make(info)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.find(path)
|
98
|
+
@dictionary[path]
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.read(path)
|
102
|
+
(offset, length) = @dictionary[path]
|
103
|
+
@data.seek(@data_offset + offset)
|
104
|
+
Zlib::Inflate.inflate(@data.read(length))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/modulation/paths.rb
CHANGED
@@ -4,14 +4,37 @@ module Modulation
|
|
4
4
|
# Implements methods for expanding relative or incomplete module file names
|
5
5
|
module Paths
|
6
6
|
class << self
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
path[TAGGED_REGEXP, 1]
|
7
|
+
def process(path, caller_location)
|
8
|
+
tagged_path(path) || absolute_path(path, caller_location) ||
|
9
|
+
lookup_gem_path(path)
|
11
10
|
end
|
12
11
|
|
13
12
|
# Regexp for extracting filename from caller reference
|
14
|
-
CALLER_FILE_REGEXP = /^([^\:]+)
|
13
|
+
CALLER_FILE_REGEXP = /^([^\:]+)\:?/.freeze
|
14
|
+
TAGGED_REGEXP = /^@([^\/]+)(\/.+)?$/.freeze
|
15
|
+
|
16
|
+
def add_tags(tags, caller_location)
|
17
|
+
caller_file = caller_location[CALLER_FILE_REGEXP, 1]
|
18
|
+
caller_dir = caller_file ? File.dirname(caller_file) : nil
|
19
|
+
|
20
|
+
@tags ||= {}
|
21
|
+
tags.each do |k, path|
|
22
|
+
@tags[k.to_s] = caller_dir ? File.expand_path(path, caller_dir) : path
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def tagged_path(path)
|
27
|
+
return nil unless @tags
|
28
|
+
|
29
|
+
_, tag, path = path.match(TAGGED_REGEXP).to_a
|
30
|
+
return nil unless tag
|
31
|
+
|
32
|
+
base_path = @tags[tag]
|
33
|
+
return nil unless base_path
|
34
|
+
|
35
|
+
path = path ? File.join(base_path, path) : base_path
|
36
|
+
check_path(path)
|
37
|
+
end
|
15
38
|
|
16
39
|
# Resolves the absolute path to the provided reference. If the file is not
|
17
40
|
# found, will try to resolve to a gem
|
data/lib/modulation/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: modulation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.28'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -61,7 +61,7 @@ files:
|
|
61
61
|
- lib/modulation/ext.rb
|
62
62
|
- lib/modulation/gem.rb
|
63
63
|
- lib/modulation/module_mixin.rb
|
64
|
-
- lib/modulation/
|
64
|
+
- lib/modulation/packer.rb
|
65
65
|
- lib/modulation/paths.rb
|
66
66
|
- lib/modulation/version.rb
|
67
67
|
homepage: http://github.com/digital-fabric/modulation
|
data/lib/modulation/packing.rb
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../modulation'
|
4
|
-
require 'zlib'
|
5
|
-
require 'digest/md5'
|
6
|
-
|
7
|
-
module Modulation
|
8
|
-
# Implements packing functionality
|
9
|
-
module Packing
|
10
|
-
BOOTSTRAP = <<~SRC.encode('ASCII-8BIT')
|
11
|
-
# encoding: ASCII-8BIT
|
12
|
-
require 'modulation'
|
13
|
-
require 'zlib'
|
14
|
-
|
15
|
-
b = Modulation::Builder
|
16
|
-
z = Zlib::Inflate
|
17
|
-
b::C = {%<module_dictionary>s}
|
18
|
-
|
19
|
-
class << b
|
20
|
-
alias_method :orig_make, :make
|
21
|
-
|
22
|
-
def make(info)
|
23
|
-
(pr = Modulation::Builder::C.delete(info[:location])) ? pr.call : orig_make(info)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
import(%<entry_point>s).send(:main)
|
28
|
-
SRC
|
29
|
-
|
30
|
-
MAKE_CODE = <<~SRC
|
31
|
-
proc { b.orig_make(location: %<location>s, source: %<source>s) }
|
32
|
-
SRC
|
33
|
-
|
34
|
-
UNZIP_CODE = 'z.inflate(%<data>s)'
|
35
|
-
|
36
|
-
# MAKE_CODE = <<~SRC
|
37
|
-
# Modulation::Builder.orig_make(location: %s, source: %s)
|
38
|
-
# SRC
|
39
|
-
|
40
|
-
# UNCAN_CODE = <<~SRC
|
41
|
-
# proc { RubyVM::InstructionSequence.load_from_binary(
|
42
|
-
# Zlib::Inflate.inflate(%s)).eval
|
43
|
-
# }
|
44
|
-
# SRC
|
45
|
-
|
46
|
-
def self.pack(paths, _options = {})
|
47
|
-
paths = [paths] unless paths.is_a?(Array)
|
48
|
-
|
49
|
-
entry_point_filename = nil
|
50
|
-
dictionary = paths.each_with_object({}) do |path, dict|
|
51
|
-
warn "Processing #{path}"
|
52
|
-
source = IO.read(path)
|
53
|
-
entry_point_filename ||= path
|
54
|
-
dict[path] = pack_module(path, source)
|
55
|
-
end
|
56
|
-
|
57
|
-
generate_bootstrap(dictionary, entry_point_filename)
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.pack_module(path, source)
|
61
|
-
# code = (MAKE_CODE % [fn.inspect, module_source.inspect])
|
62
|
-
# seq = RubyVM::InstructionSequence.compile(code, options)
|
63
|
-
# canned = Zlib::Deflate.deflate(seq.to_binary)
|
64
|
-
# dict[fn] = UNCAN_CODE % canned.inspect
|
65
|
-
|
66
|
-
zipped = Zlib::Deflate.deflate(source)
|
67
|
-
code = format(UNZIP_CODE, data: zipped.inspect)
|
68
|
-
format(MAKE_CODE, location: path.inspect, source: code)
|
69
|
-
end
|
70
|
-
|
71
|
-
def self.generate_bootstrap(module_dictionary, entry_point)
|
72
|
-
format(
|
73
|
-
BOOTSTRAP,
|
74
|
-
module_dictionary: format_module_dictionary(module_dictionary),
|
75
|
-
entry_point: entry_point.inspect
|
76
|
-
).chomp.gsub(/^\s+/, '').gsub(/\n+/, "\n")
|
77
|
-
end
|
78
|
-
|
79
|
-
def self.format_module_dictionary(module_dictionary)
|
80
|
-
module_dictionary.map do |fn, code|
|
81
|
-
format(
|
82
|
-
'%<filename>s => %<code>s',
|
83
|
-
filename: fn.inspect,
|
84
|
-
code: code
|
85
|
-
).chomp
|
86
|
-
end.join(',')
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|