modulation 0.26 → 0.27
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 +6 -1
- data/README.md +80 -1
- data/bin/mdl +47 -0
- data/lib/modulation/builder.rb +1 -1
- data/lib/modulation/packing.rb +89 -0
- data/lib/modulation/paths.rb +6 -0
- data/lib/modulation/version.rb +1 -1
- metadata +5 -4
- data/bin/rbm +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04f2ab2cc42f117c9c8fb1b306b2239fbe9bca4a96f890e693bb92fb6c900318
|
4
|
+
data.tar.gz: 9ead222ae4fc275174b3e5d62a3b3b30e094761e5801de7b3a9fe21a346174be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bd5727f5ca807fcc6238b0679234ba5df79102eaa6153a5acb8db8eb66054a1d9bdb2f8250292cad4784101c1c58b343766b19a3a07be013dfb67ce13b7c175
|
7
|
+
data.tar.gz: 63a1bc298932c0b1d4bbf804099df2940755b23358f4e4c72023c61054d03102c97f4138324570821685eaeb82bd0101146c9a4d469f245d8af093b906a623bd
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
0.27 2019-08-21
|
2
|
+
---------------
|
3
|
+
|
4
|
+
* Add initial packing functionality.
|
5
|
+
|
1
6
|
0.26 2019-08-20
|
2
7
|
---------------
|
3
8
|
|
@@ -24,7 +29,7 @@
|
|
24
29
|
0.22 2019-05-15
|
25
30
|
---------------
|
26
31
|
|
27
|
-
* Export_default of a method now
|
32
|
+
* Export_default of a method now exports a proc calling that method
|
28
33
|
* Raise error on export of undefined symbols
|
29
34
|
|
30
35
|
0.21 2019-02-19
|
data/README.md
CHANGED
@@ -83,7 +83,8 @@ easy to understand.
|
|
83
83
|
- Module dependencies can be [introspected](#dependency-introspection).
|
84
84
|
- Facilitates [unit-testing](#unit-testing-modules) of private methods and
|
85
85
|
constants.
|
86
|
-
- Can load all source files in directory at once.
|
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).
|
87
88
|
|
88
89
|
## Installing Modulation
|
89
90
|
|
@@ -517,6 +518,42 @@ m1.__depedencies #=> [<Module m2>]
|
|
517
518
|
m1.__dependencies.first.__dependent_modules #=> [<Module m1>]
|
518
519
|
```
|
519
520
|
|
521
|
+
## Running Modulation-based applications
|
522
|
+
|
523
|
+
Modulation provides a binary script for running Modulation-based applications.
|
524
|
+
`mdl` is a wrapper around Ruby that loads your application's main file as a
|
525
|
+
module, and then runs your application's entry point method. Let's look at a
|
526
|
+
sample application:
|
527
|
+
|
528
|
+
*app.rb*
|
529
|
+
```ruby
|
530
|
+
def greet(name)
|
531
|
+
puts "Hello, #{name}!"
|
532
|
+
end
|
533
|
+
|
534
|
+
def main
|
535
|
+
print "Enter your name: "
|
536
|
+
name = gets
|
537
|
+
greet(name)
|
538
|
+
end
|
539
|
+
```
|
540
|
+
|
541
|
+
To run this application, execute `mdl app.rb`, or `mdl run app.rb`. `mdl` will
|
542
|
+
automatically require the `modulation` gem and call the application's entry
|
543
|
+
point, `#main`.
|
544
|
+
|
545
|
+
## Packing applications with Modulation
|
546
|
+
|
547
|
+
> *Note*: application packing is at the present time an experimental feature.
|
548
|
+
> There might be security concerns for packaging your app, such as leaking
|
549
|
+
> filenames from the developer's machine.
|
550
|
+
|
551
|
+
Modulation can also be used to package your entire application into a single
|
552
|
+
portable file that can be copied to another machine and run as is. To package
|
553
|
+
your app, use `mdl pack`. This command will perform a dynamic analysis of all
|
554
|
+
the app's dependencies and will put them together into a single Ruby file.
|
555
|
+
For more information have a look at the [app](examples/app) example.
|
556
|
+
|
520
557
|
## Writing gems using Modulation
|
521
558
|
|
522
559
|
Modulation can be used to write gems, providing fine-grained control over your
|
@@ -553,6 +590,48 @@ MyFeature = import 'my_gem/my_feature'
|
|
553
590
|
> use Modulation to export symbols, Modulation will refuse to import any gem
|
554
591
|
> that does not depend on Modulation.
|
555
592
|
|
593
|
+
## Writing modules that patch external classes or modules
|
594
|
+
|
595
|
+
It is generally recommended you refrain from causing side effects or patching
|
596
|
+
external code in your modules. When you do have to patch external classes or
|
597
|
+
modules (i.e. core, stdlib, or some third-party code) in your module, it's
|
598
|
+
useful to remember that any module may be eventually reloaded by the application
|
599
|
+
code. This means that any patching done during the loading of your module must
|
600
|
+
be [idempotent](https://en.wikipedia.org/wiki/Idempotence), i.e. have the same
|
601
|
+
effect when performed multiple times. Take for example the following module
|
602
|
+
code:
|
603
|
+
|
604
|
+
```ruby
|
605
|
+
module ::Kernel
|
606
|
+
# aliasing #sleep more than once will break your code
|
607
|
+
alias_method :orig_sleep, :sleep
|
608
|
+
|
609
|
+
def sleep(duration)
|
610
|
+
STDERR.puts "Going to sleep..."
|
611
|
+
orig_sleep(duration)
|
612
|
+
STDERR.puts "Woke up!"
|
613
|
+
end
|
614
|
+
end
|
615
|
+
```
|
616
|
+
|
617
|
+
Running the above code more than once would cause an infinite loop when calling
|
618
|
+
`Kernel#sleep`. In order to prevent this situation, modulation provides the
|
619
|
+
`Module#alias_method_once` method, which prevents aliasing the original method
|
620
|
+
more than once:
|
621
|
+
|
622
|
+
```ruby
|
623
|
+
module ::Kernel
|
624
|
+
# alias_method_once is idempotent
|
625
|
+
alias_method_once :orig_sleep, :sleep
|
626
|
+
|
627
|
+
def sleep(duration)
|
628
|
+
STDERR.puts "Going to sleep..."
|
629
|
+
orig_sleep(duration)
|
630
|
+
STDERR.puts "Woke up!"
|
631
|
+
end
|
632
|
+
end
|
633
|
+
```
|
634
|
+
|
556
635
|
## Coding style recommendations
|
557
636
|
|
558
637
|
* Import modules into constants, not variables:
|
data/bin/mdl
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'modulation'
|
6
|
+
|
7
|
+
def self.run
|
8
|
+
ARGV.each do |arg|
|
9
|
+
fn, method = (arg =~ /^([^\:]+)\:(.+)$/) ? [$1, $2.to_sym] : [arg, :main]
|
10
|
+
mod = import(File.expand_path(fn))
|
11
|
+
mod.send(method) if method
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def collect_deps(fn, paths)
|
16
|
+
if File.directory?(fn)
|
17
|
+
Dir["#{fn}/**/*.rb"].each { |fn| collect_deps(fn, paths) }
|
18
|
+
else
|
19
|
+
paths << File.expand_path(fn)
|
20
|
+
mod = import(File.expand_path(fn))
|
21
|
+
if mod.respond_to?(:__traverse_dependencies)
|
22
|
+
mod.__traverse_dependencies { |m| paths << m.__module_info[:location] }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.deps
|
28
|
+
paths = []
|
29
|
+
ARGV.each { |arg| collect_deps(arg, paths) }
|
30
|
+
puts *paths
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.pack
|
34
|
+
paths = []
|
35
|
+
ARGV.each do |arg|
|
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)
|
44
|
+
end
|
45
|
+
|
46
|
+
cmd = ARGV.shift
|
47
|
+
respond_to?(cmd.to_sym) ? send(cmd) : (ARGV.unshift(cmd); run)
|
data/lib/modulation/builder.rb
CHANGED
@@ -0,0 +1,89 @@
|
|
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
|
data/lib/modulation/paths.rb
CHANGED
@@ -4,6 +4,12 @@ module Modulation
|
|
4
4
|
# Implements methods for expanding relative or incomplete module file names
|
5
5
|
module Paths
|
6
6
|
class << self
|
7
|
+
TAGGED_REGEXP = /^@(.+)$/.freeze
|
8
|
+
|
9
|
+
def tagged_path(path)
|
10
|
+
path[TAGGED_REGEXP, 1]
|
11
|
+
end
|
12
|
+
|
7
13
|
# Regexp for extracting filename from caller reference
|
8
14
|
CALLER_FILE_REGEXP = /^([^\:]+)\:/.freeze
|
9
15
|
|
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.27'
|
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-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -44,7 +44,7 @@ description: " Modulation provides an alternative way of organizing your Ruby c
|
|
44
44
|
global namespace with a myriad modules, or complex multi-level nested\nmodule hierarchies.\n"
|
45
45
|
email: ciconia@gmail.com
|
46
46
|
executables:
|
47
|
-
-
|
47
|
+
- mdl
|
48
48
|
extensions: []
|
49
49
|
extra_rdoc_files:
|
50
50
|
- README.md
|
@@ -52,7 +52,7 @@ extra_rdoc_files:
|
|
52
52
|
files:
|
53
53
|
- CHANGELOG.md
|
54
54
|
- README.md
|
55
|
-
- bin/
|
55
|
+
- bin/mdl
|
56
56
|
- lib/modulation.rb
|
57
57
|
- lib/modulation/builder.rb
|
58
58
|
- lib/modulation/core.rb
|
@@ -61,6 +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/packing.rb
|
64
65
|
- lib/modulation/paths.rb
|
65
66
|
- lib/modulation/version.rb
|
66
67
|
homepage: http://github.com/digital-fabric/modulation
|