Rails: passing objects between model and controller - ruby-on-rails

Is there a best practice for managing objects between model and controller in Rails? I've got code that clumsily hands files from controller to model and back like so:
In document_controller.rb:
tmp_file = Tempfile.new(Digest::MD5.hexdigest(rand(12).to_s))
pdf = Prawn::Document.generate(tmp_file.path(), :page_size => "A4", :skip_page_creation => true) do |posting|
#document.process(posting)
send_data posting.render, :filename => "#{#document.id}", :type => "application/pdf"
end
And in document.rb:
def process(pdf)
pdf.start_new_page
pdf.image self.user.logo.path, :width => pdf.bounds.width
pdf.fill_color '#444444'
pdf.image self.component.image_snapshot.path, :width => pdf.bounds.width
pdf.move_down 20
pdf.indent 20 do
pdf.text self.component.name, :size => 24
end
box_start = pdf.cursor
pdf.reflow_column_box([20, box_start-15], :width => pdf.bounds.width-20) do
format_for_prawn(pdf, self.component.body, self.user.colour1)
end
end
How can I rewrite this code so I don't have to clumsily pass the pdf/posting object around? It feels very un-Rails-like.
Thanks for your input.
EDIT: worse example.
Again in the controller:
tempfile = Magick::Image.read(#document.component.image_newsletter.path).first
overlay = Magick::Image.read(#document.user.logo.path).first.resize_to_fit(tempfile.columns)
rec = Magick::Draw.new
rec.stroke = "##{#document.user.colour1}"
rec.fill = "##{#document.user.colour1}"
rec.rectangle 0, 0, tempfile.columns, 5
lank = tempfile.extent(tempfile.columns, tempfile.rows+overlay.rows, 0 ,0)
final = lank.composite(overlay, Magick::SouthGravity, 0, 0, Magick::OverCompositeOp)
rec.draw(final)
send_data final.to_blob, :filename => "#{#document.user.name}-#{#document.id}.jpg", :type => "image/jpg"
This is in the controller because otherwise send_data can't access final. Any help?
Thanks again.

EDIT
You can create extension methods for inbuilt Class by
#lib/ext/prawn/document.rb
class Prawn::Document
def process(pdf)
pdf = self if pdf.blank?
pdf.start_new_page
pdf.image self.user.logo.path, :width => pdf.bounds.width
pdf.fill_color '#444444'
pdf.image self.component.image_snapshot.path, :width => pdf.bounds.width
pdf.move_down 20
pdf.indent 20 do
pdf.text self.component.name, :size => 24
end
box_start = pdf.cursor
pdf.reflow_column_box([20, box_start-15], :width => pdf.bounds.width-20) do
format_for_prawn(pdf, self.component.body, self.user.colour1)
end
end
end
And in your controller
tmp_file = Tempfile.new(Digest::MD5.hexdigest(rand(12).to_s))
pdf = Prawn::Document.generate(tmp_file.path(), :page_size => "A4", :skip_page_creation => true) do |posting|
posting.process
send_data posting.render, :filename => "#{#document.id}", :type => "application/pdf"
end
To load this class, you will need to require it in your config/application.rb file or in an initializer.
require 'ext/prawn/document'

To get that messy controller code into the model I've done this:
def process_media
temp = "#{::Rails.root}/public/temporary/#{temp_file_name}"
tempfile = Magick::Image.read(self.component.image_newsletter.path).first
overlay = Magick::Image.read(self.user.logo.path).first.resize_to_fit(tempfile.columns)
rec = Magick::Draw.new
rec.stroke = "##{self.user.colour1}"
rec.fill = "##{self.user.colour1}"
rec.rectangle 0, 0, tempfile.columns, 5
lank = tempfile.extent(tempfile.columns, tempfile.rows+overlay.rows, 0 ,0)
final = lank.composite(overlay, Magick::SouthGravity, 0, 0, Magick::OverCompositeOp)
rec.draw(final)
final.to_blob
final.write("#{temp}.jpg")
end
And I'm sending the actual file with send_file from the controller. Hope that helps someone.

Related

How to call a model in controller ruby on rails

class Portfolio::PortfolioImage < PortfolioPost
has_attached_file :file,
styles: { _medium: ["960x", :jpg] },
default_style: :original,
preserve_files: true,
path: base_upload_url+ ":assign_user_id" + "_" + ":file_name" + ".jpg"
validates_attachment_content_type :file, content_type: /\Aimage/
before_post_process :downcase_file_name
def downcase_file_name
self.file_file_name = self.file_file_name.downcase
end
end
def index
#portfolio_image = PortfolioImage.all.paginate(:page => params[:page], :per_page => 20)
#portfolio_video = PortfolioVideo.all.paginate(:page => params[:page], :per_page => 20)
#portfolio_doc = PortfolioDocument.all.paginate(:page => params[:page], :per_page => 20)
render json: #portfolio_image
render json: #portfolio_video
render json: #portfolio_doc
end
How to solve this problem I have to create an api for index action where three types of data I have to see when chosen image only images should show and if chosen video only video should show.?
I'm not sure, but I think what you're missing is the Module prefix.
For ex. to get all portfolio images you want to use Portfolio::PortfolioImage.all. (Except when you're calling from inside the module, but that's not very clear from your question)

XLS report in ROR

I want to generate a Excel and PDF report using ruby on rails , but I have 24,278 entries to display in both reports so it is showing an error . How to solve this error .
Controller Code - (for excel)
def from_date_wise_xls
#start = params[:day]
#end = params[:to_date]
#present = params[:present]
#employee_attendances = EmployeeAttendance.where(present: #present,day: #start..#end)
respond_to do |format|
format.xls {render template: 'employee_attendances/from_date_wise_xls.xls.erb'}
end
end
(For PDF) -
def from_date_wise_pdf
#start = params[:day]
#end = params[:to_date]
#present = params[:present]
#employee_attendances = EmployeeAttendance.where(present: #present,day: #start..#end)
respond_to do |format|
format.html
format.pdf do
render :pdf => 'from_date_wise_pdf',
layout: '/layouts/pdf.html.erb',
:template => 'employee_attendances/from_date_wise_pdf.pdf.erb',
:orientation => 'Landscape', # default , Landscape
:page_height => 1000,
:dpi => '300',
:margin => {:top => 10, # default 10 (mm)
:bottom => 10,
:left => 20,
:right => 20},
:show_as_html => params[:debug].present?
end
end
end

Rails: prawn PDF to rmagick output

What's wrong with this method? I'm totally stumped. I keep getting this error on the img.write(res) line:
ArgumentImage no images in this image list
Here's the controller method:
def convert_to_image
#document = Document.find(params[:document_id])
img = Magick::ImageList.new
pdf = Prawn::Document.generate("tmp.pdf", :margin => 0, :page_size => [398, 398]) do |p|
res = #document.post_pdf(p)
img.write(res)
send_data img
end
end
The post_pdf method mentioned above looks like this:
def post_pdf(p)
size = 398
p.image self.user.logo.path, :width => size
p.bounding_box([30, 490], :width => size) do
p.fill_color self.user.colour1
p.pad_bottom(10) { p.text self.title, :size => 28, :style => :bold }
p.fill_color '#444444'
end
self.components.each do |component|
p.image component.image_newsletter.path, :width => size
p.fill_color self.user.colour1
p.fill_rectangle([0, 20], size, 20)
end
end
SOLVED. It needed:
1.) Ghostscript.
2.) A rewritten method:
def create_pdf_image
#document = Document.find(params[:document_id])
pdf = Prawn::Document.new
temp = "#{#document.user.name.downcase.parameterize.underscore}-#{#document.id}"
#document.post_pdf(pdf)
pdf.render_file("#{temp}.pdf")
image = Magick::ImageList.new("#{temp}.pdf")
image.write("#{temp}.png") { self.quality = 100 }
send_file "#{temp}.png"
end
Might help someone. Apparently rmagick needs ghostscript. My method just wasn't playing either, although the post_pdf method remains unchanged.

generating email address images or something which can't be easily scraped

I'm looking for a better Text-to-image solution for my Rails project to replace my current method which is generating a png using ImageMagick every time a new record is created or updated.
I want to prevent a mass scraping of data and abuse of email addresses provided. I'm wondering if there is an API to generate the text or use of javascript, or SVG, etc. Anything short of Flash.
I'm looking for a better solution than my current method:
def create_new_email_image
if email_changed?
path_to_images = '/images/emails'
puts "Connecting to AWS..."
config = YAML.load(File.open("#{RAILS_ROOT}/config/s3_credentials.yml"))[RAILS_ENV]
AWS::S3::Base.establish_connection!(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
puts "Finding S3 bucket..."
begin
bucket = AWS::S3::Bucket.find config['bucket_name']
rescue AWS::S3::NoSuchBucket
AWS::S3::Bucket.create config['bucket_name']
bucket = AWS::S3::Bucket.find config['bucket_name']
end
images_path = "#{RAILS_ROOT}/tmp/"
file_name = "#{id}.png"
#file_name = "5056.png"
file_path = images_path + "/"+ file_name
File.delete file_path if File.exists? file_path
img = Magick::Image.new(400, 22) { self.background_color = 'transparent' }
img.format = 'PNG'
text = Magick::Draw.new
text.annotate(img, 0, 0, 1, 0, "#{email}") {
self.gravity = Magick::WestGravity
self.pointsize = 18
self.fill = '#000000'
self.kerning = -1
self.font_weight = Magick::BoldWeight
}
img.write file_path
if AWS::S3::S3Object.exists? file_name, bucket.name + path_to_images
puts "file exists (deleting)"
AWS::S3::S3Object.delete file_name, bucket.name + path_to_images, :force => true
end
AWS::S3::S3Object.store file_name,
File.open(file_path),
bucket.name + path_to_images,
:content_type => 'image/png',
:access => :public_read,
:reload => true
`rm #{file_path}`
end
end
Rails provides a mail_to helper.
mail_to "me#domain.com"
# => me#domain.com
mail_to "me#domain.com", "My email", :encode => "javascript"
# => <script type="text/javascript">eval(decodeURIComponent('%64%6f%63...%27%29%3b'))</script>
mail_to "me#domain.com", "My email", :encode => "hex"
# => My email
mail_to "me#domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email"
# => me_at_domain_dot_com
mail_to "me#domain.com", "My email", :cc => "ccaddress#domain.com",
:subject => "This is an example email"
# => My email
The :encode => "hex" or :encode => "javascript" options are what you are looking for.
I've had the same problem. Here is my solution:
def text_to_image(text,options={})
return if text.blank?
filename=get_text_file_path(text)
unless File.exists?(filename)
gc = Magick::Draw.new
gc.fill = options[:color] unless options[:color].blank?
gc.pointsize options[:size] unless options[:size].blank?
gc.font=options[:font] unless options[:font].blank?
gc.gravity = Magick::CenterGravity
gc.text(0,0, text)
metrics=gc.get_type_metrics(text)
image = Magick::Image.new(metrics.width, metrics.height){
self.background_color = 'transparent'
}
gc.draw(image)
image.write(filename)
end
end
I use after_save callback to update graphic cache of email attribute.

Prawn - how to generalize the common things in the pdf document into a separate module for code re-usage?

I am using the Rails Prawn to generate pdf files. Now that i am able to generate pdf with all necessary things that i need(eg. table, header, footer, logo, borders etc...). Now I need to use the common things(header, foooter, borders) in a method inside separate module and call this method from my original program?
My original program:
travel_report.rb
module TravelReport
include Header
def self.generate(start_ts, end_ts, format_type, obu_ids, interval)
Prawn::Document.generate("public/test.pdf") do
Borders
page_count.times do |i|
go_to_page(i+1)
Header.border
end
Header and Boundary Lines
page_count.times do |i|
go_to_page(i+1)
ask = "public/ashok.jpg"
image ask, :at => [15, 750], :width => 120
alert = "public/alert.jpg"
image alert, :at => [410, 740], :width => 120
end
footer
page_count.times do |i|
go_to_page(i+1)
lazy_bounding_box([bounds.left+30, bounds.bottom + 20], :width => 100) {
text "Bypass Report"
}.draw
end
end
Separate Module for Borders
module Header
#class Cheader < Prawn::Document::BoundingBox
#include Prawn
def self.border
pdf = Prawn::Document.new
pdf.bounding_box([5, 705], :width => 540, :height => 680) do
pdf.stroke_bounds
end
end
#end
end
This code doesnt creates any border... Any idea how to create separate module for this????
#create a separate module
#program
include HeaderFooter
Prawn::Document.generate("public/test.pdf") do |pdf|
pdf.page_count.times do |i|
pdf.go_to_page(i+1)
HeaderFooter.border(pdf)
#render :partial => 'header', :locals => {:ppdf => pdf}
end
#Header and Boundary Lines
pdf.page_count.times do |i|
pdf.go_to_page(i+1)
HeaderFooter.image(pdf)
end
#footer
pdf.page_count.times do |i|
pdf.go_to_page(i+1)
HeaderFooter.footer(pdf)
end
end
create a module to define the methods(header_footer.rb)
module HeaderFooter
#Method for border creation in pdf
def self.border(ppdf)
ppdf.bounding_box([5, 705], :width => 540, :height => 680) do
ppdf.stroke_bounds
end
end
#method to create the logos in the pdf
def self.image(ppdf)
ask = "public/ashok.jpg"
ppdf.image ask, :at => [15, 750], :width => 120
alert = "public/alert.jpg"
ppdf.image alert, :at => [410, 740], :width => 120
end
#method to print footer text in the pdf
def self.footer(ppdf)
ppdf.lazy_bounding_box([ppdf.bounds.left+30, ppdf.bounds.bottom + 20], :width => 100) {
ppdf.text "Bypass Report"
}.draw
end
end
This works fine...

Resources