rails_pallet 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.editorconfig +24 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +69 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +213 -0
- data/Guardfile +11 -0
- data/MIT-LICENSE +20 -0
- data/README.md +233 -0
- data/Rakefile +36 -0
- data/app/assets/images/rails_pallet/.keep +0 -0
- data/app/assets/javascripts/rails_pallet/application.js +13 -0
- data/app/assets/stylesheets/rails_pallet/application.css +15 -0
- data/app/controllers/rails_pallet/application_controller.rb +4 -0
- data/app/controllers/rails_pallet/uploads_controller.rb +19 -0
- data/app/helpers/rails_pallet/application_helper.rb +4 -0
- data/app/models/rails_pallet/upload.rb +61 -0
- data/app/responders/rails_pallet_responder.rb +36 -0
- data/app/serializers/rails_pallet/upload_serializer.rb +3 -0
- data/app/views/layouts/rails_pallet/application.html.erb +14 -0
- data/bin/rails +12 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20150612152328_create_rails_pallet_uploads.rb +7 -0
- data/db/migrate/20150612152417_add_attachment_file_to_uploads.rb +11 -0
- data/lib/generators/rails_pallet/install/USAGE +5 -0
- data/lib/generators/rails_pallet/install/install_generator.rb +21 -0
- data/lib/generators/rails_pallet/install/templates/initializer.rb +9 -0
- data/lib/generators/rails_pallet/upload_controller/USAGE +8 -0
- data/lib/generators/rails_pallet/upload_controller/templates/controller.rb +15 -0
- data/lib/generators/rails_pallet/upload_controller/upload_controller_generator.rb +47 -0
- data/lib/rails_pallet/active_record_extension.rb +78 -0
- data/lib/rails_pallet/engine.rb +5 -0
- data/lib/rails_pallet/version.rb +3 -0
- data/lib/rails_pallet.rb +26 -0
- data/lib/tasks/rails_pallet_tasks.rake +4 -0
- data/paperclip_upload.gemspec +34 -0
- metadata +343 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
module RailsPallet
|
2
|
+
class UploadsController < ApplicationController
|
3
|
+
self.responder = RailsPalletResponder
|
4
|
+
respond_to :json
|
5
|
+
|
6
|
+
skip_before_action :verify_authenticity_token
|
7
|
+
|
8
|
+
def create
|
9
|
+
new_upload = RailsPallet::Upload.create(permitted_params)
|
10
|
+
respond_with new_upload, status: :created
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def permitted_params
|
16
|
+
params.permit(:file)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# == Schema Information
|
2
|
+
#
|
3
|
+
# Table name: rails_pallet_uploads
|
4
|
+
#
|
5
|
+
# id :integer not null, primary key
|
6
|
+
# created_at :datetime not null
|
7
|
+
# updated_at :datetime not null
|
8
|
+
# file_file_name :string
|
9
|
+
# file_content_type :string
|
10
|
+
# file_file_size :integer
|
11
|
+
# file_updated_at :datetime
|
12
|
+
#
|
13
|
+
|
14
|
+
module RailsPallet
|
15
|
+
class Upload < ActiveRecord::Base
|
16
|
+
IDENTIFIER_LENGTH = 8
|
17
|
+
|
18
|
+
has_attached_file :file,
|
19
|
+
path: ':rails_root/public/uploads/:identifier/:filename',
|
20
|
+
url: "/uploads/:identifier/:basename.:extension"
|
21
|
+
|
22
|
+
do_not_validate_attachment_file_type :file
|
23
|
+
validates_attachment_presence :file
|
24
|
+
|
25
|
+
def identifier
|
26
|
+
raise "valid with saved instance only" if id.blank?
|
27
|
+
self.class.hashid.encode(id)
|
28
|
+
end
|
29
|
+
|
30
|
+
def file_extension
|
31
|
+
return unless file.exists?
|
32
|
+
File.extname(file.original_filename).split('.').last
|
33
|
+
end
|
34
|
+
|
35
|
+
def file_name
|
36
|
+
return unless file.exists?
|
37
|
+
file_file_name.gsub(".#{file_extension}", "")
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.find_by_identifier(_identifier)
|
41
|
+
decoded_id = identifier_to_id(_identifier)
|
42
|
+
RailsPallet::Upload.find(decoded_id)
|
43
|
+
end
|
44
|
+
|
45
|
+
def download_url
|
46
|
+
file.url
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.hashid
|
50
|
+
Hashids.new(RailsPallet.hash_salt, IDENTIFIER_LENGTH)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.identifier_to_id(_identifier)
|
54
|
+
hashid.decode(_identifier).first
|
55
|
+
end
|
56
|
+
|
57
|
+
Paperclip.interpolates(:identifier) do |attachment, _|
|
58
|
+
attachment.instance.identifier
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class RailsPalletResponder < ActionController::Responder
|
2
|
+
def respond
|
3
|
+
return display_errors if has_errors?
|
4
|
+
return head :no_content if delete?
|
5
|
+
|
6
|
+
display resource, status_code: status_code
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def display(_resource, given_options = {})
|
12
|
+
controller.render options.merge(given_options).merge(json: serializer.as_json)
|
13
|
+
end
|
14
|
+
|
15
|
+
def serializer
|
16
|
+
serializer_class = ActiveModel::Serializer.serializer_for(resource)
|
17
|
+
if serializer_class.present?
|
18
|
+
serializer_class.new(resource, options)
|
19
|
+
else
|
20
|
+
resource
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def status_code
|
25
|
+
return :created if post?
|
26
|
+
:ok
|
27
|
+
end
|
28
|
+
|
29
|
+
def display_errors
|
30
|
+
controller.render(status: :unprocessable_entity, json: { msg: "invalid_attributes", errors: format_errors })
|
31
|
+
end
|
32
|
+
|
33
|
+
def format_errors
|
34
|
+
resource.errors.as_json
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>RailsPallet</title>
|
5
|
+
<%= stylesheet_link_tag "rails_pallet/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "rails_pallet/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
data/bin/rails
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
|
3
|
+
|
4
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
5
|
+
ENGINE_PATH = File.expand_path('../../lib/rails_pallet/engine', __FILE__)
|
6
|
+
|
7
|
+
# Set up gems listed in the Gemfile.
|
8
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
9
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
10
|
+
|
11
|
+
require 'rails/all'
|
12
|
+
require 'rails/engine/commands'
|
data/config/routes.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
class RailsPallet::InstallGenerator < Rails::Generators::Base
|
2
|
+
source_root File.expand_path('../templates', __FILE__)
|
3
|
+
|
4
|
+
def create_initializer
|
5
|
+
template "initializer.rb", "config/initializers/rails_pallet.rb"
|
6
|
+
end
|
7
|
+
|
8
|
+
def mount_routes
|
9
|
+
line = "Rails.application.routes.draw do"
|
10
|
+
gsub_file "config/routes.rb", /(#{Regexp.escape(line)})/mi do |match|
|
11
|
+
<<-HERE.gsub(/^ {9}/, '')
|
12
|
+
#{match}
|
13
|
+
mount RailsPallet::Engine => '/'
|
14
|
+
HERE
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def copy_engine_migrations
|
19
|
+
rake "railties:install:migrations"
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
RailsPallet.setup do |config|
|
2
|
+
# The upload module uses a salt string to generate an unique hash for each instance.
|
3
|
+
# A salt string can be defined here to replace the default and increase the module's security.
|
4
|
+
# config.hash_salt = "A new and improved string"
|
5
|
+
|
6
|
+
# If true, you will need to pass [host_model_paperclip_attribute_name]_+upload|upload_identifier
|
7
|
+
# instead just "upload" or "upload_identifier" to the host model to use an upload resource.
|
8
|
+
# config.use_prefix = false
|
9
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Description:
|
2
|
+
Allows you to create a custom controller to handle uploads
|
3
|
+
|
4
|
+
Example:
|
5
|
+
rails generate rails_pallet:upload_controller api/attachments api/base
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
a controller inside app/controllers/api/attachments_controller.rb inheriting from the existent app/controllers/api/base_controller.rb
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class UploadController < ApplicationController
|
2
|
+
self.responder = RailsPalletResponder
|
3
|
+
respond_to :json
|
4
|
+
|
5
|
+
def create
|
6
|
+
new_upload = RailsPallet::Upload.create(permitted_params)
|
7
|
+
respond_with new_upload, status: :created
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def permitted_params
|
13
|
+
params.permit(:file)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class RailsPallet::UploadControllerGenerator < Rails::Generators::NamedBase
|
2
|
+
source_root File.expand_path('../templates', __FILE__)
|
3
|
+
argument :base_controller, type: :string, default: "application"
|
4
|
+
|
5
|
+
def generate_controller
|
6
|
+
generate "controller #{resource_path} --no-helper --no-assets --no-view-specs --no-controller-specs"
|
7
|
+
end
|
8
|
+
|
9
|
+
def replace_controller_with_template
|
10
|
+
copy_file "controller.rb", controller_path, force: true
|
11
|
+
end
|
12
|
+
|
13
|
+
def customize_controller
|
14
|
+
line = "class UploadController < ApplicationController"
|
15
|
+
gsub_file controller_path, /(#{Regexp.escape(line)})/mi do
|
16
|
+
"class #{controller_class} < #{base_controller_class}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_routes
|
21
|
+
line = "Rails.application.routes.draw do"
|
22
|
+
gsub_file "config/routes.rb", /(#{Regexp.escape(line)})/mi do |match|
|
23
|
+
<<-HERE.gsub(/^ {9}/, '')
|
24
|
+
#{match}
|
25
|
+
post "#{resource_path}", to: "#{resource_path}#create", defaults: { format: :json }
|
26
|
+
HERE
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def controller_class
|
33
|
+
"#{name.classify.pluralize}Controller"
|
34
|
+
end
|
35
|
+
|
36
|
+
def base_controller_class
|
37
|
+
"#{base_controller.classify}Controller"
|
38
|
+
end
|
39
|
+
|
40
|
+
def controller_path
|
41
|
+
"app/controllers/#{resource_path}_controller.rb"
|
42
|
+
end
|
43
|
+
|
44
|
+
def resource_path
|
45
|
+
name.tableize
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module RailsPallet
|
2
|
+
module ActiveRecordExtension
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def has_attached_upload(_paperclip_attr_name, _options = {})
|
7
|
+
load_upload_options(_options)
|
8
|
+
upload_identifier_attr_name = build_upload_attr_name(_paperclip_attr_name, "identifier")
|
9
|
+
upload_attr_name = build_upload_attr_name(_paperclip_attr_name)
|
10
|
+
|
11
|
+
attr_accessor upload_identifier_attr_name, upload_attr_name
|
12
|
+
|
13
|
+
before_validation do
|
14
|
+
identifier = send(upload_identifier_attr_name)
|
15
|
+
|
16
|
+
if identifier
|
17
|
+
found_upload = RailsPallet::Upload.find_by_identifier(identifier)
|
18
|
+
send("#{upload_attr_name}=", found_upload)
|
19
|
+
end
|
20
|
+
|
21
|
+
upload_object = send(upload_attr_name)
|
22
|
+
|
23
|
+
if upload_object
|
24
|
+
if !upload_object.is_a?(RailsPallet::Upload)
|
25
|
+
raise "invalid RailsPallet::Upload instance"
|
26
|
+
end
|
27
|
+
|
28
|
+
send("#{_paperclip_attr_name}=", upload_object.file)
|
29
|
+
upload_object.destroy
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
has_attached_file(_paperclip_attr_name, _options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def allow_encoded_file_for(_paperclip_attr_name)
|
37
|
+
encoded_attr_name = "encoded_#{_paperclip_attr_name}"
|
38
|
+
attr_accessor encoded_attr_name
|
39
|
+
|
40
|
+
before_validation do
|
41
|
+
encoded_attr = send(encoded_attr_name)
|
42
|
+
if encoded_attr
|
43
|
+
headerless_encoded_file = encoded_attr.split(",").last
|
44
|
+
StringIO.open(Base64.decode64(headerless_encoded_file)) do |data|
|
45
|
+
send("#{_paperclip_attr_name}=", data)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def build_upload_attr_name(_paperclip_attr_name, _sufix = nil)
|
54
|
+
if !!upload_options.fetch(:use_prefix, RailsPallet.use_prefix)
|
55
|
+
prefix = _paperclip_attr_name
|
56
|
+
end
|
57
|
+
|
58
|
+
upload_attr_name = [prefix, ["upload", _sufix].compact.join("_")].compact.join("_")
|
59
|
+
|
60
|
+
if self.method_defined?(upload_attr_name)
|
61
|
+
raise "you are trying to redefine #{upload_attr_name} attribute"
|
62
|
+
end
|
63
|
+
|
64
|
+
upload_attr_name
|
65
|
+
end
|
66
|
+
|
67
|
+
def load_upload_options(_options = {})
|
68
|
+
@upload_options = _options.fetch(:upload, {})
|
69
|
+
end
|
70
|
+
|
71
|
+
def upload_options
|
72
|
+
@upload_options ||= {}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
ActiveRecord::Base.send(:include, RailsPallet::ActiveRecordExtension)
|
data/lib/rails_pallet.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "paperclip"
|
2
|
+
require "responders"
|
3
|
+
require 'hashids'
|
4
|
+
require "active_model_serializers"
|
5
|
+
require "rails_pallet/active_record_extension"
|
6
|
+
require "rails_pallet/engine"
|
7
|
+
|
8
|
+
module RailsPallet
|
9
|
+
extend self
|
10
|
+
|
11
|
+
attr_writer :hash_salt, :use_prefix
|
12
|
+
|
13
|
+
def hash_salt
|
14
|
+
return "default" unless @hash_salt
|
15
|
+
@hash_salt
|
16
|
+
end
|
17
|
+
|
18
|
+
def use_prefix
|
19
|
+
@use_prefix == true
|
20
|
+
end
|
21
|
+
|
22
|
+
def setup
|
23
|
+
yield self
|
24
|
+
require "rails_pallet"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
# Maintain your gem's version:
|
4
|
+
require "rails_pallet/version"
|
5
|
+
|
6
|
+
# Describe your gem and declare its dependencies:
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "rails_pallet"
|
9
|
+
s.version = RailsPallet::VERSION
|
10
|
+
s.authors = ["Leandro Segovia"]
|
11
|
+
s.email = ["ldlsegovia@gmail.com"]
|
12
|
+
s.homepage = "https://github.com/platanus/rails_pallet"
|
13
|
+
s.summary = "Rails engine to save paperclip attachments asynchronously"
|
14
|
+
s.description = "This gem allows you: 1) perform multiple POST requests to create multiple files. 2) relate those files with a model using identifiers instead the files themselves"
|
15
|
+
s.license = "MIT"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split($/).reject { |fn| fn.start_with? "spec" }
|
18
|
+
|
19
|
+
s.add_runtime_dependency 'rails', '~> 4.2', '>= 4.2.1'
|
20
|
+
s.add_runtime_dependency 'hashids', '~> 1.0', '>= 1.0.2'
|
21
|
+
s.add_runtime_dependency 'paperclip', '~> 4.2', '>= 4.2.0'
|
22
|
+
s.add_runtime_dependency 'responders', '~> 2.1', '>= 2.1.0'
|
23
|
+
s.add_dependency "active_model_serializers", "~> 0.9.3"
|
24
|
+
|
25
|
+
s.add_development_dependency 'rspec-rails', '~> 3.2', '>= 3.2.1'
|
26
|
+
s.add_development_dependency "pry-rails", "~> 0.3.3"
|
27
|
+
s.add_development_dependency 'factory_girl_rails', '~> 4.5', '>= 4.5.0'
|
28
|
+
s.add_development_dependency 'shoulda-matchers', '~> 2.8', '>= 2.8.0'
|
29
|
+
s.add_development_dependency 'guard', '~> 2.12', '>= 2.12.5'
|
30
|
+
s.add_development_dependency 'guard-rspec', '~> 4.5', '>= 4.5.0'
|
31
|
+
s.add_development_dependency 'annotate', '~> 2.6', '>= 2.6.6'
|
32
|
+
s.add_development_dependency "sqlite3", '~> 1.3', '>= 1.3.10'
|
33
|
+
s.add_development_dependency "recursive-open-struct", "~> 0.6.4"
|
34
|
+
end
|