"Use" Keyword/Word in Ruby/Rails/Rack Code

use keyword/word in Ruby/Rails/Rack code

The Documentation

As people have pointed out, use is not a Ruby keyword, it is in fact a method of the Rack::Builder class:

use(middleware, *args, &block)

Specifies middleware to use in a stack.

This documentation (pointed out by @user166390) describes it like this:

Rack::Builder implements a small DSL to iteratively construct Rack applications.

Example:

app = Rack::Builder.new {
use Rack::CommonLogger
use Rack::ShowExceptions
map "/lobster" do
use Rack::Lint
run Rack::Lobster.new
end
}

Or

app = Rack::Builder.app do
use Rack::CommonLogger
lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] }
end

use adds a middleware to the stack, run dispatches to an application.

The Source Code

I'm not too familiar with the Rack::Builder source code, but it looks like each time you call use with a new middleware module, it gets added to an array, and each module is run/injected in the reverse order in which it was added (last-in-first-out order, a.k.a. stack order). The result of running the previous middleware is passed to the next middleware in the stack via inject:

  1. Lines 53-56:

    def initialize(default_app = nil,&block)
    # @use is parallel assigned to [].
    @use, @map, @run = [], nil, default_app
    instance_eval(&block) if block_given?
    end
  2. Lines 81-87:

    def use(middleware, *args, &block)
    if @map
    mapping, @map = @map, nil
    @use << proc { |app| generate_map app, mapping }
    end
    # The new middleware is added to the @use array.
    @use << proc { |app| middleware.new(app, *args, &block) }
    end
  3. Lines 131-135:

    def to_app
    app = @map ? generate_map(@run, @map) : @run
    fail "missing run or map statement" unless app
    # The middlewares are injected in reverse order.
    @use.reverse.inject(app) { |a,e| e[a] }
    end

Additional resources

  1. A Quick Introduction to Rack.
  2. Ruby on Rack #2 - The Builder.

Why is app.Add*() and app.Use*() naming convention used in ASP.NET Core application?

The actions we take are described by the terms Add and Use, respectively:

The term "Add" refers to the act of adding services to the application, however this does not imply that they will be used - "Add" implies an action without any direct consequences.

The term "Use" refers to the act of actually using them in your application (as they have already been added) - the term "Use" implies an action with consequences.

Your authentication example clearly shows the logic described above:

AddAuthentication - Registers services required by authentication services (the authentication process would not take place unless you actually use these registered services in the application pipeline)

UseAuthentication - Adds the AuthenticationMiddleware to the specified IApplicationBuilder, which enables authentication capabilities.

rewrite non www to www except subdomain rails 3 and rack

The code you have right now will rewrite "sub.example.com", your call function could be rewritten like this :

def call(env)
request = Rack::Request.new(env)

# Redirect only if the host is a naked TLD
if request.host =~ /^[^.]+\.[^.]+$/
[301, {"Location" => request.url.sub("//","//www.")}, self]
else
@app.call(env)
end
end

visit_Psych_Nodes_Alias: Unknown alias: default (Psych::BadAlias)

The problem is related to the Ruby 3.1 / Psych 4.x incompatibility described in this issue: https://bugs.ruby-lang.org/issues/17866

Ruby 3.0 comes with Psych 3, while Ruby 3.1 comes with Psych 4, which has a major breaking change (diff 3.3.2 → 4.0.0).

  • The new YAML loading methods (Psych 4) do not load aliases unless they get the aliases: true argument.
  • The old YAML loading methods (Psych 3) do not support the aliases keyword.

At this point, it seems like anyone, anywhere that wants to load YAML in the same way it worked prior to Ruby 3.1, need to do something like this:

begin
YAML.load(source, aliases: true, **options)
rescue ArgumentError
YAML.load(source, **options)
end

as patched by the Rails team.

In your case, I suspect you will need to see which Rails version that matches your major version preference includes this patch before you can upgrade to Ruby 3.1.

Personally, wherever I have control of the code that loads YAML, I am adding an extension like this:

module YAML
def self.properly_load_file(path)
YAML.load_file path, aliases: true
rescue ArgumentError
YAML.load_file path
end
end

or, if I want to completely revert this change done in Psych 4, I add this to my code:

module YAML
class << self
alias_method :load, :unsafe_load if YAML.respond_to? :unsafe_load
end
end

This has the advantage of restoring YAML::load, and YAML::load_file (which uses it) to their original glory, and solves everything including libraries that you have no control of, in all Ruby versions.

Lastly, as I mentioned in the comments, in case you prefer a minimally invasive stopgap measure, you might be able to get away with pinning Psych to < 4 in your Gemfile:

gem 'psych', '< 4'

Note for Rails users (>= 7.0.3.1)

ActiveRecord 7.0.3.1 changed an internal call and it now calls safe_load directly. If you see a Psych related error during rails tests, you might be affected by this change.

To resolve it, you can add this to a new or existing initializer:

# config/initializers/activerecord_yaml.rb
ActiveRecord.use_yaml_unsafe_load = true

You may also use ActiveRecord.yaml_column_permitted_classes to configure the allowed classes instead.

More info in this post.

When to use RSpec let()?

I always prefer let to an instance variable for a couple of reasons:

  • Instance variables spring into existence when referenced. This means that if you fat finger the spelling of the instance variable, a new one will be created and initialized to nil, which can lead to subtle bugs and false positives. Since let creates a method, you'll get a NameError when you misspell it, which I find preferable. It makes it easier to refactor specs, too.
  • A before(:each) hook will run before each example, even if the example doesn't use any of the instance variables defined in the hook. This isn't usually a big deal, but if the setup of the instance variable takes a long time, then you're wasting cycles. For the method defined by let, the initialization code only runs if the example calls it.
  • You can refactor from a local variable in an example directly into a let without changing the
    referencing syntax in the example. If you refactor to an instance variable, you have to change
    how you reference the object in the example (e.g. add an @).
  • This is a bit subjective, but as Mike Lewis pointed out, I think it makes the spec easier to read. I like the organization of defining all my dependent objects with let and keeping my it block nice and short.

A related link can be found here: http://www.betterspecs.org/#let

Rspec for Detecting devises

If your rails version is not too ancient in controller you can use request.user_agent (it will look into env anyway, but this makes code cleaner)

Browsers pass user agent in header User-agent (which in turn ends up in rack env), so you need to simulate this in your tests.

For testing this I'd recommend using request specs instead of controller ones (which are deprecated in rails 5):

 RSpec.describe 'Topics...', type: :request do
it "redirects for ios" do
get '/your/topcis/path/here', headers: { 'HTTP_USER_AGENT' => 'iPhone' }
expect(response).to redirect_to(/\.apple\.com/)
end
end

(above uses rails 5, for older rails headers will be just hash, not a keyword argument)

Also you can write your method with case statement:

def devise
redirect_to case request.user_agent.downcase
when /mac/i then 'https://itunes.apple.com/us/app/apple-store/id375380948?mt=8'
when /windows/i then 'https://www.microsoft.com/en-in/store/apps/windows'
when /android/i then 'https://play.google.com/store?hl=en'
else
root_path
end
end


Related Topics



Leave a reply



Submit