Skip to content

Commit

Permalink
Merge pull request ManageIQ#22698 from jrafanie/ruby31
Browse files Browse the repository at this point in the history
Support Ruby 3.1
  • Loading branch information
agrare committed Dec 18, 2023
2 parents 999306b + 1d6a861 commit 634efa7
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 7 deletions.
5 changes: 2 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
raise "Ruby versions < 3.0.0 are unsupported!" if RUBY_VERSION < "3.0.0"
raise "Ruby versions >= 3.1.0 are unsupported!" if RUBY_VERSION >= "3.1.0"
raise "Ruby versions < 3.0.0 are unsupported!" if RUBY_VERSION < "3.0.0"
raise "Ruby versions >= 3.2.0 are unsupported!" if RUBY_VERSION >= "3.2.0"

source 'https://rubygems.org'

Expand Down Expand Up @@ -61,7 +61,6 @@ gem "net-ldap", "~>0.16.1", :require => false
gem "net-ping", "~>1.7.4", :require => false
gem "openscap", "~>0.4.8", :require => false
gem "optimist", "~>3.0", :require => false
gem "psych", "<4", :require => false
gem "pg", ">=1.4.1", :require => false
gem "pg-dsn_parser", "~>0.1.1", :require => false
gem "query_relation", "~>0.1.0", :require => false
Expand Down
2 changes: 2 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ class Application < Rails::Application
Vmdb::Initializer.init
ActiveRecord::Base.connection_pool.release_connection
puts "** #{Vmdb::Appliance.BANNER}" unless Rails.env.production?

YamlPermittedClasses.initialize_app_yaml_permitted_classes
end

console do
Expand Down
22 changes: 22 additions & 0 deletions lib/extensions/yaml_load_aliases.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module YamlLoadAliases
# Psych 4 aliases load as safe_load. Some loads happen early, like reading the database.yml so we don't want to load our
# constants at that time, such as MiqExpression, Ruport, so we have two sets of permitted classes.
def safe_load(yaml, permitted_classes: [], aliases: false, **kwargs)
# permitted_classes kwarg is provided because rails 6.1.7.x expects it as a defined kwarg. See: https://github.com/rails/rails/blob/9ab33753b6bab1809fc73d35b98a5c1d0c96ba1b/activerecord/lib/active_record/coders/yaml_column.rb#L52
permitted_classes += YamlPermittedClasses.permitted_classes
super(yaml, permitted_classes: permitted_classes, aliases: true, **kwargs)
rescue Psych::DisallowedClass => err
# Temporary hack to fallback to psych 3 behavior to go back to unsafe load if it's a disallowed class.
# See: https://stackoverflow.com/questions/71191685/visit-psych-nodes-alias-unknown-alias-default-psychbadalias/71192990#71192990
# The alternative is to enumerate all the classes we will allow to be loaded from YAML, such as many of the various models.
raise unless Rails.env.production?

warn "WARNING: Using fallback to unsafe_load due to DisallowedClass: #{err}"
unsafe_load(yaml, **kwargs.except(:aliases, :permitted_classes, :permitted_symbols))
end
end

if Psych::VERSION >= "3.1"
require 'yaml'
YAML.singleton_class.prepend(YamlLoadAliases)
end
41 changes: 41 additions & 0 deletions lib/yaml_permitted_classes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class YamlPermittedClasses
DEFAULT_PERMITTED_CLASSES = [
ActiveSupport::Duration,
ActiveSupport::HashWithIndifferentAccess,
ActiveSupport::TimeWithZone,
ActiveSupport::TimeZone,
Date,
DateTime,
Object,
Range,
Regexp,
Symbol,
Time
].freeze
def self.app_yaml_permitted_classes
@app_yaml_permitted_classes ||= DEFAULT_PERMITTED_CLASSES + [MiqExpression]
end

def self.app_yaml_permitted_classes=(classes)
@app_yaml_permitted_classes = Array(classes)
end

def self.default_permitted_classes
@default_permitted_classes ||= DEFAULT_PERMITTED_CLASSES
end

def self.permitted_classes
initialized? ? app_yaml_permitted_classes : default_permitted_classes
end

def self.initialize_app_yaml_permitted_classes
@initialize_app_yaml_permitted_classes ||= begin
ActiveRecord::Base.yaml_column_permitted_classes = YamlPermittedClasses.app_yaml_permitted_classes
true
end
end

def self.initialized?
!!@initialize_app_yaml_permitted_classes
end
end
6 changes: 3 additions & 3 deletions spec/lib/extensions/ar_yaml_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@
inst = Vm.new
inst.access1 = 1
inst.access2 = 2
result = YAML.load(YAML.dump(inst))
result = YAML.safe_load(YAML.dump(inst), :permitted_classes => [Vm, ActiveModel::Attribute.const_get(:FromDatabase), ActiveModel::Attribute::const_get(:FromUser), ActiveModel::Type::String])
expect(result.access1).to eq(1)
expect(result.access2).to eq(2)
end

it "attr_reader_that_yamls" do
inst = Vm.new
inst.instance_variable_set("@read1", 1)
result = YAML.load(YAML.dump(inst))
result = YAML.safe_load(YAML.dump(inst), :permitted_classes => [Vm, ActiveModel::Attribute.const_get(:FromDatabase), ActiveModel::Attribute::const_get(:FromUser), ActiveModel::Type::String])
expect(result.read1).to eq(1)
end

it "attr_writer_that_yamls" do
inst = Vm.new
inst.write1 = 1
result = YAML.load(YAML.dump(inst))
result = YAML.safe_load(YAML.dump(inst), :permitted_classes => [Vm, ActiveModel::Attribute.const_get(:FromDatabase), ActiveModel::Attribute::const_get(:FromUser), ActiveModel::Type::String])
expect(result.instance_variable_get("@write1")).to eq(1)
end
end
2 changes: 1 addition & 1 deletion spec/models/miq_report_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@
fake_ruport_data_table = {:data => result, :column_names => column_names}
before = MiqReport.new
before.table = fake_ruport_data_table
after = YAML.load(YAML.dump(before))
after = YAML.safe_load(YAML.dump(before), :permitted_classes => [MiqReport, ActiveModel::Attribute.const_get(:FromDatabase)])
expect(after.table).to eq(fake_ruport_data_table)
end

Expand Down
9 changes: 9 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@
c.syntax = :expect
end

config.before do
# TODO: The following locations YAML load the Ruport objects, see if we can avoid serializing them here.
# rspec ./spec/models/miq_report_result_spec.rb:8 # MiqReportResult #_async_generate_result
# rspec ./spec/models/miq_report_result_spec.rb:111 # MiqReportResult persisting generated report results should save the original report metadata and the generated table as a binary blob
# rspec ./spec/models/miq_report/generator_spec.rb:275 # MiqReport::Generator sorting handles sort columns with nil values properly, when column is string
# rspec ./spec/models/service_spec.rb:510 # Service Chargeback report generation #chargeback_report returns chargeback report
YamlPermittedClasses.app_yaml_permitted_classes |= [Ruport::Data::Record, Ruport::Data::Table]
end

config.file_fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
config.use_instantiated_fixtures = false
Expand Down

0 comments on commit 634efa7

Please sign in to comment.