How can I set default_url_options for Sidekiq workers? - ruby-on-rails

I have a Sidekiq worker that is intended to perform social actions (e.g.: like pages on Facebook). Part of this requires knowing the URL for the object being liked.
Fortunately, Rails 3 makes it easy to access app-specific routes by including Rails.application.routes.url_helpers in whatever class or module needs access to the path/url helper method.
The problem I'm running into is that my default url/port are not accessible from within my Sidekiq worker despite various attempts to define them in my development.rb or production.rb.
class Facebook::LikeRecipeWorker
include Sidekiq::Worker
include Rails.application.routes.url_helpers
sidekiq_options queue: :facebook
def perform(recipe_id, user_id)
recipe = Recipe.find(recipe_id)
user = User.find(user_id)
if user.facebook_token
api = Koala::Facebook::API.new(user.facebook_token)
api.put_connections 'me', 'my_namespace:like', object: recipe_url(recipe)
end
end
end
When the recipe_url method is access, an ArgumentError is raised with the message:
ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
I know that I can specify default_url_options for ActionController or ActionMailer in the environment-specific config files, e.g.:
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_controller.default_url_options = { host: 'localhost', port: 3000 }
However, these (rightfully) appear to have no influence on my Sidekiq worker classes. How should I go about defining default_url_options for these classes?

What we did was this:
In your config file(s) (e.g. config/application.rb, config/production.db, etc.), have a line that sets the default:
routes.default_url_options = { host: 'www.example.org' }

I found a potential solution to this, though it feels like a little bit of a hack. I'm definitely open to better answers.
First, in my environment files (e.g.: config/environments/development.rb), I specify the default_url_options for my controllers:
config.action_controller.default_url_options = { host: 'localhost', port: 3000 }
Then in my worker class, I define a default_url_options instance method there:
class Facebook::LikeRecipeWorker
# ...
private
def default_url_options
ActionController::Base.default_url_options
end
end

Related

Where to set host for Rails.application.routes.url_helpers

In a controller I call a service like:
MyService.call
In the MyService.call method I want to use a url helper:
Rails.application.routes.url_helpers.something_url
However, I get the error:
Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
in config/environments/development.rb I have:
config.action_mailer.default_url_options = { host: 'localhost:3000' }
config.action_controller.default_url_options = { host: 'localhost:3000' }
What should I set not to get the error?
You could set the host in the config files as:
Rails.application.routes.default_url_options[:host] = 'your_host'
This will set the default host not just for action_mailer and action_controller, but for anything using the url_helpers.
According to this thread, whenever using route helpers, they don't defer default_url_options to ActionMailer or ActionController config and it is expected that there will be a default_url_options method in the class. This worked for me:
class MyService
include Rails.application.routes.url_helpers
def call
something_url
end
class << self
def default_url_options
Rails.application.config.action_mailer.default_url_options
end
end
end
Note that you can use action_mailer or action_controller config depending which you've configured.

Missing host to link to! Please provide the :host parameter, for Rails 4

Missing host to link to! Please provide the :host parameter, set
default_url_options[:host], or set :only_path to true
I randomly get this error at time, generally restarting the server fixes the issue for a while, and then it shows up again.
I have added
config.action_mailer.default_url_options = "localhost:3000", in the development and test.rb files.
Also, I have used include Rails.application.routes.url_helpers
in one module to get access to the routes, I read this could be the reason I get these errors but removing it will leave me with no access to the routes.
The module is for the datatables gem.
For Rails 5, use:
Rails.application.routes.default_url_options[:host] = "XXX"
You can put this in a config/environments/ file (or any other initializer), or e.g. at the beginning of config/routes.rb.
You should write in the following way
For Development(development.rb)
config.action_mailer.default_url_options = { :host => "localhost:3000" }
In production (production.rb)
config.action_mailer.default_url_options = { :host => "myproductionsite.com" }
I had a similar error. The problem is in your configuration.
Try rewriting your configuration for your development.rb and test.rb like this:
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
Also check that your configuration in production.rb is written correctly like this:
config.action_mailer.default_url_options = { host: 'myherokuapp.herokuapp.com' }
You have updated the default url options for action mailer.
URL helpers will take the option from action_controller settings.
config.action_controller.default_url_options = .....
BR

how to declare and refer to a single hash variable in several models (DRY, ruby on rails)

I have several models that need to call a method that takes a particular hash as input. Where should I declare this hash to keep things DRY, and how can I then use it in multiple models?
The hash will be the same for all uses. Specifically, it's just a hash of connection details that I want to pass to a call to establish_connection. I realize that that method takes a URL, though I'm still on rails 3.1, and that method was not made available until a little later.
Instead of repeating the following in five models, I just want to call something like establish_connection(legacy_db_connection_hash).
establish_connection(
:adapter => 'mysql',
:host => ENV['LEGACY_DATABASE_HOST'],
:username => ENV['LEGACY_DATABASE_USERNAME'],
:password => ENV['LEGACY_DATABASE_PASSWORD'],
:database => ENV['LEGACY_DATABASE_NAME']
)
I was thinking that perhaps there's a way to do it via /lib, but I'm really not sure how to go about it.
Thank you for your time!
You can also declare the configuration in your application.rb file
module Yourapp
class Application < Rails::Application
config.db_connection_config = {
.....
}
end
end
And you can use it Yourapp::Application.config.db_connection_config
You can modify them in separate environment files as well, if need.
And may be instead of using establish_connection each_time you can put in a separate module in the lib module, like its suggested in the previous answer.
It should go into a module.
For example, declare a module Foo in the file lib/foo.rb (and don't forget self. in front of the method name):
module Foo
# returns a hash of connection settings
def self.settings
{
adapter: 'mysql2',
database: ENV['LEGACY_DATABASE_NAME'],
username: ENV['LEGACY_DATABASE_USERNAME'],
password: ENV['LEGACY_DATABASE_PASSWORD'],
host: ENV['LEGACY_DATABASE_HOST']
}
end
end
Foo.settings will be available when you include Foo in your models.
Also, make sure to add the lib directory to config.autoload_paths in config/application.rb, so that lib/foo.rb is read and its contained Foo module is loaded:
config.autoload_paths += %W(#{config.root}/lib)
Here's how I do it:
config/initializers/profile_store.rb
config = YAML.load_file('config/profile_store.yml')[Rails.env]
Rails.application.config.profile_store = config.symbolize_keys
config/profile_store.yml
development:
host: localhost
prefix: dev-profile
test:
host: localhost
prefix: test-profile
You access it later with Rails.application.config.profile_store and in development you'll get
{:host => 'localhost', :prefix => 'dev-profile'}

Understanding rails configuration

So... First, I should make my goal clear. My goal is to have an environment defined constant filled with attributes from a yaml file that my app can reference.
The simplest example I can give is something like this:
#
initalizers/config.rb
CONFIG = YAML.load_file("#{Rails.root}/config/config.yml")[RAILS_ENV]
#
config.yml:
production:
host:
foo.com
development:
host:
localhost
port:
3000
#
config/environments/development.rb
Foo::Application.configure do
#...
config.action_mailer.default_url_options = { :host => CONFIG[:host], :port => CONFIG[:port] }
config.after_initialize do
Rails.application.routes.default_url_options = { :host => CONFIG[:host], :port => CONFIG[:port] }
end
end
My problems are:
The constant "CONFIG" does not exist prior to the after_initialize block (obviously because CONFIG is set as part of the initialize process), so I need to move this into the after_initialize block, but I cannot reference "config.action_mailer" inside the after_initialize block because "config" does not exist in that scope... Which is really confusing to me. Shouldn't "config" be accessible inside the block since it exists outside of it?
And as a side question, I am really really really confused how this config.x business works. The block is not yielding any variables so, how is "config" even valid in the context of Foo::Application.configure ?
I would think for it to work at all that it should be:
Foo::Application.configure do |config|
But that's not the case, so I really am curious how this works..
If you define your configuration loader inside of config/application.rb instead of an initializer you should be able to pre-empt the call to configure and have everything set up in time.
Sometimes it's best to make a sub-class that handles configuration files. A very basic example is this:
class Foo::Configuration
def initialize
# Read in contents of config file
end
def method_missing(name)
#config[name.to_s]
end
end
Then in your application you define a method like this:
class Foo::Application
def self.config
#config ||= Foo::Configuration.new
end
end
This will auto-load as required. It also avoids having a messy constant that shows up in the context of every single object.

How do I set default host for url helpers in rails?

I would like to do something like this
config.default_host = 'www.subdomain.example.com'
in some of my configuration files, so that object_url helpers (ActionView::Helpers::UrlHelper) produce links beginning with http://www.subdomain.example.com
I have tried to search the docs but I did not find anything except ActionMailer docs and http://api.rubyonrails.org/classes/Rails/Configuration.html which is not useful for me, because I do not know in which pat to look. Is there a place which describes the whole structure of Rails::Initializer.config?
asset_host doesn't work for urls
You need to override default_url_options in your ApplicationController (at least in Rails 3)
http://edgeguides.rubyonrails.org/action_controller_overview.html#default-url-options
class ApplicationController < ActionController::Base
def default_url_options
if Rails.env.production?
{:host => "myproduction.com"}
else
{}
end
end
end
Define the default host in your environment config:
# config/environments/staging.rb
MyApp::Application.configure do
# ...
Rails.application.routes.default_url_options[:host] = 'preview.mydomain.com'
# ...
end
Then you can create a URL anywhere in your app:
Rails.application.routes.url_helpers.widgets_url()
Or include the URL helpers in your class:
class MyLib
include Rails.application.routes.url_helpers
def make_a_url
widgets_url
end
end
If you don't define the default host, you will need to pass it as an option:
widgets_url host: (Rails.env.staging? ? 'preview.mydomain.com' : 'www.mydomain.com')
It's also useful to specify things like the protocol:
widgets_url protocol: 'https'
Another way is to set it like this
# config/production.rb
config.action_controller.default_url_options = { host: 'myproduction.com' }
You can easily set :host or/and :only_path parameter for every url_helper.
yours_url(params, :host => "http://example.com", :only_path => Rails.env.test?)
This way you are not setting global default_url_options in your environments, unless you want that.
In Rails 6.1 (at least), application-wide default_url_options can be set as follows:
# config/environments/development.rb
Rails.application.default_url_options = { host: 'localhost', port: 3000 }
Rails.application.configure do
# ...
end
See: https://github.com/rails/rails/issues/29992#issuecomment-761892658
As far as I know, the *_url helpers use the server's configured host name. So for example if my Apache installation is accepting requests for this Rails app at http://www.myapp.com/ then Rails will use that address. That's why the *_url methods in a development environment point to http://localhost:3000 by default.
The asset host suggested in the previous answer will only affect the image_tag, stylesheet_link_tag and javascript_link_tag helpers.
NSD's solution is how I do it, but I had to add a block to make it work with https:
config.action_controller.asset_host = Proc.new { |source, request|
(request ? request.protocol : 'http://') + "www.subdomain.example.com"
}
There's this, but I'm not terribly sure if they're the helpers you're referring to:
ActionController::Base.asset_host = "assets.example.com"
http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html

Resources