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 constructRack
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
:
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?
endLines 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) }
endLines 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
- A Quick Introduction to Rack.
- 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. Sincelet
creates a method, you'll get aNameError
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 bylet
, 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 myit
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
Sleep Until Condition Is True in Ruby
Ruby - How to Write a New File with Output from Script
How to Allow Binary File Download Using Grape API
Where to Insert Rack::Deflater in the Rack
Memory Usage Increase with Ruby 2.1 Versus Ruby 2.0 or 1.9
How to Get Generators Call Other Generators in Rails 3
Adding a "Like/Unlike" Button to a Post in Rails
Override the Protect_From_Forgery Strategy in a Controller
Differencebetween 'File.Read' and 'Io.Read'
Set Default Stage with Capistrano 3
Making a Soap Request by Using Xml in Rails
Is Returning a Value from 'Next' a Bad Idea
Are There Any Good Mutation Testing Tools for Ruby 1.9 and Rspec2
How to Properly Chain Custom Methods in Ruby
What's the Difference Between "=" & "=>" and "@Variable", "@@Variable" and ":Variable" in Ruby