I have a set of images on Amazon S3, and I'd like to automatically generate thumbnails for them to serve on my site.
I've considered Cloudinary, but it seems that I'd have to copy over all my images to Cloudinary servers first. I want to keep them on S3.
I've considered Dragonfly, but it seems that Dragonfly only works with files I'd upload after installing Dragonfly. I already have uploaded all my files.
What's a good solution for me? I'm in a Rails environment (rails 3.2).
Thanks!
If it's just a 'set of images' it's not really that well structured. You're better off reorganizing the way you store and manage images.
Try Paperclip.
class ModelName < ActiveRecord::Base
attr_accessible :image #more here
has_attached_file :image, :styles => { :large => "450x450>", :medium => "300x300>", :thumb => "150x150>" },
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:path => "people/:style/:id/:filename",
:s3_protocol => "https"
def original_image_url
image.url
end
def large_image_url
image.url(:large)
end
def medium_image_url
image.url(:medium)
end
def small_image_url
image.url(:thumb)
end
#etc
end
Then simply do this to assign an existing image to an existing instance through the console:
require 'uri'
#instance.image = open(URI::encode(url))
#instance.save
# s3 will now contain the images in the appropriate sizes in the format specified above.
Since the original will also be saved, I'd advise to then delete the 'set of images' on s3 you started with, otherwise you'll be duplicating them.
I was wrong about Dragonfly. You can use Dragonfly on already-uploaded files. I'm using it on my project and it's working out great.
I have models specs, controllers spec and request spec. When I run:
rspec spec
models spec are run first, then request and then when controller specs are run the specs for the first controller are OK, but the next fail. But when I run only the controller specs they all pass. I am with rails 3.0.9, ruby 1.8, factory_girl 2.2.0. I have
config.cache_classes = true
in test.rb and I can't change the version of ruby or factory_girl. Can someone help me?
UPDATE:
This is the error:
96) UsersController reset_password: as non-master_admin: does not reset a user's password
Failure/Error: let!(:user) { Factory(:admin_user) }
ActiveRecord::AssociationTypeMismatch:
AdminUser(#-630697398) expected, got MerchantUser(#-629918188)
# ./app/models/activity.rb:33:in `log'
# ./config/initializers/add_activity_logging.rb:8:in `_callback_after_759'
# ./spec/controllers/users_controller_spec.rb:8
in spec/controllers/users_controller_spec.rb:8:
let!(:user) { Factory(:admin_user) }
in activity.rb:33:
create(:user => user, :title => title, :changeable_id => changeable.id,
:changeable_type => changeable.class.to_s, :data => attributes)
also there is:
belongs_to :user, :class_name => 'AdminUser'
in the class AdminUser there isn't has_many activities but when I tried to add it I couldn't add it correctly I guess.
Thanks for the help
UPDATE:
AdminUser and MerchantUser are descendants of User
POSIBLE FIX
The line that gave error was actually:
admin_user = Factory(:tech_admin)
I replaced it with:
admin_user = FactoryGirl.build_stubbed(:tech_admin)
This way the file activity.rb is not reached
FINAL FIX
Apparently the problem was with Factory(:reseller). I replaced it with FactoryGirl.create(:reseller) and it everything work. Though now I am wondering and searching what is the difference between the two uses
I'm using Paperclip and have a default_url option like this for one of my attachments:
:default_url => '/images/missing_:style.png'
The asset pipeline obviously doesn't like this since the directories moved. What's the best way to handle this? I have two styles for this picture (:mini and :thumb).
:default_url => ActionController::Base.helpers.asset_path('missing_:style.png')
Then put the default images in app/assets/images/
Tested only on Rails 4.
To make it work in production, we have to pass the name of an existing file to the asset_path helper. Passing a string containing a placeholder like "missing_:style.png" therefore doesn't work. I used a custom interpolation as a workaround:
# config/initializers/paperclip.rb
Paperclip.interpolates(:placeholder) do |attachment, style|
ActionController::Base.helpers.asset_path("missing_#{style}.png")
end
Note that you must not prefix the path with images/ even if your image is located in app/assets/images. Then use it like:
# app/models/some_model.rb
has_attached_file(:thumbnail,
:default_url => ':placeholder',
:styles => { ... })
Now default urls with correct digest hashes are played out in production.
The default_url option also takes a lambda, but I could not find a way to determine the requested style since interpolations are only applied to the result of the lambda.
Just make sure that in your views all your paperclip images are rendered with image_tag.
<%= image_tag my_model.attachment.url(:icon) %>
That way, all of paperclip's :crazy :symbol :interpolation will have happened to the url string before Rails tries to resolve it to an asset in the pipeline.
Also, make sure your :default_url is asset compatible...if missing_icon.png is at app/assets/images/missing_icon.png, then :default_url should be simply "missing_:style.png"
<%= image_tag my_model.attachment.url(:icon) %>
# resolves to...
<%= image_tag "missing_icon.png" %>
# which in development resolves to...
<img src="/assets/missing_icon.png">
I got the error(even for a single style) at assets:precompile with
:default_url => ActionController::Base.helpers.asset_path('missing.png')
So I hooked with a method like this
# supposing this is for avatar in User model
has_attached_file :avatar,
:styles => {..},
:default_url => lambda { |avatar| avatar.instance.set_default_url}
def set_default_url
ActionController::Base.helpers.asset_path('missing.png')
end
I didn't try for multiple styles, but this works for my situation.
this works for me:
has_attached_file :avatar, :styles => { :small => "52x52",
:medium => "200x200>", :large=> "300x300", :thumb => "100x100>" },
:default_url => "missing_:style.png"
just place images in your assets/images folder named: missing_large.png, missing_medium.png, missing_small.png and missing_thumb.png
In rails 4.0.0 and paperclip 4.1.1 this worked for me:
has_attached_file :avatar,
styles: { medium: '300x300#', small: '100x100#', thumb: '25x25#' },
default_url: ->(attachment) { 'avatar/:style.gif' },
convert_options: { all: '-set colorspace sRGB -strip' }
I ended up having to use something like the following.
DEFAULT_URL = "#{Rails.configuration.action_controller.asset_host}#{Rails.configuration.assets.prefix}/:attachment/:style/missing.png"
has_attached_file :art, :styles => { :large => "398x398#", :medium => "200x200#", :small=>"100x100#", :smaller=>"50x50#", :smallest=>"25x25"}, :path=>"images/:attachment/:id/:style/:basename.:extension", :default_url => DEFAULT_URL
I statically compile the assets and was getting an error in production, this helped me.
In your model file, change this line:
has_attached_file :avatar, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
by removing this:
/images/
Create a folder for each style, in this example medium and thumb, in assests/images and place an image called missing.png there (or whatever name you want it to have of course, as long as it matches the file name in the model)
Worked for me.
I've solved this problem by using a custom interpolator.
The problem from other solutions that suggest using
:default_url => ActionController::Base.helpers.asset_path('missing_:style.png')
is that you will get an error saying "missing_style.png" is not precompiled.
I created an initializer with the following code:
module Paperclip
module AssetPipeline
module Interpolator
def self.interpolate(pattern, *args)
ActionController::Base.helpers.asset_path Paperclip::Interpolations.interpolate(pattern, *args)
end
end
end
end
Then in my model I would do:
has_attached_file :image, interpolator: Paperclip::AssetPipeline::Interpolator, ...
Just remove the / from /images/pic.png: images/pic.png
I used enumerated_attribute with formtastic ~> 1.2.3 with "monkey patch" for field :as => :enum and everything worked fine.
But when I updated formtastic to 2.0.2 version appeared an error with message "Formtastic::UnknownInputError".
For more details here is patch, that was added to /initialisers/formtastic.rb:
module Formtastic #:nodoc:
class SemanticFormBuilder #:nodoc:
def enum_input(method, options)
unless options[:collection]
enum = #object.enums(method.to_sym)
choices = enum ? enum.select_options : []
options[:collection] = choices
end
if (value = #object.__send__(method.to_sym))
options[:selected] ||= value.to_s
else
options[:include_blank] ||= true
end
select_input(method, options)
end
end
end
P.S. I tried to change SemanticFormBuilder to FormBuilder (as I understood from new formtastic documentation there was such change for all custom inputs), but I was still getting error
Maybe anybody already used these gems together successfully?
They way custom fields are defined has changed completely in Formtastic 2.x
You need to subclass the internal Formtastic classes to get what you want. A select input would look something like this:
module FormtasticExtensions
class EnumeratedInput < Formtastic::Inputs::SelectInput
def collection
# programmatically build an array of options in here and return them
# they should be in this format:
# [['name', 'value'],['name2', 'value2']]
end
end
end
Include the module in the Formtastic initializer:
include FormtasticExtensions
and this will give you a field :as => :enumerated and you should be good to go. In my case (some other custom field) it selects the current option, but you may need to tweak the code for yours to work.
You could also just pass the collection in:
f.input :thing, :as => :select, :collection => your_collection, :label_method => :your_name, :value_method => :your_id
I'm having problems displaying data from a separate controller. I have a number of users, each with many pages. I've followed this tutorial with a few minor adjustments.
The error that keeps appearing is:
NoMethodError in SitesController#show
undefined method `page' for #<ActionDispatch::Request:0x00000102452d30>
My routes.rb is as follows:
devise_for :users
resources :users, :only => [:index, :show] do
resources :pages, :shallow => true
end
match '/' => 'sites#show', :constraints => { :subdomain => /.+/ }
root :to => "home#index"
And I have a sites controller:
class SitesController < ApplicationController
def show
#site = Site.find_by_name!(request.page)
end
end
I've also tried:
def show
#site = Site.find_by_name!(params[:site])
end
Which gives a different error.
Am totally stuck trying to figure this out!
Looking forward to your assistance.
Bob
The problem is here: request.page
The request object is of the class ActionDispatch::Request, which does not have a page method.
To track down errors like this, you can try either looking at the docs or messing around in the debugger.
Try running your controller with --debugger enabled.
If you are running Ruby 1.8, install the ruby-debug gem.
If you are running Ruby 1.9, install the ruby-debug19 gem.
Add a debugger call here:
class SitesController < ApplicationController
def show
debugger
#site = Site.find_by_name!(request.page)
end
end
Run your server with the --debugger option.
See what p request.page does. I bet it will have an "undefined method" error, just you see when you try to view that controller action.
If you do a p request.class you can find out what class the object is, and then look up the docs to see how to use it.