modulation 0.34 → 1.0
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 -2
- data/README.md +60 -20
- data/lib/modulation/module_mixin.rb +5 -5
- data/lib/modulation/modules/bootstrap.rb +3 -4
- data/lib/modulation/modules/cli.rb +10 -9
- data/lib/modulation/modules/creator.rb +15 -13
- data/lib/modulation/modules/packer.rb +14 -16
- data/lib/modulation/paths.rb +5 -1
- data/lib/modulation/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a81a52fa3760bfbda94193a1e1de70bd498dd8112455ec774fc9b5735bf8fbfa
|
4
|
+
data.tar.gz: 37e1ca88843461ccc8e3d137e4c686d3969641a381210ec7cb40a0215ec184c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f57985001fea20c87d1b8a7a1e06244fd21a52a5e1830177ee3d379e9cb81913a03dde161fdcc78fe4d7f14d8a486def00058cca2b01b8f8e233cb70b413107
|
7
|
+
data.tar.gz: 0cbc73dc2b7684a7b63fe50c6d505175f78b894fa0b043991ae4591cfc38e1a3849c3ac6c4b7f50e1cdb190f8570e6e00cd4405cb3f95d9494aff7485cf64437
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
1.0 2019-10-18
|
2
|
+
--------------
|
3
|
+
|
4
|
+
* Cleanup code
|
5
|
+
* Obfuscate bootstrap dictionary code in packages
|
6
|
+
* Move packer, bootstrap, CLI, creator code into stock modules
|
7
|
+
|
1
8
|
0.34 2019-10-14
|
2
9
|
---------------
|
3
10
|
|
4
|
-
* Obfuscate bootstrapping code in packages
|
5
|
-
* Move packer, bootstrap, CLI, creator code into stock modules
|
6
11
|
* Improve README
|
7
12
|
|
8
13
|
0.33 2019-10-02
|
data/README.md
CHANGED
@@ -34,11 +34,13 @@ a functional style, minimizing boilerplate code.
|
|
34
34
|
[importing](#importing-declarations) of methods and constants** lets you
|
35
35
|
control the public interface for each module, as well as keep track of all
|
36
36
|
dependencies in your code.
|
37
|
+
- **[Tagged paths](#using-tags-to-designate-common-subdirectories)** simplify
|
38
|
+
the management of dependencies in large applications.
|
37
39
|
- **[Lazy Loading](#lazy-loading)** improves start up time and memory
|
38
40
|
consumption.
|
39
41
|
- **[Hot module reloading](#reloading-modules)** streamlines your development
|
40
42
|
process.
|
41
|
-
- **[
|
43
|
+
- **[Module mocking](#mocking-modules)** facilitates testing.
|
42
44
|
- **[Dependency introspection](#dependency-introspection)** lets you introspect
|
43
45
|
your dependencies at runtime.
|
44
46
|
- **[Application packing](#packing-applications-with-modulation)** lets you
|
@@ -472,7 +474,7 @@ class FibTest < Minitest::Test
|
|
472
474
|
end
|
473
475
|
```
|
474
476
|
|
475
|
-
### Mocking
|
477
|
+
### Mocking modules
|
476
478
|
|
477
479
|
Modules loaded by Modulation can be easily mocked when running tests or specs,
|
478
480
|
using `Modulation.mock`:
|
@@ -503,6 +505,9 @@ class UserControllerTest < Minitest::Test
|
|
503
505
|
end
|
504
506
|
```
|
505
507
|
|
508
|
+
`Modulation.mock` accepts a module path and a receiver, and the module stays
|
509
|
+
mocked within the given block.
|
510
|
+
|
506
511
|
### Lazy Loading
|
507
512
|
|
508
513
|
Modulation allows the use of lazy-loaded modules - loading of modules only once
|
@@ -583,6 +588,30 @@ settings = import('settings')
|
|
583
588
|
settings = settings.__reload!
|
584
589
|
```
|
585
590
|
|
591
|
+
Please note that Modulation does not include a directory watcher that
|
592
|
+
automatically reloads changed modules. This is due to multiple considerations
|
593
|
+
that include the chosen threading model, or the reactor engine in use, or even
|
594
|
+
the chosen solution for watching files (whether it's an external gem or an
|
595
|
+
internal tool).
|
596
|
+
|
597
|
+
It is, however, quite trivial to watch files using
|
598
|
+
[`directory_watcher`](https://rubygems.org/gems/directory_watcher/):
|
599
|
+
|
600
|
+
```ruby
|
601
|
+
require 'directory_watcher'
|
602
|
+
|
603
|
+
dw = DirectoryWatcher.new 'lib', glob: '**/*.rb', interval: 2, pre_load: true
|
604
|
+
dw.add_observer do |*events|
|
605
|
+
events.each do |e|
|
606
|
+
next unless e.type == :modified
|
607
|
+
|
608
|
+
Modulation.reload e.path
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
dw.start
|
613
|
+
```
|
614
|
+
|
586
615
|
### Retaining state between reloads
|
587
616
|
|
588
617
|
Before a module is reloaded, all of its methods and constants are removed. In
|
@@ -780,41 +809,52 @@ end
|
|
780
809
|
|
781
810
|
## API Reference
|
782
811
|
|
783
|
-
|
812
|
+
### Kernel
|
784
813
|
|
785
|
-
#### `
|
814
|
+
#### `Kernel#auto_import_map(path, options = {})`
|
786
815
|
|
787
|
-
|
816
|
+
Returns a hash mapping keys to corresponding module files inside the given
|
817
|
+
directory path. Modules are loaded automatically upon accessing hash keys.
|
788
818
|
|
789
|
-
#### `
|
819
|
+
#### `Kernel#import(path)`
|
790
820
|
|
791
|
-
#### `
|
821
|
+
#### `Kernel#import_all(path)`
|
792
822
|
|
793
|
-
#### `
|
823
|
+
#### `Kernel#import_map(path, options = {})`
|
794
824
|
|
795
|
-
|
825
|
+
### Module
|
796
826
|
|
797
|
-
#### `
|
827
|
+
#### `Module#__module_info`
|
798
828
|
|
799
|
-
|
829
|
+
Returns a hash containing information about the module. This currently includes
|
830
|
+
the following entries:
|
800
831
|
|
801
|
-
|
832
|
+
location|Absolute module file path
|
833
|
+
exported_symbols|Array containing all symbols exported by the module
|
802
834
|
|
803
|
-
|
835
|
+
### `Module#__reload!`
|
804
836
|
|
805
|
-
#### `
|
837
|
+
#### `Module#alias_method_once(new_name, old_name)`
|
806
838
|
|
807
|
-
#### `
|
839
|
+
#### `Module#auto_import(sym, path)`
|
808
840
|
|
809
|
-
#### `
|
841
|
+
#### `Module#export(*symbols)`
|
810
842
|
|
811
|
-
#### `
|
843
|
+
#### `Module#export_default(value)`
|
844
|
+
|
845
|
+
#### `Module#export_from_receiver(receiver)`
|
812
846
|
|
813
|
-
#### `
|
847
|
+
#### `Module#extend_from(path)`
|
814
848
|
|
815
|
-
#### `
|
849
|
+
#### `Module#include_from(path, *symbols)`
|
850
|
+
|
851
|
+
#### `Module::MODULE`
|
852
|
+
|
853
|
+
### Modulation
|
854
|
+
|
855
|
+
#### `Modulation.full_backtrace!`
|
816
856
|
|
817
|
-
#### `
|
857
|
+
#### `Modulation.reload`
|
818
858
|
|
819
859
|
## Why you should not use Modulation
|
820
860
|
|
@@ -31,8 +31,8 @@ module Modulation
|
|
31
31
|
|
32
32
|
@__export_directives ||= []
|
33
33
|
@__export_directives << {
|
34
|
-
method:
|
35
|
-
args:
|
34
|
+
method: :export,
|
35
|
+
args: symbols,
|
36
36
|
export_caller: caller
|
37
37
|
}
|
38
38
|
end
|
@@ -46,8 +46,8 @@ module Modulation
|
|
46
46
|
|
47
47
|
@__export_directives ||= []
|
48
48
|
@__export_directives << {
|
49
|
-
method:
|
50
|
-
args:
|
49
|
+
method: :export_from_receiver,
|
50
|
+
args: name,
|
51
51
|
export_caller: caller
|
52
52
|
}
|
53
53
|
end
|
@@ -113,7 +113,7 @@ module Modulation
|
|
113
113
|
|
114
114
|
def __traverse_dependencies(&block)
|
115
115
|
__dependencies.each do |mod|
|
116
|
-
block.
|
116
|
+
block.(mod)
|
117
117
|
if mod.respond_to?(:__traverse_dependencies)
|
118
118
|
mod.__traverse_dependencies(&block)
|
119
119
|
end
|
@@ -27,9 +27,8 @@ def patch_builder
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def transform_module_info(info)
|
30
|
-
|
31
|
-
|
32
|
-
end
|
30
|
+
location = info[:location]
|
31
|
+
info[:source] = read_file(location) if location
|
33
32
|
info
|
34
33
|
end
|
35
34
|
|
@@ -46,4 +45,4 @@ def read_file(path)
|
|
46
45
|
(offset, length) = @dictionary[path]
|
47
46
|
@data.seek(@data_offset + offset)
|
48
47
|
Zlib::Inflate.inflate(@data.read(length))
|
49
|
-
end
|
48
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
export_default :CLI
|
4
4
|
|
5
|
+
# Command line interface functionality
|
5
6
|
class CLI
|
6
7
|
def initialize(argv)
|
7
8
|
@argv = argv
|
@@ -25,15 +26,15 @@ class CLI
|
|
25
26
|
cleanup_backtrace(e)
|
26
27
|
raise e
|
27
28
|
end
|
28
|
-
|
29
|
+
|
29
30
|
def run_file(arg)
|
30
31
|
fn, method = filename_and_method_from_arg(arg)
|
31
32
|
mod = import(File.expand_path(fn))
|
32
33
|
mod.send(method) if method
|
33
34
|
end
|
34
|
-
|
35
|
+
|
35
36
|
FILENAME_AND_METHOD_RE = /^([^\:]+)\:(.+)$/.freeze
|
36
|
-
|
37
|
+
|
37
38
|
def filename_and_method_from_arg(arg)
|
38
39
|
if arg =~ FILENAME_AND_METHOD_RE
|
39
40
|
match = Regexp.last_match
|
@@ -42,14 +43,14 @@ class CLI
|
|
42
43
|
[arg, :main]
|
43
44
|
end
|
44
45
|
end
|
45
|
-
|
46
|
+
|
46
47
|
BACKTRACE_RE = /^(#{Modulation::DIR})|(bin\/mdl)/.freeze
|
47
|
-
|
48
|
+
|
48
49
|
def cleanup_backtrace(error)
|
49
50
|
backtrace = error.backtrace.reject { |l| l =~ BACKTRACE_RE }
|
50
51
|
error.set_backtrace(backtrace)
|
51
52
|
end
|
52
|
-
|
53
|
+
|
53
54
|
def collect_deps(path, array)
|
54
55
|
if File.directory?(path)
|
55
56
|
Dir["#{path}/**/*.rb"].each { |fn| collect_deps(fn, array) }
|
@@ -61,17 +62,17 @@ class CLI
|
|
61
62
|
end
|
62
63
|
end
|
63
64
|
end
|
64
|
-
|
65
|
+
|
65
66
|
def deps
|
66
67
|
paths = []
|
67
68
|
@argv.each { |arg| collect_deps(arg, paths) }
|
68
69
|
puts(*paths)
|
69
70
|
end
|
70
|
-
|
71
|
+
|
71
72
|
def pack
|
72
73
|
STDOUT << import('@modulation/packer').pack(@argv, hide_filenames: true)
|
73
74
|
end
|
74
|
-
|
75
|
+
|
75
76
|
def version
|
76
77
|
puts "Modulation version #{Modulation::VERSION}"
|
77
78
|
end
|
@@ -4,7 +4,7 @@ export :from_block,
|
|
4
4
|
:from_hash,
|
5
5
|
:from_string
|
6
6
|
|
7
|
-
RE_ATTR
|
7
|
+
RE_ATTR = /^@(.+)$/.freeze
|
8
8
|
|
9
9
|
def from_block(block)
|
10
10
|
Module.new.tap { |m| m.instance_eval(&block) }
|
@@ -16,20 +16,22 @@ end
|
|
16
16
|
def from_hash(hash)
|
17
17
|
Module.new.tap do |m|
|
18
18
|
s = m.singleton_class
|
19
|
-
hash.each
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
19
|
+
hash.each { |k, v| process_hash_entry(k, v, m, s) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_hash_entry(key, value, mod, singleton)
|
24
|
+
if key =~ Modulation::RE_CONST
|
25
|
+
mod.const_set(key, value)
|
26
|
+
elsif key =~ RE_ATTR
|
27
|
+
mod.instance_variable_set(key, value)
|
28
|
+
elsif value.respond_to?(:to_proc)
|
29
|
+
singleton.send(:define_method, key) { |*args| instance_exec(*args, &value) }
|
30
|
+
else
|
31
|
+
singleton.send(:define_method, key) { value }
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
33
35
|
def from_string(str)
|
34
|
-
|
36
|
+
Modulation::Builder.make(source: str)
|
35
37
|
end
|
@@ -44,29 +44,27 @@ def generate_packed_data(deps, entry_point_filename)
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def pack_files(files, entry_point_filename)
|
47
|
+
dictionary = { entry_point: entry_point_filename }
|
47
48
|
data = (+'').encode('ASCII-8BIT')
|
48
49
|
last_offset = 0
|
49
|
-
dictionary = {
|
50
|
-
entry_point: entry_point_filename
|
51
|
-
}
|
52
50
|
files.each_with_object(dictionary) do |(path, content), dict|
|
53
|
-
|
54
|
-
size = zipped.bytesize
|
55
|
-
|
56
|
-
data << zipped
|
57
|
-
dict[path] = [last_offset, size]
|
58
|
-
last_offset += size
|
51
|
+
last_offset = add_packed_file(path, content, data, dict, last_offset)
|
59
52
|
end
|
60
|
-
|
61
|
-
|
53
|
+
data << Zlib::Deflate.deflate(dictionary.inspect)
|
54
|
+
|
55
|
+
{ dict_offset: last_offset, data: data }
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_packed_file(path, content, data, dict, last_offset)
|
59
|
+
zipped = Zlib::Deflate.deflate(content)
|
60
|
+
size = zipped.bytesize
|
62
61
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
}
|
62
|
+
data << zipped
|
63
|
+
dict[path] = [last_offset, size]
|
64
|
+
last_offset + size
|
67
65
|
end
|
68
66
|
|
69
|
-
def generate_bootstrap(package_info,
|
67
|
+
def generate_bootstrap(package_info, _entry_point)
|
70
68
|
format(
|
71
69
|
bootstrap_template,
|
72
70
|
modulation_version: Modulation::VERSION,
|
data/lib/modulation/paths.rb
CHANGED
@@ -16,10 +16,14 @@ module Modulation
|
|
16
16
|
|
17
17
|
def tags
|
18
18
|
@tags ||= {
|
19
|
-
'modulation' =>
|
19
|
+
'modulation' => modules_path
|
20
20
|
}
|
21
21
|
end
|
22
22
|
|
23
|
+
def modules_path
|
24
|
+
File.join(Modulation::DIR, 'modulation/modules')
|
25
|
+
end
|
26
|
+
|
23
27
|
def add_tags(new_tags, caller_location)
|
24
28
|
caller_file = caller_location[CALLER_FILE_REGEXP, 1]
|
25
29
|
caller_dir = caller_file ? File.dirname(caller_file) : nil
|
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: '1.0'
|
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-10-
|
11
|
+
date: 2019-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|