React Rails components won't work Rails 5.2.4 - ruby-on-rails

I installed react-rails since I want to use rails on my Rails 5.2.4 application.
When I installed it, I basically got the following order on my app/javascripts/ directory:
components [directory]
---.keep file
packs [directory]
---applicationCache.js
---hello_react.jsx
---server_rendering.jsx
Inside my components directory I've added a file called appointments.jsx inside app\javascripts\components\' which I link to my Appointments controller and views via<%= react_component 'Appointments' %>here is the content of theappointments.jsx` file:
const Appointments = () => {
return (
<div>
<h1>Hello Appointments</h1>
</div>
);
};
Since I already link this to my index view via <%= react_component 'Appointments' %> I expected this to display an h1 tag with the text Hello Appointments on it on my root (i already set this up on my routes.rb file) instead I got the ff errors on my console:
Hello World from Webpacker
fromRequireContextWithGlobalFallback.js:21 Error: Cannot find module './Appointments'
at webpackContextResolve (.*$:13)
at webpackContext (.*$:8)
at fromRequireContext.js:13
at Object.getConstructor (fromRequireContextWithGlobalFallback.js:15)
at Object.mountComponents (index.js:89)
at HTMLDocument../node_modules/react_ujs/react_ujs/index.js.ReactRailsUJS.handleMount (index.js:149)
at Object.e.dispatch (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6)
at r.notifyApplicationAfterPageLoad (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7)
at r.pageLoaded (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7)
at turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6
(anonymous) # fromRequireContextWithGlobalFallback.js:21
mountComponents # index.js:89
./node_modules/react_ujs/react_ujs/index.js.ReactRailsUJS.handleMount # index.js:149
e.dispatch # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6
r.notifyApplicationAfterPageLoad # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7
r.pageLoaded # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7
(anonymous) # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6
fromRequireContextWithGlobalFallback.js:22 ReferenceError: Appointments is not defined
at eval (eval at ./node_modules/react_ujs/react_ujs/src/getConstructor/fromGlobal.js.module.exports (fromGlobal.js:12), <anonymous>:1:1)
at ./node_modules/react_ujs/react_ujs/src/getConstructor/fromGlobal.js.module.exports (fromGlobal.js:12)
at Object.getConstructor (fromRequireContextWithGlobalFallback.js:19)
at Object.mountComponents (index.js:89)
at HTMLDocument../node_modules/react_ujs/react_ujs/index.js.ReactRailsUJS.handleMount (index.js:149)
at Object.e.dispatch (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6)
at r.notifyApplicationAfterPageLoad (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7)
at r.pageLoaded (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7)
at turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6
(anonymous) # fromRequireContextWithGlobalFallback.js:22
mountComponents # index.js:89
./node_modules/react_ujs/react_ujs/index.js.ReactRailsUJS.handleMount # index.js:149
e.dispatch # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6
r.notifyApplicationAfterPageLoad # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7
r.pageLoaded # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7
(anonymous) # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6
index.js:100 [react-rails] Cannot find component: 'Appointments' for element <div data-react-class=​"Appointments" data-react-props=​"{}​" data-react-cache-id=​"Appointments-0">​</div>​
index.js:103 Uncaught Error: Cannot find component: 'Appointments'. Make sure your component is available to render.
at Object.mountComponents (index.js:103)
at HTMLDocument../node_modules/react_ujs/react_ujs/index.js.ReactRailsUJS.handleMount (index.js:149)
at Object.e.dispatch (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6)
at r.notifyApplicationAfterPageLoad (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7)
at r.pageLoaded (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7)
at turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6
mountComponents # index.js:103
./node_modules/react_ujs/react_ujs/index.js.ReactRailsUJS.handleMount # index.js:149
e.dispatch # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6
r.notifyApplicationAfterPageLoad # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7
r.pageLoaded # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7
(anonymous) # turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6
For the record here's the content of my application.js file under app/packs/ directory:
console.log('Hello World from Webpacker')
// Support component names relative to this directory:
var componentRequireContext = require.context("components", true);
var ReactRailsUJS = require("react_ujs");
ReactRailsUJS.useContext(componentRequireContext);
Any idea what am I doing wrong here? Why does the text not showing up on view?

Did you export your component? I.e. export default Appointments;.
Also it could be a case sensitivity issue, i.e. you are calling it with upper case react_component 'Appointments' and have appointments.jsx.

Related

What cause 'Can't verify CSRF token authenticity' and '422' errors on upload image with dropzone JS?

Working on app for months that has few models accept image upload with dropzone JS using ActiveStorage, and out of no where I can't upload images anymore using dropzone anymore. The only thing I know I changed across the app is changed view engine from ERB to HAML. Could that cause the issue.?
I spent hours searching for a solution, but nothing works. Here is the app error when I attempt to upload an image.
Error creating Blob for "image_name.jpg". Status: 422
And here is terminal log:
Started GET "/rails/active_storage/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDVG9JYTJWNVNTSWhkemx3TjNKMU5teDNhRzQxYzI1bU9YWnBZM1ZzTVRCMk1ua3hOUVk2QmtWVU9oQmthWE53YjNOcGRHbHZia2tpQVl4cGJteHBibVU3SUdacGJHVnVZVzFsUFNJeU1ESXdNRFV3TmpFNE1URXhPUzA1TW1NNU9HRmpaV1ExTW1SaE56UXlZelkyWldWbU4yUTFZMlV6TkdabU55NXFjR2NpT3lCbWFXeGxibUZ0WlNvOVZWUkdMVGduSnpJd01qQXdOVEEyTVRneE1URTVMVGt5WXprNFlXTmxaRFV5WkdFM05ESmpOalpsWldZM1pEVmpaVE0wWm1ZM0xtcHdad1k3QmxRNkVXTnZiblJsYm5SZmRIbHdaVWtpRDJsdFlXZGxMMnB3WldjR093WlVPaEZ6WlhKMmFXTmxYMjVoYldVNkNteHZZMkZzIiwiZXhwIjoiMjAyMS0wMy0yNlQxOTo1Nzo0MS4yMDRaIiwicHVyIjoiYmxvYl9rZXkifX0=--45a242e52828942cb9933781663d53482286244d/20200506181119-92c98aced52da742c66eef7d5ce34ff7.jpg" for ::1 at 2021-03-26 23:52:42 +0400
Processing by ActiveStorage::DiskController#show as JPEG
Parameters: {"encoded_key"=>"[FILTERED]", "filename"=>"20200506181119-92c98aced52da742c66eef7d5ce34ff7"}
Completed 200 OK in 1ms (ActiveRecord: 0.0ms | Allocations: 471)
Started POST "/rails/active_storage/direct_uploads" for ::1 at 2021-03-27 05:21:48 +0400
Processing by ActiveStorage::DirectUploadsController#create as JSON
Parameters: {"blob"=>{"filename"=>"88a04c2e5f589fcebda2641d00b8427f.jpg", "content_type"=>"image/jpeg", "byte_size"=>511762, "checksum"=>"y/nNLQR9mGaB5haUie2M5Q=="}, "direct_upload"=>{"blob"=>{"filename"=>"88a04c2e5f589fcebda2641d00b8427f.jpg", "content_type"=>"image/jpeg", "byte_size"=>511762, "checksum"=>"y/nNLQR9mGaB5haUie2M5Q=="}}}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms | Allocations: 1029)
ActionController::InvalidAuthenticityToken - ActionController::InvalidAuthenticityToken:
I'm using Rails6, and ruby 3. No calling API involve at this stage. I noticed that uploading images with dropzone JS cause this issue. And upload without dropzone JS works fine. What could be the cause?
Here is console error:
dropzone.js:8185 Uncaught TypeError: Cannot read property 'apply' of undefined
at Dropzone.emit (dropzone.js:8185)
at dropzone.js:10197
at dropzone.js:10423
at loadExif (dropzone.js:10346)
at HTMLImageElement.img.onload (dropzone.js:10357)
emit # dropzone.js:8185
(anonymous) # dropzone.js:10197
(anonymous) # dropzone.js:10423
loadExif # dropzone.js:10346
img.onload # dropzone.js:10357
load (async)
createThumbnailFromUrl # dropzone.js:10344
fileReader.onload # dropzone.js:10294
load (async)
createThumbnail # dropzone.js:10283
_processThumbnailQueue # dropzone.js:10196
(anonymous) # dropzone.js:10179
setTimeout (async)
_enqueueThumbnail # dropzone.js:10178
addFile # dropzone.js:10113
(anonymous) # dropzone.js:9533
activestorage.js:739 POST
http://localhost:5000/rails/active_storage/direct_uploads 422 (Unprocessable Entity)
create # activestorage.js:739
(anonymous) # activestorage.js:873
fileReaderDidLoad # activestorage.js:620
(anonymous) # activestorage.js:605
load (async)
create # activestorage.js:604
create # activestorage.js:584
create # activestorage.js:865
start # dropzone_controller.js:93
(anonymous) # dropzone_controller.js:31
setTimeout (async)
(anonymous) # dropzone_controller.js:30
emit # dropzone.js:8185
addFile # dropzone.js:10111
(anonymous) # dropzone.js:9533
UPDATE
Here is my dropzone_controller.js file with stimulus
import { Controller } from "stimulus";
import Dropzone from "dropzone";
import "dropzone/dist/min/dropzone.min.css";
import "dropzone/dist/min/basic.min.css";
import { DirectUpload } from "#rails/activestorage";
export default class extends Controller {
static targets = ["input"];
connect() {
Dropzone.autoDiscover = false;
this.inputTarget.disable = true;
this.inputTarget.style.display = "none";
const dropzone = new Dropzone(this.element, {
url: "/",
maxFiles: "10",
maxFilesize: "10",
});
dropzone.on("addedfile", (file) => {
setTimeout(() => {
if (file.accepted) {
const upload = new DirectUpload(file, this.url);
upload.create((error, attributes) => {
this.hiddenInput = document.createElement("input");
this.hiddenInput.type = "hidden";
this.hiddenInput.name = this.inputTarget.name;
this.hiddenInput.value = attributes.signed_id; << error here
this.inputTarget.parentNode.insertBefore(
this.hiddenInput,
this.inputTarget.nextSibling
);
dropzone.emit("success", file);
dropzone.emit("complete", file);
});
}
}, 500);
});
}
get url() {
return this.inputTarget.getAttribute("data-direct-upload-url");
}
}
When I attempt to upload an image, an error related to blob shows up.
Uncaught TypeError: can't access property "signed_id", blob is undefined

Rails - How to pass Sprockets::Context in manual sass compiling

I'm using the following code snippet to manually compile a sass manifest with some variable overrides appended.
template = File.read("#{Rails.root}/app/assets/schemes/#{scheme}/css/styles.css.scss")
scheme_variables.each do |key, value|
template << "$#{key}:#{value};\n"
end
engine = Sass::Engine.new(template, {
:syntax => :scss,
:cache => false,
:read_cache => false,
:style => :compressed,
:filesystem_importer => Sass::Rails::SassImporter,
:load_paths => MyApp::Application.assets.paths,
:sprockets => {
:context => ?,
:environment => MyApp::Application.assets
}
})
output = engine.render
The Sass::Engine constructor wants a sprockets context and environment in the options hash. What do I put in for the context? The first thing I tried was...
:context => MyApp::Application.assets.context_class,
...but that gives me the following error "undefined method `font_path' for #" when it hits one of my sass asset helpers.
The second thing I tried was...
:context => ActionController::Base.helpers,
...That fixed the asset helper issue, but throws the following error "undefined method `depend_on' for #" when it tries to work through my glob imports (e.g. #import "mixins/*").
I'm using Rails 4.2 and sass-rails 5.0.3.
Any advice on this would be much appreciated. Thanks!
With using Sass::Rails::ScssTemplate you can render your sass code with this snippet:
template = '...' # Your sass code
logical_path = pathname = ''
environment = Rails.application.assets
context = environment.context_class.new(environment, logical_path, pathname)
template = Sass::Rails::ScssTemplate.new(pathname) { template }
output = template.render(context, {})
If you want to render from a file then just add its path to pathname and its asset path to logical_path.
For me it works with Rails 4.2.5.1 and sass-rails 5.0.4.
I ended up solving this in a slightly different way - using Sass::Rails::ScssTemplate's render method. Basically, I write my altered css string out to a file and pass it into the Sass::Rails::ScssTemplate constructor. I then compile and remove the temp file when it's done. This doesn't feel great, but it's working well for me.
scheme_css_dir = "#{Rails.root}/app/assets/schemes/#{scheme}/css"
css = File.read("#{scheme_css_dir}/styles.css.scss")
variables_str = ''
scheme_variables.each do |key, value|
variables_str << "$#{key}:#{value};\n"
end
css.gsub!('#import "./variables";', variables_str)
file = Tempfile.new(['styles', '.css.scss'], scheme_css_dir)
file.write(css)
file.close
abs_path = file.path
relative_path = abs_path[Rails.root.to_s.size + 1..-1]
template = Sass::Rails::ScssTemplate.new(abs_path)
environment = Evrconnect::Application.assets
context = environment.context_class.new(
:environment => environment,
:name => relative_path,
:filename => abs_path,
:metadata => {}
)
output = template.render(context)
file.unlink
To answer your original question, you need to supply either the current view_context, or create one with ActionView::Base.new.
http://apidock.com/rails/AbstractController/Rendering/view_context
In Rails 5.x, Sprockets v3.7.x
NOTE: Following method requires: Rails.application.config.assets.compile == true, i.e., set config.assets.compile = true. Check Sprockets README
I manually returned compiled source for sass/js, using the following code.
# For Scss Assets
def pdf_stylesheet_link_tag(name)
if Rails.env.development?
asset = Rails.application.assets.find_asset(name + '.scss')
raw ('<style type="text/css">' + asset.source + '</style>')
else
raw ('<style type="text/css">' + pdf_asset_contents(name, '.css') + '</style>')
end
end
# For Javascript Assets
def pdf_javascript_include_tag(name, *type)
if Rails.env.development?
if debug? # Can be used to check `debug == true` in params for current route
javascript_include_tag(name)
else
asset = Rails.application.assets.find_asset(name + '.js')
raw ('<script>' + asset.source + '</script>')
end
else
javascript_tag pdf_asset_contents(name, '.js')
end
end
Above code actually helps to dynamically pass compiled version of sass/js
to Wicked_PDF, which actually helps to load styles and js on Generated PDFs, for Dev Environments.
pdf_stylesheet_link_tag can be used as a helper for templates, in development/stage (where, config.assets.precompile == false), instead of using wicked_pdf_stylesheet_link_tag(which actually requires a path to precompiled source-file).
After overnight trial and errors, I found and want to share todays's way of doing it ("today" meaning: rails 5.2.1 and sprockets 3.7.2).
Work as expected: no need of a temp file, accept #import, allow asset path helpers.
# Compile SASS and return the resulting string
# Pass the file path and name without 'sass' extension, relative to assets/stylesheets
def compile_sass(stylesheet)
# Load file content
path = Rails.root.join 'app', 'assets', 'stylesheets', "#{stylesheet}.sass"
sass = File.read path
# Configure engine
environment = Sprockets::Railtie.build_environment Rails.application
scope = environment.context_class.new environment: environment, \
filename: "/", metadata: {}
scope.sass_config.merge! cache: false, style: :compressed
# Compile
engine = Sass::Rails::SassTemplate.new {sass}
engine.render scope
end
Other sass_config options can be found here : https://github.com/sass/ruby-sass/blob/stable/doc-src/SASS_REFERENCE.md#options
The following works with rails 5.2.0, sprockets 3.7.2, sassc-rails 1.3.0 and sassc 1.12.1:
template = '...' # Your sass code
environment = Sprockets::Railtie.build_environment(Rails.application)
engine = SassC::Rails::SassTemplate.new
engine.call(environment: environment,
filename: '/',
data: template,
metadata: {})[:data]

how to get a relative path in Rails 3.2?

I have a list of files , and I try to get a relative path
file = "/Users/yves/github/local/workshop/public/uploads/craftwork/image/1/a1d0.jpg"
Rails.public_path => "/Users/yves/github/local/workshop/public"
# I am trying to get => "uploads/craftwork/image/1/a1d0.jpg"
file.relative_path_from(Rails.public_path) # is wrong
# raising : undefined method `relative_path_from' for #<String ( file is a String..)
# so I tried to use Pathname class
Pathname.new(file).relative_path_from(Rails.public_path)
# but the I get another error
# undefined method `cleanpath' for String
Is relative_path_from deprecated in Rails 3.2 ? if yes , what's the good one now?
Since these properties now return strings, we can convert them back into path names:
public_path = Pathname.new( Rails.public_path )
file_path = Pathname.new( file )
and then use the relative path function, finally converting it back into a string
relative_path = file_path.relative_path_from( public_path ).to_s
That together becomes
Pathname.new( file ).relative_path_from( Pathname.new( Rails.public_path ) ).to_s
You could 'cheat' and just remove the public_path using sub...
$ cat foo.rb
file = "/Users/yves/github/local/workshop/public/uploads/craftwork/image/1/a1d0.jpg"
public_path = "/Users/yves/github/local/workshop/public"
puts file.sub(/^#{public_path}\//, '')
$ ruby foo.rb
uploads/craftwork/image/1/a1d0.jpg
This is what I have used:
"#{Rails.root}/public/spreadsheets/file_name.xlsx"

Rails 2.3.8 Render a html as pdf

I am using Prawn pdf library however i am doing a complex design, So I need a quick solution as to convert a html to pdf file.
Thanks in advance
I would use wkhtmltopdf shell tool
together with the wicked_pdf ruby gem, its free, and uses qtwebkit to render your html to pdf. Also executes javascript as well, for charts for example. You can find more info about installation: https://github.com/mileszs/wicked_pdf
I have one Rails app that's been using PrinceXML in production for a few years. It is pricey - around $4K for a server license - but does a very good job of rendering HTML+CSS in PDF files. I haven't looked at newer solutions since this one is paid for and working quite well.
For what it's worth, here's some code I adapted from Subimage Interactive to make the conversion simple:
lib/prince.rb
# Prince XML Ruby interface.
# http://www.princexml.com
#
# Library by Subimage Interactive - http://www.subimage.com
#
#
# USAGE
# -----------------------------------------------------------------------------
# prince = Prince.new()
# html_string = render_to_string(:template => 'some_document')
# send_data(
# prince.pdf_from_string(html_string),
# :filename => 'some_document.pdf'
# :type => 'application/pdf'
# )
#
class Prince
attr_accessor :exe_path, :style_sheets, :log_file
# Initialize method
#
def initialize()
# Finds where the application lives, so we can call it.
#exe_path = '/usr/local/bin/prince'
case Rails.env
when 'production', 'staging'
# use default hard-coded path
else
if File.exist?(#exe_path)
# use default hard-coded path
else
#exe_path = `which prince`.chomp
end
end
#style_sheets = ''
#log_file = "#{::Rails.root}/log/prince.log"
end
# Sets stylesheets...
# Can pass in multiple paths for css files.
#
def add_style_sheets(*sheets)
for sheet in sheets do
#style_sheets << " -s #{sheet} "
end
end
# Returns fully formed executable path with any command line switches
# we've set based on our variables.
#
def exe_path
# Add any standard cmd line arguments we need to pass
#exe_path << " --input=html --server --log=#{#log_file} "
#exe_path << #style_sheets
return #exe_path
end
# Makes a pdf from a passed in string.
#
# Returns PDF as a stream, so we can use send_data to shoot
# it down the pipe using Rails.
#
def pdf_from_string(string)
path = self.exe_path()
# Don't spew errors to the standard out...and set up to take IO
# as input and output
path << ' --silent - -o -'
# Show the command used...
#logger.info "\n\nPRINCE XML PDF COMMAND"
#logger.info path
#logger.info ''
# Actually call the prince command, and pass the entire data stream back.
pdf = IO.popen(path, "w+")
pdf.puts(string)
pdf.close_write
output = pdf.gets(nil)
pdf.close_read
return output
end
end
lib/pdf_helper.rb
module PdfHelper
require 'prince'
private
def make_pdf(template_path, pdf_name, stylesheets = [], skip_base_pdf_stylesheet = false)
# application notices should never be included in PDFs, pull them from session here
notices = nil
if !flash.now[:notice].nil?
notices = flash.now[:notice]
flash.now[:notice] = nil
end
if !flash[:notice].nil?
notices = '' if notices.nil?
notices << flash[:notice]
flash[:notice] = nil
end
prince = Prince.new()
# Sets style sheets on PDF renderer.
stylesheet_base = "#{::Rails.root}/public/stylesheets"
prince.add_style_sheets(
"#{stylesheet_base}/application.css",
"#{stylesheet_base}/print.css"
)
prince.add_style_sheets("#{stylesheet_base}/pdf.css") unless skip_base_pdf_stylesheet
if 0 < stylesheets.size
stylesheets.each { |s| prince.add_style_sheets("#{stylesheet_base}/#{s}.css") }
end
# Set RAILS_ASSET_ID to blank string or rails appends some time after
# to prevent file caching, messing up local - disk requests.
ENV['RAILS_ASSET_ID'] = ''
html_string = render_to_string(:template => template_path, :layout => 'application')
# Make all paths relative, on disk paths...
html_string.gsub!("src=\"", "src=\"#{::Rails.root}/public")
html_string.gsub!("src=\"#{::Rails.root}/public#{::Rails.root}", "src=\"#{::Rails.root}")
# re-insert any application notices into the session
if !notices.nil?
flash[:notice] = notices
end
return prince.pdf_from_string(html_string)
end
def make_and_send_pdf(template_path, pdf_name, stylesheets = [], skip_base_pdf_stylesheet = false)
send_data(
make_pdf(template_path, pdf_name, stylesheets, skip_base_pdf_stylesheet),
:filename => pdf_name,
:type => 'application/pdf'
)
end
end
sample controller action
include PdfHelper
def pdf
index
make_and_send_pdf '/ads/index', "#{filename}.pdf"
end
You can directly convert your existing HTML to PDF using acts_as_flying_saucer library.For header and footer you can refer
https://github.com/amardaxini/acts_as_flying_saucer/wiki/PDF-Header-Footer

Problems Rendering View (ActionView::MissingTemplate ... Error) in Custom Plugin

I am trying to develop a plugin for Ruby on Rails and came across problems rendering my html view. My directory structure looks like so:
File Structure
---/vendor
|---/plugins
|---/todo
|---/lib
|---/app
|---/controllers
|---todos_controller.rb
|---/models
|---todos.rb
|---/views
|---index.html.erb
|---todo_lib.rb
|---/rails
|---init.rb
In /rails/init.rb
require 'todo_lib'
In /lib/app/todo_lib.rb
%w{ models controllers views }.each do |dir|
# Include the paths:
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/lib/app/models
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/lib/app/controllers
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/lib/app/views
path = File.expand_path(File.join(File.dirname(__FILE__), 'app', dir))
# We add the above path to be included when Rails boots up
$LOAD_PATH << path
ActiveSupport::Dependencies.load_paths << path
ActiveSupport::Dependencies.load_once_paths.delete(path)
end
In todo/lib/app/controllers/todos_controller.rb
class TodosController < ActionController::Base
def index
end
end
In todo/lib/app/views/index.html.erb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"[url]http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd[/url]">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>Todos:</title>
</head>
<body>
<p style="color: green" id="flash_notice"><%= flash[:notice] %></p>
<h1>Listing Todos</h1>
</body>
</html>
In /myRailsApp/config/routes.rb
ActionController::Routing::Routes.draw do |map|
# The priority is based upon order of creation: first created -> highest priority.
map.resources :todos
...
The error I get is the following:
Template is missing
Missing template todos/index.erb in view path app/views
Can anyone give me a hand up and tell me what am I doing wrong here that is causing my index.html.erb file to not render? Much appreciated!
EDIT:
I have already tried the following without success:
In /todo/lib/app/controllers/todos_controller.rb
def index
respond_to do |format|
format.html # index.html.erb
end
end
EDIT:
hakunin solved this problem. Here's the solution.
He says that I'm building a Rails engine plugin (I had no idea I was doing this), and it requires a different directory structure, one that appears like so:
File Structure
---/vendor
|---/plugins
|---/todo
|---/lib
|---/app
|---/controllers
|---todos_controller.rb
|---/models
|---todos.rb
|---/views
|---/todos
|---index.html.erb
|---todo_lib.rb
|---/rails
|---init.rb
This required the following changes:
In todo/lib/todo_lib.rb
%w{ models controllers views }.each do |dir|
# Include the paths:
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/app/models
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/app/controllers
# /Users/Me/Sites/myRailsApp/vendor/plugins/todo/app/views
path = File.expand_path(File.join(File.dirname(__FILE__), '../app', dir))
# We add the above path to be included when Rails boots up
$LOAD_PATH << path
ActiveSupport::Dependencies.load_paths << path
ActiveSupport::Dependencies.load_once_paths.delete(path)
end
The change made above is in the line: path = File.expand_path(File.join(File.dirname(FILE), '../app', dir)). [Ignore the boldened 'FILE', this is an issue with the website].
Running script/server will render the index.html.erb page under todo/app/views/todos.
Looks like you want to build an "engine" plugin. Create "app" and "config" dirs in the root of your plugin dir (not under /lib). You can use app/views/ and app/controllers in your plugin as if it was a full featured Rails app. In config/routes.rb you should declare routes introduced by your engine.
See http://github.com/neerajdotname/admin_data for a decent example of what engine looks like.

Resources