ryac 0.2.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.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +145 -0
  4. data/Rakefile +45 -0
  5. data/Steepfile +9 -0
  6. data/bin/console +11 -0
  7. data/bin/ryac +134 -0
  8. data/bin/setup +8 -0
  9. data/lib/ryac/analysis/constant/collection.rb +332 -0
  10. data/lib/ryac/analysis/constant/rename_mapping.rb +346 -0
  11. data/lib/ryac/analysis/cvar/collection.rb +37 -0
  12. data/lib/ryac/analysis/gvar/collection.rb +36 -0
  13. data/lib/ryac/analysis/gvar/rename_mapping.rb +82 -0
  14. data/lib/ryac/analysis/ivar/collection.rb +281 -0
  15. data/lib/ryac/analysis/keyword/collection.rb +213 -0
  16. data/lib/ryac/analysis/keyword/rename_mapping.rb +167 -0
  17. data/lib/ryac/analysis/local_scopes.rb +582 -0
  18. data/lib/ryac/analysis/method/collection.rb +298 -0
  19. data/lib/ryac/analysis/method/rename_mapping.rb +337 -0
  20. data/lib/ryac/analysis/method_aliases.rb +83 -0
  21. data/lib/ryac/analysis/method_aliasing.rb +65 -0
  22. data/lib/ryac/analysis/nesting.rb +123 -0
  23. data/lib/ryac/analysis/scope_management.rb +25 -0
  24. data/lib/ryac/analysis/site_bucket_mapping.rb +127 -0
  25. data/lib/ryac/analysis/type_oracle.rb +386 -0
  26. data/lib/ryac/ast_utils.rb +321 -0
  27. data/lib/ryac/errors.rb +32 -0
  28. data/lib/ryac/gem_resolver.rb +55 -0
  29. data/lib/ryac/minifier.rb +169 -0
  30. data/lib/ryac/name_generator.rb +68 -0
  31. data/lib/ryac/pipeline/analyzer.rb +264 -0
  32. data/lib/ryac/pipeline/attr_decl_shorten.rb +61 -0
  33. data/lib/ryac/pipeline/boolean_shorten.rb +26 -0
  34. data/lib/ryac/pipeline/char_shorten.rb +20 -0
  35. data/lib/ryac/pipeline/compactor.rb +1202 -0
  36. data/lib/ryac/pipeline/concatenator.rb +248 -0
  37. data/lib/ryac/pipeline/constant_aliaser.rb +240 -0
  38. data/lib/ryac/pipeline/constant_fold.rb +93 -0
  39. data/lib/ryac/pipeline/control_flow_simplify.rb +352 -0
  40. data/lib/ryac/pipeline/data_types.rb +179 -0
  41. data/lib/ryac/pipeline/endless_method.rb +60 -0
  42. data/lib/ryac/pipeline/errors.rb +94 -0
  43. data/lib/ryac/pipeline/file_collector.rb +360 -0
  44. data/lib/ryac/pipeline/method_renamer.rb +151 -0
  45. data/lib/ryac/pipeline/paren_optimizer.rb +161 -0
  46. data/lib/ryac/pipeline/rename_patcher.rb +274 -0
  47. data/lib/ryac/pipeline/source_patcher.rb +54 -0
  48. data/lib/ryac/pipeline/spelling_shorten.rb +60 -0
  49. data/lib/ryac/pipeline/stage.rb +72 -0
  50. data/lib/ryac/pipeline/stage_runner.rb +84 -0
  51. data/lib/ryac/pipeline/variable_renamer.rb +125 -0
  52. data/lib/ryac/union_find.rb +42 -0
  53. data/lib/ryac/version.rb +5 -0
  54. data/lib/ryac.rb +29 -0
  55. data/rbs_collection.lock.yaml +224 -0
  56. data/rbs_collection.yaml +25 -0
  57. data/sig/ryac/analysis/constant/collection.rbs +30 -0
  58. data/sig/ryac/analysis/constant/rename_mapping.rbs +60 -0
  59. data/sig/ryac/analysis/cvar/collection.rbs +11 -0
  60. data/sig/ryac/analysis/gvar/collection.rbs +9 -0
  61. data/sig/ryac/analysis/gvar/rename_mapping.rbs +23 -0
  62. data/sig/ryac/analysis/ivar/collection.rbs +26 -0
  63. data/sig/ryac/analysis/keyword/collection.rbs +27 -0
  64. data/sig/ryac/analysis/keyword/rename_mapping.rbs +31 -0
  65. data/sig/ryac/analysis/local_scopes.rbs +77 -0
  66. data/sig/ryac/analysis/method/collection.rbs +25 -0
  67. data/sig/ryac/analysis/method/rename_mapping.rbs +82 -0
  68. data/sig/ryac/analysis/method_aliases.rbs +10 -0
  69. data/sig/ryac/analysis/method_aliasing.rbs +9 -0
  70. data/sig/ryac/analysis/nesting.rbs +9 -0
  71. data/sig/ryac/analysis/scope_management.rbs +5 -0
  72. data/sig/ryac/analysis/site_bucket_mapping.rbs +26 -0
  73. data/sig/ryac/analysis/type_oracle.rbs +61 -0
  74. data/sig/ryac/ast_utils.rbs +34 -0
  75. data/sig/ryac/errors.rbs +21 -0
  76. data/sig/ryac/gem_resolver.rbs +28 -0
  77. data/sig/ryac/minifier.rbs +33 -0
  78. data/sig/ryac/name_generator.rbs +14 -0
  79. data/sig/ryac/pipeline/analyzer.rbs +34 -0
  80. data/sig/ryac/pipeline/attr_decl_shorten.rbs +17 -0
  81. data/sig/ryac/pipeline/boolean_shorten.rbs +11 -0
  82. data/sig/ryac/pipeline/char_shorten.rbs +11 -0
  83. data/sig/ryac/pipeline/compactor.rbs +125 -0
  84. data/sig/ryac/pipeline/concatenator.rbs +28 -0
  85. data/sig/ryac/pipeline/constant_aliaser.rbs +35 -0
  86. data/sig/ryac/pipeline/constant_fold.rbs +20 -0
  87. data/sig/ryac/pipeline/control_flow_simplify.rbs +34 -0
  88. data/sig/ryac/pipeline/data_types.rbs +151 -0
  89. data/sig/ryac/pipeline/endless_method.rbs +14 -0
  90. data/sig/ryac/pipeline/errors.rbs +46 -0
  91. data/sig/ryac/pipeline/file_collector.rbs +34 -0
  92. data/sig/ryac/pipeline/method_renamer.rbs +25 -0
  93. data/sig/ryac/pipeline/paren_optimizer.rbs +16 -0
  94. data/sig/ryac/pipeline/rename_patcher.rbs +38 -0
  95. data/sig/ryac/pipeline/source_patcher.rbs +12 -0
  96. data/sig/ryac/pipeline/spelling_shorten.rbs +14 -0
  97. data/sig/ryac/pipeline/stage.rbs +28 -0
  98. data/sig/ryac/pipeline/stage_runner.rbs +18 -0
  99. data/sig/ryac/pipeline/variable_renamer.rbs +21 -0
  100. data/sig/ryac/union_find.rbs +17 -0
  101. data/sig/ryac.rbs +13 -0
  102. data/sig/shims/env.rbs +19 -0
  103. data/sig/shims/typeprof/core/ast.rbs +299 -0
  104. data/sig/shims/typeprof/core/type.rbs +22 -0
  105. data/sig/shims/typeprof/core.rbs +73 -0
  106. data/tools/regen_pins.rb +45 -0
  107. metadata +201 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2bddb3f3efea0d09081975f55f29c156ef437180470e5dc08ef30046dd19b995
4
+ data.tar.gz: 062e95d78fa295320ef0ea800d01aa3dff509e9f2c20b341781a06e9ed60671e
5
+ SHA512:
6
+ metadata.gz: 308731a116d5765d712d85be56b819821345847b21cdb1feb591a34071c865f0073addb6e36de31d21c117138e74b6967068fb1e4e5d897176b4a4edff44d5f6
7
+ data.tar.gz: bcd1419c8b24b9f718e20a0b529636b265ecf531689d0468920f101d10f82e10e5019da28ac183a31fe5fa8c072cbd2f00ff1e9f78b5525b916009adf971ad42
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ahogappa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # ryac
2
+
3
+ **ryac** — minify the word *ruby* and you are left with *ry*; this tool then analyzes and compacts, so: **R**ub**y**, **A**nalyzed & **C**ompacted. Pronounced *ryaku*, like 略 — the Japanese word for "abbreviation".
4
+
5
+ A Ruby code minifier that uses [TypeProf](https://github.com/ruby/typeprof) for type-aware analysis and AST-based transformations to achieve high compression rates while preserving functional equivalence.
6
+
7
+ ## Philosophy
8
+
9
+ This project takes an **aggressive optimization** approach. In Ruby, even comment removal requires context awareness — for example, `foo\n# comment\n.to_s` works because the comment connects the method chain, but removing it causes a syntax error. Whether a given comment is safe to remove depends entirely on context, making the boundary between safe and unsafe transformations inherently blurry. Since conservative minification is already difficult in Ruby, we choose to optimize as aggressively as possible. TypeProf's type analysis helps make these transformations more informed, but the goal is always maximum compression.
10
+
11
+ ## Features
12
+
13
+ - **Multi-file support**: Follows `require_relative` and `autoload` to collect and concatenate dependencies into a single output
14
+ - **Whitespace & comment removal**: Strips all unnecessary whitespace and comments
15
+ - **AST transformations**: Boolean/char shortening, constant folding, control flow simplification, endless methods, parenthesis optimization
16
+ - **Constant aliasing**: Renames user-defined constants and shortens repeated external constant paths
17
+ - **Variable renaming**: Shortens local variables, keyword arguments, instance/class/global variables
18
+ - **Method renaming**: Shortens method names with `send(:sym)` coordination and attr-backed ivar optimization
19
+ - **Method alias shortening**: Replaces long stdlib method names with shorter aliases (e.g., `collect` → `map`)
20
+ - **Dead code elimination**: Removes unreachable code after `return`, `break`, `next`, `raise`
21
+ - **Dynamic code detection**: Disables renaming in scopes containing `eval`, `binding`, `send`, etc.
22
+
23
+ ## Installation
24
+
25
+ ```ruby
26
+ gem 'ryac'
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Command Line
32
+
33
+ ```bash
34
+ # Minify a file (follows require_relative automatically)
35
+ bin/ryac path/to/entry.rb
36
+
37
+ # Specify compression level (stable or unstable)
38
+ bin/ryac path/to/entry.rb -c stable
39
+ bin/ryac path/to/entry.rb -c unstable
40
+
41
+ # Write to output file
42
+ bin/ryac path/to/entry.rb -o minified.rb
43
+
44
+ # Write constant aliases to a separate file
45
+ bin/ryac path/to/entry.rb -o minified.rb -a aliases.rb
46
+
47
+ # Multiple entry points
48
+ bin/ryac file1.rb file2.rb
49
+
50
+ # Minify installed gem(s) by name (resolved via Gem::Specification)
51
+ bin/ryac -g rack
52
+ bin/ryac -g rack,rack-session -o bundle.rb
53
+
54
+ # Show version / help
55
+ bin/ryac -v
56
+ bin/ryac -h
57
+ ```
58
+
59
+ ### Ruby API
60
+
61
+ ```ruby
62
+ require 'ryac'
63
+
64
+ minifier = Ryac::Minifier.new
65
+ result = minifier.call('path/to/entry.rb')
66
+
67
+ puts result.content # minified code
68
+ puts result.preamble # external prefix declarations (e.g., "A=TypeProf::Core::AST")
69
+ puts result.aliases # backward-compat alias declarations (e.g., "MyClass=A")
70
+ puts result.stats.file_count # number of files processed
71
+ puts result.stats.compression_ratio # e.g., 0.44 (56% reduction)
72
+
73
+ # Specify optimization level (:stable or :unstable, default: :stable)
74
+ result = minifier.call('path/to/entry.rb', level: :unstable)
75
+ ```
76
+
77
+ ## Optimization Levels
78
+
79
+ There are exactly two levels, named for their promise. The default is **`stable`**.
80
+
81
+ | Level | Transformations | Promise |
82
+ |-------|----------------|---------|
83
+ | `stable` | AST compaction and folding, constant/class/module renaming with compatibility aliases, external prefix aliasing, local/keyword/instance/class/global variable renaming | Verified frame-for-frame on a real program (Optcarrot). Sound under closed-world analysis; reflection over *names the program renames* is the caveat. |
84
+ | `unstable` | + Method renaming, attr-backed ivar coordination | A program can defeat method renaming by construction (names inside strings, `eval`'d source, `send(computed)`), so this works only when the program plays along. Verified by self-hosting. |
85
+
86
+ Finer configurations are not levels: the pipeline is built from steps, and callers can pass an explicit stage list in place of a level name (`Minifier#call(path, level: [...stage defs...])`). The unit tests pin each step's behavior through exactly that mechanism.
87
+
88
+ See [`tests/ryac/pipeline/`](tests/ryac/pipeline/) for per-stage transformation examples, and [`tests/ryac/levels/`](tests/ryac/levels/) for end-to-end compression examples.
89
+
90
+ ### What the levels are verified against
91
+
92
+ The supported boundary is defined by two programs, both verified in CI:
93
+
94
+ - **[Optcarrot](https://github.com/mame/optcarrot) at `stable`** — the minified emulator matches the original frame-for-frame across the 180-frame demo and three scripted play scenarios (1,820 frames of title menus, piece rotation, pausing and button mashing) ([`tests/test_optcarrot.rb`](tests/test_optcarrot.rb))
95
+ - **This minifier itself at `unstable`** — the minified minifier re-minifies the original source to identical output, and minifying its own output is a byte-identical fixed point ([`tests/test_integration.rb`](tests/test_integration.rb))
96
+
97
+ Whether `unstable` holds for a given program depends on the program. Optcarrot stops at `stable` because it defeats method renaming by construction: it builds its CPU/PPU cores as source strings and `eval`s them, scans that text for `@ivar` names with a regexp, and dispatches through `send(computed_symbol)` — method names survive inside strings, out of reach of static analysis. The sinatra and rubocop suites run in CI as regression canaries on a keyword-only step composition, but they sit outside this boundary and do not define it.
98
+
99
+ ## Development
100
+
101
+ ```bash
102
+ bundle install
103
+
104
+ # Run tests (fast, excludes self-hosting)
105
+ rake test
106
+
107
+ # Run all tests including self-hosting
108
+ rake test:all
109
+
110
+ # Run self-hosting test only
111
+ rake test:integration
112
+
113
+ # Run gem integration tests (minifies real gems and runs their test suites)
114
+ rake test:gems
115
+
116
+ # Minify optcarrot at :stable and compare rendered frames against the original
117
+ # (requires gem_tests/optcarrot: git clone https://github.com/mame/optcarrot gem_tests/optcarrot)
118
+ rake test:optcarrot
119
+
120
+ # Show compression ratio on self-hosting
121
+ rake benchmark
122
+ ```
123
+
124
+ ## Architecture
125
+
126
+ The minification pipeline:
127
+
128
+ ```
129
+ FileCollector → Concatenator → StageRunner (Compactor → stage list) → Output
130
+ ```
131
+
132
+ 1. **FileCollector** — Resolves `require_relative` / `autoload` and collects all source files into a dependency graph
133
+ 2. **Concatenator** — Topologically sorts files and concatenates them into a single source
134
+ 3. **StageRunner** — Compacts the source (AST rebuilt into minimal whitespace form), then walks the level's ordered stage list. Every stage implements one contract — `needs_analysis?` / `fixpoint?` / `collect(ctx, patches)` / `finish(ctx)` — and phase is list position:
135
+ - **Syntactic stages** (no analysis): `ControlFlowSimplify`, `EndlessMethod`, `ConstantFold`, `BooleanShorten`, `CharShorten` before the rename batch; `ParenOptimizer` after it
136
+ - **Analysis stages**: `ConstantAliaser`, `AttrDeclShorten`, `VariableRenamer` (plus `MethodRenamer` at `unstable`) — consecutive analysis stages form one batch sharing a single TypeProf pass, and the runner rejects a list that would need two
137
+
138
+ ## Dependencies
139
+
140
+ - [TypeProf](https://github.com/ruby/typeprof) - Type-aware AST analysis
141
+ - [Prism](https://github.com/ruby/prism) - Syntax validation
142
+
143
+ ## License
144
+
145
+ The gem is available as open source under the terms of the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "lib"
8
+ t.test_files = FileList["tests/**/test_*.rb"].exclude("tests/test_integration.rb", "tests/test_fixtures.rb", "tests/test_gem_minification.rb", "tests/test_optcarrot.rb", "tests/_archive/**/*")
9
+ end
10
+
11
+ Rake::TestTask.new("test:all") do |t|
12
+ t.libs << "lib"
13
+ t.test_files = FileList["tests/**/test_*.rb"].exclude("tests/test_fixtures.rb", "tests/test_gem_minification.rb", "tests/test_optcarrot.rb", "tests/_archive/**/*")
14
+ end
15
+
16
+ Rake::TestTask.new("test:integration") do |t|
17
+ t.libs << "lib"
18
+ t.test_files = FileList["tests/test_integration.rb"]
19
+ end
20
+
21
+ Rake::TestTask.new("test:gems") do |t|
22
+ t.libs << "lib"
23
+ t.test_files = FileList["tests/test_gem_minification.rb"]
24
+ end
25
+
26
+ desc "Minify optcarrot at the supported level and compare rendered frames"
27
+ Rake::TestTask.new("test:optcarrot") do |t|
28
+ t.libs << "lib"
29
+ t.test_files = FileList["tests/test_optcarrot.rb"]
30
+ end
31
+
32
+ desc "Show self-hosting compression ratio"
33
+ task :benchmark do
34
+ $LOAD_PATH.unshift File.expand_path("lib", __dir__)
35
+ require "ryac"
36
+
37
+ entry_path = File.join(__dir__, "lib", "ryac.rb")
38
+ minifier = Ryac::Minifier.new
39
+ result = minifier.call(entry_path)
40
+ stats = result.stats
41
+ compression = ((1 - stats.compression_ratio) * 100).round(1)
42
+ puts "#{stats.file_count} files: #{stats.original_size} -> #{stats.minified_size} bytes (#{compression}% reduction)"
43
+ end
44
+
45
+ task default: :test
data/Steepfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ target :lib do
4
+ signature "sig"
5
+
6
+ check "lib"
7
+
8
+ collection_config "rbs_collection.yaml"
9
+ end
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "ryac"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
data/bin/ryac ADDED
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'optparse'
5
+ require_relative '../lib/ryac'
6
+
7
+ options = {
8
+ output: nil,
9
+ aliases: nil,
10
+ level: nil,
11
+ gems: nil
12
+ }
13
+
14
+ parser = OptionParser.new do |opts|
15
+ opts.banner = "Usage: minify [options] [FILE...]"
16
+
17
+ opts.on("-o", "--output FILE", "Write output to FILE instead of stdout") do |file|
18
+ options[:output] = file
19
+ end
20
+
21
+ opts.on("-a", "--aliases FILE", "Write constant alias declarations to FILE") do |file|
22
+ options[:aliases] = file
23
+ end
24
+
25
+ opts.on("-g", "--gem GEM_NAMES", "Minify installed gem(s) by name (comma-separated)") do |names|
26
+ options[:gems] = names.split(',').map(&:strip).reject(&:empty?)
27
+ end
28
+
29
+ opts.on("-c", "--compress LEVEL", "Set compression level (stable or unstable)") do |value|
30
+ begin
31
+ options[:level] = Ryac::Minifier.resolve_level(value)
32
+ rescue ArgumentError => e
33
+ warn "Error: #{e.message}"
34
+ warn opts
35
+ exit 1
36
+ end
37
+ end
38
+
39
+ opts.on("-h", "--help", "Display this help message") do
40
+ puts opts
41
+ exit 0
42
+ end
43
+
44
+ opts.on("-v", "--version", "Display version") do
45
+ puts "ryac version #{Ryac::VERSION}"
46
+ exit 0
47
+ end
48
+ end
49
+
50
+ begin
51
+ parser.parse!
52
+ rescue OptionParser::InvalidOption => e
53
+ warn "Error: #{e.message}"
54
+ warn parser
55
+ exit 1
56
+ end
57
+
58
+ if options[:gems]
59
+ unless ARGV.empty?
60
+ warn "Error: Cannot specify both --gem and file arguments"
61
+ exit 1
62
+ end
63
+
64
+ if options[:gems].empty?
65
+ warn "Error: No gem names specified"
66
+ exit 1
67
+ end
68
+
69
+ begin
70
+ gem_args = Ryac::GemResolver.minifier_args(options[:gems])
71
+ entry = gem_args[:entry]
72
+ project_root = gem_args[:project_root]
73
+ gem_require_paths = gem_args[:gem_require_paths]
74
+ rescue Ryac::Pipeline::GemNotFoundError => e
75
+ warn "Error: #{e.message}"
76
+ exit 1
77
+ end
78
+ else
79
+ if ARGV.empty?
80
+ warn "Error: No entry file specified"
81
+ warn parser
82
+ exit 1
83
+ end
84
+
85
+ ARGV.each do |file|
86
+ unless File.exist?(file)
87
+ warn "Error: File not found: #{file}"
88
+ exit 1
89
+ end
90
+ end
91
+
92
+ entry = ARGV.size == 1 ? ARGV[0] : ARGV.dup
93
+ project_root = nil
94
+ gem_require_paths = []
95
+ end
96
+
97
+ gem_names = options[:gems] || []
98
+
99
+ begin
100
+ minifier = Ryac::Minifier.new
101
+ level_opts = options[:level] ? { level: options[:level] } : {}
102
+ result = minifier.call(entry, **level_opts, project_root: project_root, gem_names: gem_names, gem_require_paths: gem_require_paths)
103
+
104
+ content = if options[:aliases]
105
+ File.write(options[:aliases], result.aliases)
106
+ result.content
107
+ else
108
+ result.full_content
109
+ end
110
+
111
+ if options[:output]
112
+ File.write(options[:output], content)
113
+ else
114
+ puts content
115
+ end
116
+
117
+ warn "Compression: #{((1 - result.stats.compression_ratio) * 100).round(1)}%"
118
+ warn "Files processed: #{result.stats.file_count}"
119
+ rescue Ryac::SyntaxError => e
120
+ warn "Error: the input does not parse"
121
+ warn " #{e.message}"
122
+ exit 2
123
+ rescue Ryac::InputError => e
124
+ warn "Error: #{e.message}"
125
+ exit 2
126
+ rescue Ryac::InternalError => e
127
+ warn "Error: this is a bug in ryac, not in your program — please report it"
128
+ warn " #{e.message}"
129
+ exit 3
130
+ rescue => e
131
+ warn "Error: this is a bug in ryac, not in your program — please report it"
132
+ warn " #{e.class}: #{e.message}"
133
+ exit 3
134
+ end
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here