Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Rack::Test.default_host= to change the default_host more easily. #317

Closed
wants to merge 1 commit into from

Conversation

chrisftw
Copy link

@chrisftw chrisftw commented Aug 6, 2022

As with the people in #15 AND #239 I was having issues setting/changing the DEFAULT_HOST.

Current work around is ugly:

require 'rack/test'
Rack::Test::DEFAULT_HOST='sub.example.org'

My tests have to check the app changes databases based on the subdomain, so an easy way to change the default host makes my tests much easier to read.

@@ -30,7 +30,14 @@ module Rack
module Test
# The default host to use for requests, when a full URI is not
# provided.
DEFAULT_HOST = 'example.org'.freeze
def self.default_host
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could maybe leave in the constant DEFAULT_HOST for people using a work around?

@@ -30,7 +30,14 @@ module Rack
module Test
# The default host to use for requests, when a full URI is not
# provided.
DEFAULT_HOST = 'example.org'.freeze
def self.default_host
@@default_host ||= 'example.org'.freeze
Copy link
Member

@ioquatix ioquatix Aug 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider the following implementation:

DEFAULT_HOST = 'example.org'.freeze
class << self
  @default_host = DEFAULT_HOST
  attr_accessor :default_host
end

I'm not sure if we can remove DEFAULT_HOST.

Consider that ||= isn't thread safe and we could in theory have multiple threads accessing this field.

We've followed this model elsewhere: https://github.com/rack/rack/blob/main/lib/rack/request.rb#L17-L41

Don't have a strong opinion about it, just wanted to make a suggestion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely cannot accept the use of class variables. Class variables in Ruby are broken and should never be used.

This approach:

class << self
  @default_host = DEFAULT_HOST
  attr_accessor :default_host
end

Is broken. It sets the instance variable in the singleton class instead of the class. However, with that fixed, because Rack::Test is a module and not a class, the approach seems acceptable (it wouldn't be acceptable for a class, but then it breaks in a subclass unless you copy the instance variable during subclassing).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah using the approach above failed 1-44 tests in a somewhat random nature. Moving to this:

@default_host = DEFAULT_HOST
class << self
  attr_accessor :default_host
end

makes all the tests passing again. Since it is a module, I am not sure I see the difference.

@@ -54,7 +61,7 @@ class Session
extend Forwardable
include Rack::Test::Utils

def self.new(app, default_host = DEFAULT_HOST) # :nodoc:
def self.new(app, default_host = Rack::Test.default_host) # :nodoc:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just thinking about alternatives, is there no easy way to inject the default host argument on a per-test/spec basis?

What about if we made this default_host: options and provided some kind of app_options or something similar so you could write:

let(:app_options) {{default_host: '...'}}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think per test/spec is possible. You can override the URI provided for each request, but the CookieJar always uses the default host for the session, and Rack::Test::Methods does not provide a way to set the default_host, without overriding build_rack_test_session.

I think it may be better to modify build_rack_test_session. If the current scope responds to default_host, it calls it and uses it as the second argument. Otherwise, it continues to pass one argument (app). That way you can allow for global, per-suite, and per-test/spec setting of default_host, with minimal change to the current implementation. That seems like a better approach to me, what do you think? If you like that approach, I can implement it.

@ioquatix
Copy link
Member

ioquatix commented Aug 7, 2022

I took a look at the code, and there are a number of constants which might benefit from this transformation.

@jeremyevans what do you think about migrating all of these to class << self attr_accessors + methods?

https://github.com/rack/rack-test/blob/main/lib/rack/test.rb#L31-L43

This would be similar to what you did for Rack::Request.

Copy link
Contributor

@jeremyevans jeremyevans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I'm positive to supporting this feature in some way, but negative to this approach (adding Rack::Test.default_host).

@@ -30,7 +30,14 @@ module Rack
module Test
# The default host to use for requests, when a full URI is not
# provided.
DEFAULT_HOST = 'example.org'.freeze
def self.default_host
@@default_host ||= 'example.org'.freeze
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely cannot accept the use of class variables. Class variables in Ruby are broken and should never be used.

This approach:

class << self
  @default_host = DEFAULT_HOST
  attr_accessor :default_host
end

Is broken. It sets the instance variable in the singleton class instead of the class. However, with that fixed, because Rack::Test is a module and not a class, the approach seems acceptable (it wouldn't be acceptable for a class, but then it breaks in a subclass unless you copy the instance variable during subclassing).

@@ -54,7 +61,7 @@ class Session
extend Forwardable
include Rack::Test::Utils

def self.new(app, default_host = DEFAULT_HOST) # :nodoc:
def self.new(app, default_host = Rack::Test.default_host) # :nodoc:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think per test/spec is possible. You can override the URI provided for each request, but the CookieJar always uses the default host for the session, and Rack::Test::Methods does not provide a way to set the default_host, without overriding build_rack_test_session.

I think it may be better to modify build_rack_test_session. If the current scope responds to default_host, it calls it and uses it as the second argument. Otherwise, it continues to pass one argument (app). That way you can allow for global, per-suite, and per-test/spec setting of default_host, with minimal change to the current implementation. That seems like a better approach to me, what do you think? If you like that approach, I can implement it.

@ioquatix
Copy link
Member

ioquatix commented Aug 9, 2022

@jeremyevans I don't fully understand why it's acceptable in Rack::Request but not here. Is it because of the @variable assignment?

In any case, maybe you can propose a good way to support this feature/functionality?

@jeremyevans
Copy link
Contributor

@jeremyevans I don't fully understand why it's acceptable in Rack::Request but not here. Is it because of the @variable assignment?

In my comment (#317 (comment)), I stated if you changed where the variable was assigned, it would be acceptable. Rack::Request assigns the instance variable in the class, not in the singleton class, so it doesn't have the problem that your code in #317 (comment) does.

However, as I pointed out, I'm negative adding Rack::Test.default_host.

In any case, maybe you can propose a good way to support this feature/functionality?

I already proposed a way to get global, per suite, and per-test/spec default host: #317 (comment). What are you thoughts on that?

@ioquatix
Copy link
Member

@jeremyevans I'm fine to defer to your judgement on this. I'd like to fully understand the implications of the singleton_class variable so that I don't repeat the mistakes.

Just so I understand, on this line:

Session.new(app)

app is referring to whatever is self.app which could be conceivably provided by let(:app) {...} in rspec or def app in minitest. You are proposing to add an additional hook, default_host in the same way. I'm fine with that.

jeremyevans added a commit to jeremyevans/rack-test that referenced this pull request Aug 10, 2022
This allows you to configure a default host the same way you
configure the application.

Fixes rack#317
jeremyevans added a commit that referenced this pull request Aug 11, 2022
This allows you to configure a default host the same way you
configure the application.

Fixes #317
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants