How can I make a custom carrierwave post processor? - ruby-on-rails-3

I need to do some post processing on a file upload that isn't images - in paperclip I can have a custom post processor, but I can't find any way to do this in carrierwave.
Ruby 1.9.3, Rails 3.2.7 and CarrierWave 0.6.2.

The OP's question was how to process files which are not images.
Please have a look at this source file on GitHub:
carrierwave/lib/carrierwave/uploader/processing.rb and check the comments.
You'll have create your own CarrierWave uploader sub-class and mount it in your model like this:
def MyModel < ActiveRecord::Base
# this is where the uploaded file will be available in your model,
# as a `MyUploader` instance:
#
mount_uploader :uploaded_file, MyUploader
...
end
Please note that it's mounted on the ActiveRecord attribute :uploaded_file.
This means when you access :uploaded_file from your model, you will get an instance of your CarrierWave uploader for the particular file which was uploaded.
You can simply define your processing inside your uploader as follows:
class MyUploader < CarrierWave:Uploader::Base
process :my_custom_processing => [param1,param2,param3]
def my_custom_processing(param1,param2,param3)
...
# e.g. you could call a method which is defined elsewhere,
# which operates on a file:
my_nifty_file_processor( self.uploaded_file )
#
# or you could just do this directly:
uploaded_data = self.uploaded_file.read
...
end
end
Inside my_nifty_file_processor, you would simply call read on the object that gets passed in to read the file.
CarrierWave lets you call read on any Uploader instance (= any instance of an uploaded file) and it will read that file.
One more Tip:
Sometimes you need to access the ActiveRecord model within your uploader, for which the file was uploaded.
Simply access it inside your uploader code like this:
self.model
This lets you store additional information about the uploaded file, such as format, directly in your AR model.

I wrote a blog post on how to create custom post-processor to create thumbnails of videos, maybe you will find it useful.
https://prograils.com/posts/video-encoding-processor-for-carrierwave/

Related

Access EXIF data from file uploaded with Paperclip in tests

Can I safely use self.image.staged_path to access file which is uploaded to Amazon S3 using Paperclip ? I noticed that I can use self.image.url (which returns https...s3....file) to read EXIF from file on S3 in Production or Development environments. I can't use the same approach in test though.
I found staged_path method which allows me to read EXIF from file in all environments (it returns something like: /var/folders/dv/zpc...-6331-fq3gju )
I couldn't find more information about this method, so the question is: does anyone have experience with this and could advise on reliability of this approach? I'm reading EXIF data in before_post_process callback
before_post_process :load_date_from_exif
def load_date_from_exif
...
EXIFR::JPEG.new(self.image.staged_path).date_time
...
end

How to work with Dragonfly's before_serve block to watermark pdf files

I'm trying to serve PDF files that are watermarked with a users email address and timestamp at the time of downloading a file using the dragonfly gem and I'm having a bit of trouble working with the before_serve block. What I'm not able to work out is how to use the job object dragonfly passes to the block and how to apply a custom processor. Calling process on the job object directly doesn't appear to run the processor
app.configure do |config|
config.server.before_serve do |job, env|
user = # user record grabbed from database
job.process(:watermark, user: user)
end
end
However if I call process on the dragonfly object returned by the rails model the pdf is processed correctly but I'm not sure how to actually instruct dragonfly on what to send to the browser
app.configure do |config|
config.server.before_serve do |job, env|
user = # user record grabbed from database
report = # report grabbed from database
report.pdf.process(:watermark, user: user)
end
end
So in both cases the same file is returned to the browser, the original non-watermarked version. Maybe I'm trying to get dragonfly to do something it's not supposed to do in a before_serve block? My alternative implementation would be to block access to direct file downloads and do all of this in a rails controller instead. I'd like to use dragonfly's before_serve block if possible as I've already added all of the authentication within the block to make sure users are allowed to download the file.
Thanks
After further investigation it would appear the best solution is to block all direct file access in the before_serve block and use a standard rails controller to watermark the file and present it for download.

Rails 3: Choose and run a Mechanize script from inside Rails action.

My application scrapes information from various sites using Mechanize. Naturally, each site requires custom Mechanize code. Each site is stored in my database, including the url to scrape and the string name of an .rb file containing that site's Mechanize code. For this case, let's assume the scripts are available in the assets folder.
I would like to call http://example.com/site/:id, then have the show action dynamically choose which Mechanize script to run (say, #site.name + ".rb" ). The script will massage the data into a common model, so all sites can use the same show template.
I can't find a way to dynamically load a .rb script within an action and obtain the result. It may be easier to have the scripts return a JSON string, which I can parse before passing on to the template, but I can't see a solution for that either. Ideally, the script will run in the action's scope. The ugly solution is an enormous if-else chain (testing the site name to determine which code block to run), but there must be a better way.
Any suggestions would be greatly appreciated, as would any general solutions to running different code dependent upon the properties of database objects.
If you have all the code in your app already why are you eval'ing Ruby code?
Create classes like:
class GoogleSpider < Spider; end
class NewYorkTimesSpider < Spider; end
class SomeOtherSpider < Spider; end
And the site class will hold the class name that will be used, so you would be able to easily do something like this in your controller action:
def show
#site = Site.find(params[:id])
# name contains SomeOtherSpider
#process_output = #site.name.constantize.new.process
# do something with the output here
end
And then you don't need to mess around evaluating Ruby code, just call the class needed. You can even make them all singletons or keep them all in a hash for faster access.

Using memcache in a Rails 3 app

Do you have to add code to your Rails app to use memcache? I've added config.cache_store = :dalli_store to my production environment and Rails.cache.write('color', 'red') works.
I haven't made any modifications to my models, views, or controllers. When I create and save an object, for example
user = User.new
user.name = 'John Doe'
user.email = 'john#doe.com'
user.save
it is going straight to the database and not going to memcache. I was hoping activerecord would use memcache automatically if configured for it. Is that not the case? Any good articles on rewriting your models to use Rails.cache (or should this be something done in the controller)?
config.cache_store is only going to be used for configuring the store used in writing and retrieving cache. This is completely separate from your model data storage.
http://guides.rubyonrails.org/caching_with_rails.html
Are you trying to substitute an ActiveRecord database like MySQL with memcached? Writing to memcached when you create a new record, instead of writing to a database?
UPDATE BASED ON YOUR COMMENT
cache_money is doing the write_through using activerecord callbacks.
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Look at the line 15-17 and you can see where they call the after callbacks:
https://github.com/nkallen/cache-money/blob/master/lib/cash/write_through.rb
You could accomplish what you need in the same manner. Just code a custom method and make it available to your models. Then call the method with after_create, after_update, after_destroy in the model. If you want to make it happen to all models, you could probably do this in an initializer:
#UNTESTED, but should be close
ActiveRecord::Base.descendants.each do |model|
model.class_eval do
after_create :your_cache_method
#... other callbacks
end
end
You could then code a module with your_cache_method in it and include it in ActiveRecord::Base, so it would be available to all models

Mongoid, confused with embedded document with timestamps and versioning?

I have been using Mongoid for about 3 months now, and I have managed to get done pretty much anything I need thanks to the great document and resources out there.
But going back to improve some stuff I have made a few backs, I am definitely struggling a lot on embedded documents.
In a nutshell what I am trying to do, is to maintain versioning and timestamps on embedded documents, but that I cannot manage to do.
Here is the relevant part of my model:
class Content
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
embeds_many :localized_contents
accepts_nested_attributes_for :localized_contents
end
class LocalizedContent
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
include Mongoid::Versioning
embedded_in :content, :inverse_of => :localized_contents
end
Nothing really complicated here, everything works fine regarding the behavior of the Content model, however the LocalizedContent model is not behaving the way I am expecting to, so my expectations either needs to get straighten up, or I need help fixing what I am doing wrong.
To create a new embedded document I do the following:
my_content = Content.find(params[:id])
my_content.localized_contents.build(params[:localized_content])
if parent.save
#redirect, etc.
end
This works in the sense that it successfully creates a new embedded document in the correct Content, however the timestamps fields I left a nil
Now, if I try to update that localized_content:
my_content = Content.find(params[:content_id])
localized_content = my_content.localized_contents.find(params[:id])
Now, if I do: localized_content.update_attributes(params[:localized_content]) I get the following error:
=> Mongoid::Errors::InvalidCollection: Access to the collection for LocalizedContent is not allowed since it is an embedded document, please access a collection from the root document.
Fair enough, then I update atomically the fields on the localized content and save the parent:
localized_content.fieldA = "value"
localized_content.fieldB = "value"
localized_content.fieldC = "value"
my_content.save
This works in updating the localized content properly but:
- timesteamps (udpated_at and created_at) are still nil
- versions does not receive the a copy of the current localized_content and version does not get incremented !
So as I read in many occasion in this groups and on some forums on the web, the call backs are not triggered on the embedded document for performance reason, since I am calling save on the parent. Again, faire enough, but as suggested in those places, I should call save on the embedded docs instead... but how !?!?! because every time I do I get the dreaded:
=> Mongoid::Errors::InvalidCollection: Access to the collection for LocalizedContent is not allowed since it is an embedded document, please access a collection from the root document.
Even more so, I tried to manually call the call back for versioning on my embedded: localized_content.revise, and again same error:
=> Mongoid::Errors::InvalidCollection: Access to the collection for LocalizedContent is not allowed since it is an embedded document, please access a collection from the root document.
I am going nuts here ! Please help. What I am doing wrong ? How should an embedded document be create and updated so I can call (even manually I don't care) the proper callbacks to update the time stamps and versioning ?
Thanks,
Alex
ps: I am using rails 3.0.3 and mongoid 2.0.1
Just in case this answer is still useful to anyone, Mongoid has added a tag which makes callbacks run on embedded child objects when the parent object is saved.
Your parent object should now look like this:
class Content
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
embeds_many :localized_contents, cascade_callbacks: true
accepts_nested_attributes_for :localized_contents
end
That's it! Now, saving the parent object will run callbacks on the child objects (and Mongoid::Timestamps is smart enough to only run on the objects which were actually changed). This information is in the mongoid documentation, at the very bottom of the embedded documents page.
Try using create instead of build. EmbeddedDoc.build and EmbeddedDoc.new won't fire any save callbacks (because nothing's being saved yet), and saving the parent doc won't call the embedded children's callbacks (performance decision). EmbeddedDoc.create should fire the embedded docs callbacks though.
my_content = Content.find(params[:id])
puts my_content.localized_contents.create(params[:localized_content])