I want to perform the similar thing as from base64 photo and paperclip -Rails, but with Carrierwave.
Could anybody explain me using of base64 images in Carrierwave?
class ImageUploader < CarrierWave::Uploader::Base
class FilelessIO < StringIO
attr_accessor :original_filename
attr_accessor :content_type
end
before :cache, :convert_base64
def convert_base64(file)
if file.respond_to?(:original_filename) &&
file.original_filename.match(/^base64:/)
fname = file.original_filename.gsub(/^base64:/, '')
ctype = file.content_type
decoded = Base64.decode64(file.read)
file.file.tempfile.close!
decoded = FilelessIO.new(decoded)
decoded.original_filename = fname
decoded.content_type = ctype
file.__send__ :file=, decoded
end
file
end
The accepted answer did not worked for me (v0.9).
It seems to be a check that fails before the cache callback.
This implementation works:
class ImageUploader < CarrierWave::Uploader::Base
# Mimick an UploadedFile.
class FilelessIO < StringIO
attr_accessor :original_filename
attr_accessor :content_type
end
# Param must be a hash with to 'base64_contents' and 'filename'.
def cache!(file)
if file.respond_to?(:has_key?) && file.has_key?(:base64_contents) && file.has_key?(:filename)
local_file = FilelessIO.new(Base64.decode64(file[:base64_contents]))
local_file.original_filename = file[:filename]
extension = File.extname(file[:filename])[1..-1]
local_file.content_type = Mime::Type.lookup_by_extension(extension).to_s
super(local_file)
else
super(file)
end
end
end
Related
Using: Rails 4.2, Prawn, Paperclip, DelayedJobs via ActiveJobs, Heroku.
I have a PDF that is very large and needs to be handled in the background. Inside a custom Job I want to create it, upload it to S3, and then email the user with a url when its ready. I facilitate this via a PdfUpload model.
Is there anything wrong with my approach/code? Im using File.open() as outlined in examples I found, but this seems to be the root of my error ( TypeError: no implicit conversion of FlightsWithGradesReport into String ).
class PdfUpload < ActiveRecord::Base
has_attached_file :report,
path: "schools/:school/pdf_reports/:id_:style.:extension"
end
/pages_controller.rb
def flights_with_grades_report
flash[:success] = "The report you requested is being generated. An email will be sent to '#{ current_user.email }' when it is ready."
GenerateFlightsWithGradesReportJob.perform_later(current_user.id, #rating.id)
redirect_to :back
authorize #rating, :reports?
end
/ the job
class GenerateFlightsWithGradesReportJob < ActiveJob::Base
queue_as :generate_pdf
def perform(recipient_user_id, rating_id)
rating = Rating.find(rating_id)
pdf = FlightsWithGradesReport.new( rating.id )
pdf_upload = PdfUpload.new
pdf_upload.report = File.open( pdf )
pdf_upload.report_processing = true
pdf_upload.report_file_name = "report.pdf"
pdf_upload.report_content_type = "application/pdf"
pdf_upload.save!
PdfMailer.pdf_ready(recipient_user_id, pdf_upload.id)
end
end
This results in an error:
TypeError: no implicit conversion of FlightsWithGradesReport into String
Changing this:
pdf_upload.report = File.open( pdf )
to this:
pdf_upload.report = StringIO.new(pdf.render)
fixed my problem.
I would like to set some images without uploading. (They already exist, or another task saves them...)
If I try (in rails console):
user = User.last
user.picture = '/some/picture.jpg'
user.save
user.picture # nil
The only way to do that is to set remote_picture_url, and then delete the upload (which is stupid)
Is there any method in carrierwave that lets you modify only the filename ?
class User < ActiveRecord::Base
attr_accessible :picture
# Don't want to write to the database, but want to be able to check
attr_writer :skip
# set a default value
def skip
#skip ||= false
end
mount_uploader :image, PictureUploader
# Make sure that the skip callback comes after the mount_uploader
skip_callback :save, :before, :store_picture!, if: :skip_saving?
# Other callbacks which might be triggered depending on the usecase
#skip_callback :save, :before, :write_picture_identifier, id: :skip_saving?
def skip_saving?
skip
end
end
class PictureUploader < Carrierwave::Uploader::Base
# You could also implement filename=
def set_filename(name)
#filename = name
end
end
Assuming you have the setup above, in your console:
user = User.last
user.picture.set_filename('/some/picture.jpg')
user.skip = true
# Save will now skip the callback store_picture!
user.save
user.picture # /some/picture.jpg
It should be noted that if you're in the console and you update an existing record that has an attached file (ie user.picture.file) it will show the old url/location. If you quit the console (assuming you're not in sandbox mode) and come back and query the same object it will have the updated url/location.
I have a helper_method that allows links to escape from a subdomain. However it is impacting my videos_controller, as it essentially seems to negate the 'current_event' method when not in the events controlller.
I've tried several dozen different ways over the last 4 days to make it so I can still escape my links from the subdomain, but still allow the videos_controller to work.
I think the best way to achieve this is to exclude the videos_controller from the helper method, but I'm not sure how (or if it is actually the best way forward - I'm obviously a noob!) Any suggestions please?! Relevant code below:
module UrlHelper
def url_for(options = nil)
if request.subdomain.present? and request.subdomain.downcase != 'www' and !options.nil? and options.is_a?(Hash) and options.has_key? :only_path and options[:only_path]
options[:only_path] = false
end
super
end
end
Videos_controller
def new
if current_event?
#video = current_event.videos.new
else
#video = Video.new
end
end
def create
if current_event.present?
#video = current_event.videos.new(params[:video])
#video.user_id = current_user.id
key = get_key_from_the_cloud
#video.key = key
else
#video = current_user.videos.new(params[:video])
#video.user_id = current_user.id
key = get_key_from_the_cloud
#video.key = key
end
if #video.save
flash[:success] = "Video uploaded!"
redirect_to root_url(subdomain: => current_event.name)
else
flash[:error] = "#{#video.errors.messages}"
render :new
end
end
current_event method
def current_event
if request.subdomain.present?
#event = Event.find_by_name(request.subdomain)
end
end
Did you take a look at this post yet?
You might want to create a new function test that only does something like
module UrlHelper
def test
puts "Test is called"
end
end
If that works you know its not including that fails but it has to be the method.
Otherwise you know the module is not included and you can narrow down the search.
I have two tables for checking views (visits of the page) - views of pic (PhotoView) in gallery and photographers(PhotographerView).
Because these two models (and tables) are the same, I want to create a model for them - something like:
class Func < ActiveRecord::Base
def self.check_views(model_view, data)
last_view = model_viewView.where('ip_address = ? AND request_url = ?', request.remote_ip, request.url).order('created_at DESC').first
unless last_view
model_view+View.new(...).save
model_view.increment_counter(:views, data.id)
else
if (DateTime.now - last_view.created_at.to_datetime) > 1.day
model_view+View.new(...).save
model_view.increment_counter(:views, data.id)
end
end #comparing dates
end
end
and call this method like:
#photo = Photo.find(params[:id])
Func.check_views('Photo', #photo)
When I try use it with the way above, I'll get the error undefined method `check_views' for Func(Table doesn't exist):Class
Could you give me a help, how to make it work?
Thank you
You can use ActiveRecord::Concern and modules to move the common functionality into one place as follows:
module CheckViews
extend ActiveSupport::Concern
module ClassMethods
# all class methods go here, if you don't have any just leave it blank
end
def check_views(data)
last_view = where('ip_address = ? AND request_url = ?', request.remote_ip, request.url).order('created_at DESC').first
unless last_view
##views_class.new(...).save
increment_counter(:views, data.id)
else
if (DateTime.now - last_view.created_at.to_datetime) > 1.day
##views_class.new(...).save
increment_counter(:views, data.id)
end
end #comparing dates
end
end
class Photo < ActiveRecord::Base
include CheckViews
end
you can now do the following:
#photo = Photo.find(params[:id])
#photo.check_views
I'd be very tempted to do this as a module extending the classes which want the Views functionality. Something like the following ought to work; but it's entirely untested and entirely unlike anything I've ever done before so it may be completely buggy. Fair warning.
module CheckViews
def self.extended(host_class)
host_class.class_variable_set("##views_class", "#{host_class}View".constantize)
end
def check_views(data)
last_view = where('ip_address = ? AND request_url = ?', request.remote_ip, request.url).order('created_at DESC').first
unless last_view
##views_class.new(...).save
increment_counter(:views, data.id)
else
if (DateTime.now - last_view.created_at.to_datetime) > 1.day
##views_class.new(...).save
increment_counter(:views, data.id)
end
end #comparing dates
end
end
class Photo < ActiveRecord::Base
extend CheckViews
...
end
(extend adds all the instance methods of the target Module as class methods of the calling class; so Photo gains Photo.check_views(data), and self in that function is the class Photo.)
I've been working on a rails project where I am needed to serialize permissions for user roles and store in the database. As far as that goes I'm all good. Now my problem comes when I want to modify the serialized data from a rails generated form.
I acted on instinct and tried with the expected behavior.
That would be to use something like this:
f.check_box :permissions_customer_club_events_read
But as no getters or setters exist for the serialized data, this doesn't work (obviously :p). Now I wonder how I would go about tackling this problem and the only thing that comes to mind is dynamically generating getter and setter methods from my serialized hash.
Example:
def permissions_customer_club_events_read=(val)
permissions[:customer][:club][:events][:read] = val
end
def permissions_customer_club_events_read
permissions[:customer][:club][:events][:read]
end
Anyone understand what I'm getting at?
Here is my Model:
class User::Affiliation::Role < ActiveRecord::Base
require 'yajl'
class YajlCoder
def dump data
Yajl.dump data
end
def load data
return unless data
Yajl.load data
end
end
serialize :permissions, YajlCoder.new
after_initialize :init
def init
## Sets base permission structure ##
self.permissions ||= YAML.load_file("#{Rails.root}/config/permissions.yml")
end
end
I suggest you have a look at something like attr_bucket. Ostensibly, this can be used to solve some inheritance annoyances, but it will also solve your problem for you. Here is the essence.
It looks like you know what all your permissions are, but you want to serialize all of them into the same database field. But within your actual rails app, you want to treat all your permissions as if they were totally separate fields. This is exactly what a solution like attr_bucket will let you do. Let's take your example, you would do something like this:
class User::Affiliation::Role < ActiveRecord::Base
attr_bucket :permissions => [:permissions_customer_club_events_read, :permissions_customer_club_events_write, :permission_do_crazy_things]
after_initialize :init
def init
## Sets base permission structure ##
self.permissions ||= YAML.load_file("#{Rails.root}/config/permissions.yml")
end
end
Now you will be able to use permissions_customer_club_events_read, permissions_customer_club_events_write, permission_do_crazy_things as if they were separate database fields (this includes using them in forms etc.), but when you actually save your objects all those fields would get 'bucketed' together and serialized into the :permissions field.
The only caveat is the serialization mechanism, I believe attr_bucket will serialize everything using YAML, whereas you were using JSON. If this doesn't matter then you're golden, otherwise you might need to patch attr_bucket to use json instead of YAML which should be pretty straight forward.
Sorry if I did not understand the question ;)
You could have a customdata module, included in your model, and use method_missing:
module CustomData
def self.included(base)
base.instance_eval do
after_save :save_data
end
def method_missing(method, *args, &block)
if method.to_s =~ /^data_/
data[method] ? data[method] : nil
else
super
end
end
def data
#data ||= begin
#get and return your data
end
end
private
def save_data
end
end
With this method, you would have to use f.check_box :data_permissions_customer_club_events_read
It's not really complete, but I hope you get the idea ;)
attr_bucket seems like a good solution too.
This worked out for me in the end, this is how I solved it.
serialize :permissions, YajlCoder.new
after_initialize :init
def init
self.permissions ||= YAML.load_file("#{Rails.root}/config/permissions.yml")['customer']
build_attributes_from self.permissions, :permissions
end
private
def build_attributes_from store, prefix, path=[]
store.each do |k,v|
if v.class == Hash
build_attributes_from v, prefix, ( path + [k] )
else
create_attr_accessors_from prefix, ( path + [k] )
end
end
end
def create_attr_accessors_from prefix, path=[]
method_name = prefix.to_s + "_" + path.join('_')
class << self
self
end.send :define_method, method_name do
self.permissions.dig(:path => path)
end
class << self
self
end.send :define_method, "#{method_name}=" do |value|
self.permissions.dig(:path => path, :value => value)
end
end
And some monkey patching for hashes...
class Hash
def dig(args={})
path = args[:path].to_enum || []
value = args[:value] || nil
if value == nil
path.inject(self) do |location, key|
location.respond_to?(:keys) ? location[key] : nil
end
else
path.inject(self) do |location, key|
location[key] = ( location[key].class == Hash ) ? location[key] : value
end
end
end
end
Now getter and setter methods are generated for all of the serialized fields.