modulation 0.31 → 1.0.1

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.
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ export :run, :transform_module_info
4
+
5
+ require 'zlib'
6
+
7
+ def run(data, dict_offset)
8
+ setup(data, dict_offset)
9
+ import(@dictionary[:entry_point]).send(:main)
10
+ end
11
+
12
+ def setup(data, dict_offset)
13
+ patch_builder
14
+ @data = data
15
+ @data_offset = data.pos
16
+ @dictionary = read_dictionary(dict_offset)
17
+ end
18
+
19
+ def patch_builder
20
+ class << Modulation::Builder
21
+ alias_method :orig_make, :make
22
+ def make(info)
23
+ info = MODULE.transform_module_info(info)
24
+ orig_make(info)
25
+ end
26
+ end
27
+ end
28
+
29
+ def transform_module_info(info)
30
+ location = info[:location]
31
+ info[:source] = read_file(location) if location
32
+ info
33
+ end
34
+
35
+ def find(path)
36
+ @dictionary[path]
37
+ end
38
+
39
+ def read_dictionary(offset)
40
+ @data.seek(@data_offset + offset)
41
+ eval Zlib::Inflate.inflate(@data.read)
42
+ end
43
+
44
+ def read_file(path)
45
+ (offset, length) = @dictionary[path]
46
+ @data.seek(@data_offset + offset)
47
+ Zlib::Inflate.inflate(@data.read(length))
48
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ export_default :CLI
4
+
5
+ # Command line interface functionality
6
+ class CLI
7
+ def initialize(argv)
8
+ @argv = argv
9
+
10
+ process
11
+ end
12
+
13
+ def process
14
+ cmd = ARGV.shift
15
+ if respond_to? cmd.to_sym
16
+ send cmd
17
+ else
18
+ ARGV.unshift cmd
19
+ run
20
+ end
21
+ end
22
+
23
+ def run
24
+ @argv.each { |arg| run_file arg }
25
+ rescue StandardError => e
26
+ cleanup_backtrace(e)
27
+ raise e
28
+ end
29
+
30
+ def run_file(arg)
31
+ fn, method = filename_and_method_from_arg(arg)
32
+ mod = import(File.expand_path(fn))
33
+ mod.send(method) if method
34
+ end
35
+
36
+ FILENAME_AND_METHOD_RE = /^([^\:]+)\:(.+)$/.freeze
37
+
38
+ def filename_and_method_from_arg(arg)
39
+ if arg =~ FILENAME_AND_METHOD_RE
40
+ match = Regexp.last_match
41
+ [match[1], match[2].to_sym]
42
+ else
43
+ [arg, :main]
44
+ end
45
+ end
46
+
47
+ BACKTRACE_RE = /^(#{Modulation::DIR})|(bin\/mdl)/.freeze
48
+
49
+ def cleanup_backtrace(error)
50
+ backtrace = error.backtrace.reject { |l| l =~ BACKTRACE_RE }
51
+ error.set_backtrace(backtrace)
52
+ end
53
+
54
+ def collect_deps(path, array)
55
+ if File.directory?(path)
56
+ Dir["#{path}/**/*.rb"].each { |fn| collect_deps(fn, array) }
57
+ else
58
+ array << File.expand_path(path)
59
+ mod = import(File.expand_path(path))
60
+ if mod.respond_to?(:__traverse_dependencies)
61
+ mod.__traverse_dependencies { |m| array << m.__module_info[:location] }
62
+ end
63
+ end
64
+ end
65
+
66
+ def deps
67
+ paths = []
68
+ @argv.each { |arg| collect_deps(arg, paths) }
69
+ puts(*paths)
70
+ end
71
+
72
+ def pack
73
+ STDOUT << import('@modulation/packer').pack(@argv, hide_filenames: true)
74
+ end
75
+
76
+ def version
77
+ puts "Modulation version #{Modulation::VERSION}"
78
+ end
79
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ export :from_block,
4
+ :from_hash,
5
+ :from_string
6
+
7
+ RE_ATTR = /^@(.+)$/.freeze
8
+
9
+ def from_block(block)
10
+ Module.new.tap { |m| m.instance_eval(&block) }
11
+ end
12
+
13
+ # Creates a module from a prototype hash
14
+ # @param hash [Hash] prototype hash
15
+ # @return [Module] created object
16
+ def from_hash(hash)
17
+ Module.new.tap do |m|
18
+ s = m.singleton_class
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 }
32
+ end
33
+ end
34
+
35
+ def from_string(str)
36
+ Modulation::Builder.make(source: str)
37
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ export :pack
4
+
5
+ require 'modulation/version'
6
+ require 'zlib'
7
+
8
+ BOOTSTRAP_CODE = <<~SRC
9
+ # encoding: ASCII-8BIT
10
+ require 'bundler/inline'
11
+
12
+ gemfile do
13
+ source 'https://rubygems.org'
14
+ gem 'modulation', '~> %<modulation_version>s'
15
+ end
16
+
17
+ import('@modulation/bootstrap').run(DATA, %<dict_offset>d)
18
+ __END__
19
+ %<data>s
20
+ SRC
21
+
22
+ def pack(paths, _options = {})
23
+ paths = [paths] unless paths.is_a?(Array)
24
+ entry_point_filename = File.expand_path(paths.first)
25
+
26
+ deps = collect_dependencies(paths)
27
+ package_info = generate_packed_data(deps, entry_point_filename)
28
+ generate_bootstrap(package_info, entry_point_filename)
29
+ end
30
+
31
+ def collect_dependencies(paths)
32
+ paths.each_with_object([]) do |fn, deps|
33
+ mod = import(File.expand_path(fn))
34
+ deps << File.expand_path(fn)
35
+ mod.__traverse_dependencies { |m| deps << m.__module_info[:location] }
36
+ end
37
+ end
38
+
39
+ def generate_packed_data(deps, entry_point_filename)
40
+ files = deps.each_with_object({}) do |path, dict|
41
+ dict[path] = IO.read(path)
42
+ end
43
+ pack_files(files, entry_point_filename)
44
+ end
45
+
46
+ def pack_files(files, entry_point_filename)
47
+ dictionary = { entry_point: entry_point_filename }
48
+ data = (+'').encode('ASCII-8BIT')
49
+ last_offset = 0
50
+ files.each_with_object(dictionary) do |(path, content), dict|
51
+ last_offset = add_packed_file(path, content, data, dict, last_offset)
52
+ end
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
61
+
62
+ data << zipped
63
+ dict[path] = [last_offset, size]
64
+ last_offset + size
65
+ end
66
+
67
+ def generate_bootstrap(package_info, _entry_point)
68
+ format(
69
+ bootstrap_template,
70
+ modulation_version: Modulation::VERSION,
71
+ dict_offset: package_info[:dict_offset],
72
+ data: package_info[:data]
73
+ )
74
+ end
75
+
76
+ def bootstrap_template
77
+ BOOTSTRAP_CODE.encode('ASCII-8BIT').gsub(/^\s+/, '').chomp
78
+ end
@@ -5,7 +5,8 @@ module Modulation
5
5
  module Paths
6
6
  class << self
7
7
  def process(path, caller_location)
8
- tagged_path(path) || absolute_path(path, caller_location) ||
8
+ path = expand_tag(path)
9
+ absolute_path(path, caller_location) ||
9
10
  lookup_gem_path(path)
10
11
  end
11
12
 
@@ -13,27 +14,32 @@ module Modulation
13
14
  CALLER_FILE_REGEXP = /^([^\:]+)\:?/.freeze
14
15
  TAGGED_REGEXP = /^@([^\/]+)(\/.+)?$/.freeze
15
16
 
16
- def add_tags(tags, caller_location)
17
+ def tags
18
+ @tags ||= {
19
+ 'modulation' => modules_path
20
+ }
21
+ end
22
+
23
+ def modules_path
24
+ File.join(Modulation::DIR, 'modulation/modules')
25
+ end
26
+
27
+ def add_tags(new_tags, caller_location)
17
28
  caller_file = caller_location[CALLER_FILE_REGEXP, 1]
18
29
  caller_dir = caller_file ? File.dirname(caller_file) : nil
19
30
 
20
- @tags ||= {}
21
- tags.each do |k, path|
22
- @tags[k.to_s] = caller_dir ? File.expand_path(path, caller_dir) : path
31
+ new_tags.each do |k, path|
32
+ tags[k.to_s] = caller_dir ? File.expand_path(path, caller_dir) : path
23
33
  end
24
34
  end
25
35
 
26
- def tagged_path(path)
27
- return nil unless @tags
36
+ RE_TAG = /^@([^\/]+)/.freeze
28
37
 
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)
38
+ def expand_tag(path)
39
+ path.sub RE_TAG do
40
+ tag = Regexp.last_match[1]
41
+ tags[tag] || (raise "Invalid tag #{tag}")
42
+ end
37
43
  end
38
44
 
39
45
  # Resolves the absolute path to the provided reference. If the file is not
@@ -54,6 +60,7 @@ module Modulation
54
60
  # @param caller_location [String] caller location
55
61
  # @return [String] absolute directory path
56
62
  def absolute_dir_path(path, caller_location)
63
+ path = expand_tag(path)
57
64
  caller_file = caller_location[CALLER_FILE_REGEXP, 1]
58
65
  return nil unless caller_file
59
66
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Modulation
4
- VERSION = '0.31'
4
+ VERSION = '1.0.1'
5
5
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modulation
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.31'
4
+ version: 1.0.1
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-28 00:00:00.000000000 Z
11
+ date: 2021-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: minitest
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,12 +84,16 @@ files:
56
84
  - lib/modulation.rb
57
85
  - lib/modulation/builder.rb
58
86
  - lib/modulation/core.rb
59
- - lib/modulation/default_export.rb
87
+ - lib/modulation/export_default.rb
88
+ - lib/modulation/export_from_receiver.rb
60
89
  - lib/modulation/exports.rb
61
90
  - lib/modulation/ext.rb
62
91
  - lib/modulation/gem.rb
63
92
  - lib/modulation/module_mixin.rb
64
- - lib/modulation/packer.rb
93
+ - lib/modulation/modules/bootstrap.rb
94
+ - lib/modulation/modules/cli.rb
95
+ - lib/modulation/modules/creator.rb
96
+ - lib/modulation/modules/packer.rb
65
97
  - lib/modulation/paths.rb
66
98
  - lib/modulation/version.rb
67
99
  homepage: http://github.com/digital-fabric/modulation
@@ -88,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
120
  - !ruby/object:Gem::Version
89
121
  version: '0'
90
122
  requirements: []
91
- rubygems_version: 3.0.3
123
+ rubygems_version: 3.0.8
92
124
  signing_key:
93
125
  specification_version: 4
94
126
  summary: 'Modulation: explicit dependency management for Ruby'
@@ -1,105 +0,0 @@
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
11
- # encoding: ASCII-8BIT
12
- require 'bundler/inline'
13
-
14
- gemfile do
15
- source 'https://rubygems.org'
16
- gem 'modulation', '~> %<modulation_version>s', require: 'modulation/packer'
17
- end
18
-
19
- Modulation::Bootstrap.setup(DATA, %<dictionary>s)
20
- import(%<entry_point>s).send(:main)
21
- __END__
22
- %<data>s
23
- SRC
24
-
25
- def self.pack(paths, _options = {})
26
- paths = [paths] unless paths.is_a?(Array)
27
- deps = collect_dependencies(paths)
28
- entry_point_filename = File.expand_path(paths.first)
29
- dictionary, data = generate_packed_data(deps)
30
- generate_bootstrap(dictionary, data, entry_point_filename)
31
- end
32
-
33
- def self.collect_dependencies(paths)
34
- paths.each_with_object([]) do |fn, deps|
35
- mod = import(File.expand_path(fn))
36
- deps << File.expand_path(fn)
37
- mod.__traverse_dependencies { |m| deps << m.__module_info[:location] }
38
- end
39
- end
40
-
41
- def self.generate_packed_data(deps)
42
- files = deps.each_with_object({}) do |path, dict|
43
- dict[path] = IO.read(path)
44
- end
45
- pack_files(files)
46
- end
47
-
48
- def self.pack_files(files)
49
- data = (+'').encode('ASCII-8BIT')
50
- last_offset = 0
51
- dictionary = files.each_with_object({}) do |(path, content), dict|
52
- zipped = Zlib::Deflate.deflate(content)
53
- size = zipped.bytesize
54
-
55
- data << zipped
56
- dict[path] = [last_offset, size]
57
- last_offset += size
58
- end
59
- [dictionary, data]
60
- end
61
-
62
- def self.generate_bootstrap(dictionary, data, entry_point)
63
- format(bootstrap_template, modulation_version: Modulation::VERSION,
64
- dictionary: dictionary.inspect,
65
- entry_point: entry_point.inspect,
66
- data: data)
67
- end
68
-
69
- def self.bootstrap_template
70
- BOOTSTRAP_CODE.encode('ASCII-8BIT').gsub(/^\s+/, '').chomp
71
- end
72
- end
73
-
74
- # Packed app bootstrapping code
75
- module Bootstrap
76
- def self.setup(data, dictionary)
77
- patch_builder
78
- @data = data
79
- @data_offset = data.pos
80
- @dictionary = dictionary
81
- end
82
-
83
- def self.patch_builder
84
- class << Modulation::Builder
85
- alias_method :orig_make, :make
86
- def make(info)
87
- if Modulation::Bootstrap.find(info[:location])
88
- info[:source] = Modulation::Bootstrap.read(info[:location])
89
- end
90
- orig_make(info)
91
- end
92
- end
93
- end
94
-
95
- def self.find(path)
96
- @dictionary[path]
97
- end
98
-
99
- def self.read(path)
100
- (offset, length) = @dictionary[path]
101
- @data.seek(@data_offset + offset)
102
- Zlib::Inflate.inflate(@data.read(length))
103
- end
104
- end
105
- end