Life as Clay

Archive for the ‘Rails’ Category

Paperclip, S3 & Delayed Job in Rails on Heroku – Jan 2012

with 2 comments

Edit: I forgot to mention here that with Paperclip 2.4.5 you have to use the ‘paperclip-aws’ gem in order for Paperclip to work with Amazon’s newer ‘aws-sdk’ gem. That is no longer true with Paperclip 2.5.

I followed this good tutorial on how to push paperclip image processing to the background with delayed_job. My Rails app is deployed to Heroku on the cedar stack. I’ve had problems with the NoMethod errors using delayed_job 3.0.0, so I downgraded to 2.1.4. Also using paperclip version 2.4.5. In the end, I found that I could ditch the struct presented in the aforementioned tutorial and just call handle_asynchronously on my reprocessing method. This is what the codes looks like:


class Image < ActiveRecord::Base
  attr_accessible :profile_pic,
                  :caption,
                  :note,
                  :pic_file_name,
                  :pic_content_type,
                  :pic_file_size,
                  :pic_updated_at,
                  :pic,
                  :pic_attributes,
                  :processing
  
  belongs_to :imageable, :polymorphic => true
  
  # Added for paperclip-aws
  def self.s3_config
      @@s3_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/s3.yml")).result)[Rails.env]    
  end
  
  has_attached_file :pic,
                    :styles => { 
                      :large => "500x500>",
                      :thumb => "100x100>", 
                      :tiny => "50>x50>", 
                      :smallest => "24>x24>" },   
                      :default_url => '/:attachment/:style/missing.png',               
                      
                      # Added for paperclip-aws
                      :storage => :aws,
                      :s3_permissions => :authenticated_read,
                      :path => "images/:id/:style/:filename",
                      :s3_credentials => {
                        :access_key_id => self.s3_config['access_key_id'],
                        :secret_access_key => self.s3_config['secret_access_key']
                      },
                      :bucket => self.s3_config['bucket'],
                      :s3_protocol => "https"    
                      
  validates_attachment_content_type :pic, :content_type => [ /^image\/(?:jpeg|gif|png)$/, nil ]
  
  # How to implement on Heroku with processing in the background
  # http://madeofcode.com/posts/42-paperclip-s3-delayed-job-in-rails
  
  # cancel post-processing now, and set flag...
     before_pic_post_process do |image|
       if !image.processing && image.pic_changed?
         image.processing = true
         false # halts processing
       end
     end
     
     # call method from after_save that will be processed in the background
     after_save do |image| 
       if image.processing
         processImageJob(image)
       end
     end
     
     def processImageJob(image)
         image.regenerate_styles!
     end
     handle_asynchronously :processImageJob
     
     # generate styles (downloads original first)
     def regenerate_styles!
       self.pic.reprocess! 
       self.processing = false   
       self.save(:validations => false)
     end
     
     # detect if our source file has changed
     def pic_changed?
       self.pic_file_size_changed? || 
       self.pic_file_name_changed? ||
       self.pic_content_type_changed? || 
       self.pic_updated_at_changed?
     end
                      
end

Written by Clay

January 12, 2012 at 14:29

Posted in Code, Rails, Ruby

Tagged with , , , , ,

Using Colorbox with Rails 3.1

with 2 comments

I’m in the midst of creating a mini-Facebook like Rails app that I can use to track events in my daughter’s life. Part of that includes the ability to attach photos to events. I wanted to use a friendly modal popup for displaying larger versions of photos, so I elected to go with ColorBox. It’s a jQuery plugin that is dead simple to use, especially on static pages.

For the “timeline” page, a dynamic page, I never know whether there will be photos to display and if there are, how they are grouped. The idea is that photos that are attached to the same event should all be part of one gallery.

Here’s what I did…

Anything that can end up on the timeline is a “mark” in my app. I use a partial when iterating through the marks to display photos that may be associated with the mark. Here is that partial:

<% if mark.images.count > 0 %>
	<div class="mark_images">
		<% for image in mark.images %>
				<a href="<%= image.pic(:large) %>" class="gallery_<%= mark.id %>" title="<%= image.caption %>"><%= image_tag(image.pic(:tiny), :title => image.caption, :class => "mark_image") %></a>
		<% end%>
	</div>
<% end %>

As you can see, I’m using thumbnail images as links. The images, by the way, are attached using the Paperclip gem from thoughtbot. The code above sets up the HTML that ColorBox needs in order to display the modal.

The rest of the work comes in javascript. I elected to put the ColorBox javascript at the end of my application.js file. It looks like this:

$(document).ready(function() {
	galleries = [];
	$('a[class^="gallery_"]').each( function() {
		if ($.inArray($(this).attr("class"), galleries) == -1) {
			$(this).colorbox({
				rel: $(this).attr("class"),
				maxWidth: "95%",
				maxHeight: "95%"
			});
			galleries.push($(this).attr("class"));
		}
	});
});

Here I create an empty array called galleries. I then find all link elements on the page that have a class that begins with “gallery_“. I iterate on each of them. If the class of the element is not in the galleries array, then I set up ColorBox for that class (gallery) and I add the class name to the galleries array so that I know not to set it up again.

Finally, I use the class of the gallery to relate the images that share the same class together, so that I get the navigational buttons on the modal viewbox.

To make this all work, you have to drop the ColorBox .js file into your assets/javascripts directory and you need to include both the .css file that you prefer (from the ColorBox downloadable examples) and the images that accompany it. I chose to use example 3. I had to modify the css image paths so that it would locate the ColorBox images in my assets/images directory. I elected to rename the enclosing folder to colorbox_images. The css file I ended up with looks like this:

/*
    ColorBox Core Style:
    The following CSS is consistent between example themes and should not be altered.
*/
#colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:9999; overflow:hidden;}
#cboxOverlay{position:fixed; width:100%; height:100%;}
#cboxMiddleLeft, #cboxBottomLeft{clear:left;}
#cboxContent{position:relative;}
#cboxLoadedContent{overflow:auto;}
#cboxTitle{margin:0;}
#cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%;}
#cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;}
.cboxPhoto{float:left; margin:auto; border:0; display:block;}
.cboxIframe{width:100%; height:100%; display:block; border:0;}

/*
    User Style:
    Change the following styles to modify the appearance of ColorBox.  They are
    ordered & tabbed in a way that represents the nesting of the generated HTML.
*/
#cboxOverlay{background:#000;}
#colorbox{}
    #cboxContent{margin-top:20px;margin-bottom:20px}
        .cboxIframe{background:#fff;}
        #cboxError{padding:50px; border:1px solid #ccc;}
        #cboxLoadedContent{border:5px solid #000; background:#fff;}
        #cboxTitle{position:absolute; bottom:-15px; left:0; color:#ccc;}
        #cboxCurrent{position:absolute; top:-20px; right:0px; color:#ccc;}
        #cboxSlideshow{position:absolute; top:-20px; right:90px; color:#fff;}
        #cboxPrevious{position:absolute; top:50%; left:5px; margin-top:-32px; background:url(/assets/colorbox_images/controls.png) no-repeat top left; width:28px; height:65px; text-indent:-9999px;}
        #cboxPrevious:hover{background-position:bottom left;}
        #cboxNext{position:absolute; top:50%; right:5px; margin-top:-32px; background:url(/assets/colorbox_images/controls.png) no-repeat top right; width:28px; height:65px; text-indent:-9999px;}
        #cboxNext:hover{background-position:bottom right;}
        #cboxLoadingOverlay{background:#000;}
        #cboxLoadingGraphic{background:url(/assets/colorbox_images/loading.gif) no-repeat center center;}
        #cboxClose{position:absolute; top:5px; right:5px; display:block; background:url(/assets/colorbox_images/controls.png) no-repeat top center; width:38px; height:19px; text-indent:-9999px;}
        #cboxClose:hover{background-position:bottom center;}

I also chose to move the title to the bottom of the image because that way, it doesn’t bump up against the image count when viewed on a mobile device. That required setting a bottom margin for #cboxContent and changing this line: #cboxTitle{position:absolute; bottom:-15px; left:0; color:#ccc;}.

There are a variety of other options that can be used with the javascript call to alter the modal dialogue. It’s worth taking a look at the ColorBox page for some examples.

Overall, I’m really happy with this solution and probably will use it again in the future. If you’re interested in the app that I’m developing, you can find it on github, called Blytheline. It’s an early work in progress, so it’s a bit rough around the edges!

Written by Clay

November 29, 2011 at 12:53

Returning / Dragging Myself to Rails 3.1

leave a comment »

I’ve been away for a few months, on paternity leave. It was a delight. It now is my turn to return to work and pick up where I left off. You probably know that I work with Rails sometimes if you read this blog. The last time I built a large Rails app was just prior to the 3.0 release. I return to find 3.1 on rc5 and I decided it would be a good idea to get up to speed with it.

Rails 3.1 is a huge departure from Rails 2.x. I feel lost — almost as if I have to learn an entirely new framework! Between updating gems, rails, rack, etc., I decided that I had better start with a new gemset. After stumbling through an RVM upgrade last night (stumbling because I allowed too many versions to pass in-between and the upgrade was far from smooth), I installed 3.1.0.rc5 and started a test app. I ran into a few bugs related to the rc status of the release. The first is that I was receiving a JSON error on a vanilla blank app. If you bump into that, just require these gems:

gem 'multi_json'
gem 'json'

The second is that there was a problem with the arel version. Including the following in the Gemfile fixed the issue for the time being:

gem 'arel', '2.1.4' # remove when 3.1.0 is released

The app boots without errors now, so I’m off to plug away at the new Asset Pipeline and other features. If you’re starting fresh with Rails 3.1, check out this page at Github for a great tutorial on how to get started.

Before I go, however, I want to point to a lovely bit of Processing work by Justin Livi. He created a sketch that randomly generates dandelions like the one below. Check out his site to see the interactive applet.

Written by Clay

August 11, 2011 at 08:34

Posted in Rails

Tagged with ,