Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Paweł Pacana
Paweł Pacana

Posted on • Originally published atblog.arkency.com

     

Explaining Rack — desugaring Rack::Builder DSL

Yesterday Iwrote a post highlighting Basic Auth and how can we protect Rack applications mounted in Rails with it.
Today when discussing some ideas from this post with my colleague, our focus immediately shifted toRack::Builder.

On one hand Rack interface is simple, fairly constrained and well described inspec. And ships with alinter to help your Rack apps and middleware pass this compliance.

A Rack application is a Ruby object (not a class) that responds to call. It takes exactly one argument, the environment and returns an Array of exactly three values:The status,the headers, andthe body.

classHelloWorlddefcall(env)[200,{"Content-Type"=>"text/plain"},["Hello world!"]]endend
Enter fullscreen modeExit fullscreen mode

On the other hand your first exposure to Rack is usually viaconfig.ru in Rails application:

# This file is used by Rack-based servers to start the application.require_relative"config/environment"runRails.application
Enter fullscreen modeExit fullscreen mode

Behind the scenes, this fileis eventually passed toRack::Builder. It is a convenient DSL to compose Rack application out of other Rack applications. Inyesterday's blogpost we've seen it being used directly:

Rack::Builder.newdouseRack::Auth::Basicdo|username,password|ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(username),::Digest::SHA256.hexdigest(ENV.fetch("DEV_UI_USERNAME")))&ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(password),::Digest::SHA256.hexdigest(ENV.fetch("DEV_UI_PASSWORD")))endrunSidekiq::Webend
Enter fullscreen modeExit fullscreen mode

Whether you useRack::Builder directly or viarackup files, you're immediately associating Rack with theuse andrun DSL.
And that triggered an honest question from my colleague — how does this DSL relate to that rather simple Rack interface?

De-sugaring Rack::Builder DSL

To add even more nuance, some Rack apps are called amiddleware. What is a middleware? In simple words — it's a Rack application that wraps another Rack application. It may affect the input passed to the wrapped app. Or it may affect the output if it.

An example of amiddleware that makes everything sound more dramatic:

classDramatizedefinitialize(app)@app=appenddefcall(env)status,headers,body=@app.call(env)[status,headers,body.map{|x|"#{x}111one!1"}]endend
Enter fullscreen modeExit fullscreen mode

A composition of such middleware and our previous sampleHelloWorld application withconfig.ru would look like this:

# config.ruclassHelloWorld# omitted for brevityendclassDramatize# omitted for brevityenduseDramatizerunHelloWorld.new
Enter fullscreen modeExit fullscreen mode

When executed, it would return very dramatic greeting:

$ bundle exec rackup config.ru* Listening on http://127.0.0.1:9292* Listening on http://[::1]:9292Use Ctrl-C to stop$ curl localhost:9292                                                    Hello world!111one!1⏎
Enter fullscreen modeExit fullscreen mode

Now back to the question that started it all:

How does this DSL relate to that rather simple Rack interface?

The last example of composition viaRack::Builder can be rewritten to avoid some of the DSL:

# config.rurunDramatize.new(HelloWorld.new)
Enter fullscreen modeExit fullscreen mode

A singlerun is needed to tell a Ruby application server what is our Rack application that we'd like to run. The use ofuse is on the other hand just optional.

If this post got you curious on Rack, a fun way to learn more about it is to check the code of each middleware powering your Rails application:

$ bin/rails middlewareuse Webpacker::DevServerProxyuse Honeybadger::Rack::UserInformeruse Honeybadger::Rack::UserFeedbackuse Honeybadger::Rack::ErrorNotifieruse Rack::Corsuse ActionDispatch::HostAuthorizationuse Rack::Sendfileuse ActionDispatch::Staticuse ActionDispatch::Executoruse ActiveSupport::Cache::Strategy::LocalCache::Middlewareuse Rack::Runtimeuse Rack::MethodOverrideuse ActionDispatch::RequestIduse ActionDispatch::RemoteIpuse Rails::Rack::Loggeruse ActionDispatch::ShowExceptionsuse ActionDispatch::DebugExceptionsuse ActionDispatch::ActionableExceptionsuse ActionDispatch::Reloaderuse ActionDispatch::Callbacksuse ActionDispatch::Cookiesuse ActionDispatch::Session::CookieStoreuse ActionDispatch::Flashuse ActionDispatch::ContentSecurityPolicy::Middlewareuse ActionDispatch::PermissionsPolicy::Middlewareuse Rack::Headuse Rack::ConditionalGetuse Rack::ETaguse Rack::TempfileReaperuse Warden::Manageruse Rack::Deflateruse RailsEventStore::Middlewarerun MyApp::Application.routes
Enter fullscreen modeExit fullscreen mode

Happy learning!

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Holistic Developer @arkency, 13 years in ITInto Domain-Driven Design, Continous Delivery and Elm. I dig legacy Rails codebases and release @RailsEventStore
  • Location
    Wrocław, PL
  • Work
    VP of Bicycle Repairs at Arkency
  • Joined

More fromPaweł Pacana

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp