Logo: Relish

  1. Sign in

Project: Couch-photo

Auto-generated and Custom Variations

Let's imagine we want to auto-generate small, medium, and large variations of our image everytime it gets updated.

First, define an Image document and set the versions on it:

class Image < CouchRest::Model::Base
  use_database IMAGE_DB
  include CouchPhoto

  override_id! # the id of the document will be the basename of the original image file

  variations do
    small "20x20"
    medium "100x100"
    large "500x500"
  end
end

Next, create an instance of the image document and set the "original" image on it.

i = Image.new
i.load_original_from_file "/path/to/avatar.jpg"
i.save

Now, if you look in your image document in CouchDB, you'll see the following attachments:

http://your_couch_server/your_image_database/avatar.jpg/original.jpg
http://your_couch_server/your_image_database/avatar.jpg/variations/small.jpg
http://your_couch_server/your_image_database/avatar.jpg/variations/medium.jpg
http://your_couch_server/your_image_database/avatar.jpg/variations/large.jpg

h3. Complex Variations

The previous variations were all simple image resizings. What if we wanted to do something a little more complex? Like grayscale the original image? Or create a watermark? No prob!

class Image < CouchRest::Model::Base
  use_database IMAGE_DB
  include CouchPhoto

  override_id! # the id of the document will be the basename of the original image file

  variations do
    grayscale do |original_image|
      original_image.monochrome
    end

    watermark do |original_image|
      original_image.composite(MiniMagick::Image.open("watermark.png", "jpg") do |c|
        c.gravity "center"
      end
    end
  end
end

The original_image variable in the blocks is simply the MiniMagick::Image instance of your original image.

h3. Accessing Variations

So, now that you've got some variations, how do you access them? Simple!

Let's go back to our original image example:

class Image < CouchRest::Model::Base
  include CouchPhoto

  override_id!

  variations do
    small "20x20"
    medium "100x100"
    large "500x500"
  end
end

i = Image.new
i.load_original_from_file "/path/to/couchdb.jpg"
i.save

The variations method on our model is a hash of all the variations; the keys are the variation names, and the values are a variation object:

We can access our variations in one of three ways:

  • the variations method, which will allow you to iterate over the variation_name/variation pairs via the each method
  • the variation.<variation_name> method, which returns a specific variation
  • the variation[:<variation_name>], which also returns a specific variation

For example:

i.variations.each do |variation_name, variation_image|
  puts variation_name
  puts variation.filename
end

i.variations.small
i.variations[:large]

Once you've got a variation, you can call several methods on it:

i.variations[:small].name        # ==> "small"
i.variations[:small].path        # ==> "/your_image_database/avatar.jpg/variations/small.jpg"
i.variations[:small].url         # ==> "http://your.couch.server/your_image_database/avatar.jpg/variations/small.jpg"
i.variations[:small].width       # ==> 50
i.variations[:small].height      # ==> 50
i.variations[:small].basename    # ==> "small.jpg"
i.variations[:small].filetype    # ==> "jpg"
i.variations[:small].mimetype    # ==> "image/jpg"
i.variations[:small].data        # ==> BINARY BLOB

h2. Creating Custom Variations

If you'd like to add a variation to your image document that's not predefined / autogenerated, you can use the load_custom_variation and load_custom_variation_from_file methods:

i = Image.new
i.load_original_from_file "/path/to/somefile.jpg"
i.load_custom_variation_from_file "/path/to/small_watermark.jpg"
i.load_custom_variation :filename => "large_greyscale.jpg", :data => File.read("/path/to/some_image.jpg")
i.save

These images would be accessible via the following urls:

http://your_couch_server/your_image_database/somefile.jpg/variations/original.jpg
http://your_couch_server/your_image_database/somefile.jpg/variations/small_watermark.jpg
http://your_couch_server/your_image_database/somefile.jpg/variations/large_watermark.jpg

h2. Adding extra properties to your variations

Perhaps you'd like to add some extra properties that you can save for each of your image variations, like "alt" text, or captions. Simply use the metadata hash on your variations.

For example, given the following definition:

class Image < CouchRest::Model::Base
  use_database IMAGE_DB
  include CouchPhoto

  variations do
    thumbnail "20x20"
    grayscale do |original_image|
      original_image.monochrome
    end
  end
end

You could now set any metadata properties you desire on your variations:

image = Image.first
image.variations[:small].metadata["alt"] = "alt text"
image.variations[:grayscale].metadata["caption"] = "Grayscale version of: #{@image.original.url}"
image.save
Scenarios
Variations auto-generated on save
Given
an image class definition with a thumbnail variation definition:
  class Image < CouchRest::Model::Base
    include CouchPhoto

    variations do
      thumbnail "50x50"
    end
  end
When
I create an instance of it and load the original and save:
  @image = Image.new
  @image.load_original_from_file "features/fixtures/avatar.jpg"
  @image.save
Then
the image document should have a thumbnail variation:
  @image.variations[:thumbnail].path.should      == "/couch_photo_test/#{@image.id}/variations/thumbnail.jpg"
  @image.variations[:thumbnail].url.should       == "#{Image.database.host}/couch_photo_test/#{@image.id}/variations/thumbnail.jpg"
  @image.variations[:thumbnail].extension.should == "jpg"
  @image.variations[:thumbnail].mime_type.should == "image/jpeg"
  @image.variations[:thumbnail].width.should     == 50
  @image.variations[:thumbnail].height.should    == 50
  @image.variations[:thumbnail].data.should_not be(nil)
  @image.variations[:thumbnail].custom_variation?.should be(false)
When
I load the image from the database:
  @image = Image.first
Then
the image document should have a thumbnail variation:
  @image.variations[:thumbnail].path.should      == "/couch_photo_test/#{@image.id}/variations/thumbnail.jpg"
  @image.variations[:thumbnail].url.should       == "#{Image.database.host}/couch_photo_test/#{@image.id}/variations/thumbnail.jpg"
  @image.variations[:thumbnail].extension.should == "jpg"
  @image.variations[:thumbnail].mime_type.should == "image/jpeg"
  @image.variations[:thumbnail].width.should     == 50
  @image.variations[:thumbnail].height.should    == 50
  @image.variations[:thumbnail].data.should_not be(nil)
  @image.variations[:thumbnail].custom_variation?.should be(false)
Complex variation definitions
Given
an image class with a complex grayscale variation definition on it:
  class Image < CouchRest::Model::Base
    include CouchPhoto

    variations do
      grayscale do |original_image|
        original_image.monochrome
      end
    end
  end
When
I create an instance of it and load the original and save:
  @image = Image.new
  @image.load_original_from_file "features/fixtures/avatar.jpg"
  @image.save
Then
the image document should have a thumbnail variation:
  @image.variations[:grayscale].path.should      == "/couch_photo_test/#{@image.id}/variations/grayscale.jpg"
  @image.variations[:grayscale].url.should       == "#{Image.database.host}/couch_photo_test/#{@image.id}/variations/grayscale.jpg"
  @image.variations[:grayscale].extension.should == "jpg"
  @image.variations[:grayscale].mime_type.should == "image/jpeg"
  @image.variations[:grayscale].width.should     == 128
  @image.variations[:grayscale].height.should    == 128
  @image.variations[:grayscale].data.should_not  be(nil)
  @image.variations[:grayscale].custom_variation?.should be(false)
When
I load the image from the database:
  @image = Image.first
Then
the image document should have a thumbnail variation:
  @image.variations[:grayscale].path.should      == "/couch_photo_test/#{@image.id}/variations/grayscale.jpg"
  @image.variations[:grayscale].url.should       == "#{Image.database.host}/couch_photo_test/#{@image.id}/variations/grayscale.jpg"
  @image.variations[:grayscale].extension.should == "jpg"
  @image.variations[:grayscale].mime_type.should == "image/jpeg"
  @image.variations[:grayscale].width.should     == 128
  @image.variations[:grayscale].height.should    == 128
  @image.variations[:grayscale].data.should_not  be(nil)
  @image.variations[:grayscale].custom_variation?.should be(false)
Creating a custom variation via "load_custom_variation_from_file" method
Given
an instance of an image class that includes CouchPhoto:
  class Image < CouchRest::Model::Base
    include CouchPhoto
  end

  @image = Image.new
When
I add a custom variation to it via the 'load_custom_variation_from_file' method:
  @image.load_custom_variation_from_file "features/fixtures/monochrome.jpg"
And
I save:
  @image.save
Then
I should have a custom variation named 'monochrome':
  @image.variations["monochrome.jpg"].path.should      == "/couch_photo_test/#{@image.id}/variations/monochrome.jpg"
  @image.variations["monochrome.jpg"].url.should       == "#{Image.database.host}/couch_photo_test/#{@image.id}/variations/monochrome.jpg"
  @image.variations["monochrome.jpg"].extension.should == "jpg"
  @image.variations["monochrome.jpg"].mime_type.should == "image/jpeg"
  @image.variations["monochrome.jpg"].width.should     == 128
  @image.variations["monochrome.jpg"].height.should    == 128
  @image.variations["monochrome.jpg"].data.should_not  be(nil)
  @image.variations["monochrome.jpg"].custom_variation?.should be(true)
When
I load the image from the database:
  @image = Image.first
Then
I should have a custom variation named 'monochrome':
  @image.variations["monochrome.jpg"].path.should      == "/couch_photo_test/#{@image.id}/variations/monochrome.jpg"
  @image.variations["monochrome.jpg"].url.should       == "#{Image.database.host}/couch_photo_test/#{@image.id}/variations/monochrome.jpg"
  @image.variations["monochrome.jpg"].extension.should == "jpg"
  @image.variations["monochrome.jpg"].mime_type.should == "image/jpeg"
  @image.variations["monochrome.jpg"].width.should     == 128
  @image.variations["monochrome.jpg"].height.should    == 128
  @image.variations["monochrome.jpg"].data.should_not  be(nil)
  @image.variations["monochrome.jpg"].custom_variation?.should be(true)
Creating a custom variation via "load_custom_variation" method
Given
an instance of an image class that includes CouchPhoto:
  class Image < CouchRest::Model::Base
    include CouchPhoto
  end

  @image = Image.new
When
I add a custom variation to it via the 'load_custom_variation' method:
  @image.load_custom_variation :filename => "crazy_variations/my_awesome_custom_variation.jpg", :data => File.read("features/fixtures/monochrome.jpg")
And
I save:
  @image.save
Then
I should have a custom variation named 'crazy_variations/my_awesome_custom_variation.jpg':
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].path.should      == "/couch_photo_test/#{@image.id}/variations/crazy_variations/my_awesome_custom_variation.jpg"
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].url.should       == "#{Image.database.host}/couch_photo_test/#{@image.id}/variations/crazy_variations/my_awesome_custom_variation.jpg"
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].extension.should == "jpg"
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].mime_type.should == "image/jpeg"
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].width.should     == 128
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].height.should    == 128
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].data.should_not  be(nil)
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].custom_variation?.should be(true)
When
I load the image from the database:
  @image = Image.first
Then
I should have a custom variation named 'crazy_variations/my_awesome_custom_variation.jpg':
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].path.should      == "/couch_photo_test/#{@image.id}/variations/crazy_variations/my_awesome_custom_variation.jpg"
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].url.should       == "#{Image.database.host}/couch_photo_test/#{@image.id}/variations/crazy_variations/my_awesome_custom_variation.jpg"
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].extension.should == "jpg"
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].mime_type.should == "image/jpeg"
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].width.should     == 128
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].height.should    == 128
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].data.should_not  be(nil)
  @image.variations["crazy_variations/my_awesome_custom_variation.jpg"].custom_variation?.should be(true)
Adding metadata onto your image variations
Given
an image class definition with a thumbnail variation definition:
  class Image < CouchRest::Model::Base
    include CouchPhoto

    variations do
      thumbnail "50x50"
    end
  end
When
I create an instance of it and add an original image to it:
  @image = Image.new
  @image.load_original_from_file "features/fixtures/avatar.jpg"
Then
I should be able to add metadata to the original:
  @image.original.metadata["alt"] = "Hi!"
And
I should be able to access metadata on the original:
  @image.variations["original.jpg"].metadata["alt"].should == "Hi!"
  @image.original.metadata["alt"].should == "Hi!"
When
I save:
  @image.save
Then
I should be able to access metadata on the original:
  @image.variations["original.jpg"].metadata["alt"].should == "Hi!"
  @image.original.metadata["alt"].should == "Hi!"
When
I load the image from the database:
  @image = Image.first
Then
I should be able to access metadata on the original:
  @image.variations["original.jpg"].metadata["alt"].should == "Hi!"
  @image.original.metadata["alt"].should == "Hi!"
Adding metadata onto your image variations
Given
an image class definition with a thumbnail variation definition:
  class Image < CouchRest::Model::Base
    include CouchPhoto

    variations do
      thumbnail "50x50"
    end
  end
When
I create an instance of it and add an original image to it:
  @image = Image.new
  @image.load_original_from_file "features/fixtures/avatar.jpg"
Then
I should be able to add metadata to the thumbnail variation:
  @image.variations[:thumbnail].metadata["alt"] = "Hi!"
And
I should be able to access metadata on the thumbnail variation:
  @image.variations[:thumbnail].metadata["alt"].should == "Hi!"
When
I save:
  @image.save
Then
I should be able to access metadata on the thumbnail variation:
  @image.variations[:thumbnail].metadata["alt"].should == "Hi!"
When
I load the image from the database:
  @image = Image.first
Then
I should be able to access metadata on the thumbnail variation:
  @image.variations[:thumbnail].metadata["alt"].should == "Hi!"
Creating a custom variation via "load_custom_variation_from_file" method
Given
an instance of an image class that includes CouchPhoto:
  class Image < CouchRest::Model::Base
    include CouchPhoto

    variations do
      thumbnail "50x50"
    end
  end

  @image = Image.new
  @image.load_original_from_file "features/fixtures/avatar.jpg"
When
I add a custom variation to it via the 'load_custom_variation_from_file' method:
  @image.load_custom_variation_from_file "features/fixtures/monochrome.jpg"
And
I save:
  @image.save
Then
I should have a custom variation named 'monochrome.jpg':
  @image.variations["monochrome.jpg"].should_not be_nil
When
I destroy the 'monochrome.jpg' variation:
  @image.variations["monochrome.jpg"].destroy
And
I save:
  @image.save
Then
the image should not have a custom variation named 'monochrome.jpg':
  @image.variations["monochrome.jpg"].should be_nil
When
I load the image from the database:
  @image = Image.first
Then
the image should not have a custom variation named 'monochrome.jpg':
  @image.variations["monochrome.jpg"].should be_nil
When
I attempt to destroy a defined variation:
  @attempt = proc { @image.variations[:thumbnail].destroy }
Then
CouchPhoto should raise an exception:
  @attempt.should raise_exception("You may only delete custom variations")

Last published almost 7 years ago by moonmaster9000.