Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions lib/console1984.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,28 @@ class << self
def running_protected_environment?
protected_environments.collect(&:to_sym).include?(Rails.env.to_sym)
end

def require_ruby_parser_dependencies
if RUBY_VERSION >= "3.3"
require 'parser'
require 'prism'
else
Kernel.silence_warnings do
require 'parser/current'
end
end
end

# Returns the parser class used for parsing console commands.
# Uses Prism on Ruby >= 3.3 for forward-compatibility with Ruby 4.0+.
# Falls back to the parser gem on older Rubies.
def ruby_parser
if RUBY_VERSION >= "3.3"
Prism::Translation::ParserCurrent
else
Parser::CurrentRuby
end
end
end
end

Expand Down
6 changes: 3 additions & 3 deletions lib/console1984/command_validator/.command_parser.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Naming class with dot so that it doesn't get loaded eagerly by Zeitwerk. We want to load
# only when a console session is started, when +parser+ is loaded.
#
# See +Console1984::Supervisor#require_dependencies+
# only when a console session is started.
Console1984.require_ruby_parser_dependencies

class Console1984::CommandValidator::CommandParser < ::Parser::AST::Processor
include AST::Processor::Mixin
include Console1984::Freezeable
Expand Down
4 changes: 1 addition & 3 deletions lib/console1984/command_validator/parsed_command.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# Parses a command string and exposes different constructs to be used by validations.
#
# Internally, it uses the {parser}[https://github.com/whitequark/parser] gem to perform the parsing.
class Console1984::CommandValidator::ParsedCommand
include Console1984::Freezeable

Expand All @@ -15,7 +13,7 @@ def initialize(raw_command)
private
def command_parser
@command_parser ||= Console1984::CommandValidator::CommandParser.new.tap do |processor|
ast = Parser::CurrentRuby.parse(raw_command)
ast = Console1984.ruby_parser.parse(raw_command)
processor.process(ast)
rescue Parser::SyntaxError
# Fail open with syntax errors
Expand Down
2 changes: 1 addition & 1 deletion lib/console1984/refrigerator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ def freeze_external_modules_and_classes

def external_modules_and_classes_to_freeze
# Not using a constant because we want this to run lazily (console-dependant dependencies might not be loaded).
[Parser::CurrentRuby]
[Console1984.ruby_parser]
end
end
6 changes: 0 additions & 6 deletions lib/console1984/supervisor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,7 @@ def current_username

private
def require_dependencies
Kernel.silence_warnings do
require 'parser/current'
end
require 'rainbow'

# Explicit lazy loading because it depends on +parser+, which we want to only load
# in console sessions.
require_relative "./command_validator/.command_parser"

# This solves a weird class loading error where ActiveRecord dosn't resolve +Relation+ properly.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ class ForbiddenConstantReferenceValidationTest < ActiveSupport::TestCase
end
end

test "validate assigning a forbidden constant to a new constant" do
assert_raise Console1984::Errors::ForbiddenCommandAttempted do
run_validation <<~RUBY, always: ["SomeClass"]
MyAlias = SomeClass
RUBY
end
end

test "referencing other constants won't raise any error" do
run_validation <<~RUBY, always: ["SomeConstant"]
SomeNotForbiddenClass.some_method
Expand Down
8 changes: 8 additions & 0 deletions test/command_validator/forbidden_reopening_validation_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ module Some::Base::Class
end
end

test "validate assigning a forbidden class to a new constant" do
assert_raise Console1984::Errors::ForbiddenCommandAttempted do
run_validation <<~RUBY, ["SomeClass"]
MyAlias = SomeClass
RUBY
end
end

test "doesn't prevent reopening classes when the constant is a partial match" do
run_validation <<~RUBY, ["SomeClass"]
class SomeClass2
Expand Down
10 changes: 10 additions & 0 deletions test/command_validator/parsed_command_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ class t::Subtopic
end
end

if RUBY_VERSION >= "4.0"
test "parse constants from Ruby 4.0 syntax" do
# Logical operators at line beginning is new syntax in Ruby 4.0
assert_constants ["Foo", "Bar"], <<~RB
result = Foo
|| Bar
RB
end
end

test "syntax errors are handled gracefully" do
parsed_command = Console1984::CommandValidator::ParsedCommand.new <<~RB
def 12'39u````
Expand Down
11 changes: 11 additions & 0 deletions test/ruby_parser_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require "test_helper"

class RubyParserTest < ActiveSupport::TestCase
test "ruby_parser returns the appropriate parser for the current Ruby version" do
if RUBY_VERSION >= "3.3"
assert_equal Prism::Translation::ParserCurrent, Console1984.ruby_parser
else
assert_equal Parser::CurrentRuby, Console1984.ruby_parser
end
end
end
1 change: 0 additions & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
require "rails/test_help"
require "mocha/minitest"
require "minitest/mock"
require "parser/current"
require "activeresource"
require "ostruct"

Expand Down