modulation 0.27 → 0.28
Sign up to get free protection for your applications and to get access to all the features.
- 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
|