Rails 3.1 absolute URL to an image - ruby-on-rails

I'm using Rails 3.1. I'm trying to figure this out, and to my surprise, it is starting to seem that rails does not come with this method at all. Maybe im wrong.
Can anyone show how I can get a full absolute URL to an image?
I use asset_path(image.png) which gives me the relative path to use within the app. I tried doing a root_url + asset_path(image.png) but that just gives me a http://localhost:3000//assets/image.png with the double slashes
Anyone have an efficient way of doing this?

See the Using asset hosts section in the documentation. You need to specify an asset_host. You can also construct it dynamically from the request chaining "#{request.protocol}#{request.host_with_port}"

put this in application_helper.rb
def asset_url asset
"#{request.protocol}#{request.host_with_port}#{asset_path(asset)}"
end
then you can use asset_url in your views.

For Rails 4, and maybe earlier, use:
config.action_mailer.asset_host = 'https://assets.com'
per https://github.com/fphilipe/premailer-rails/issues/16

In my config/environments/*.rb I already have this tailored for each environment:
config.domain = 'mysite.dev'
So it was a simple matter of adding
config.action_controller.asset_host = "http://" + config.domain
to each file. Then asset_path will miraculously behave as if it were asset_url.

Example folder structure.
app/
assets/
flags/
32x32/
en.png
256x256/
en.png
If you want to generate absolute flag image path we can add in to our ApplicationHelper two methods:
module ApplicationHelper
# Generate flag path by locale
# - locale. Can be "en", "it", etc.
# - flag_size. Will be used to set folder size. Folder size can be "32x32", "256x256".
# Return flag image path. Path will absolute
def generate_flag_path_by_locale(locale, folder_size = "32")
folder = "#{flag_size}x#{flag_size}"
domain_absolute_path = generate_domain_absolute_path
flag_path = ("#{domain_absolute_path}/assets/flags/#{folder}/#{locale}.png")
return flag_path
end
# Generate domain absolute path
def generate_domain_absolute_path
request_protocol = request.protocol
request_host_with_port = request.host_with_port
domain_absolute_path = request_protocol + request_host_with_port
return domain_absolute_path
end
end
Into our apps/view/products.html.erb. We must to call only:
<% flag_path = generate_flag_path_by_locale("en") %>
Final result:
http://my_domain.com:3000/assets/flags/32x32/en.png

Could you just do:
root_url[0..-2] + asset_path(image.png)
...to trim the trailing slash in the root url?

You need to use 'asset_url' instead *asset_path*.
Bcz '_path' always return relative path and '_url' will return absolute url.

Related

Rails.root showing correct path but Rails.root.join is not showing correct path

Rails.root work but Rails.root.join is not working as expected.
puts Rails.root # /work/project
src_dir = "/public/files"
puts Rails.root.join(src_dir)
# expected: /work/project/public/files
# showing: /public/files
The problem is that with the first slash in public you're making a reference to the folder public located in /, not in the relative path where your project is.
Removing that first slash might give you the expected output:
Rails.root.join('public/files')

Write a script in Ruby to rename all *.htm files to *.html

I need to write a script in Ruby to rename all *.htm files to *.html in a given
directory.
I've been given a script with some pieces missing.
I need to "METHOD" with the appropriate method name and "REGEX" with an appropriate
regular expression to match all the files that end in .htm.
Dir.METHOD("*.htm").each do |html_file|
FileUtils.METHOD html_file, "#{html_file.METHOD(/REGEX/,'.html')}"
end
Does anyone know what I should replace "METHOD" and "REGEX" with?
Dir.glob("*.htm") do |html_file|
FileUtils.mv(html_file, "#{File.basename(html_file, ".htm")}.html")
end
Dir.glob("*.htm").each do |html_file|
FileUtils.mv html_file, "#{html_file.sub(/.htm/,'.html')}"
end
Here's how I did it though it did not use a FileUtils method and I skipped ahead and did string manipulation before it was discussed in the lessons.
Dir.glob("*.htm") {|old_filename| #Save file names w/ .htm to old_
tmp_filename = old_filename.slice(0..-5) #Remove (.htm) the file extension
new_filename = tmp_filename + '.html' #Append the .html extension
puts new_filename #Display renamed file names
}

Rails: What is the correct usage of Rails.root.join when pointing to "tmp/caching-dev.txt"?

I'm setting up a new Rails project and after giving it an initial tidy with Rubocop, I'm left with a single offense.
Rubop complains:
config/environments/development.rb:16:6: C: Please use Rails.root.join('path', 'to') instead.
if Rails.root.join("tmp/caching-dev.txt").exist?
I see that Rails.root returns the path of the current project. So I've tried
if File.join(Rails.root, "tmp/caching-dev.text").exist?
instead. But still, Rubocop complains:
config/environments/development.rb:17:6: C: Please use Rails.root.join('path', 'to') instead.
if File.join(Rails.root, "tmp/caching-dev.text").exist?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
What are the path and root arguments meant to be? Surely Rails.root is the path?!
I think Rubocop is suggesting you to do something like this
if File.exist?(Rails.root.join('tmp', 'caching-dev.txt'))
IMHO, Pathname and / are not used enough.
if (Rails.root / 'tmp' / 'caching-dev.txt').exist?
I solved problem by config .rubocop.yml:
Style/ExpandPathArguments:
EnforcedStyle: Style/ExpandPathArguments
Enabled: false

Dragonfly set original filename

I'm using Dropbox with Dragonfly to store my file uploads.
I have the following code:
if hash[:file_dropbox].present? #DROPBOX
path = "Attachments/" + Time.now.strftime("%d%m%Y_%H%M") + "_" + rand(1000).to_s + "_" + File.basename(hash[:file_dropbox])
data = open(hash[:file_dropbox]) { |f| f.read }
file["file"] = Dragonfly[:images].store(data, path: path)
end
Problem:
Dragonfly uses the filename I use for path as name for storing. Is it possible to set a different separate filename when using Dragonfly.store?
With normal files Dragonfly uses original_filename, but I cannot seem to set this via Dragonfly.store.
Any suggestions?
EDIT1:
I've tried the following:
file["file"] = Dragonfly[:images].store(data, path: path,
original_filename: "hello.docx")
file["original_filename"] = File.basename(hash[:file_dropbox])
When using normal file upload, the original_filename can be set. But I can't set data.original_filename because it's just binary data.
Not sure what is your setup, but I have model Photo and dragonfly attachment is set as image. In my case, it works like that:
photo.image.meta["name"] = "new_file_name.jpg"
I never used Dragonfly but I was going through their source code and i think passing a name option might work. Please have a look at Github repo
def name
meta["name"] || temp_object.original_filename
end
In your Model class using DragonFly, add an accessor for your image:
dragonfly_accessor :image do
storage_options{|attachment| {path: "<somehow generate your path>"}
end

Sanitising a download URL with params being passed to send_file

I have numerous files in a directory app/assets/downloadables/ and I want authenticated users to be able to download arbitrary files by name in params within that directory or any subdirectories.
How can I sanitise the input to send_file to prevent users from accessing arbitrary files on the server?
At the moment I'm looking at doing something like this, but I'm not confident that it's safe:
DOWNLOADS_ROOT = File.join(Rails.root, "app", "assets", "downloadables")
# e.g. request.path = "/downloads/subdir/file1.pdf"
file = request.path.gsub(/^\/downloads\//,'').gsub('..','').split('/')
# send file /app/assets/downloadables/subdir/file1.pdf
send_file File.join(DOWNLOADS_ROOT, file)
Would this sufficiently protect against app-wide arbitrary file access or are there improvements or a different approach that would be better?
I found an answer to this here: http://makandracards.com/makandra/1083-sanitize-user-generated-filenames-and-only-send-files-inside-a-given-directory
This file needed to be created per the link:
app/controllers/shared/send_file_inside_trait.rb
module ApplicationController::SendFileInsideTrait
as_trait do
private
def send_file_inside(allowed_path, filename, options = {})
path = File.expand_path(File.join(allowed_path, filename))
if path.match Regexp.new('^' + Regexp.escape(allowed_path))
send_file path, options
else
raise 'Disallowed file requested'
end
end
end
end
To be used as follows in controllers:
send_file_inside File.join(Rails.root, 'app', 'assets', 'downloadables'), request.path.gsub(/^\/downloads\//,'').split('/')
The magic happens where the calculated path of allowed_path + file_name is entered into expand_path which will strip out any special directory browsing strings and just return an absolute path. That resolved path is then compared against the allowed_path to ensure that the file being requested resides within the allowed path and/or sub-directories.
Note that this solution requires the Modularity gem v2 https://github.com/makandra/modularity

Resources