Skip to content

Commit 37ec654

Browse files
authored
[ruby/padrino] Upgrade to Ruby 3.4 (#9600)
Also use ActiveRecord instead of DataMapper, as that is no longer maintained. +-------------------+-----+-----+-----+------+-------+---------+--------------+ | branch_name| json| db|query|update|fortune|plaintext|weighted_score| +-------------------+-----+-----+-----+------+-------+---------+--------------+ | master|16348| 4309| 6315| 3785| 4251| 12539| 439| |padrino/ruby-3.4-ar|35491|12931|12760| 7322| 10133| 17696| 880| +-------------------+-----+-----+-----+------+-------+---------+--------------+
1 parent 29ac63e commit 37ec654

File tree

12 files changed

+136
-92
lines changed

12 files changed

+136
-92
lines changed

frameworks/Ruby/padrino/Gemfile

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
source 'https://rubygems.org'
22

3-
gem 'mysql2', '~> 0.4'
4-
gem "unicorn", '~> 6.1'
5-
gem 'puma', '~> 6.4'
6-
gem 'json', '~> 2.0'
3+
gem 'mysql2', '> 0.5'
4+
gem 'json'
5+
gem 'activerecord', '>= 7.1', :require => 'active_record'
6+
77
gem 'slim', '2.0.3'
8-
gem 'dm-mysql-adapter', '1.2.0'
9-
gem 'dm-core', '1.2.1'
10-
gem 'padrino', '0.15.3'
11-
gem 'rack', '~> 2.2'
8+
gem 'padrino', git: 'https://github.com/padrino/padrino-framework.git'
9+
gem 'rack'
10+
11+
group :puma, optional: true do
12+
gem 'puma', '~> 6.4', require: false
13+
end
14+
15+
group :unicorn, optional: true do
16+
gem 'unicorn', '~> 6.1', platforms: [:ruby, :mswin], require: false
17+
end

frameworks/Ruby/padrino/app/app.rb

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@ class App < Padrino::Application
1414
#
1515
# You can customize caching store engines:
1616
#
17-
# set :cache, Padrino::Cache::Store::Memcache.new(::Memcached.new('127.0.0.1:11211', :exception_retry_limit => 1))
18-
# set :cache, Padrino::Cache::Store::Memcache.new(::Dalli::Client.new('127.0.0.1:11211', :exception_retry_limit => 1))
19-
# set :cache, Padrino::Cache::Store::Redis.new(::Redis.new(:host => '127.0.0.1', :port => 6379, :db => 0))
20-
# set :cache, Padrino::Cache::Store::Memory.new(50)
21-
# set :cache, Padrino::Cache::Store::File.new(Padrino.root('tmp', app_name.to_s, 'cache')) # default choice
17+
# set :cache, Padrino::Cache.new(:LRUHash) # Keeps cached values in memory
18+
# set :cache, Padrino::Cache.new(:Memcached) # Uses default server at localhost
19+
# set :cache, Padrino::Cache.new(:Memcached, :server => '127.0.0.1:11211', :exception_retry_limit => 1)
20+
# set :cache, Padrino::Cache.new(:Memcached, :backend => memcached_or_dalli_instance)
21+
# set :cache, Padrino::Cache.new(:Redis) # Uses default server at localhost
22+
# set :cache, Padrino::Cache.new(:Redis, :host => '127.0.0.1', :port => 6379, :db => 0)
23+
# set :cache, Padrino::Cache.new(:Redis, :backend => redis_instance)
24+
# set :cache, Padrino::Cache.new(:Mongo) # Uses default server at localhost
25+
# set :cache, Padrino::Cache.new(:Mongo, :backend => mongo_client_instance)
26+
# set :cache, Padrino::Cache.new(:File, :dir => Padrino.root('tmp', app_name.to_s, 'cache')) # default choice
2227
#
2328

2429
##
@@ -32,8 +37,8 @@ class App < Padrino::Application
3237
# set :reload, false # Reload application files (default in development)
3338
# set :default_builder, 'foo' # Set a custom form builder (default 'StandardFormBuilder')
3439
# set :locale_path, 'bar' # Set path for I18n translations (default your_apps_root_path/locale)
35-
# disable :sessions # Disabled sessions by default (enable if needed)
36-
# disable :flash # Disables sinatra-flash (enabled by default if Sinatra::Flash is defined)
40+
disable :sessions # Disabled sessions by default (enable if needed)
41+
disable :flash # Disables sinatra-flash (enabled by default if Sinatra::Flash is defined)
3742
# layout :my_layout # Layout can be in views/layouts/foo.ext or views/foo.ext (default :application)
3843
#
3944

@@ -53,8 +58,8 @@ class App < Padrino::Application
5358
# render 'errors/404'
5459
# end
5560
#
56-
# error 505 do
57-
# render 'errors/505'
61+
# error 500 do
62+
# render 'errors/500'
5863
# end
5964
#
6065
end
Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,65 @@
1-
MAX_PK = 10_000
2-
QUERIES_MIN = 1
3-
QUERIES_MAX = 500
1+
QUERY_RANGE = (1..10_000).freeze
2+
ALL_IDS = QUERY_RANGE.to_a
43

54
HelloWorld::App.controllers do
5+
6+
after do
7+
response['Server'] = 'padrino'
8+
end
9+
10+
after do
11+
response['Date'] = Time.now.httpdate
12+
end if defined?(Puma)
13+
614
get '/json', :provides => [:json] do
7-
response.headers['Server'] = 'padrino'
8-
response.headers['Date'] = Time.now.httpdate
915
{message: "Hello, World!"}.to_json
1016
end
1117

1218
get '/db', :provides => [:json] do
13-
response.headers['Server'] = 'padrino'
14-
response.headers['Date'] = Time.now.httpdate
15-
id = Random.rand(MAX_PK) + 1
16-
World.get(id).attributes.to_json
19+
world = ActiveRecord::Base.with_connection do
20+
World.find(rand1).attributes
21+
end
22+
world.to_json
1723
end
1824

1925
get '/queries', :provides => [:json] do
20-
response.headers['Server'] = 'padrino'
21-
response.headers['Date'] = Time.now.httpdate
22-
queries = params['queries'].to_i.clamp(QUERIES_MIN, QUERIES_MAX)
23-
24-
results = (1..queries).map do
25-
World.get(Random.rand(MAX_PK) + 1).attributes
26-
end.to_json
26+
worlds = ActiveRecord::Base.with_connection do
27+
ALL_IDS.sample(bounded_queries).map do |id|
28+
World.find(id).attributes
29+
end
30+
end
31+
worlds.to_json
2732
end
2833

2934
get '/fortunes' do
30-
response.headers['Server'] = 'padrino'
31-
response.headers['Date'] = Time.now.httpdate
32-
@fortunes = Fortune.all
33-
@fortunes << Fortune.new(:id => 0, :message => "Additional fortune added at request time.")
34-
@fortunes = @fortunes.sort_by { |x| x.message }
35+
@fortunes = Fortune.all.to_a
36+
@fortunes << Fortune.new(
37+
id: 0,
38+
message: "Additional fortune added at request time."
39+
)
40+
@fortunes = @fortunes.sort_by(&:message)
3541

3642
render 'fortunes', layout: "layout"
3743
end
3844

3945
get '/updates', :provides => [:json] do
40-
response.headers['Server'] = 'padrino'
41-
response.headers['Date'] = Time.now.httpdate
42-
queries = params['queries'].to_i.clamp(QUERIES_MIN, QUERIES_MAX)
43-
44-
worlds = (1..queries).map do
45-
# get a random row from the database, which we know has 10000
46-
# rows with ids 1 - 10000
47-
world = World.get(Random.rand(MAX_PK) + 1)
48-
world.update(randomNumber: Random.rand(MAX_PK) + 1)
49-
world.attributes
46+
worlds = []
47+
ActiveRecord::Base.with_connection do
48+
worlds = ALL_IDS.sample(bounded_queries).map do |id|
49+
world = World.find(id)
50+
new_value = rand1
51+
new_value = rand1 while new_value == world.randomNumber
52+
world.randomNumber = new_value
53+
world
54+
end
55+
World.upsert_all(worlds)
5056
end
5157

5258
worlds.to_json
5359
end
5460

5561
get '/plaintext' do
56-
response.headers['Server'] = 'padrino'
57-
response.headers['Date'] = Time.now.httpdate
5862
content_type 'text/plain'
5963
"Hello, World!"
6064
end
61-
6265
end
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
# Helper methods defined here can be accessed in any controller or view in the application
22

3+
MAX_PK = 10_000
4+
QUERIES_MIN = 1
5+
QUERIES_MAX = 500
6+
37
HelloWorld::App.helpers do
4-
# def simple_helper_method
5-
# ...
6-
# end
8+
def rand1
9+
rand(MAX_PK) + 1
10+
end
11+
12+
def bounded_queries
13+
queries = params[:queries].to_i
14+
queries.clamp(QUERIES_MIN, QUERIES_MAX)
15+
end
716
end

frameworks/Ruby/padrino/benchmark_config.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
},
2626
"unicorn": {
2727
"json_url": "/json",
28-
"db_url": "/db",
29-
"query_url": "/queries?queries=",
28+
"db_url": "/db",
29+
"query_url": "/queries?queries=",
3030
"fortune_url": "/fortunes",
31-
"update_url": "/updates?queries=",
31+
"update_url": "/updates?queries=",
3232
"plaintext_url": "/plaintext",
3333
"port": 8080,
3434
"approach": "Realistic",

frameworks/Ruby/padrino/config/apps.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
Padrino.configure_apps do
2929
# enable :sessions
3030
set :session_secret, 'b941cbcb2d647360c0d1fb3c54a7039ed4f71cc0b7785d2aac689cc37d7757b7'
31-
set :protection, true
32-
set :protect_from_csrf, true
31+
set :protection, false
32+
set :protect_from_csrf, false
3333
end
3434

3535
# Mounts the core application for this project

frameworks/Ruby/padrino/config/boot.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
# Add your after (RE)load hooks here
4040
#
4141
Padrino.after_load do
42-
DataMapper.finalize
4342
end
4443

4544
Padrino.load!
Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
1-
##
2-
# A MySQL connection:
3-
# DataMapper.setup(:default, 'mysql://user:password@localhost/the_database_name')
4-
#
5-
# # A Postgres connection:
6-
# DataMapper.setup(:default, 'postgres://user:password@localhost/the_database_name')
7-
#
8-
# # A Sqlite3 connection
9-
# DataMapper.setup(:default, "sqlite3://" + Padrino.root('db', "development.db"))
10-
#
1+
Bundler.require('mysql')
2+
opts = {
3+
adapter: 'mysql2',
4+
username: 'benchmarkdbuser',
5+
password: 'benchmarkdbpass',
6+
host: 'tfb-database',
7+
database: 'hello_world'
8+
}
119

12-
DataMapper.logger = logger
13-
DataMapper::Property::String.length(255)
14-
15-
case Padrino.env
16-
when :production then DataMapper.setup(:default, "mysql://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world")
10+
# Determine threading/thread pool size and timeout
11+
if defined?(Puma) && (threads = Puma.cli_config.options.fetch(:max_threads)) > 1
12+
opts[:pool] = (2 * Math.log(threads)).floor
13+
opts[:checkout_timeout] = 10
14+
else
15+
# TODO: ActiveRecord doesn't have a single-threaded mode?
16+
opts[:pool] = 1
17+
opts[:checkout_timeout] = 0
1718
end
19+
20+
21+
# Setup our logger
22+
ActiveRecord::Base.logger = logger
23+
24+
# Use ISO 8601 format for JSON serialized times and dates.
25+
ActiveSupport.use_standard_json_time_format = true
26+
27+
# Don't escape HTML entities in JSON, leave that for the #json_escape helper
28+
# if you're including raw JSON in an HTML page.
29+
ActiveSupport.escape_html_entities_in_json = false
30+
31+
# Now we can establish connection with our db.
32+
ActiveRecord::Base.establish_connection(opts)
Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
class Fortune
2-
include DataMapper::Resource
3-
4-
storage_names[:default] = 'Fortune'
5-
6-
# property <name>, <type>
7-
property :id, Serial
8-
property :message, String
1+
class Fortune < ActiveRecord::Base
2+
self.table_name = name
93
end
Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
class World
2-
include DataMapper::Resource
1+
class World < ActiveRecord::Base
2+
self.table_name = name
33

4-
storage_names[:default] = 'World'
4+
alias_attribute(:randomNumber, :randomnumber) \
5+
if connection.adapter_name.downcase.start_with?('postgres')
56

6-
# property <name>, <type>
7-
property :id, Serial
8-
property :randomNumber, Integer, field: 'randomNumber'
7+
if connection.adapter_name.downcase.start_with?('mysql')
8+
def self.upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
9+
# On MySQL Batch updates verification isn't supported yet by TechEmpower.
10+
# https://github.com/TechEmpower/FrameworkBenchmarks/issues/5983
11+
attributes.each do |attrs|
12+
where(id: attrs[:id]).update_all(randomNumber: attrs[:randomNumber])
13+
end
14+
end
15+
end
916
end

0 commit comments

Comments
 (0)