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.
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