GithubHelp home page GithubHelp logo

guniorobot / carrierwave-meta Goto Github PK

View Code? Open in Web Editor NEW

This project forked from gzigzigzeo/carrierwave-meta

1.0 2.0 0.0 175 KB

File's metadata saving plugin for carrierwave. Under development

Ruby 100.00%

carrierwave-meta's Introduction

carrierwave-meta

Installation

Add the following line to your Gemfile:

gem 'carrierwave-meta'

Usage example

class TestUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick
  include CarrierWave::Meta

  storage :file

  process :store_meta
  version :version do
    process :resize_to_fill => [200, 200]
    process :store_meta
  end
end

file = File.open('test.jpg') # JPEG 500x300, 20000 bytes
uploader = TestUploader.new
uploader.store!(file)

uploader.width        # 500
uploader.height       # 300
uploader.image_size   # [500, 300]
uploader.file_zie     # 20000
uploader.content_type # "image/jpeg"

uploader.version.width        # 200
uploader.version.height       # 200
uploader.version.image_size   # [200, 200]
uploader.version.file_zie     # less than 20000
uploader.version.content_type # "image/jpeg"

Saving values to database

Simply create database columns to hold metadata in your model’s table. Currently gem supports width, height, image_size ([width, height]), content_type and file_size fields. Versions are supported too.

class TestModel
  attr_accessor :image_width  
  attr_accessor :image_height
  attr_accessor :image_image_size  
  attr_accessor :image_content_type
  attr_accessor :image_file_size

  attr_accessor :image_version_width  
  attr_accessor :image_version_height
  attr_accessor :image_version_image_size
  attr_accessor :image_version_content_type
  attr_accessor :image_version_file_size  
end

file = File.open('test.jpg')
model = TestModel.new
uploader = TestUploader.new(model, :image)
uploader.store!(file)

uploader.width      # 500
model.image_width   # 500
model.image_height  # 300
...

When columns are available in the model instance, metadata is stored in that columns.

Behind the scenes

After the file is retrieved from store or cache metadata is recalculated unless uploader has attached model instance. If uploader has attached model instance values are read from that instance.

uploader = TestUploader.new
uploader.retrieve_from_store!('test.jpg')

uploader.version.width # 200

model_delegate_attribute

Used to synchronize data between uploader and mounted model instance. Model’s instance is used like value cache.

class DelegateTestModel
  attr_accessor :processed
  attr_accessor :a_processed
  attr_accessor :a_b_processed
end

class DelegateTestUploader < CarrierWave::Uploader::Base
  model_delegate_attribute :uploaded

  set_processed

  version :a do
    set_processed
    version :b do
      set_processed
    end
  end

  def set_processed
    self.processed = true
  end
end

model = DelegateTestModel.new
uploader = DelegateTestUploader.new(model, :image)
file = File.open('test.jpg')

uploader.store!(file)

model.processed     # true
model.a_processed   # true
model.a_b_processed # true

model.a_processed = false

uploader.processed     # true
uploader.a_processed   # false
uploader.a_b_processed # true

When model is mounted to uploader:

  1. If attribute is assigned inside uploader then corresponding property in model is also assigned.

  2. If attribute is retrieved from uploader instance first checks that value is defined in model and returns it. Otherwise returns uploader’s instance variable.

  3. If file is deleted, value becomes nil.

Otherwise acts as regular uploader’s instance variables.

This is very useful for:

Integrating CarrierWave with JCrop

Let implement the behavior like at this demo: deepliquid.com/projects/Jcrop/demos.php?demo=thumbnail

The uploader:

class CropUploader < SobakaUploader
  include CarrierWave::Meta

  # Crop source is a source image converted from original which could be bigger than source area (left image in the example).
  version :crop_source do
    process :resize_to_fit => [300, 300]
    process :store_meta

    # This is the cropped version of parent image. Let crop to 50x50 square.
    version :crop do
      process :crop_to => [50, 50]
    end
  end

  # Defines crop area dimensions.
  # This should be assigned before #store! and #cache! called and should be saved in the model's instance.
  # Otherwise cropped image would be lost after #recreate_versions! is called.
  # If crop area dimensions are'nt assigned, uploader calculates crop area dimensions inside the
  # parent image and creates the default image.
  model_delegate_attribute :x
  model_delegate_attribute :y
  model_delegate_attribute :w
  model_delegate_attribute :h

  # Crop processor
  def crop_to(width, height)
    # Checks that crop area is defined and crop should be done.
    if ((crop_args[0] == crop_args[2]) || (crop_args[1] == crop_args[3]))
      # If not creates default image and saves it's dimensions.
      resize_to_fill_and_save_dimensions(width, height)
    else
      args = crop_args + [width, height]
      crop_and_resize(*args)
    end
  end

  def crop_and_resize(x, y, width, height, new_width, new_height)
    manipulate! do |img|
      cropped_img = img.crop(x, y, width, height)
      new_img = cropped_img.resize_to_fill(new_width, new_height)
      destroy_image(cropped_img)
      destroy_image(img)      
      new_img
    end
  end

  # Creates the default crop image.
  # Here the original crop area dimensions are restored and assigned to the model's instance.
  def resize_to_fill_and_save_dimensions(new_width, new_height)
    manipulate! do |img|
      width, height = img.columns, img.rows
      new_img = img.resize_to_fill(new_width, new_height)
      destroy_image(img)

      w_ratio = width.to_f / new_width.to_f
      h_ratio = height.to_f / new_height.to_f

      ratio = [w_ratio, h_ratio].min

      self.w = ratio * new_width
      self.h = ratio * new_height
      self.x = (width - self.w) / 2
      self.y = (height - self.h) / 2

      new_img
    end
  end

  private
  def crop_args
    %w(x y w h).map { |accessor| send(accessor).to_i }
  end    
end

# Post should have :crop_source_version_x, :crop_source_version_y, :crop_source_version_h, :crop_source_version_w columns
class Post < ActiveRecord::Base
  mount_uploader CropUploader, :image
end

# Let's upload an image
post = Post.new
post.image = params[:image] # Let the uploaded file is 800x600 JPEG
post.save!

post.image.crop_source.width  # 300
post.image.crop_source.height # 200
post.image.crop_source.crop.width  # 50
post.image.crop_source.crop.height # 50

# Default crop area coordinates within the limits of big image dimensions: square at the center of an image
post.image.crop_source.crop.x # 50
post.image.crop_source.crop.y # 50
post.image.crop_source.crop.w # 200
post.image.crop_source.crop.h # 200

# Let user change the crop area with JCrop script. Pass new crop area parameters to the model.
post.crop_source_crop_x = 100
post.crop_source_crop_y = 100
post.crop_source_crop_w = 100
post.crop_source_crop_h = 100

post.save! # Crop image is reprocessed

post.image.crop_source.crop.width  # 50
post.image.crop_source.crop.height # 50

A note about testing

fschwahn added support for mini-magick. To run tests with mini-magick do:

bundle exec rake spec PROCESSOR=mini_magick

TODO

  1. I do not know how it would work with S3 and other remote storages. Should be tested.

  2. Write integration guide for JCrop.

  3. A notice about content-type.

  4. Improve my english skills.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.