The following code does not work. It says undefined method 'table_name' for nil:NilClass
#members = Members.all
table member_list_rows do
if #members.table_name == members
row(0).background_color = '3498db'
end
end
Full code
class MemberPdf < Prawn::Document
def initialize(members, view, allcount)
super(top_margin: 50)
if members.size != allcount
#warn = " (Not all members)"
else
#all = " All"
end
text "Showing#{#all} #{members.size} Members", size: 18, style: :bold, align: :center, color: "636363"
text "#{#warn}", size: 11, align: :center, color: "858585"
#members = members
#view = view
member_list
end
def member_list
move_down 20
table member_list_rows do
self.row(0).align = :center
if #members.table_name == "members"
row(0).background_color = '3498db'
else
end
row(0).text_color = "FFFFFF"
self.row_colors = ["DDDDDD", "FFFFFF"]
self.header = true
#self.cell.text_color = "B3B3B3"
row(0).columns(0).style size: 20
end
end
def member_list_rows
[["Name", "Awardunit", "Address", "Contact", "Level of Entry", "Current Award", "Disabled?" ]] +
#members.map do |member|
[member.name, member.awardunit.name, member.address, member.name, member.entrylvl, member.currentaward, #view.yesno(member.disabled)]
end
end
end
Members controller
if params[:commit] == "Clear"
params[:q] = nil
end
respond_to do |format|
format.html
format.pdf do
pdf = MemberPdf.new(Member.search(params[:q]).result.order( 'name ASC' ), view_context, Member.all.size)
send_data pdf.render, filename: "Members_List.pdf", type: "application/pdf", disposition: "inline"
end
end
It is due to #members is nil.You are doing it wrong.
Change this
#members = Members.all #Wrong
to
#members = Member.all #Right
Always remember,the Model name should be singular.
Those are called Naming Conventions. For more information,read these Style guides(Ruby and Rails)
Most likely table method is changing context, in which you don't have access to the #members instance variable anymore. This can be achieved easily by this sample code:
def do_stuff(&block)
cls = Class.new
cls.instance_eval(&block)
end
#test_var = "test_var"
do_stuff { puts #test_var }
You will receive nothing, because #test_var does not exist in the cls.
Am not sure what you are doing with "table member_list_rows". Didn't get that.
In the third line though, it should be
if #members.table_name == "members"
Related
In my view I have
<h4><%= number_to_currency #grand_total, precision: 0, unit: "EUR ", separator: "," %></h4>
This shows a correct total for a column. #grand_total is defined in my controller and it's the sum of total defined in model.
My model
class Ticketline < ActiveRecord::Base
belongs_to :ticket, :foreign_key => 'TICKET'
belongs_to :product, :foreign_key => 'PRODUCT'
def discount
(self.AMOUNT - self.TOTAL)
end
def total_amount
( pricesell = self.try(:PRICESELL) || 0
units = self.try(:UNITS) || 0
pricesell * units)
end
def total
(
price = self.try(:PRICE) || 0
units = self.UNITS || 0
price * units)
end
def consignor_cost
cost = product.location.try(:DISCOUNT_CONSIGNOR) || 0
cost ? (self.total * cost) : 0
end
def cost_of_goods_sold
cost = product.PRICEBUY || 0
cost ? (cost * self.TOTALUNITS) : 0
end
def gross_profit
(self.total - self.consignor_cost - self.cost_of_goods_sold)
end
class ProductSale < Ticketline
end
end
My controller
class ProductSalesController < TicketlinesController
def index
params.permit!
#q = Ticketline.joins(:product, :product => :location).group(:PRODUCT, :TICKET, :DISCOUNT_CONSIGNOR).select("PRODUCT, DISCOUNT_CONSIGNOR, UNITS, TICKET, SUM(ticketlines.PRICESELL*UNITS) AS AMOUNT, SUM(PRICE*UNITS) AS TOTAL, PRICE, UNITS, ticketlines.PRICESELL, SUM(UNITS) AS TOTALUNITS").ransack(params[:q])
#product_sales = #q.result.paginate(:page => params[:page], :per_page => 30)
#product_salesnp = #q.result
#amount_total = #q.result.map(&:total_amount).sum
#discount_total = #q.result.map(&:discount).sum
#grand_total = #q.result.map(&:total).sum
#consignor_cost_total = #q.result.map(&:consignor_cost).sum
#cost_of_goods_sold_total = #q.result.map(&:cost_of_goods_sold).sum
#gross_profit_total = #q.result.map(&:gross_profit).sum
respond_to do |format|
format.html
format.pdf do
pdf = SalesByProductPdf.new(#product_salesnp)
pdf.render_file "report.pdf"
send_data pdf.render, filename: 'report.pdf', type: 'application/pdf', disposition: 'inline'
end
end
end
end
On the pdf generated by prawn I want to show the same so I tried to enter on the corresponding pdf.rb file:
class SalesByProductPdf < Prawn::Document
include ActionView::Helpers::NumberHelper
def initialize(product_sales)
super()
#product_sales = product_sales
header
text_content
table_content
footer
end
def header
#something
end
def text_content
#something
end
def table_content
#something
end
def footer
text number_to_currency(grand_total, precision: 0, unit: "EUR ", separator: ",")
end
end
which gives me no error but shows no value.
What is the correct syntax?
You can explicitly include the ActionView::Helpers::NumberHelper module or whatever module you want in your prawn file.
Try passing in the #grand_total instance variable in your pdf file's initialize method:
class SalesByProductPdf < Prawn::Document
include ActionView::Helpers::NumberHelper
def initialize(product_salesnp, grand_total)
super()
#product_salesnp = product_salesnp
#grand_total = grand_total
header
text_content
table_content
footer
end
...
def footer
text number_to_currency(#grand_total, precision: 0, unit: "EUR ", separator: ",")
end
end
And pass in #grand_total in your controller too when you create a new Pdf object:
format.pdf do
pdf = SalesByProductPdf.new(#product_salesnp, #grand_total)
pdf.render_file "report.pdf"
send_data pdf.render, filename: 'report.pdf', type: 'application/pdf', disposition: 'inline'
end
Hopefully that should work..
I am working on an Ruby on Rails application which uses prawn to generate PDF's when a user clicks on a link. The functionality works when running the application on the localhost but when i deploy it to the server and click on the link the file doesnt load with the message "Failed to load PDF document".
I have done some research and seen references in other places regarding other documents generating gems but none specific to Prawn. Any requests for additional information will gladly be met.
I think showing the code might help so here goes. It is financial application where a page lists statements with a view button and a pdf icon to view them pdf format. When the user clicks on the pdf icon prawn then comes into play. Hope this help clarify my problem somewhat.
Controller Code:
require 'json'
InternetBanking.controllers :statements do
get :index do
#today = Time.now
render 'statements/listing'
end
get :listing do
#today = Time.now
render 'statements/listing'
end
get :activity, provides: [:json] do
content_type :json
#customer.accounts.map{|x| {x.name => x.activity}}.to_json
end
get :show, map: "/statements/:id", provides: [:html, :pdf] do
begin
m = params[:id].force_encoding('UTF-8').match(/(\d+)-(\w+)-(\d+)/)
accountno = m[1].to_i
month = Date::MONTHNAMES.index(m[2])
year = m[3].to_i
raise Exception if month.nil? or year.nil?
# Fetch account_id from logged in customer, prevent crafted URLs.
#account = #customer.fetch_account(accountno)
raise Exception if #account.nil?
#statement = #account.statement(year, month)
# TODO: Deal with no transactions
if content_type == :pdf
content_type 'application/pdf'
#statement.to_pdf
else # HTML
render 'statements/show'
end
rescue
flash[:error]= 'You cannot have an empty month field'
end
end
end
Model Code:
Statement.rb
class Statement
attr_accessor :opening_balance, :closing_balance
def initialize(acct, year = nil, month = nil)
#db = Database.instance
#account = acct
#year = year
#month = month
#month_name = Date::MONTHNAMES[#month]
end
def to_s
"#{#account.number}-#{#month_name}-#{#year}"
end
def to_pdf
title = #account.name
subtitle = "Account Statement: #{#month_name} #{#year}"
StatementPDF.new(title, subtitle, transactions).render
end
def transactions
return #transactions if #transactions
start = Date.civil(#year, #month).strftime('%Y-%m-%d')
finish = Date.civil(#year, #month, -1).strftime('%Y-%m-%d')
rows = #db.call('IBStatement2', #account.account_id, start, finish)
#transactions = rows.map {|txn| Transaction.new(txn)}
end
end
Statement_pdf.rb
require 'prawn'
class StatementPDF < Prawn::Document
BOX_MARGIN = 36
# Additional indentation to keep the line measure with a reasonable size
INNER_MARGIN = 30
# Vertical Rhythm settings
RHYTHM = 10
LEADING = 2
# Colors
BLACK = "000000"
LIGHT_GRAY = "F2F2F2"
GRAY = "DDDDDD"
DARK_GRAY = "333333"
BROWN = "A4441C"
ORANGE = "F28157"
LIGHT_GOLD = "FBFBBE"
DARK_GOLD = "EBE389"
BLUE = "0000D0"
GREY = "CCCCCC"
def initialize(title, subtitle, rows)
#rows = rows
#title = title
#subtitle = subtitle
super(page_size: 'A4') do
define_grid(columns: 4, rows: 16, gutter: 10)
header
transactions_table
footer
end
end
private
def header
grid([0,0],[1,0]).bounding_box do
image 'public/images/Statement/logo.png', width: 110
end
grid([0,1],[1,1]).bounding_box do
font_size 10
text 'Yada', font_weight: 'bold'
font_size 9
text 'Yada'
text 'Yada'
text 'Yada'
text 'Yada'
text 'Yada'
text 'Yada'
end
grid([0,2],[0,3]).bounding_box do
font_size(20)
text #title, color: '#0044AA', :align => :right
font_size(14)
text #subtitle, color: '#0044AA', :align => :right, :valign => :bottom
end
font_size(12)
end
def footer
grid([15,0],[15,3]).bounding_box do
image 'public/images/statement-logo.png', width: 100
end
end
def transactions_table
grid([2,0], [13,3]).bounding_box do
data = [%w(Date Description Amount Balance)]
data += #rows.map{|r| [r.value_date, r.description, r.amount, r.balance]}
options = { header: true, width: 520,
column_widths: {0 => 100, 2 => 100},
row_colors: ['EEEEEE', 'FFFFFF']}
table(data, options) do
cells.padding = 5
cells.border_width = 0.5
cells.border_color = GREY
row(0).font_weight = 'bold'
row(0).border_color = BLACK
row(1).border_top_color = BLACK
column(2).align = :right
column(3).align = :right
end
end
end
end
View Code:
- content_for :title, 'Full Statement Listing'
- content_for :toolbar do
%a.back(href='/balances') Back
%table
%thead
%tr
%th.year(rowspan=2) Year
%th.month(rowspan=2) Month
%th.accounts{colspan: #customer.accounts.size} Accounts
%tr
- #customer.accounts.each do |account|
%th= account.name
- #today.year.downto(#today.year - 3) do |year|
%tbody
- x = (#today.year == year) ? #today.month : 12
- x.downto(1) do |month|
%tr
- if month == x
%td{rowspan: x}= year
%td= Date::MONTHNAMES[month]
- #customer.accounts.each do |acct|
- if acct.activity[year] && acct.activity[year][month]
%td{'data-transactions' => acct.activity[year][month]}
- stmt = acct.statement(year, month)
= link_to 'View', url(:statements, :show, id: stmt.to_s)
= link_to image_tag('/icons/document-pdf.png', alt: 'Download PDF'), url(:statements, :show, id: stmt, format: :pdf), class: :pdf
- else
%td
Im using the Prawn gem to generate a pdf table. I need to set the width of the table to 100%. How can I do this?
Here's my slmun_pdf.rb
class SlmunPdf < Prawn::Document
def initialize(slmunDbs, view, allcount)
if slmunDbs.table_name == "schools"
super(top_margin: 50)
if slmunDbs.size != allcount
#warn = " (Not all Schools)"
else
#all = " All #{slmunDbs.size} Schools"
end
text "Showing#{#all}", size: 18, style: :bold, align: :center, color: "636363"
text "#{#warn}", size: 11, align: :center, color: "858585"
#schools = slmunDbs
#view = view
school_list
end
end
def school_list
move_down 20
table school_list_rows, :cell_style => { :font => "Helvetica", :size => 9, :border_width => 0.5, :borders => [:top, :bottom], :border_color => "B0B0B0", :text_color => "737373"} do
self.row(0).align = :center
row(0).background_color = 'A0B046'
row(0).text_color = "FFFFFF"
self.row_colors = ["DDDDDD", "FFFFFF"]
self.header = true
end
end
def school_list_rows
[["Name", "Contact", "Country", "Pre Registration", "Full Registration", "Assigned Countries", "Total Delegates" ]] +
#schools.map do |school|
countries = ""
len = school.countries.count
school.countries.each_with_index do |country, index|
countries << "#{country.country }"
if index+1 != len
countries << ","
end
end
[school.name, school.contact, school.country, #view.yesno(school.prereg), #view.yesno(school.fullreg), countries, school.delegates.size ]
end
end
end
The existing documentations didn't help. I might be using a different method? I learnt this from rails cast!
You can use the width method of the Prawn::Document::BoundingBox class:
require 'prawn'
require 'prawn/table'
Prawn::Document.generate("hello.pdf") do
table_content = [["This table"], ["covers the"], ["whole page width"]]
table table_content, width: bounds.width
end
My code is
class pdfController < ApplicationController
def index
#posts = Post.all
respond_to do |format|
format.html
format.pdf do
pdf = Prawn::Document.new
send_data pdf.render, :filename => "report.pdf", :type => "application/pdf", :disposition => "inline"
end
end
end
end
Now the problem is, pdf file downloaded. But I am getting an empty file, there is no report in that downloaded file.
And the code#posts = Post.all Post is a table, I can get the data from Post to downloaded pdf file.
As suggested in this post: http://adamalbrecht.com/2014/01/14/generate-clean-testable-pdf-reports-in-rails-with-prawn/
You can inherit Prawn::Document class for an easy configuration of your table border color and font size.
class PdfReport < Prawn::Document
# Often-Used Constants
TABLE_ROW_COLORS = ["FFFFFF","DDDDDD"]
TABLE_FONT_SIZE = 9
TABLE_BORDER_STYLE = :grid
def initialize(default_prawn_options={})
super(default_prawn_options)
font_size 10
end
def header(title=nil)
text "My Awesome Invoice!!", size: 18, style: :bold, align: :center
if title
text title, size: 14, style: :bold_italic, align: :center
end
end
def footer
# ...
end
# ... More helpers
end
Now, the above class is handful to use at other places by inheriting to some other classes, for example: posts reports, comments reports, etc. For your posts reports, We could do something like this:
class PostSummaryReportPdf < PdfReport
TABLE_WIDTHS = [20, 100, 30, 60]
TABLE_HEADERS = ["ID", "Name", "Date", "User"]
def initialize(posts=[])
super()
#posts = posts
header "Posts' Invoice Summary Report"
display_event_table
footer
end
private
def display_event_table
if table_data.empty?
text "No Events Found"
else
table table_data,
headers: TABLE_HEADERS,
column_widths: TABLE_WIDTHS,
row_colors: TABLE_ROW_COLORS,
font_size: TABLE_FONT_SIZE
end
end
def table_data
#table_data ||= #posts.map { |p| [p.id, p.name, p.created_at.strftime("%m/%d/%y"), p.created_by.try(:full_name)] }
end
end
Everything is now up and ready to be used in DRY-way within your controllers.
class pdfController < ApplicationController
def index
#posts = Post.all
respond_to do |format|
format.html
format.pdf do
pdf = PostSummaryReportPdf.new(#posts)
send_data pdf.render, :filename => "report.pdf", :type => "application/pdf", :disposition => "inline"
end
end
end
end
I hope that helps.
I am trying to search through my model using 3 columns. Also if the column is empty, it is valid. This is how I am doing it
def getactivityfortoday
#temp = params[:temp]
logger.debug "params temp:#{#temp.inspect}"
#sky = params[:sky]
#day = params[:day]
#todaysactivities = []
#activities=[]
#finaldata = []
#activities = Weatherclockactivity.all
#attemptactivities = []
#attemptactivities = #user.attempts
for activity in #activities do
logger.debug "activity: #{activity.attributes.inspect}"
if #temp.to_i < activity.temperatureMax.to_i && #temp.to_i > activity.temperatuureMin.to_i
if #sky == activity.sky || activity.sky == ""
if #day == activity.day
#todaysactivities << activity
end
end
end
end
for activity in #todaysactivities
for attempt in #attemptactivities
if attempt == activity
finaldata << {activity: activity, attempt: "yes"}
else
finaldata << {activity: activity, attempt: "no"}
end
end
end
respond_to do |format|
format.html { render action: "new" }
format.json { render json: #finaldata }
end
The response I get is an empty array but I should be getting 3 rows as a response.
spelling mistake here
activity.temperatuureMin.to_i
And
finaldata << {activity: activity, attempt: "yes"}
should be
#finaldata << {activity: activity, attempt: "yes"}
Also you could be more concise
def getactivityfortoday
#temp = params[:temp]
logger.debug "params temp:#{#temp.inspect}"
#sky = params[:sky]
#day = params[:day]
#activities = Weatherclockactivity.all
#attemptactivities = #user.attempts
#finaldata = #activities.map do |activity|
if (activity.temperatureMin.to_i + 1...activity.temperatureMax.to_i).include?(#temp.to_i) && ( #sky == activity.sky || activity.sky == "") && #day
#attemptactivities.include?(activity) ? {activity: activity, attempt: "yes"} : {activity: activity, attempt: "no"}
end
end.compact
respond_to do |format|
format.html { render action: "new" }
format.json { render json: #finaldata }
end
end
How about something like this?
I tried to make it a balance of readability and conciseness. First we filter for the desired activities. Then we structure the output. This should be easier to debug.
def getactivityfortoday
#temp = params[:temp].to_i
#sky = params[:sky]
#day = params[:day]
#activities = Weatherclockactivity.all
#attemptactivities = #user.attempts
selected_activities = #activities.select do |activity|
# Make sure it's the right temperaure
return false unless (activity.temperatureMin.to_i + 1 ... activity.temperatureMax.to_i).include? #temp
# Make sure the sky matches, or the sky is blank
return false unless (#sky.blank? || #sky.activity == activity.sky)
# Make sure the day matches
return false unless #day == activity.day
# Otherwise, it's good!
return true
end
selected_attempted_activities = selected_activities.map do|activity|
ret = {activity: activity}
ret[:attempt] = #attemptactivities.include?(activity) ? "yes" : "no"
ret
end
respond_to do |format|
format.html { render action: "new" }
format.json { render json: selected_attempted_activities }
end
end
There are a few typos in your original (for instance, #finaldata not finaldata). Make sure that you spell instance variables (things starting with #, like #sky) correctly, since if you try to access an undefined instance variable, it'll silently default to nil.
The best and flexible way is to use ActiveModel::Model
It allows you to use many more useful methods.
it will seems like:
app/models/activity_report.rb
Class ActivityReport
include ActiveModel::Model
attr_accessor :day, :activity # and etc.
validates :day, presence: true
def day
#day.to_s # for example
end
def day=(value)
#day = value - 1.month # for example every date which user set will set on one month ago
end
# and etc
end
app/controllers/posts_controller.rb
...
def index
#activity = ActivityReport.new(params[:activity])
end
def create
#activity.create!
end
...
app/views/posts/index.html.haml
= form_for #activity do |f|
= f.day
For more information you could take a look at:
http://edgeapi.rubyonrails.org/classes/ActiveModel/Model.html
http://railscasts.com/episodes/219-active-model (old)
http://railscasts.com/episodes/416-form-objects (newer, but a little complex)