glebtv-rails-uploader 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +241 -0
  3. data/Rakefile +26 -0
  4. data/app/assets/javascripts/uploader/application.js +9 -0
  5. data/app/assets/javascripts/uploader/rails_admin.js +25 -0
  6. data/app/assets/stylesheets/uploader/application.css +7 -0
  7. data/app/controllers/uploader/attachments_controller.rb +67 -0
  8. data/app/views/rails_admin/main/_form_rails_uploader.haml +10 -0
  9. data/app/views/uploader/default/_container.html.erb +67 -0
  10. data/app/views/uploader/default/_download.html.erb +17 -0
  11. data/app/views/uploader/default/_upload.html.erb +14 -0
  12. data/config/locales/en.yml +5 -0
  13. data/config/locales/ru.yml +5 -0
  14. data/config/locales/uk.yml +5 -0
  15. data/config/routes.rb +7 -0
  16. data/lib/file_size_validator.rb +66 -0
  17. data/lib/glebtv-rails-uploader.rb +3 -0
  18. data/lib/uploader.rb +35 -0
  19. data/lib/uploader/asset.rb +47 -0
  20. data/lib/uploader/engine.rb +25 -0
  21. data/lib/uploader/fileuploads.rb +109 -0
  22. data/lib/uploader/helpers/field_tag.rb +100 -0
  23. data/lib/uploader/helpers/form_builder.rb +16 -0
  24. data/lib/uploader/helpers/form_tag_helper.rb +16 -0
  25. data/lib/uploader/hooks/active_record.rb +5 -0
  26. data/lib/uploader/hooks/formtastic.rb +14 -0
  27. data/lib/uploader/hooks/simple_form.rb +9 -0
  28. data/lib/uploader/rails_admin/field.rb +28 -0
  29. data/lib/uploader/version.rb +3 -0
  30. data/spec/dummy/README.rdoc +261 -0
  31. data/spec/dummy/Rakefile +7 -0
  32. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  33. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  34. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  35. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  36. data/spec/dummy/app/models/article.rb +7 -0
  37. data/spec/dummy/app/models/asset.rb +27 -0
  38. data/spec/dummy/app/models/picture.rb +5 -0
  39. data/spec/dummy/app/uploaders/picture_uploader.rb +55 -0
  40. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  41. data/spec/dummy/config.ru +4 -0
  42. data/spec/dummy/config/application.rb +58 -0
  43. data/spec/dummy/config/boot.rb +10 -0
  44. data/spec/dummy/config/database.yml +25 -0
  45. data/spec/dummy/config/environment.rb +5 -0
  46. data/spec/dummy/config/environments/development.rb +37 -0
  47. data/spec/dummy/config/environments/production.rb +67 -0
  48. data/spec/dummy/config/environments/test.rb +37 -0
  49. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  50. data/spec/dummy/config/initializers/inflections.rb +15 -0
  51. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  52. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  53. data/spec/dummy/config/initializers/session_store.rb +8 -0
  54. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  55. data/spec/dummy/config/locales/en.yml +5 -0
  56. data/spec/dummy/config/routes.rb +3 -0
  57. data/spec/dummy/db/migrate/20120508093416_create_assets.rb +19 -0
  58. data/spec/dummy/db/migrate/20120508093830_create_articles.rb +10 -0
  59. data/spec/dummy/public/404.html +26 -0
  60. data/spec/dummy/public/422.html +26 -0
  61. data/spec/dummy/public/500.html +25 -0
  62. data/spec/dummy/public/favicon.ico +0 -0
  63. data/spec/dummy/script/rails +6 -0
  64. data/spec/factories/articles.rb +8 -0
  65. data/spec/factories/assets.rb +11 -0
  66. data/spec/factories/files/rails.png +0 -0
  67. data/spec/fileuploads_spec.rb +57 -0
  68. data/spec/requests/attachments_controller_spec.rb +38 -0
  69. data/spec/spec_helper.rb +52 -0
  70. data/spec/uploader_spec.rb +18 -0
  71. data/vendor/assets/images/uploader/but_del_tag2.png +0 -0
  72. data/vendor/assets/images/uploader/ico_attach.png +0 -0
  73. data/vendor/assets/images/uploader/preloader.gif +0 -0
  74. data/vendor/assets/images/uploader/progressBarFillBg.png +0 -0
  75. data/vendor/assets/javascripts/uploader/jquery.fileupload-ip.js +160 -0
  76. data/vendor/assets/javascripts/uploader/jquery.fileupload-ui.js +637 -0
  77. data/vendor/assets/javascripts/uploader/jquery.fileupload.js +904 -0
  78. data/vendor/assets/javascripts/uploader/jquery.iframe-transport.js +171 -0
  79. data/vendor/assets/javascripts/uploader/jquery.ui.widget.js +282 -0
  80. data/vendor/assets/javascripts/uploader/load-image.min.js +1 -0
  81. data/vendor/assets/javascripts/uploader/locales/en.js +27 -0
  82. data/vendor/assets/javascripts/uploader/locales/ru.js +27 -0
  83. data/vendor/assets/javascripts/uploader/locales/uk.js +27 -0
  84. data/vendor/assets/javascripts/uploader/tmpl.min.js +1 -0
  85. data/vendor/assets/stylesheets/uploader/default.css +214 -0
  86. data/vendor/assets/stylesheets/uploader/jquery.fileupload-ui.css +30 -0
  87. metadata +317 -0
@@ -0,0 +1,17 @@
1
+ <!-- The template to display files available for download -->
2
+ <script id="template-download-<%= field.klass %>" type="text/x-tmpl">
3
+ {% for (var i=0, file; file=o.files[i]; i++) { %}
4
+ <div class="attach_item template-download" data-id="{%=file.id%}">
5
+ <div class="delete">
6
+ <a href="/" class="del_btn" data-type="DELETE" data-url="/uploader/attachments/{%=file.id%}?klass=<%= field.klass %>"></a>
7
+ </div>
8
+ <div class="thumbnail preview">
9
+ <a href="{%=file.url%}"><img src="{%=file.thumb_url%}" title="{%=file.filename%}" rel="gallery"></a>
10
+ </div>
11
+ <div class="infoHolder">
12
+ <div class="fileName">{%=file.filename%}</div>
13
+ <div class="fileWeight">{%=o.formatFileSize(file.size)%}</div>
14
+ </div>
15
+ </div>
16
+ {% } %}
17
+ </script>
@@ -0,0 +1,14 @@
1
+ <!-- The template to display files available for upload -->
2
+ <script id="template-upload-<%= field.klass %>" type="text/x-tmpl">
3
+ {% for (var i=0, file; file=o.files[i]; i++) { %}
4
+ <div class="attach_item loading template-upload">
5
+ <div class="cancel"><a href="#" class="del_btn"></a></div>
6
+ <div class="thumbnail preview"><img class="preloader" src="/assets/uploader/preloader.gif" alt=""></div>
7
+ <div class="infoHolder">
8
+ <div class="fileName">{%=file.name%}</div>
9
+ <div class="fileWeight">{%=o.formatFileSize(file.size)%}</div>
10
+ <div class="progressBar progress"><div class="bar fill" style="width: 0%;"></div></div>
11
+ </div>
12
+ </div>
13
+ {% } %}
14
+ </script>
@@ -0,0 +1,5 @@
1
+ en:
2
+ uploader:
3
+ button: "Choose file"
4
+ drop: "Drag&Drop files here"
5
+ or: "or"
@@ -0,0 +1,5 @@
1
+ ru:
2
+ uploader:
3
+ button: "Выберите файл на компьютере"
4
+ drop: "Перетащите файл сюда"
5
+ or: "или"
@@ -0,0 +1,5 @@
1
+ uk:
2
+ uploader:
3
+ button: "Виберіть файл на комп'ютері"
4
+ drop: "Перетягніть файл сюди"
5
+ or: "або"
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ Uploader::Engine.routes.draw do
2
+ resources :attachments, :only => [:create, :destroy] do
3
+ collection do
4
+ post :sort
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,66 @@
1
+ # lib/file_size_validator.rb
2
+ # Based on: https://gist.github.com/795665
3
+
4
+ class FileSizeValidator < ActiveModel::EachValidator
5
+ MESSAGES = { :is => :wrong_size, :minimum => :size_too_small, :maximum => :size_too_big }.freeze
6
+ CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
7
+
8
+ DEFAULT_TOKENIZER = lambda { |value| value.split(//) }
9
+ RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
10
+
11
+ def initialize(options)
12
+ if range = (options.delete(:in) || options.delete(:within))
13
+ raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
14
+ options[:minimum], options[:maximum] = range.begin, range.end
15
+ options[:maximum] -= 1 if range.exclude_end?
16
+ end
17
+
18
+ super
19
+ end
20
+
21
+ def check_validity!
22
+ keys = CHECKS.keys & options.keys
23
+
24
+ if keys.empty?
25
+ raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
26
+ end
27
+
28
+ keys.each do |key|
29
+ value = options[key]
30
+
31
+ unless value.is_a?(Integer) && value >= 0
32
+ raise ArgumentError, ":#{key} must be a nonnegative Integer"
33
+ end
34
+ end
35
+ end
36
+
37
+ def validate_each(record, attribute, value)
38
+ raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected") unless value.kind_of? CarrierWave::Uploader::Base
39
+ value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.kind_of?(String)
40
+
41
+ CHECKS.each do |key, validity_check|
42
+ next unless check_value = options[key]
43
+
44
+ value ||= [] if key == :maximum
45
+
46
+ value_size = value.size
47
+ next if value_size.send(validity_check, check_value)
48
+
49
+ errors_options = options.except(*RESERVED_OPTIONS)
50
+ errors_options[:file_size] = help.number_to_human_size check_value
51
+
52
+ default_message = options[MESSAGES[key]]
53
+ errors_options[:message] ||= default_message if default_message
54
+
55
+ record.errors.add(attribute, MESSAGES[key], errors_options)
56
+ end
57
+ end
58
+ def help
59
+ Helper.instance
60
+ end
61
+
62
+ class Helper
63
+ include Singleton
64
+ include ActionView::Helpers::NumberHelper
65
+ end
66
+ end
@@ -0,0 +1,3 @@
1
+ # encoding: utf-8
2
+ require 'uploader'
3
+ require 'file_size_validator'
data/lib/uploader.rb ADDED
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+ require 'securerandom'
3
+
4
+ module Uploader
5
+ autoload :Fileuploads, 'uploader/fileuploads'
6
+ autoload :Asset, 'uploader/asset'
7
+
8
+ module Helpers
9
+ autoload :FormTagHelper, 'uploader/helpers/form_tag_helper'
10
+ autoload :FormBuilder, 'uploader/helpers/form_builder'
11
+ autoload :FieldTag, 'uploader/helpers/field_tag'
12
+ end
13
+
14
+ def self.guid
15
+ SecureRandom.base64(15).tr('+/=', 'xyz').slice(0, 10)
16
+ end
17
+
18
+ def self.root_path
19
+ @root_path ||= Pathname.new( File.dirname(File.expand_path('../', __FILE__)) )
20
+ end
21
+
22
+ def self.assets
23
+ ['uploader/application.css', 'uploader/application.js', 'uploader/rails_admin.js'] +
24
+ Dir[root_path.join('vendor/assets/javascripts/uploader/**', '*.{js,css}')].inject([]) do |list, path|
25
+ list << Pathname.new(path).relative_path_from(root_path.join('vendor/assets/javascripts')).to_s
26
+ list
27
+ end
28
+ end
29
+ end
30
+
31
+ require 'uploader/engine'
32
+
33
+ if Object.const_defined?("RailsAdmin")
34
+ require "uploader/rails_admin/field"
35
+ end
@@ -0,0 +1,47 @@
1
+ module Uploader
2
+ module Asset
3
+ # Save asset
4
+ # Usage:
5
+ #
6
+ # class Asset < ActiveRecord::Base
7
+ # include Uploader::Asset
8
+ #
9
+ # def uploader_create(params, request = nil)
10
+ # self.user = request.env['warden'].user
11
+ # super
12
+ # end
13
+ # end
14
+ #
15
+ def uploader_create(params, request = nil)
16
+ self.guid = params[:guid]
17
+ self.assetable_type = params[:assetable_type]
18
+ if self.class.respond_to?(:collection)
19
+ self.assetable_id = Moped::BSON::ObjectId.from_string(params[:assetable_id])
20
+ else
21
+ self.assetable_id = params[:assetable_id]
22
+ end
23
+
24
+ save
25
+ end
26
+
27
+ # Destroy asset
28
+ # Usage (cancan example):
29
+ #
30
+ # class Asset < ActiveRecord::Base
31
+ # include Uploader::Asset
32
+ #
33
+ # def uploader_destroy(params, request = nil)
34
+ # ability = Ability.new(request.env['warden'].user)
35
+ # if ability.can? :delete, self
36
+ # super
37
+ # else
38
+ # errors.add(:id, :access_denied)
39
+ # end
40
+ # end
41
+ # end
42
+ #
43
+ def uploader_destroy(params, request)
44
+ destroy
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ require 'rails'
2
+ require 'uploader'
3
+
4
+ module Uploader
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace Uploader
7
+
8
+ initializer "uploader.assets_precompile" do |app|
9
+ app.config.assets.precompile += Uploader.assets
10
+ end
11
+
12
+ initializer "uploader.helpers" do
13
+ ActiveSupport.on_load :action_view do
14
+ ActionView::Base.send(:include, Uploader::Helpers::FormTagHelper)
15
+ ActionView::Helpers::FormBuilder.send(:include, Uploader::Helpers::FormBuilder)
16
+ end
17
+ end
18
+
19
+ initializer "uploader.hooks" do
20
+ require "uploader/hooks/active_record" if Object.const_defined?("ActiveRecord")
21
+ require "uploader/hooks/formtastic" if Object.const_defined?("Formtastic")
22
+ require "uploader/hooks/simple_form" if Object.const_defined?("SimpleForm")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,109 @@
1
+ module Uploader
2
+ module Fileuploads
3
+ def self.included(base)
4
+ base.send :extend, SingletonMethods
5
+ end
6
+
7
+ module SingletonMethods
8
+ # Join ActiveRecord object with uploaded file
9
+ # Usage:
10
+ #
11
+ # class Article < ActiveRecord::Base
12
+ # has_one :picture, :as => :assetable, :dependent => :destroy
13
+ #
14
+ # fileuploads :picture
15
+ # end
16
+ #
17
+ #
18
+ def fileuploads(*args)
19
+ options = args.extract_options!
20
+
21
+ class_attribute :fileuploads_options, :instance_writer => false
22
+ self.fileuploads_options = options
23
+
24
+ class_attribute :fileuploads_columns, :instance_writer => false
25
+ self.fileuploads_columns = args.map(&:to_sym)
26
+
27
+ unless self.is_a?(ClassMethods)
28
+ include InstanceMethods
29
+ extend ClassMethods
30
+
31
+ after_save :fileuploads_update, :if => :fileupload_changed?
32
+
33
+ fileuploads_columns.each { |asset| accepts_nested_attributes_for asset, :allow_destroy => true }
34
+ end
35
+ end
36
+ end
37
+
38
+ module ClassMethods
39
+ # Update reflection klass by guid
40
+ def fileupload_update(record_id, guid, method)
41
+ query = fileupload_klass(method).where(:guid => guid, :assetable_type => base_class.name.to_s)
42
+ query.update_all(:assetable_id => record_id, :guid => nil)
43
+ end
44
+
45
+ # Find asset by guid
46
+ def fileupload_find(method, guid)
47
+ klass = fileupload_klass(method)
48
+ klass.where(:guid => guid).first
49
+ end
50
+
51
+ # Find class by reflection
52
+ def fileupload_klass(method)
53
+ reflect_on_association(method.to_sym).klass
54
+ end
55
+
56
+ unless respond_to?(:base_class)
57
+ def base_class
58
+ self
59
+ end
60
+ end
61
+ end
62
+
63
+ module InstanceMethods
64
+ # Generate unique key
65
+ def fileupload_guid
66
+ @fileupload_guid ||= Uploader.guid
67
+ end
68
+
69
+ def fileupload_guid=(value)
70
+ @fileupload_changed = true unless value.blank?
71
+ @fileupload_guid = value.blank? ? nil : value
72
+ end
73
+
74
+ def fileupload_changed?
75
+ @fileupload_changed === true
76
+ end
77
+
78
+ def fileupload_multiple?(method)
79
+ association = self.class.reflect_on_association(method.to_sym)
80
+ if association.respond_to?(:collection?)
81
+ !!(association && association.collection?)
82
+ else
83
+ association.macro == :has_many
84
+ end
85
+ end
86
+
87
+ # Find or build new asset object
88
+ def fileupload_asset(method)
89
+ if fileuploads_columns.include?(method.to_sym)
90
+ asset = new_record? ? self.class.fileupload_find(method, fileupload_guid) : send(method)
91
+ asset ||= send("build_#{method}") if respond_to?("build_#{method}")
92
+ asset
93
+ end
94
+ end
95
+
96
+ def fileuploads_columns
97
+ self.class.fileuploads_columns
98
+ end
99
+
100
+ protected
101
+
102
+ def fileuploads_update
103
+ fileuploads_columns.each do |method|
104
+ self.class.fileupload_update(id, fileupload_guid, method)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,100 @@
1
+ module Uploader
2
+ module Helpers
3
+ class FieldTag
4
+ attr_reader :template, :object, :theme
5
+
6
+ delegate :uploader, :to => :template
7
+
8
+ # Wrapper for render uploader field
9
+ # Usage:
10
+ #
11
+ # uploader = FieldTag.new(object_name, method_name, template, options)
12
+ # uploader.to_s
13
+ #
14
+ def initialize(object_name, method_name, template, options = {}) #:nodoc:
15
+ options = { :object_name => object_name, :method_name => method_name }.merge(options)
16
+
17
+ @template, @options = template, options.dup
18
+
19
+ @theme = (@options.delete(:theme) || "default")
20
+ @value = @options.delete(:value) if @options.key?(:value)
21
+
22
+ @object = @options.delete(:object) if @options.key?(:object)
23
+ @object ||= @template.instance_variable_get("@#{object_name}")
24
+ end
25
+
26
+ def to_s(locals = {}) #:nodoc:
27
+ locals = { :field => self }.merge(locals)
28
+ @template.render :partial => "uploader/#{@theme}/container", :locals => locals
29
+ end
30
+
31
+ def id
32
+ @id ||= @template.dom_id(@object, [method_name, 'uploader', @object.fileupload_guid].join('_'))
33
+ end
34
+
35
+ def method_name
36
+ @options[:method_name]
37
+ end
38
+
39
+ def object_name
40
+ @options[:object_name]
41
+ end
42
+
43
+ def multiple?
44
+ @object.fileupload_multiple?(method_name)
45
+ end
46
+
47
+ def value
48
+ @value ||= @object.fileupload_asset(method_name)
49
+ end
50
+
51
+ def values
52
+ if value.first.respond_to?(:sort)
53
+ Array.wrap(value).sort_by(&:sort)
54
+ else
55
+ Array.wrap(value)
56
+ end
57
+ end
58
+
59
+ def exists?
60
+ values.map(&:persisted?).any?
61
+ end
62
+
63
+ def klass
64
+ @klass ||= @object.class.fileupload_klass(method_name)
65
+ end
66
+
67
+ def attachments_path(options = {})
68
+ options = {
69
+ :guid => @object.fileupload_guid,
70
+ :assetable_type => @object.class.base_class.name.to_s,
71
+ :klass => klass.to_s
72
+ }.merge(options)
73
+
74
+ options[:assetable_id] = @object.id if @object.persisted?
75
+
76
+ uploader.attachments_path(options)
77
+ end
78
+
79
+ def sort_path(options = {})
80
+ options = {
81
+ :guid => @object.fileupload_guid,
82
+ :assetable_type => @object.class.base_class.name.to_s,
83
+ :klass => klass.to_s
84
+ }.merge(options)
85
+
86
+ options[:assetable_id] = @object.id if @object.persisted?
87
+
88
+ uploader.sort_attachments_path(options)
89
+ end
90
+
91
+ def input_html
92
+ @input_html ||= {
93
+ :"data-url" => attachments_path,
94
+ :multiple => multiple?,
95
+ :class => "uploader"
96
+ }.merge(@options[:input_html] || {})
97
+ end
98
+ end
99
+ end
100
+ end