Skip to content
This repository has been archived by the owner on Jan 9, 2019. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Pedro Rodrigues committed Oct 2, 2012
1 parent c5b1226 commit e8b3b6f
Show file tree
Hide file tree
Showing 12 changed files with 335 additions and 1 deletion.
20 changes: 20 additions & 0 deletions MIT_LICENSE
@@ -0,0 +1,20 @@
Copyright (c) 2010 [Martin Caruso]

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
57 changes: 56 additions & 1 deletion README.md
@@ -1,4 +1,59 @@
paperclip_database
==================

Adds support for storing a paperclip attachment file contents in a database table
Adds support for storing a paperclip attachment file contents in a database table.

Requirements
------------

Paperclip_database requires [Paperclip](https://github.com/thoughtbot/paperclip) version **>= 3.2.0**.

Installation
------------

Paperclip_database is distributed as a gem, and that is how it should be used in your app.

Include the gem in your `Gemfile`, from rubygems:

gem 'paperclip_database', '>= 3.2.0'

Or, get the master branch from the repository:

gem 'paperclip_database', :git => 'git://github.com/gokuu/paperclip_database.git'

Usage
-----

All you need to do is, when defining a [Paperclip](https://github.com/thoughtbot/paperclip) attachment, set its `:storage` option as `:database`:

```ruby
class MyModel < ActiveRecord::Base
has_attached_file :attachment,
:storage => :database,
:styles => {
:medium => "300x300>",
:thumb => "100x100>"
},
:url => "/:class/:attachment/:id/:style/:basename.:extension"
end
```

Remarks
-------

The migration defined by `paperclip_database` contains several indexes, as every possible combination of the fields that can identify a single attachment. This should help getting an attachment as quickly as possible using any combination of parameters

Limitations
-----------

* Paperclip-database currently only supports one database attachment per model.
* Although you can define a custom :url option for getting the attachment, make sure you define one that uniquelly identifies each attachment, otherwise the plugin will raise an Exception.

TO-DO
-----

* Add tests!
* Add support for more than one database attachment per model
* Enable defining a separate table for each different attachment

Copyright (c) 2012 [Pedro Rodrigues], released under the MIT license
23 changes: 23 additions & 0 deletions Rakefile
@@ -0,0 +1,23 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

desc 'Default: run unit tests.'
task :default => :test

desc 'Test the paperclip_database gem'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end

desc 'Generate documentation for the paperclip_database gem'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'Paperclipdb'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end
19 changes: 19 additions & 0 deletions app/controllers/paperclip_database/attachments_controller.rb
@@ -0,0 +1,19 @@
class PaperclipDatabase::AttachmentsController < ApplicationController
def get_attachment
conditions = {}
conditions[:attached_type] = params[:class] if params[:class]
conditions[:attached_id] = params[:id] if params[:id]
conditions[:attached_id] ||= params[:id_partition].gsub(/\//, '').to_i if params[:id_partition]
conditions[:attachment_name] = params[:attachment] if params[:attachment]
conditions[:style] = params[:style] if params[:style]


attachments = PaperclipDatabase::Attachment.where(conditions)

raise ActionController::RoutingError.new('Image not Found') if attachments.empty?
raise ActionController::RoutingError.new('Too many images found. Check your route definition') if attachments.length > 1

attachment = attachments.first
send_data attachment.file_data, :type => attachment.content_type
end
end
11 changes: 11 additions & 0 deletions app/models/paperclip_database/attachment.rb
@@ -0,0 +1,11 @@
module PaperclipDatabase
class Attachment < ActiveRecord::Base
belongs_to :attached, :polymorphic => true

attr_accessible :style, :file_data, :content_type, :file_size, :attachment_name

def self.table_name
return 'paperclip_database_attachments'
end
end
end
26 changes: 26 additions & 0 deletions config/routes.rb
@@ -0,0 +1,26 @@
Rails.application.routes.draw do
# Dynamically create routes based on defined attachments
Rails.application.eager_load!

loaded_url_templates = []
must_create_default_route = false

Paperclip.classes_with_attachments.each do |class_name|
klass = class_name.constantize

klass.attachment_definitions.each do |attachment_name, definition|
must_create_default_route = true and next unless definition.has_key?(:url)
next if loaded_url_templates.include?(definition[:url])

loaded_url_templates << definition[:url]

#ap [klass.name, attachment_name, definition]
get definition[:url] => 'paperclip_database/attachments#get_attachment'
end
end

# Generate the default route, if necessary
if must_create_default_route
get Paperclip::Attachment.default_options[:url] => 'paperclip_database/attachments#get_attachment'
end
end
31 changes: 31 additions & 0 deletions db/migrate/create_paperclip_database_attachments.rb
@@ -0,0 +1,31 @@
class CreatePaperclipDatabaseAttachments < ActiveRecord::Migration
def self.up
create_table :paperclip_database_attachments do |t|
t.string :attached_type, :null => false
t.integer :attached_id, :null => false
t.string :attachment_name, :null => false
t.string :style, :null => false
t.binary :file_data, :null => false
t.string :content_type
t.integer :file_size
t.timestamps
end
add_index :paperclip_database_attachments, [ :attached_type, :attached_id, :attachment_name, :style ], :unique => true, :name => :idx_attachments_1
add_index :paperclip_database_attachments, [ :attached_type, :attached_id, :attachment_name ], :name => :idx_attachments_2
add_index :paperclip_database_attachments, [ :attached_type, :attached_id ], :name => :idx_attachments_3
add_index :paperclip_database_attachments, [ :attached_type ], :name => :idx_attachments_4
add_index :paperclip_database_attachments, [ :attached_type, :attached_id, :style ], :name => :idx_attachments_5
add_index :paperclip_database_attachments, [ :attached_type, :style ], :name => :idx_attachments_6
add_index :paperclip_database_attachments, [ :style ], :name => :idx_attachments_7
add_index :paperclip_database_attachments, [ :attached_type, :attachment_name, :style ], :name => :idx_attachments_8
add_index :paperclip_database_attachments, [ :attachment_name, :style ], :name => :idx_attachments_9
add_index :paperclip_database_attachments, [ :attachment_name ], :name => :idx_attachments_10
add_index :paperclip_database_attachments, [ :attached_type, :attachment_name, :style ], :name => :idx_attachments_11
add_index :paperclip_database_attachments, [ :attachment_name, :style ], :name => :idx_attachments_12
add_index :paperclip_database_attachments, [ :style ], :name => :idx_attachments_13
end

def self.down
drop_table :paperclip_database_attachments
end
end
11 changes: 11 additions & 0 deletions lib/paperclip_database.rb
@@ -0,0 +1,11 @@
#require 'paperclip_database/storage.rb'
require 'paperclip_database'
require 'paperclip_database/storage/database'
require 'rails'
require 'paperclip'

# This will load the necessary models and controllers
module PaperclipDatabase
class Engine < Rails::Engine
end
end
87 changes: 87 additions & 0 deletions lib/paperclip_database/storage/database.rb
@@ -0,0 +1,87 @@
module Paperclip
module Storage
module Database
def self.extended(base)
base.instance_eval do
override_default_options base
end
end

def override_default_options(base)
@path = @url
end

private :override_default_options

def exists?(style = default_style)
return !get_attachment(style).nil?
end

def path(style = default_style)
return style
end

def get_attachment(style)
return PaperclipDatabase::Attachment.find(:first, :conditions => {
:style => style,
:attached_type => self.instance.class.name,
:attached_id => self.instance.id,
:attachment_name => self.get_attachment_definitions.keys.first
})
end

def get_attachment_definitions
attachment_definitions = self.instance.class.attachment_definitions

if attachment_definitions.select { |k,v| v[:storage] == :database }.count > 1
raise Exception.new('paperclip-database does not support more than one attachment per model')
end

return attachment_definitions
end


def to_file style = default_style
if @queued_for_write[style]
@queued_for_write[style]
elsif exists?(style)
attachment = get_attachment(style)
tempfile = Tempfile.new attachment.base_name
tempfile.write attachment.file_data
tempfile
else
nil
end
end

def flush_writes
puts("[paperclip] Writing files #{@queued_for_write.count}")
attachment_definitions = get_attachment_definitions

@queued_for_write.each do |style, file|
puts("[paperclip] Writing files for #{file} #{style}")

PaperclipDatabase::Attachment.new do |a|
a.attached_type = self.instance.class.name
a.attached_id = self.instance.id
a.style = style
a.content_type = self.instance_variable_get("@_#{self.name.to_s}_content_type")
a.attachment_name = attachment_definitions.keys.first
a.file_size = file.size
a.file_data = file.read
end.save
end
@queued_for_write = {}
end

def flush_deletes
@queued_for_delete.each do |style|
puts("[paperclip] Deleting files for #{style}")
attachment = get_attachment(style)
attachment.destroy if !attachment.nil?
end
@queued_for_delete = []
end
end
end
end
3 changes: 3 additions & 0 deletions lib/paperclip_database/version.rb
@@ -0,0 +1,3 @@
module PaperclipDatabase
VERSION = "3.0.0" unless defined? PaperclipDatabase::VERSION
end
25 changes: 25 additions & 0 deletions lib/tasks/paperclip_database.rake
@@ -0,0 +1,25 @@
namespace :'paperclip-database' do
desc 'Copy necessary migrations to the rails project'
task :setup => :environment do
plugin_root = File.dirname(File.dirname(File.dirname(__FILE__)))

Dir[File.join(plugin_root, 'db', 'migrate', '*.rb')].each do |file|
# First, check if the migration has already been copied
if Dir[File.join('db', 'migrate', "*#{File.basename(file)}*")].any?
puts "Migration #{File.basename(file)} has already been copied, skipping..."
else
dest_file = "#{Time.now.strftime('%Y%m%d%H%M%S')}_#{File.basename(file)}"
dest_dir = File.join(Rails.root, 'db', 'migrate')

puts "Copying #{File.basename(file)} to #{dest_file}"

FileUtils.mkdir_p dest_dir
FileUtils.cp(file, File.join(dest_dir, dest_file))
end
end
end

task :list => :environment do
ap Paperclip.classes_with_attachments
end
end
23 changes: 23 additions & 0 deletions paperclip_database.gemspec
@@ -0,0 +1,23 @@
$LOAD_PATH.push File.expand_path("../lib", __FILE__)
require 'paperclip_database/version'

Gem::Specification.new do |s|
s.name = 'paperclip_database'
s.version = PaperclipDatabase::VERSION

s.authors = ['Pedro Rodrigues']
s.date = '2012-10-01'
s.summary = 'Database storage support for paperclip file attachment plugin'
s.description = 'Adds support for storing the contents of files attachment via paperclip plugin on the database'
s.email = 'pedro@bbde.org'
s.extra_rdoc_files = [ 'README' ]
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ['lib']
s.required_ruby_version = ">= 1.9.2"

s.homepage = 'http://github.com/gokuu/paperclip_database'

s.add_dependency 'paperclip', [">= 3.0.0"]
end

0 comments on commit e8b3b6f

Please sign in to comment.