Top Level Namespace

Defined Under Namespace

Modules: Kernel, Modulation Classes: CLI, Module

Constant Summary collapse

BOOTSTRAP_CODE =
<<~SRC
  # encoding: ASCII-8BIT
  require 'bundler/inline'

  gemfile do
    source 'https://rubygems.org'
    gem 'modulation', '~> %<modulation_version>s'
  end

  import('@modulation/bootstrap').run(DATA, %<dict_offset>d)
  __END__
  %<data>s
SRC
RE_ATTR =
/^@(.+)$/.freeze

Instance Method Summary collapse

Instance Method Details

#add_packed_file(path, content, data, dict, last_offset) ⇒ Object



58
59
60
61
62
63
64
65
# File 'lib/modulation/modules/packer.rb', line 58

def add_packed_file(path, content, data, dict, last_offset)
  zipped = Zlib::Deflate.deflate(content)
  size = zipped.bytesize

  data << zipped
  dict[path] = [last_offset, size]
  last_offset + size
end

#bootstrap_templateObject



76
77
78
# File 'lib/modulation/modules/packer.rb', line 76

def bootstrap_template
  BOOTSTRAP_CODE.encode('ASCII-8BIT').gsub(/^\s+/, '').chomp
end

#collect_dependencies(paths) ⇒ Object



31
32
33
34
35
36
37
# File 'lib/modulation/modules/packer.rb', line 31

def collect_dependencies(paths)
  paths.each_with_object([]) do |fn, deps|
    mod = import(File.expand_path(fn))
    deps << File.expand_path(fn)
    mod.__traverse_dependencies { |m| deps << m.__module_info[:location] }
  end
end

#find(path) ⇒ Object



35
36
37
# File 'lib/modulation/modules/bootstrap.rb', line 35

def find(path)
  @dictionary[path]
end

#from_block(block) ⇒ Object



9
10
11
# File 'lib/modulation/modules/creator.rb', line 9

def from_block(block)
  Module.new.tap { |m| m.instance_eval(&block) }
end

#from_hash(hash) ⇒ Module

Creates a module from a prototype hash

Parameters:

  • hash (Hash)

    prototype hash

Returns:



16
17
18
19
20
21
# File 'lib/modulation/modules/creator.rb', line 16

def from_hash(hash)
  Module.new.tap do |m|
    s = m.singleton_class
    hash.each { |k, v| process_hash_entry(k, v, m, s) }
  end
end

#from_string(str) ⇒ Object



35
36
37
# File 'lib/modulation/modules/creator.rb', line 35

def from_string(str)
  Modulation::Builder.make(source: str)
end

#generate_bootstrap(package_info, _entry_point) ⇒ Object



67
68
69
70
71
72
73
74
# File 'lib/modulation/modules/packer.rb', line 67

def generate_bootstrap(package_info, _entry_point)
  format(
    bootstrap_template,
    modulation_version: Modulation::VERSION,
    dict_offset:        package_info[:dict_offset],
    data:               package_info[:data]
  )
end

#generate_packed_data(deps, entry_point_filename) ⇒ Object



39
40
41
42
43
44
# File 'lib/modulation/modules/packer.rb', line 39

def generate_packed_data(deps, entry_point_filename)
  files = deps.each_with_object({}) do |path, dict|
    dict[path] = IO.read(path)
  end
  pack_files(files, entry_point_filename)
end

#pack(paths, _options = {}) ⇒ Object



22
23
24
25
26
27
28
29
# File 'lib/modulation/modules/packer.rb', line 22

def pack(paths, _options = {})
  paths = [paths] unless paths.is_a?(Array)
  entry_point_filename = File.expand_path(paths.first)

  deps = collect_dependencies(paths)
  package_info = generate_packed_data(deps, entry_point_filename)
  generate_bootstrap(package_info, entry_point_filename)
end

#pack_files(files, entry_point_filename) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
# File 'lib/modulation/modules/packer.rb', line 46

def pack_files(files, entry_point_filename)
  dictionary = { entry_point: entry_point_filename }
  data = (+'').encode('ASCII-8BIT')
  last_offset = 0
  files.each_with_object(dictionary) do |(path, content), dict|
    last_offset = add_packed_file(path, content, data, dict, last_offset)
  end
  data << Zlib::Deflate.deflate(dictionary.inspect)

  { dict_offset: last_offset, data: data }
end

#patch_builderObject



19
20
21
22
23
24
25
26
27
# File 'lib/modulation/modules/bootstrap.rb', line 19

def patch_builder
  class << Modulation::Builder
    alias_method :orig_make, :make
    def make(info)
      info = MODULE.transform_module_info(info)
      orig_make(info)
    end
  end
end

#process_hash_entry(key, value, mod, singleton) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/modulation/modules/creator.rb', line 23

def process_hash_entry(key, value, mod, singleton)
  if key =~ Modulation::RE_CONST
    mod.const_set(key, value)
  elsif key =~ RE_ATTR
    mod.instance_variable_set(key, value)
  elsif value.respond_to?(:to_proc)
    singleton.send(:define_method, key) { |*args| instance_exec(*args, &value) }
  else
    singleton.send(:define_method, key) { value }
  end
end

#read_dictionary(offset) ⇒ Object



39
40
41
42
# File 'lib/modulation/modules/bootstrap.rb', line 39

def read_dictionary(offset)
  @data.seek(@data_offset + offset)
  eval Zlib::Inflate.inflate(@data.read)
end

#read_file(path) ⇒ Object



44
45
46
47
48
# File 'lib/modulation/modules/bootstrap.rb', line 44

def read_file(path)
  (offset, length) = @dictionary[path]
  @data.seek(@data_offset + offset)
  Zlib::Inflate.inflate(@data.read(length))
end

#run(data, dict_offset) ⇒ Object



7
8
9
10
# File 'lib/modulation/modules/bootstrap.rb', line 7

def run(data, dict_offset)
  setup(data, dict_offset)
  import(@dictionary[:entry_point]).send(:main)
end

#setup(data, dict_offset) ⇒ Object



12
13
14
15
16
17
# File 'lib/modulation/modules/bootstrap.rb', line 12

def setup(data, dict_offset)
  patch_builder
  @data = data
  @data_offset = data.pos
  @dictionary = read_dictionary(dict_offset)
end

#transform_module_info(info) ⇒ Object



29
30
31
32
33
# File 'lib/modulation/modules/bootstrap.rb', line 29

def transform_module_info(info)
  location = info[:location]
  info[:source] = read_file(location) if location
  info
end