Module: Mxrb::RubyApp

Defined in:
lib/mxrb/ruby_app.rb,
lib/mxrb/ruby_app/preset.rb,
lib/mxrb/ruby_app/exporter.rb,
lib/mxrb/ruby_app/session_manager.rb

Overview

Runtime and reversible metadata contract for projects exported with mxrb export --mode ruby.

Defined Under Namespace

Modules: Registry Classes: Application, AuthenticationError, DTO, Exporter, Manifest, NativeBridge, Page, Preset, RackAdapter, Record, Server, Service, SessionManager, Supervisor, Synchronizer

Constant Summary collapse

MANIFEST_PATH =

rubocop:disable Metrics

File.join('.mxrb', 'ruby-app.json')
SOURCE_GLOBS =
[
  'app/**/*.rb', 'config/**/*', 'frontend/**/*', 'spec/**/*.rb',
  'db/migrate/**/*', 'bin/**/*', '.rspec', 'config.ru',
  'Gemfile', 'Rakefile', 'README.md', '.gitignore', '.env.example'
].freeze
SOURCE_EXCLUSIONS =
%w[frontend/node_modules/ frontend/dist/].freeze
ARTIFACT_DIRECTORIES =
%w[models dtos services pages].freeze

Class Method Summary collapse

Class Method Details

.application_files(root) ⇒ Object



25
26
27
28
29
# File 'lib/mxrb/ruby_app.rb', line 25

def self.application_files(root)
  ARTIFACT_DIRECTORIES.flat_map do |directory|
    Dir.glob(File.join(root, 'app', directory, '**', '*.rb'))
  end.sort
end

.compile(root, target = nil, mendix_version: nil) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/mxrb/ruby_app.rb', line 64

def self.compile(root, target = nil, mendix_version: nil)
  manifest = Manifest.load(root)
  destination = File.expand_path(
    target || ENV.fetch('MXRB_OUTPUT_PATH', File.join(root, 'build', manifest.mpr_name))
  )
  previous = ENV['MXRB_OUTPUT_PATH']
  ENV['MXRB_OUTPUT_PATH'] = destination
  load manifest.absolute_path('mendix_project')
  transition(destination, mendix_version) if mendix_version
  Synchronizer.new(root, destination, manifest:).synchronize!
  destination
ensure
  if previous
    ENV['MXRB_OUTPUT_PATH'] = previous
  else
    ENV.delete('MXRB_OUTPUT_PATH')
  end
end

.safe_source_mode(value, relative) ⇒ Object

Raises:



56
57
58
59
60
61
62
# File 'lib/mxrb/ruby_app.rb', line 56

def self.safe_source_mode(value, relative)
  fallback = relative.to_s.start_with?('bin/') ? 0o755 : 0o644
  mode = Integer(value || fallback)
  raise ValidationError, "unsafe embedded Ruby source mode: #{mode}" unless mode.between?(0, 0o777)

  mode
end

.safe_source_path(root, relative) ⇒ Object

Raises:



48
49
50
51
52
53
54
# File 'lib/mxrb/ruby_app.rb', line 48

def self.safe_source_path(root, relative)
  clean = Pathname.new(relative.to_s).cleanpath
  raise ValidationError, "unsafe embedded Ruby source path: #{relative}" \
    if clean.absolute? || clean.to_s == '..' || clean.to_s.start_with?('../')

  File.join(File.expand_path(root), clean.to_s)
end

.source_bundle(root) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/mxrb/ruby_app.rb', line 31

def self.source_bundle(root)
  expanded = File.expand_path(root)
  candidates = SOURCE_GLOBS.flat_map { Dir.glob(File.join(expanded, _1), File::FNM_DOTMATCH) }
                           .select { File.file?(_1) }
  files = candidates.filter_map do |path|
    relative = Pathname.new(path).relative_path_from(Pathname.new(expanded)).cleanpath.to_s
    next if SOURCE_EXCLUSIONS.any? { relative.start_with?(_1) }

    contents = File.binread(path)
    {
      path: relative, contents:, sha256: Digest::SHA256.hexdigest(contents),
      mode: File.stat(path).mode & 0o777
    }
  end
  files.uniq { _1.fetch(:path) }.sort_by { _1.fetch(:path) }
end

.transition(path, version) ⇒ Object



83
84
85
86
87
88
# File 'lib/mxrb/ruby_app.rb', line 83

def self.transition(path, version)
  project = Model::Project.open(path, readonly: false)
  project.migrate_to!(version) unless project.mendix_version == version.to_s
ensure
  project&.close
end