rails_pallet 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +24 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +3 -0
  5. data/CHANGELOG.md +69 -0
  6. data/Gemfile +15 -0
  7. data/Gemfile.lock +213 -0
  8. data/Guardfile +11 -0
  9. data/MIT-LICENSE +20 -0
  10. data/README.md +233 -0
  11. data/Rakefile +36 -0
  12. data/app/assets/images/rails_pallet/.keep +0 -0
  13. data/app/assets/javascripts/rails_pallet/application.js +13 -0
  14. data/app/assets/stylesheets/rails_pallet/application.css +15 -0
  15. data/app/controllers/rails_pallet/application_controller.rb +4 -0
  16. data/app/controllers/rails_pallet/uploads_controller.rb +19 -0
  17. data/app/helpers/rails_pallet/application_helper.rb +4 -0
  18. data/app/models/rails_pallet/upload.rb +61 -0
  19. data/app/responders/rails_pallet_responder.rb +36 -0
  20. data/app/serializers/rails_pallet/upload_serializer.rb +3 -0
  21. data/app/views/layouts/rails_pallet/application.html.erb +14 -0
  22. data/bin/rails +12 -0
  23. data/config/routes.rb +3 -0
  24. data/db/migrate/20150612152328_create_rails_pallet_uploads.rb +7 -0
  25. data/db/migrate/20150612152417_add_attachment_file_to_uploads.rb +11 -0
  26. data/lib/generators/rails_pallet/install/USAGE +5 -0
  27. data/lib/generators/rails_pallet/install/install_generator.rb +21 -0
  28. data/lib/generators/rails_pallet/install/templates/initializer.rb +9 -0
  29. data/lib/generators/rails_pallet/upload_controller/USAGE +8 -0
  30. data/lib/generators/rails_pallet/upload_controller/templates/controller.rb +15 -0
  31. data/lib/generators/rails_pallet/upload_controller/upload_controller_generator.rb +47 -0
  32. data/lib/rails_pallet/active_record_extension.rb +78 -0
  33. data/lib/rails_pallet/engine.rb +5 -0
  34. data/lib/rails_pallet/version.rb +3 -0
  35. data/lib/rails_pallet.rb +26 -0
  36. data/lib/tasks/rails_pallet_tasks.rake +4 -0
  37. data/paperclip_upload.gemspec +34 -0
  38. 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,4 @@
1
+ module RailsPallet
2
+ module ApplicationHelper
3
+ end
4
+ 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,3 @@
1
+ class RailsPallet::UploadSerializer < ActiveModel::Serializer
2
+ attributes :identifier, :file_extension, :file_name, :download_url
3
+ 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,3 @@
1
+ RailsPallet::Engine.routes.draw do
2
+ resources :uploads, only: [:create], defaults: { format: :json }
3
+ end
@@ -0,0 +1,7 @@
1
+ class CreateRailsPalletUploads < ActiveRecord::Migration
2
+ def change
3
+ create_table :rails_pallet_uploads do |t|
4
+ t.timestamps null: false
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ class AddAttachmentFileToUploads < ActiveRecord::Migration
2
+ def self.up
3
+ change_table :rails_pallet_uploads do |t|
4
+ t.attachment :file
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ remove_attachment :rails_pallet_uploads, :file
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Install the engine in host app.
3
+
4
+ Example:
5
+ rails generate rails_pallet:install
@@ -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)
@@ -0,0 +1,5 @@
1
+ module RailsPallet
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RailsPallet
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module RailsPallet
2
+ VERSION = "2.0.0"
3
+ end
@@ -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,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rails_pallet do
3
+ # # Task goes here
4
+ # 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