cannot split': bad URI(is not URI?): - ruby-on-rails

I've got the following CarrierWave initializer which works fine on my Heroku/MONGOHQ/GridFS env :
CarrierWave.configure do |config|
config.storage = :grid_fs
uri = URI.parse(ENV['MONGOHQ_URL'])
config.grid_fs_database = File.basename(uri.path)
config.grid_fs_host = uri.host unless uri.host.blank?
config.grid_fs_port = uri.port unless uri.port.blank?
config.grid_fs_username = uri.user unless uri.user.blank?
config.grid_fs_password = uri.password unless uri.password.blank?
config.grid_fs_access_url = '/gridfs'
config.cache_dir = "uploads"
config.root = Rails.root.join('tmp')
end
but, when I try to run the code locally (in developement) I get the following error:
`split': bad URI(is not URI?): (URI::InvalidURIError)
here is the full stack : http://pastie.org/1630069 I tried to add require 'uri/generic' on top of initializer but doesn't works.
Does anybody know way ?
Thanks in advance
luca

another solution would be to add a ".env" file in the project root and define there environment variables like:
MONGOHQ_URL=mongodb://someuser:somepass#paulo.mongohq.com:10040/appid

As suggested by KenB the ENV['MONGOHQ_URL'] was not setted on my local machine developement environment :
lsoave#ubuntu:~/rails/heroku/mp3upload$ rails c
Loading development environment (Rails 3.0.5)
ruby-1.9.2-p136 :001 > ENV['MONGOHQ_URL']
=> nil
ruby-1.9.2-p136 :002 >
this was a branch without the initializer, so on my local machine I had to skip that. I did it like that:
if ENV['MONGOHQ_URL']
CarrierWave.configure do |config|
config.storage = :grid_fs
uri = URI.parse(ENV['MONGOHQ_URL'])
config.grid_fs_database = File.basename(uri.path)
config.grid_fs_host = uri.host unless uri.host.blank?
config.grid_fs_port = uri.port unless uri.port.blank?
config.grid_fs_username = uri.user unless uri.user.blank?
config.grid_fs_password = uri.password unless uri.password.blank?
config.grid_fs_access_url = '/gridfs'
config.cache_dir = "uploads"
config.root = Rails.root.join('tmp')
end
end
I think that should be a very better way to skip a Ralis 3.0.5 initializer during the boot process, conditionally to the ENV['MONGOHQ_URL'] parameter value.
If you have a better way, could you please share it ?
Many thanks :-)
luca

In your initializer, you can do this: URI.parse(ENV['MONGOHQ_URL'] || 'some_other_link') as specified in the MongoHQ Heroku docs

Related

Yelp ruby gem on Heroku

I have this code as an initializer in yelp.rb:
Yelp.client.configure do |config|
config.consumer_key = ENV['config.consumer_key']
config.consumer_secret = ENV['config.consumer_secret']
config.token = ENV['config.token']
config.token_secret = ENV['config.token_secret']
end
I have a yelp.yml file that loads all this in and it works great in development.
As soon as I push it to heroku (I have all my keys set in Heroku as well and have triple verified no spelling errors) I get this error 'Yelp::Error::MissingAPIKeys: You're missing an API key'
I've ran code in Rails C on development (see below) and it passes, ran the exact same code in Rails c on the Heroku side and I get that error. I even tried it without using ENV and used the exact api keys and same error.
client = Yelp::Client.new({
consumer_key = ENV['config.consumer_key'],
consumer_secret = ENV['config.consumer_secret'],
token = ENV['config.token'],
token_secret = ENV['config.token_secret'] })
What is different between Production and Development?
UPDATE:
Got it working...
I was able to get it working this way...
def index
current_user.zip_code.present? ? #zip = current_user.zip_code : #zip = "94101"
parameters = { term: 'auto repair', limit: 9 }
#search = client.search(#zip, parameters)
end
private
def client
#client ||= Yelp::Client.new({ consumer_key: ENV['config.consumer_key'],
consumer_secret: ENV['config.consumer_secret'],
token: ENV['config.token'],
token_secret: ENV['config.token_secret']
})
end
Run heroku config --app app-name to check if config variables are being set correctly, if that looks correct. Then try running rails console on Heroku using heroku run rails console --app app-name to check if the Yelp::Client is loading the env properly.
If you just follow the ENV naming convention as provided in the gem readme you might avoid this issue altogether.
Yelp.client.configure do |config|
config.consumer_key = YOUR_CONSUMER_KEY
config.consumer_secret = YOUR_CONSUMER_SECRET
config.token = YOUR_TOKEN
config.token_secret = YOUR_TOKEN_SECRET
end

Cloudfront + Carrierwave

So this seems like it should be quite easy... everyone is saying just to use config.asset_host. When I set that though, all the links inside my app still point to S3.
CarrierWave.configure do |config|
config.storage = :fog
config.fog_credentials = {
:provider => 'AWS',
:aws_access_key_id => AWS_ACCESS_KEY_ID,
:aws_secret_access_key => AWS_SECRET_ACCESS_KEY,
:region => 'us-east-1'
}
config.fog_authenticated_url_expiration = 3.hours
config.asset_host = "http://xyz123.cloudfront.net"
config.fog_directory = S3_BUCKET_NAME
config.fog_public = false
config.fog_attributes = {
'Cache-Control' => "max-age=#{1.year.to_i}"
}
end
here is how I call my files...
image_tag book.attachments.first.filename.file.authenticated_url(:thumb175)
It looks to me like public_url prepends the proper host, but it takes 0 arguments... so how am I supposed to pass the proper response-content-disposition and response-content-type and the link expire time?
I had the same problem and spent far too long figuring out the answer! It turns out that when you set fog_public = false CarrierWave will ignore config.asset_host. You can demo this by setting config.fog_public = true: your URLs will now be CloudFront URLs rather than S3 URLs. This issue has been raised previously:
https://github.com/carrierwaveuploader/carrierwave/issues/1158
https://github.com/carrierwaveuploader/carrierwave/issues/1215
In a recent project I was happy using CarrierWave to handle uploads to S3, but wanted it to return a signed CloudFront URL when using Model.attribute_url. I came up with the following (admittedly ugly) workaround that I hope others can benefit from or improve upon:
Add the 'cloudfront-signer' gem to your project and configure it per the instructions. Then add the following override of /lib/carrierwave/uploader/url.rb in a new file in config/initializers (note the multiple insertions of AWS::CF::Signer.sign_url):
module CarrierWave
module Uploader
module Url
extend ActiveSupport::Concern
include CarrierWave::Uploader::Configuration
include CarrierWave::Utilities::Uri
##
# === Parameters
#
# [Hash] optional, the query params (only AWS)
#
# === Returns
#
# [String] the location where this file is accessible via a url
#
def url(options = {})
if file.respond_to?(:url) and not file.url.blank?
file.method(:url).arity == 0 ? AWS::CF::Signer.sign_url(file.url) : AWS::CF::Signer.sign_url(file.url(options))
elsif file.respond_to?(:path)
path = encode_path(file.path.gsub(File.expand_path(root), ''))
if host = asset_host
if host.respond_to? :call
AWS::CF::Signer.sign_url("#{host.call(file)}#{path}")
else
AWS::CF::Signer.sign_url("#{host}#{path}")
end
else
AWS::CF::Signer.sign_url((base_path || "") + path)
end
end
end
end # Url
end # Uploader
end # CarrierWave
Then override /lib/carrierwave/storage/fog.rb by adding the following to the bottom of the same file:
require "fog"
module CarrierWave
module Storage
class Fog < Abstract
class File
include CarrierWave::Utilities::Uri
def url
# Delete 'if statement' related to fog_public
public_url
end
end
end
end
end
Lastly, in config/initializers/carrierwave.rb:
config.asset_host = "http://d12345678.cloudfront.net"
config.fog_public = false
That's it. You can now use Model.attribute_url and it will return a signed CloudFront URL to a private file uploaded by CarrierWave to your S3 bucket.
I think you found this for yourself, but public urls will not expire. If you want that you'll need to use authenticated urls. For public urls I think you can simply get the url and append whatever query params you would like, at least for now. If that works well for you, we can certainly see about patching things to do the right thing.

Is there a way to set set config.action_controller.asset_host in development

In my production.rb I set my asset_host to CloudFront like so:
config.action_controller.asset_host = 'http://xxxxxxxx.cloudfront.net'
Now I'm finding that in some circumstances (specifically, outputting JavaScript to be embedded into another site) I need to set the asset_host in the development environment too, the default null won't cut it. Ideally I want to set:
config.action_controller.asset_host = 'http://localhost:3000'
but this port can't be guaranteed, and I'm reluctant to hard-code it. Is there a way to set asset_host to the current domain and port?
Thanks!
You can make use of environment variables or Rails initializer parameters
config.action_controller.asset_host = ENV[ASSET_HOST].empty? ? 'http://' + Rails::Server.new.options[:Host] + ':' + Rails::Server.new.options[:Port] : ENV[ASSET_HOST]
This way if you set the environment variable you use that address otherwise it will use the default.
In Rails 4 we use a dynamic asset_host setting with a proc:
# in /config/environments/development.rb
Rails.application.configure do
config.action_controller.asset_host = Proc.new { |source, request|
# source = "/assets/brands/stockholm_logo_horizontal.png"
# request = A full-fledged ActionDispatch::Request instance
# sometimes request is nil and everything breaks
scheme = request.try(:scheme).presence || "http"
host = request.try(:host).presence || "localhost:3000"
port = request.try(:port).presence || nil
["#{scheme}://#{host}", port].reject(&:blank?).join(":")
}
# more config
end
This code ensures that requests from localhost:3000, localhost:8080, 127.0.0.1:3000, local.dev and any other setup just work.
This value is available during startup and might help:
Rails::Server.new.options[:Port]
Try adding it to the asset_host variable of your development.rb file.
Based on this answer: https://stackoverflow.com/a/13839447/1882605
Try:
class ApplicationController < ActionController::Base
before_filter :find_asset_host
private
def find_asset_host
ActionController::Base.asset_host = Proc.new { |source|
if Rails.env.development?
"http://localhost:3000"
else
{}
end
}
end

Carrierwave+fog+s3 not working with Cloud Front URLs

I'm having this problem with carrierwave+fog+s3 with Amazon cloud front. With the following setup I can upload files to s3 but after uploaded, the S3 Objects URLs I get from my rails app doesn't have the assets_host based URLs i.e I'm expeting the URLs to be looks like this format https://mycloudfrontname.cloudfront.net/uploads/myfile.mp3
But they all appear in this format https://mybucketname.s3.amazonaws.com/uploads/myfile.mp3
What might be wrong here?
CarrierWave.configure do |config|
config.fog_credentials = {
:provider => 'AWS',
:aws_access_key_id => 'XXXX',
:aws_secret_access_key => 'XXXX',
:region => 'us-east-1'
}
config.fog_directory = 'mybucketname'
config.asset_host = 'https://mycloudfrontname.cloudfront.net'
config.fog_public = false
config.fog_attributes = {'Cache-Control' => 'max-age=315576000'}
end
UPDATE:
I found this code bit from Carrierwave's /lib/carrierwave/storage/fog.rb - So if we set the asset_host as on above code snippet this must work right? or is there any other configuration I must do as well?
def public_url
if host = #uploader.asset_host
if host.respond_to? :call
"#{host.call(self)}/#{path}"
else
"#{host}/#{path}"
end
else
# AWS/Google optimized for speed over correctness
case #uploader.fog_credentials[:provider]
when 'AWS'
# if directory is a valid subdomain, use that style for access
if #uploader.fog_directory.to_s =~ /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
"https://#{#uploader.fog_directory}.s3.amazonaws.com/#{path}"
else
# directory is not a valid subdomain, so use path style for access
"https://s3.amazonaws.com/#{#uploader.fog_directory}/#{path}"
end
when 'Google'
"https://commondatastorage.googleapis.com/#{#uploader.fog_directory}/#{path}"
else
# avoid a get by just using local reference
directory.files.new(:key => path).public_url
end
end
end
Change config.fog_public to true and add config.asset_host = 'YOUR_CND_ADDRESS'. The asset_host doesn't work when fog_public is false
In your environment file you need to set the asset host. Just add the line below to your config/environments/production.rb file, and you should be okay. Also may want to make sure you're using the latest version of carrierwave and fog gems.
-- config/environments/production.rb
Myapp::Application.configure do
# Use Content Delivery Network for assets
config.action_controller.asset_host = 'https://mycloudfrontname.cloudfront.net'
end
Don't use asset_host. The asset_host setting is for files served by the rails asset helpers. CarrierWave files are handled in a different manner. The config you are looking for is config.fog_host
config.fog_host = 'https://mycloudfrontname.cloudfront.net'

What happened to Mongoid.config.master.connection.host?

I am trying to get Carrierwave (0.5.1) to work with Mongoid (2.0.0.beta.20), Rails 3. I followed every step at this guide.
In config/initializers/carrierwave.rb, I have:
CarrierWave.configure do |config|
config.grid_fs_database = Mongoid.database.name
config.grid_fs_host = Mongoid.config.master.connection.host
config.storage = :grid_fs
config.grid_fs_access_url = "/uploads"
end
When I try to start my server (rails server). In the console, I get:
...config/initializers/carrierwave.rb:3:in `block in <top
(required)>': undefined method `host' for #<Mongo::Connection:
0x00000103802420> (NoMethodError)
I don't understand why I am getting this error. I've looked everywhere
and can't seem to locate why this is happening...
It seems, Mongoid.config.master.connection.host doesn't work anymore
in newer versions of Mongoid. Was this removed? What is the
replacement for this?
So far my workaround is the following code:
CarrierWave.configure do |config|
config.grid_fs_database = Mongoid.database.name
config.grid_fs_host = 'localhost'
config.storage = :grid_fs
config.grid_fs_access_url = "/uploads"
end
Line 3, should be: config.grid_fs_host = 'localhost'. <-- Is there a better way to dynamically indicate the host depending on environment?
Found out that the mongo gem has changed. So it now has to be:
config.grid_fs_host = Mongoid.database.connection.primary_pool.host

Resources