modulation 0.31 → 1.0.1

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