trying to display pdf in view in ruby on rails app - ruby-on-rails

What I'm trying to do is display a pdf inline in my RoR app. I created a new folder in my assets folder called portfolio and put a pdf file in there. Here is what I'm doing in my controller:
def portfoliopdf
send_file(Rails.root.join("assets", "portfolio", "Portfolio.pdf").to_s, :disposition => "inline", :type => "application/pdf")
end
For whatever reason I keep getting the following error - ActionController::MissingFile in PagesController#portfoliopdf
Cannot read file C:/Sites/inspired/assets/portfolio/Portfolio.pdf
Also I created portfolio.html.erb and have it blank. I also added this in application.rb (not sure if this needs to be tweaked)...
config.assets.paths << Rails.root.join("app", "assets", "portfolio")

After compiling the assets, your pdf is stored here
{Rails.root}/public/assets/Portfolio.pdf
and it's web path is
/assets/Portfolio.pdf
Therefore you may need to use
Rails.root.join("public", "assets", "Portfolio.pdf")

Related

elegant way to File.read assets in development

I have the following code
module EmailHelper
def email_image_tag(image, **options)
attachments.inline[image] = File.read(image_path(image))
image_tag attachments[image].url, **options
end
end
In production this references the correct image with hash, because the image asset is precompiled, however in development this throws a file read exception.
Is there an elegant way to do a File.read without having to check on if Rails.env.development?
The biggest misconception here was that I thought I should point to a digested asset, forgetting that these are meant to be served and not read as a file. Therefore the file can be perfectly loaded from the asset map. I solved the above question as follows:
module MailHelper
def mail_image_tag(image, **options)
path = Rails.root.join('app', 'assets', 'images', image)
attachments.inline[image] = File.read(path)
image_tag attachments[image].url, **options
end
end

How to copy over /public folder of Rails Engine to a Rails application in production?

I know a simple solution would be just to manually copy over all the files in the Rails Engine's /public folder to the Rails application's /public folder. However, this means that each installation would require manually copying.
Furthermore because the Javascript files that my engine uses have hard-coded image paths, I cannot simply throw all my static files under app/assets or vendor/assets, as then Rails would copy them over to public/assets. I can't change the path where Sprockets outputs the files as I have other gems that expect their assets to be in the default public/assets folder.
I tried doing something like
class Engine < ::Rails::Engine
if Rails.application.config.serve_static_assets
initializer "static assets" do |app|
app.middleware.insert_before(::ActionDispatch::Static, ::ActionDispatch::Static, "#{root}/public")
end
end
end
but this only works for development.
Personally, I believe that the best solution would be to update your javascript files to follow Rails' conventions by replacing the hard-coded images in the javascript with the asset path helper - http://guides.rubyonrails.org/asset_pipeline.html#coding-links-to-assets and then throwing everything into app/assets.
With that said, I can think of situations where you may not want to do that, and that may be the case here.
From your post, I'm guessing that you're precompiling assets for production (i.e. - running rake assets:precompile). In that case, you can just hook into the assets:precompile rake task and copy the files over. It would look something like this in your engine's lib/tasks/my_engine_tasks.rake:
Rake::Task["assets:precompile"].enhance do
Rake::Task["my_engine:copy_assets"].invoke
end
namespace :my_engine do
task :copy_assets => :"assets:environment" do
assets = ["file1", "file2"]
assets.each do |asset|
source_file = File.join(MyEngine::Engine.root, 'public', asset)
dest_file = File.join(Rails.root, 'public', asset)
FileUtils.copy_file source_file, dest_file, true
end
end
end
The above is a simple copy operation but you can also manually run sprockets over those files as well. Take a look at the code for the assets:precompile task (lib/sprockets/rails/task.rb in the sprockets-rails gem) and the Sprockets Manifest class (lib/sprockets/manifest.rb in the sprockets gem).
Seems Rails doesnt provide an obvious way to serve static assets from an engine. So heres my dynamic solution for this problem.
module MyApp
class Engine < ::Rails::Engine
isolate_namespace MyApp
initializer "my_app.assets.precompile" do |app|
app.config.assets.precompile << "my_app_manifest.js" ### manifest file required
### Precompile all static assets
### Note in this example use a non-standard folder (app/assets/public/)
["app/assets/images/", "app/assets/public/"].each do |folder|
dir = app.root.join(folder)
if Dir.exist?(dir)
Dir.glob(File.join(dir, "**/*")).each do |f|
asset_name = f.to_s
.split(folder).last # Remove fullpath
.sub(/^\/*/, '') ### Remove leading '/'
app.config.assets.precompile << asset_name
end
end
end
end
end
end

How to add markdown as a preprocessor to the asset pipeline?

I have markdown files in app/assets/md/*.html.md which I would like to precompile with rake assets:precompile.
How can I set the asset pipeline up so that it will use a markdown parser to produce the expected HTML files?
Also, how can I include these precompiled assets directly within my views, such as:
<div>
<%= yield asset("my_markdown_fragment.html") %>
</div>
I have since found the solution. Here is what I did:
Add rdiscount to the Gemfile (rdiscount is a Markdown processor for Ruby):
gem 'rdiscount'
Create the file lib/markdown_preprocessor.rb with these contents:
require 'rdiscount'
class MarkdownPreprocessor < Sprockets::Processor
def evaluate(context, locals)
RDiscount.new(begin;data;end).to_html
end
end
Add the MarkdownPreprocessor to the asset pipeline in the config/initializers/sprockets.rb file (create this file if it does not exist) like this:
require 'markdown_preprocessor'
Rails.application.assets.register_engine('.md', MarkdownPreprocessor)
Add the following line to the config/initializers/assets.rb file:
Rails.application.config.assets.paths << Rails.root.join('app', 'assets', 'md')
Configuration is done now. Here are some other guides and tips:
Place your Markdown files into the app/assets/md/ folder.
The extension of your files end with .md, but I suggest .html.md (since the result is an HTML file).
You can insert <%= ruby %> into your Markdown files when you add the .erb extension. For example, try creating this file app/assets/md/hello.html.md.erb with the following contents:
# Hello <%= "_World_ " * 42 %>
You can access the resulting HTML content in your views and controllers with:
Rails.application.assets[asset_path].to_s.html_safe

Rails 3 app + paperclip gem + production mode = download of empty files

I've installed the paperclip gem for a Rails 3 application. Everything works fine in development mode. However, when running in production mode, if I upload a file and then try to download it again, it downloads a file with the correct name and extension, but it is an empty file. When looking on the server, the file does get uploaded and is in the correct directory. (I have an "uploads" folder in my application root.)
Anyone had this happen?
My model:
# app/models/document.rb
class Document < ActiveRecord::Base
belongs_to :kase
has_attached_file :document, :path => (Rails.root + "uploads/:class/:kase_id/:id").to_s, :url => ":class/:id"
validates_attachment_presence :document
validates_attachment_content_type :document, :content_type => [
'application/pdf',
'image/png',
'image/jpeg',
'image/pjpeg',
'text/plain'
]
end
My controller:
# app/controllers/documents_controller.rb
class DocumentsController < ApplicationController
respond_to :html
before_filter :initialize_kase # Sets the #kase instance
def show
#document = #kase.documents.find(params[:id])
send_file #document.document.path, :filename => #document.document_file_name, :content_type => #document.document_content_type
end
end
And my initializer (setting the :kase_id placeholder used in has_attached_file above:
# config/initializers/paperclip.rb
Paperclip.interpolates('kase_id') do |attachment, style|
"kases/#{attachment.instance.kase.id.to_s}"
end
I should probably mention, too, that I am accessing this as a nested controller (/kases/XX/documents/XX). Not sure if that has an effect or not...
If you are using Apache and Passenger, (possibly other servers as well) and have the line:
config.action_dispatch.x_sendfile_header = "X-Sendfile"
in your production.rb env file, then you have two options:
Install the apache module mod-xsendfile
Comment out that line and let Rails send the files instead of Apache, like it does in development mode.
Are you carrying over the uploads directory each time you deploy your app to production? Assuming that you're using capistrano (or similar) for deployment, each time you deploy you might be creating a new uploads directory in the newly-deployed release directory. In that case, the previously-uploaded files are residing in older deployed releases (if you didn't delete those) and would no longer be accessible to your app.
You want to create e.g. shared/uploads directory that is symlinked into your app on each deploy.

How to find a File path in Rails

I'm using the .foreach method from the Ruby CSV library and I need help in finding a path to a file within my rails application.
CSV.foreach("path/to/file.csv") do |row|
# use row here...
end
To upload CSV files I'm using the Paperclip gem which has a method .url for the file location:
CSV.foreach(CsvUpload.last.csvfile.url) do |row|
#more code
end
CsvUpload Load (0.2ms) SELECT `csv_uploads`.* FROM `csv_uploads` ORDER BY csv_uploads.id DESC LIMIT 1
No such file or directory - /system/csvfiles/17/original/uploadthis.csv?1305217588
The actual path of the file is: /Users/boris/projects/chaggregator/public/system/csvfiles/17/original/uploadthis.csv?1305217588
Is there a Rails method for getting the full path?
You need to include the "/Users/boris/projects/chaggregator/public" portion. Paperclip includes a path method to give this to you:
CSV.foreach(CsvUpload.last.csvfile.path) do |row|
#more code
end

Resources