Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial import

  • Loading branch information...
commit 2d03e12c503c3fdf8d5265472426192915adcde4 0 parents
dusty authored
1  .gitignore
@@ -0,0 +1 @@
+*.gem
47 README.txt
@@ -0,0 +1,47 @@
+Mongoid::Grid / Rack::Grid
+
+ Mongoid::Grid is a plugin for mongoid that uses GridFS. Heavily inspired
+ by grip (http://github.com/jnunemaker/grip)
+
+ Rack::Grid is used to serve a GridFS file from rack. Mostly copied
+ from http://github.com/skinandbones/rack-gridfs
+
+ Download the source at
+ http://github.com/dusty/mongoid_grid
+
+
+Installation
+
+ Put the library in your project however you want.
+
+Usage
+
+ class Monkey
+ include Mongoid::Document
+ include Mongoid::Grid
+ field :name
+ attachment :image
+ end
+
+ m = Monkey.create(:name => 'name')
+
+ # To add an attachment
+ m.image = File.open('/tmp/me.jpg')
+ m.save
+
+ # To remove an attachment
+ m.image = nil
+ m.save
+
+ # To get the attachment
+ m.image.read
+
+ # To use Rack::Grid with Sinatra
+
+ configure do
+ use Rack::Grid, :database => 'my_db'
+ end
+
+ <img src="<%= m.image_url %>" alt="<%= m.image_name %>" />
+
+
169 lib/mongoid_grid.rb
@@ -0,0 +1,169 @@
+require 'mime/types'
+
+module Mongoid
+ module Grid
+
+ def self.included(base)
+ base.send(:extend, ClassMethods)
+ base.send(:include, InstanceMethods)
+ end
+
+ module ClassMethods
+
+ ##
+ # Declare an attachment for the object
+ #
+ # eg: attachment :image
+ def attachment(name,prefix='grid')
+ ##
+ # Callbacks to handle the attachment saving and deleting
+ after_save :create_attachments
+ after_save :delete_attachments
+ after_destroy :destroy_attachments
+
+ ##
+ # Fields for the attachment.
+ #
+ # Only the _id is really needed, the others are helpful cached
+ # so you don't need to hit GridFS
+ field "#{name}_id".to_sym, :type => Mongo::ObjectID
+ field "#{name}_name".to_sym, :type => String
+ field "#{name}_size".to_sym, :type => Integer
+ field "#{name}_type".to_sym, :type => String
+
+ ##
+ # Add this name to the attachment_types
+ attachment_types.push(name).uniq!
+
+ ##
+ # Return the GridFS object.
+ # eg: image.filename, image.read
+ define_method(name) do
+ grid.get(attributes["#{name}_id"]) if attributes["#{name}_id"]
+ end
+
+ ##
+ # Create a method to set the attachment
+ # eg: object.image = File.open('/tmp/somefile.jpg')
+ define_method("#{name}=") do |file|
+ if file.respond_to?(:read)
+ send(:create_attachment, name, file)
+ else
+ send(:delete_attachment, name, send("#{name}_id"))
+ end
+ end
+
+ ##
+ # Return the relative URL to the file for use with Rack::Grid
+ # eg: /grid/4ba69fde8c8f369a6e000003/somefile.png
+ define_method("#{name}_url") do
+ if attributes["#{name}_id"] && attributes["#{name}_name"]
+ "/#{prefix}/#{name}_id/#{name}_name"
+ end
+ end
+
+ end
+
+ ##
+ # Accessor to GridFS
+ def grid
+ @grid ||= Mongo::Grid.new(Mongoid.database)
+ end
+
+ ##
+ # All the attachments types for this class
+ def attachment_types
+ @attachment_types ||= []
+ end
+
+ end
+
+ module InstanceMethods
+
+ private
+ ##
+ # Accessor to GridFS
+ def grid
+ @grid ||= self.class.grid
+ end
+
+ ##
+ # Holds queue of attachments to create
+ def create_attachment_queue
+ @create_attachment_queue ||= {}
+ end
+
+ ##
+ # Holds queue of attachments to delete
+ def delete_attachment_queue
+ @delete_attachment_queue ||= {}
+ end
+
+ ##
+ # Attachments we need to add after save.
+ def create_attachment(name,file)
+ if file.respond_to?(:read)
+ filename = file.respond_to?(:original_filename) ?
+ file.original_filename : File.basename(file.path)
+ type = MIME::Types.type_for(file).first
+ mime = type ? type.content_type : "application/octet-stream"
+ send("#{name}_id=", Mongo::ObjectID.new)
+ send("#{name}_name=", filename)
+ send("#{name}_size=", File.size(file))
+ send("#{name}_type=", mime)
+ create_attachment_queue[name] = file
+ end
+ end
+
+ ##
+ # Save an attachment to GridFS
+ def create_grid_attachment(name,file)
+ grid.put(
+ file.read,
+ attributes["#{name}_name"],
+ :content_type => attributes["#{name}_type"],
+ :_id => attributes["#{name}_id"]
+ )
+ create_attachment_queue.delete(name)
+ end
+
+ ##
+ # Attachments we need to remove after save
+ def delete_attachment(name,id)
+ delete_attachment_queue[name] = id if id.is_a?(Mongo::ObjectID)
+ send("#{name}_id=", nil)
+ send("#{name}_name=", nil)
+ send("#{name}_size=", nil)
+ send("#{name}_type=", nil)
+ end
+
+ ##
+ # Delete an attachment from GridFS
+ def delete_grid_attachment(name,id)
+ grid.delete(id) if id.is_a?(Mongo::ObjectID)
+ delete_attachment_queue.delete(name)
+ end
+
+ ##
+ # Create attachments marked for creation
+ def create_attachments
+ create_attachment_queue.each {|k,v| create_grid_attachment(k,v)}
+ end
+
+ ##
+ # Delete attachments marked for deletion
+ def delete_attachments
+ delete_attachment_queue.each {|k,v| delete_grid_attachment(k,v)}
+ end
+
+ ##
+ # Deletes all attachments from document
+ def destroy_attachments
+ self.class.attachment_types.each do |name|
+ delete_attachment(name, send("#{name}_id"))
+ end
+ end
+
+ end
+ end
+end
59 lib/rack_grid.rb
@@ -0,0 +1,59 @@
+require 'timeout'
+require 'mongo'
+
+module Rack
+ class Grid
+ class ConnectionError < StandardError ; end
+
+ attr_reader :hostname, :port, :database, :prefix, :db
+
+ def initialize(app, options = {})
+ options = {
+ :hostname => 'localhost',
+ :prefix => 'grid',
+ :port => Mongo::Connection::DEFAULT_PORT,
+ }.merge(options)
+
+ @app = app
+ @hostname = options[:hostname]
+ @port = options[:port]
+ @database = options[:database]
+ @prefix = options[:prefix]
+ @db = nil
+
+ connect!
+ end
+
+ ##
+ # Strip the _id out of the path. This allows the user to send something
+ # like /grid/4ba69fde8c8f369a6e000003/filename.jpg to find the file
+ # with an id of 4ba69fde8c8f369a6e000003.
+ def call(env)
+ request = Rack::Request.new(env)
+ if request.path_info =~ /^\/#{prefix}\/(\w+).*$/
+ grid_request($1)
+ else
+ @app.call(env)
+ end
+ end
+
+ ##
+ # Get file from GridFS or return a 404
+ def grid_request(id)
+ file = Mongo::Grid.new(db).get(Mongo::ObjectID.from_string(id))
+ [200, {'Content-Type' => file.content_type}, [file.read]]
+ rescue Mongo::GridError, Mongo::InvalidObjectID
+ [404, {'Content-Type' => 'text/plain'}, ['File not found.']]
+ end
+
+ private
+ def connect!
+ Timeout::timeout(5) do
+ @db = Mongo::Connection.new(hostname).db(database)
+ end
+ rescue StandardError => e
+ raise ConnectionError, "Timeout connecting to GridFS (#{e.to_s})"
+ end
+
+ end
+end
22 mongoid_grid.gemspec
@@ -0,0 +1,22 @@
+Gem::Specification.new do |s|
+ s.name = "mongoid_grid"
+ s.version = "0.0.1"
+ s.author = "Dusty Doris"
+ s.email = "github@dusty.name"
+ s.homepage = "http://code.dusty.name"
+ s.platform = Gem::Platform::RUBY
+ s.summary = "Plugin for Mongoid to use GridFS with a Rack helper"
+ s.description = "Plugin for Mongoid to use GridFS with a Rack helper"
+ s.files = [
+ "README.txt",
+ "lib/mongoid_grid.rb",
+ "lib/rack_grid.rb",
+ "test/test_mongoid_grid.rb",
+ "test/test_rack_grid.rb"
+ ]
+ s.has_rdoc = true
+ s.extra_rdoc_files = ["README.txt"]
+ s.add_dependency('mongo')
+ s.add_dependency('rack')
+ s.rubyforge_project = "none"
+end
0  test/test_mongoid_grid.rb
No changes.
0  test/test_rack_grid.rb
No changes.
Please sign in to comment.
Something went wrong with that request. Please try again.