attachinary 0.0.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,16 +1,19 @@
1
- # Attachinary
1
+ # Attachinary v1
2
+
3
+ _Note: v1 is not backward compatible._
2
4
 
3
5
  Need lightweight attachment (photos and raw files) handler for any of your model, in either has\_one or has\_many relation, without altering your models' schema and with zero effort? Attachinary is the tool for you!
4
6
 
5
7
  Why is Attachinary different:
6
8
 
7
- * **No need to alter your model schema** any time you introduce new kind of attachment.
9
+ * Supports both **ActiveRecord** and **Mongoid** ORMs!
10
+ * **No need to alter your model schema** every time you introduce new kind of attachment.
8
11
  * Handles **both has\_one and has\_many** use cases.
9
12
  * **No need for ImageMagick** (or similar) - your thumbnails are generated on the fly by Cloudinary.
10
- * Fully customizable, built-in **jQuery plugin** for async file uploads with previews.
13
+ * Fully customizable, custom **jQuery plugin** for async file uploads with previews.
11
14
  * **Files are uploaded directly to Cloudinary** completely bypassing your app (without affecting its performance).
12
15
  * **Very easy to use**. Once set up, 1 line is enough to add attachment support to your model. **No migrations, no Uploaders**.
13
- * **Lightweight form submission**. Attachinary handles file upload asynchronously and the only thing that is passed to your server are the IDs of the files. That makes form postbacks fast and reliable.
16
+ * **Lightweight form submission**. Attachinary handles file upload asynchronously and the only thing that is passed to your server is metadata. That makes form postbacks fast and reliable.
14
17
  * All the [benefits of Cloudinary](http://cloudinary.com/documentation/image_transformations) (resizing, cropping, rotating, rounding corners, **face detection**...).
15
18
 
16
19
  Attachinary uses [Cloudinary](http://cloudinary.com) service. Gem is structured as mountable rails engine.
@@ -18,23 +21,30 @@ Attachinary uses [Cloudinary](http://cloudinary.com) service. Gem is structured
18
21
 
19
22
  ## Installation
20
23
 
21
- First, make sure that you have [cloudinary gem](https://github.com/cloudinary/cloudinary_gem) installed and properly configured. Also, make sure that you have following line in head section of your application layout file:
22
-
23
- <%= cloudinary_js_config %>
24
+ First, make sure that you have [cloudinary gem](https://github.com/cloudinary/cloudinary_gem) installed and properly configured.
24
25
 
25
- Then, add following line to your `Gemfile`:
26
+ Add following line to your `Gemfile`:
26
27
 
27
28
  gem 'attachinary'
28
29
 
29
- Run following rake command in terminal to create necessary tables:
30
+ Specify which ORM you wish to use by adding following line to your `application.rb` file (or custom initializer):
31
+
32
+ require "attachinary/orm/YOUR_ORM" # active_record or mongoid
33
+
34
+ If you're using `ActiveRecord` ORM, then run following lines to generate required table:
30
35
 
31
36
  rake attachinary:install:migrations
32
37
  rake db:migrate
33
38
 
34
- Add following line in your `routes.rb` file to mount the engine:
39
+ Next, add following line in your `routes.rb` file:
35
40
 
36
41
  mount Attachinary::Engine => "/attachinary"
37
42
 
43
+ It will generate '/attachinary/cors' which will be used for iframe file transfers (for unsupported browsers).
44
+
45
+ Finally, make sure that you have following line in head section of your application layout file:
46
+
47
+ <%= cloudinary_js_config %>
38
48
 
39
49
 
40
50
 
@@ -44,17 +54,17 @@ Lets say that we want all of our **users** to have single **avatar** and many **
44
54
 
45
55
  class User < ActiveRecord::Base
46
56
  ...
47
- has_attachment :avatar, accept: ['jpg', 'png', 'gif']
57
+ has_attachment :avatar, accept: [:jpg, :png, :gif]
48
58
  has_attachments :photos, maximum: 10
49
59
 
50
- validates :avatar_id, presence: true
60
+ validates :avatar, presence: true
51
61
  ...
52
62
  end
53
63
 
54
64
  In our `_form.html.erb` template, we need to add only this:
55
65
 
56
- <%= attachinary_file_field_tag 'user[avatar_id]', user.avatar_id, attachinary: user.avatar_options %>
57
- <%= attachinary_file_field_tag 'user[photo_ids]', user.photo_ids, attachinary: user.photo_options %>
66
+ <%= attachinary_file_field_tag 'user[avatar]', user, :avatar %>
67
+ <%= attachinary_file_field_tag 'user[photos]', user, :photos %>
58
68
 
59
69
  If you're using [SimpleForm](https://github.com/plataformatec/simple_form), you can even shorten this to:
60
70
 
@@ -76,6 +86,7 @@ Attachinary jquery plugin is based upon [jQuery File Upload plugin](https://gith
76
86
 
77
87
  Plugin is fully customizable. It uses John Resig's micro templating in the background, but you can override it with whatever you like. Check out the source code for more configuration options you can set.
78
88
 
89
+
79
90
  ### Displaying avatar and photos
80
91
 
81
92
  Here comes the good part. There is no need to transform images on your server. Instead, you can request image transformations directly from Cloudinary. First time you request image, it is created and cached on the Cloudinary server for later use. Here is sample code that you can use in your `_user.html.erb` partial:
@@ -96,7 +107,8 @@ Whenever you feel like changing image sizes, you don't need to set rake task tha
96
107
  ## Conventions
97
108
 
98
109
  * always use singular identifier after `has_attachment` (e.g. `has_attachment :photo`)
99
- * always use plural identifier after `has_attachments` (e.g. `has_attachments :photos`)
110
+ * always use plural identifier after `has_attachments` (e.g. `has_attachments :images`)
111
+ * you can't use colliding identifiers (e.g. `has_attachment :photo` and `has_attachments :photos`) on same model.
100
112
 
101
113
 
102
114
  ## Requirements and Compatibility
@@ -107,6 +119,12 @@ Whenever you feel like changing image sizes, you don't need to set rake task tha
107
119
  * jQuery
108
120
 
109
121
 
122
+ ### Browser Compatibility
123
+
124
+ Attachinary jquery plugin uses JSON2 to generate JSON data.
125
+ This works for all major browsers, but if you wish to support older ones (e.g. IE7-), include [json2.js](https://github.com/douglascrockford/JSON-js/blob/master/json2.js).
126
+
127
+
110
128
  ## Credits and License
111
129
 
112
130
  Developed by Milovan Zogovic.
data/Rakefile CHANGED
@@ -38,6 +38,15 @@ Bundler::GemHelper.install_tasks
38
38
 
39
39
  require 'rspec/core/rake_task'
40
40
  RSpec::Core::RakeTask.new(:spec)
41
- task :default => :spec
42
41
 
43
42
 
43
+ desc 'Run Devise tests for all ORMs.'
44
+ task :spec_all_orms do
45
+ Dir[File.join(File.dirname(__FILE__), 'spec', 'orm', '*.rb')].each do |file|
46
+ orm = File.basename(file).split(".").first
47
+ puts "\n\n-------- ORM: #{orm}\n\n"
48
+ exit 1 unless system "rake spec ATTACHINARY_ORM=#{orm}"
49
+ end
50
+ end
51
+
52
+ task :default => :spec_all_orms
@@ -14,7 +14,7 @@ module Attachinary
14
14
 
15
15
  private
16
16
  def file_params
17
- request.query_parameters.slice(:public_id, :version, :width, :height, :format, :resource_type)
17
+ request.query_parameters.slice(:public_id, :version, :width, :height, :format, :resource_type, :scope)
18
18
  end
19
19
 
20
20
  end
@@ -1,14 +1,9 @@
1
1
  class CreateAttachinaryTables < ActiveRecord::Migration
2
2
  def change
3
- create_table :attachinary_attachments do |t|
4
- t.belongs_to :parent, polymorphic: true
5
- t.belongs_to :file
3
+ create_table :attachinary_files do |t|
4
+ t.references :attachinariable, polymorphic: true
6
5
  t.string :scope
7
- t.timestamps
8
- end
9
- add_index :attachinary_attachments, [:parent_type, :parent_id, :scope], name: 'by_scoped_parent'
10
6
 
11
- create_table :attachinary_files do |t|
12
7
  t.string :public_id
13
8
  t.string :version
14
9
  t.integer :width
@@ -17,5 +12,6 @@ class CreateAttachinaryTables < ActiveRecord::Migration
17
12
  t.string :resource_type
18
13
  t.timestamps
19
14
  end
15
+ add_index :attachinary_files, [:attachinariable_type, :attachinariable_id, :scope], name: 'by_scoped_parent'
20
16
  end
21
17
  end
data/lib/attachinary.rb CHANGED
@@ -1,7 +1,4 @@
1
1
  require 'attachinary/engine'
2
2
 
3
- require 'attachinary/active_record_extension'
4
- ActiveRecord::Base.extend Attachinary::ActiveRecordExtension
5
-
6
3
  module Attachinary
7
4
  end
@@ -0,0 +1,5 @@
1
+ require_relative 'file_mixin'
2
+ require_relative 'active_record/extension'
3
+ require_relative 'active_record/file'
4
+
5
+ ActiveRecord::Base.extend Attachinary::Extension
@@ -0,0 +1,116 @@
1
+ module Attachinary
2
+ module Extension
3
+
4
+ def has_attachment(scope, options={})
5
+ options[:single] = true
6
+ attachinary scope, options
7
+ end
8
+
9
+ def has_attachments(scope, options={})
10
+ options[:single] = false
11
+ attachinary scope, options
12
+ end
13
+
14
+ def attachinary(scope, options)
15
+ options.reverse_merge!({
16
+ accessible: true
17
+ })
18
+
19
+ if options[:single]
20
+ singular = scope.to_s
21
+ plural = scope.to_s.pluralize
22
+ else
23
+ plural = scope.to_s
24
+ singular = scope.to_s.singularize
25
+ end
26
+
27
+ relation = "#{singular}_files"
28
+
29
+ # has_many :photo_files, ...
30
+ # has_many :image_files, ...
31
+ has_many :"#{relation}",
32
+ as: :attachinariable,
33
+ class_name: '::Attachinary::File',
34
+ conditions: { scope: scope.to_s },
35
+ dependent: :destroy
36
+
37
+ # attr_accessible :photo
38
+ # attr_accessible :images
39
+ attr_accessible :"#{scope}" if options[:accessible]
40
+
41
+
42
+ # def photo?
43
+ # photo.present?
44
+ # end
45
+ # def images?
46
+ # images.present?
47
+ # end
48
+ define_method :"#{scope}?" do
49
+ send(:"#{scope}").present?
50
+ end
51
+
52
+ # def photo_metadata
53
+ # options[:scope] = 'photo'
54
+ # options[:maximum] = 1 if options[:single]
55
+ # options
56
+ # end
57
+ define_method :"#{scope}_metadata" do
58
+ options[:scope] = scope
59
+ options[:maximum] = 1 if options[:single]
60
+ options
61
+ end
62
+
63
+ # def photo=(file)
64
+ # if file.blank?
65
+ # self.photo_files.clear
66
+ # else
67
+ # case file
68
+ # when String
69
+ # files = ... parse JSON and MAP to array of Attachinary::File ..
70
+ # self.photo_files = files
71
+ # else
72
+ # self.photo_files = [file].flatten
73
+ # end
74
+ # end
75
+ # end
76
+ define_method "#{scope}=" do |file|
77
+ if file.blank?
78
+ send("#{relation}").clear
79
+ else
80
+ case file
81
+ when String
82
+ files = [JSON.parse(file)].flatten.map do |data|
83
+ data = data.slice(*Attachinary::File.attr_accessible[:default].to_a)
84
+ Attachinary::File.new(data) do |f|
85
+ f.scope = scope.to_s
86
+ end
87
+ end
88
+ send("#{relation}=", files)
89
+ else
90
+ send("#{relation}=", [file].flatten)
91
+ end
92
+ end
93
+ end
94
+
95
+
96
+ if options[:single]
97
+ # def photo
98
+ # photo_files.first
99
+ # end
100
+ define_method "#{scope}" do
101
+ send("#{relation}").first
102
+ end
103
+
104
+ else # plural
105
+ # def images
106
+ # image_files
107
+ # end
108
+ define_method "#{scope}" do
109
+ send("#{relation}")
110
+ end
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,6 @@
1
+ module Attachinary
2
+ class File < ::ActiveRecord::Base
3
+ belongs_to :attachinariable, polymorphic: true
4
+ include FileMixin
5
+ end
6
+ end
@@ -0,0 +1,121 @@
1
+ module Attachinary
2
+ module Extension
3
+
4
+ def has_attachment(scope, options={})
5
+ options[:single] = true
6
+ attachinary scope, options
7
+ end
8
+
9
+ def has_attachments(scope, options={})
10
+ options[:single] = false
11
+ attachinary scope, options
12
+ end
13
+
14
+ def attachinary(scope, options)
15
+ options.reverse_merge!({
16
+ accessible: true
17
+ })
18
+
19
+ if options[:single]
20
+ singular = scope.to_s
21
+ plural = scope.to_s.pluralize
22
+ else
23
+ plural = scope.to_s
24
+ singular = scope.to_s.singularize
25
+ end
26
+
27
+ relation = "#{singular}_files"
28
+
29
+ # # has_many :photo_files, ...
30
+ # # has_many :image_files, ...
31
+ # has_many :"#{relation}",
32
+ # as: :attachinariable,
33
+ # class_name: '::Attachinary::File',
34
+ # conditions: { scope: scope.to_s },
35
+ # dependent: :destroy
36
+
37
+ embeds_many :"#{relation}",
38
+ as: :attachinariable,
39
+ class_name: '::Attachinary::File',
40
+ validate: false
41
+
42
+ # attr_accessible :photo
43
+ # attr_accessible :images
44
+ attr_accessible :"#{scope}" if options[:accessible]
45
+
46
+
47
+ # def photo?
48
+ # photo.present?
49
+ # end
50
+ # def images?
51
+ # images.present?
52
+ # end
53
+ define_method :"#{scope}?" do
54
+ send(:"#{scope}").present?
55
+ end
56
+
57
+ # def photo_metadata
58
+ # options[:scope] = 'photo'
59
+ # options[:maximum] = 1 if options[:single]
60
+ # options
61
+ # end
62
+ define_method :"#{scope}_metadata" do
63
+ options[:scope] = scope
64
+ options[:maximum] = 1 if options[:single]
65
+ options
66
+ end
67
+
68
+ # def photo=(file)
69
+ # if file.blank?
70
+ # self.photo_files.clear
71
+ # else
72
+ # case file
73
+ # when String
74
+ # files = ... parse JSON and MAP to array of Attachinary::File ..
75
+ # self.photo_files = files
76
+ # else
77
+ # self.photo_files = [file].flatten
78
+ # end
79
+ # end
80
+ # end
81
+ define_method "#{scope}=" do |file|
82
+ if file.blank?
83
+ send("#{relation}").clear
84
+ else
85
+ case file
86
+ when String
87
+ files = [JSON.parse(file)].flatten.map do |data|
88
+ data = data.slice(*Attachinary::File.attr_accessible[:default].to_a)
89
+ Attachinary::File.new(data) do |f|
90
+ f.scope = scope.to_s if f.respond_to?(:scope)
91
+ end
92
+ end
93
+ send("#{relation}=", files)
94
+ else
95
+ send("#{relation}=", [file].flatten)
96
+ end
97
+ end
98
+ end
99
+
100
+
101
+ if options[:single]
102
+ # def photo
103
+ # photo_files.first
104
+ # end
105
+ define_method "#{scope}" do
106
+ send("#{relation}").first
107
+ end
108
+
109
+ else # plural
110
+ # def images
111
+ # image_files
112
+ # end
113
+ define_method "#{scope}" do
114
+ send("#{relation}")
115
+ end
116
+ end
117
+
118
+ end
119
+
120
+ end
121
+ end
@@ -1,10 +1,10 @@
1
1
  module Attachinary
2
- class File < ::ActiveRecord::Base
3
- validates :public_id, :version, presence: true
4
- validates :resource_type, presence: true
5
-
6
- attr_accessible :public_id, :version, :width, :height, :format, :resource_type
7
- after_destroy :destroy_file
2
+ module FileMixin
3
+ def self.included(base)
4
+ base.validates :public_id, :version, :resource_type, presence: true
5
+ base.attr_accessible :public_id, :version, :width, :height, :format, :resource_type
6
+ base.after_destroy :destroy_file
7
+ end
8
8
 
9
9
  def as_json(options)
10
10
  super(only: [:id, :public_id, :format, :version, :resource_type], methods: [:path])
@@ -24,13 +24,6 @@ module Attachinary
24
24
  Cloudinary::Utils.cloudinary_url(path(format), options)
25
25
  end
26
26
 
27
- def self.upload!(file)
28
- if file.respond_to?(:read)
29
- response = Cloudinary::Uploader.upload(file, tags: "env_#{Rails.env}")
30
- create! response.slice('public_id', 'version', 'width', 'height', 'format', 'resource_type')
31
- end
32
- end
33
-
34
27
  private
35
28
  def destroy_file
36
29
  Cloudinary::Uploader.destroy(public_id) if public_id
@@ -0,0 +1,5 @@
1
+ require_relative 'file_mixin'
2
+ require_relative 'mongoid/extension'
3
+ require_relative 'mongoid/file'
4
+
5
+ Mongoid::Document::ClassMethods.send :include, Attachinary::Extension
@@ -0,0 +1,90 @@
1
+ module Attachinary
2
+ module Extension
3
+
4
+ def has_attachment(scope, options={})
5
+ options[:single] = true
6
+ attachinary scope, options
7
+ end
8
+
9
+ def has_attachments(scope, options={})
10
+ options[:single] = false
11
+ attachinary scope, options
12
+ end
13
+
14
+ def attachinary(scope, options)
15
+ options.reverse_merge!({
16
+ accessible: true
17
+ })
18
+
19
+ if options[:single]
20
+ singular = scope.to_s
21
+ plural = scope.to_s.pluralize
22
+ else
23
+ plural = scope.to_s
24
+ singular = scope.to_s.singularize
25
+ end
26
+
27
+ if options[:single]
28
+ # embeds_on :photo, ...
29
+ embeds_one :"#{scope}", as: :attachinariable, class_name: '::Attachinary::File'
30
+ else
31
+ # embeds_many :images, ...
32
+ embeds_many :"#{scope}", as: :attachinariable, class_name: '::Attachinary::File'
33
+ end
34
+
35
+
36
+ # attr_accessible :photo
37
+ # attr_accessible :images
38
+ attr_accessible :"#{scope}" if options[:accessible]
39
+
40
+ # def photo?
41
+ # photo.present?
42
+ # end
43
+ # def images?
44
+ # images.present?
45
+ # end
46
+ define_method :"#{scope}?" do
47
+ send(:"#{scope}").present?
48
+ end
49
+
50
+ # def photo_metadata
51
+ # options[:scope] = 'photo'
52
+ # options[:maximum] = 1 if options[:single]
53
+ # options
54
+ # end
55
+ define_method :"#{scope}_metadata" do
56
+ options[:scope] = scope
57
+ options[:maximum] = 1 if options[:single]
58
+ options
59
+ end
60
+
61
+
62
+ # alias_method :orig_photo=, :photo=
63
+ # def photo=(arg)
64
+ # arg = nil if arg.empty?
65
+ # if arg.is_a?(String)
66
+ # files = ... parse JSON and MAP to array of Attachinary::File ..
67
+ # files = files[0] if options[:singular]
68
+ # super files
69
+ # else
70
+ # super
71
+ # end
72
+ # end
73
+ alias_method "orig_#{scope}=", "#{scope}="
74
+ define_method "#{scope}=" do |arg|
75
+ arg = nil if arg.respond_to?(:empty?) && arg.empty?
76
+
77
+ if arg.is_a?(String)
78
+ files = [JSON.parse(arg)].flatten.map do |data|
79
+ data = data.slice(*Attachinary::File.attr_accessible[:default].to_a)
80
+ Attachinary::File.new(data)
81
+ end
82
+ send("orig_#{scope}=", options[:single] ? files[0] : files)
83
+ else
84
+ send("orig_#{scope}=", arg)
85
+ end
86
+ end
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,16 @@
1
+ module Attachinary
2
+ class File
3
+ include ::Mongoid::Document
4
+ include ::Mongoid::Timestamps
5
+ include FileMixin
6
+
7
+ field :public_id, type: String
8
+ field :version, type: String
9
+ field :width, type: Integer
10
+ field :height, type: Integer
11
+ field :format, type: String
12
+ field :resource_type, type: String
13
+
14
+ embedded_in :attachinariable, polymorphic: true
15
+ end
16
+ end
@@ -1,18 +1,9 @@
1
1
  class AttachinaryInput < SimpleForm::Inputs::Base
2
2
  attr_reader :attachinary_options
3
3
 
4
- def initialize(builder, attribute_name, column, input_type, options = {})
5
- @attachinary_options = builder.object.send("#{attribute_name.to_s.singularize}_options")
6
- attribute_name = @attachinary_options[:field_name]
7
-
8
- super builder, attribute_name, column, input_type, options
9
- end
10
-
11
4
  def input
12
5
  name = "#{@builder.object_name}[#{attribute_name}]"
13
- value = object.send(attribute_name)
14
-
15
- template.attachinary_file_field_tag name, value,
16
- { html: input_html_options, attachinary: attachinary_options }
6
+ template.attachinary_file_field_tag name, @builder.object, attribute_name,
7
+ { html: input_html_options }
17
8
  end
18
9
  end
@@ -1,3 +1,3 @@
1
1
  module Attachinary
2
- VERSION = "0.0.8"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -3,7 +3,9 @@ require 'mime/types'
3
3
  module Attachinary
4
4
  module ViewHelpers
5
5
 
6
- def attachinary_file_field_tag(name, value, options={})
6
+ def attachinary_file_field_tag(field_name, model, relation, options={})
7
+ options[:attachinary] = model.send("#{relation}_metadata")
8
+
7
9
  options[:cloudinary] ||= {}
8
10
  options[:cloudinary][:tags] ||= []
9
11
  options[:cloudinary][:tags]<< "#{Rails.env}_env"
@@ -33,15 +35,13 @@ module Attachinary
33
35
 
34
36
  options[:html][:data] ||= {}
35
37
  options[:html][:data][:attachinary] = options[:attachinary] || {}
36
- options[:html][:data][:attachinary][:callback] = attachinary.callback_path
37
- options[:html][:data][:attachinary][:files] = Attachinary::File.where(id: value).all
38
- options[:html][:data][:attachinary][:file_field_name] = name.gsub(options[:attachinary][:field_name], options[:attachinary][:file_field_name])
39
- options[:html][:data][:attachinary][:field_name] = name
38
+ options[:html][:data][:attachinary][:files] = [model.send(relation)].compact.flatten
39
+ options[:html][:data][:attachinary][:field_name] = field_name
40
40
 
41
41
  options[:html][:data][:form_data] = cloudinary_params.reject{ |k, v| v.blank? }
42
42
  options[:html][:data][:url] = cloudinary_upload_url
43
43
 
44
- file_field_tag(options[:html][:data][:attachinary][:file_field_name], options[:html])
44
+ file_field_tag('file', options[:html])
45
45
  end
46
46
 
47
47
  end
@@ -15,7 +15,7 @@
15
15
  <img
16
16
  src="<%= $.cloudinary.url(files[i].public_id, { "version": files[i].version, "format": 'jpg', "crop": 'fill', "width": 75, "height": 75 }) %>"
17
17
  alt="" width="75" height="75" />
18
- <a href="#" data-remove="<%= files[i].id %>">Remove</a>
18
+ <a href="#" data-remove="<%= files[i].public_id %>">Remove</a>
19
19
  </li>
20
20
  <% } %>
21
21
  </ul>
@@ -55,13 +55,12 @@
55
55
  if @$input.attr('accept')
56
56
  options.acceptFileTypes = new RegExp("^#{@$input.attr('accept').split(",").join("|")}$", "i")
57
57
 
58
- @$input.attr('name', 'file')
59
58
  @$input.fileupload(options)
60
59
 
61
60
 
62
61
  bindEventHandlers: ->
63
62
  @$input.bind 'fileuploaddone', (event, data) =>
64
- @issueCallback(data.result)
63
+ setTimeout (=> @addFile(data.result)), 0 # let 'fileuploadalways' finish
65
64
 
66
65
  @$input.bind 'fileuploadstart', (event) =>
67
66
  @$input = $(event.target) # important! changed on every file upload
@@ -72,9 +71,7 @@
72
71
  $form.addClass 'uploading'
73
72
 
74
73
  @$input.prop 'disabled', true
75
-
76
74
  if @config.disableWith
77
- $submit.prop 'disabled', true
78
75
  $submit.data 'old-val', $submit.val()
79
76
  $submit.val @config.disableWith
80
77
 
@@ -86,21 +83,11 @@
86
83
  $form.removeClass 'uploading'
87
84
 
88
85
  @$input.prop 'disabled', false
89
-
90
86
  if @config.disableWith
91
- $submit.prop 'disabled', false
92
87
  $submit.val $submit.data('old-val')
93
88
 
94
-
95
- issueCallback: (data) ->
96
- $.ajax
97
- url: @options.callback,
98
- data: data,
99
- success: (file) => @addFile(file)
100
-
101
89
  addFile: (file) ->
102
- extension = file.path.split(".")[1]
103
- if !@options.accept || $.inArray(extension, @options.accept) != -1
90
+ if !@options.accept || $.inArray(file.format, @options.accept) != -1
104
91
  @files.push file
105
92
  @redraw()
106
93
  @checkMaximum()
@@ -108,7 +95,7 @@
108
95
  alert @config.invalidFormatMessage
109
96
 
110
97
  removeFile: (fileIdToRemove) ->
111
- @files = (file for file in @files when file.id.toString() != fileIdToRemove.toString())
98
+ @files = (file for file in @files when file.public_id != fileIdToRemove)
112
99
  @redraw()
113
100
  @checkMaximum()
114
101
 
@@ -124,11 +111,9 @@
124
111
 
125
112
  redraw: ->
126
113
  @$filesContainer.empty()
127
- @$filesContainer.append @makeHiddenField(null)
128
114
 
129
115
  if @files.length > 0
130
- for file in @files
131
- @$filesContainer.append @makeHiddenField(file.id)
116
+ @$filesContainer.append @makeHiddenField(JSON.stringify(@files))
132
117
 
133
118
  @$filesContainer.append @config.render(@files)
134
119
  @$filesContainer.find('[data-remove]').on 'click', (event) =>
@@ -137,15 +122,12 @@
137
122
 
138
123
  @$filesContainer.show()
139
124
  else
125
+ @$filesContainer.append @makeHiddenField(null)
140
126
  @$filesContainer.hide()
141
127
 
142
128
  makeHiddenField: (value) ->
143
129
  $input = $('<input type="hidden">')
144
-
145
- name = @options.field_name
146
- name+= "[]" unless @options.single
147
-
148
- $input.attr 'name', name
130
+ $input.attr 'name', @options.field_name
149
131
  $input.val value
150
132
  $input
151
133
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attachinary
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-12 00:00:00.000000000Z
12
+ date: 2012-09-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &70282217818480 !ruby/object:Gem::Requirement
16
+ requirement: &70213729979420 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3.2'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70282217818480
24
+ version_requirements: *70213729979420
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: cloudinary
27
- requirement: &70282217817980 !ruby/object:Gem::Requirement
27
+ requirement: &70213729994680 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.0.30
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70282217817980
35
+ version_requirements: *70213729994680
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sqlite3
38
- requirement: &70282217817600 !ruby/object:Gem::Requirement
38
+ requirement: &70213729993740 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70282217817600
46
+ version_requirements: *70213729993740
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rspec-rails
49
- requirement: &70282217817060 !ruby/object:Gem::Requirement
49
+ requirement: &70213729992700 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '2.5'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70282217817060
57
+ version_requirements: *70213729992700
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: valid_attribute
60
- requirement: &70282217816640 !ruby/object:Gem::Requirement
60
+ requirement: &70213729991360 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70282217816640
68
+ version_requirements: *70213729991360
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: capybara
71
- requirement: &70282217816180 !ruby/object:Gem::Requirement
71
+ requirement: &70213730002500 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70282217816180
79
+ version_requirements: *70213730002500
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: factory_girl_rails
82
- requirement: &70282217815680 !ruby/object:Gem::Requirement
82
+ requirement: &70213729996140 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '3.0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70282217815680
90
+ version_requirements: *70213729996140
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: webmock
93
- requirement: &70282217815260 !ruby/object:Gem::Requirement
93
+ requirement: &70213729995080 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70282217815260
101
+ version_requirements: *70213729995080
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: launchy
104
- requirement: &70282217814800 !ruby/object:Gem::Requirement
104
+ requirement: &70213730007760 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,10 +109,21 @@ dependencies:
109
109
  version: '0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *70282217814800
112
+ version_requirements: *70213730007760
113
+ - !ruby/object:Gem::Dependency
114
+ name: database_cleaner
115
+ requirement: &70213730006740 !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: *70213730006740
113
124
  - !ruby/object:Gem::Dependency
114
125
  name: guard-rspec
115
- requirement: &70282217814380 !ruby/object:Gem::Requirement
126
+ requirement: &70213730005440 !ruby/object:Gem::Requirement
116
127
  none: false
117
128
  requirements:
118
129
  - - ! '>='
@@ -120,7 +131,7 @@ dependencies:
120
131
  version: '0'
121
132
  type: :development
122
133
  prerelease: false
123
- version_requirements: *70282217814380
134
+ version_requirements: *70213730005440
124
135
  description: Attachments handler for Rails that uses Cloudinary for storage.
125
136
  email:
126
137
  - milovan.zogovic@gmail.com
@@ -131,12 +142,17 @@ files:
131
142
  - app/controllers/attachinary/application_controller.rb
132
143
  - app/controllers/attachinary/cors_controller.rb
133
144
  - app/controllers/attachinary/files_controller.rb
134
- - app/models/attachinary/attachment.rb
135
- - app/models/attachinary/file.rb
136
145
  - config/routes.rb
137
146
  - db/migrate/20120612112526_create_attachinary_tables.rb
138
- - lib/attachinary/active_record_extension.rb
139
147
  - lib/attachinary/engine.rb
148
+ - lib/attachinary/orm/active_record/extension.rb
149
+ - lib/attachinary/orm/active_record/file.rb
150
+ - lib/attachinary/orm/active_record.rb
151
+ - lib/attachinary/orm/extension.rb
152
+ - lib/attachinary/orm/file_mixin.rb
153
+ - lib/attachinary/orm/mongoid/extension.rb
154
+ - lib/attachinary/orm/mongoid/file.rb
155
+ - lib/attachinary/orm/mongoid.rb
140
156
  - lib/attachinary/simple_form.rb
141
157
  - lib/attachinary/version.rb
142
158
  - lib/attachinary/view_helpers.rb
@@ -159,7 +175,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
159
175
  version: '0'
160
176
  segments:
161
177
  - 0
162
- hash: 1778917027853764357
178
+ hash: -3340139382106100314
163
179
  required_rubygems_version: !ruby/object:Gem::Requirement
164
180
  none: false
165
181
  requirements:
@@ -168,11 +184,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
168
184
  version: '0'
169
185
  segments:
170
186
  - 0
171
- hash: 1778917027853764357
187
+ hash: -3340139382106100314
172
188
  requirements: []
173
189
  rubyforge_project:
174
- rubygems_version: 1.8.10
190
+ rubygems_version: 1.8.11
175
191
  signing_key:
176
192
  specification_version: 3
177
- summary: attachinary-0.0.8
193
+ summary: attachinary-1.0.0
178
194
  test_files: []
@@ -1,10 +0,0 @@
1
- module Attachinary
2
- class Attachment < ::ActiveRecord::Base
3
- belongs_to :parent, polymorphic: true, touch: true
4
- belongs_to :file, class_name: 'Attachinary::File', foreign_key: 'file_id'
5
-
6
- validates :parent_id, :parent_type, :scope, presence: true
7
-
8
- attr_accessible :parent_id, :parent_type, :file_id
9
- end
10
- end
@@ -1,189 +0,0 @@
1
- module Attachinary
2
- module ActiveRecordExtension
3
-
4
- def has_attachment(scope, options={})
5
- attachinary_apply_defaults!(options)
6
-
7
- # has_one :photo_attachment, ...
8
- has_one :"#{scope}_attachment",
9
- as: :parent,
10
- class_name: '::Attachinary::Attachment',
11
- conditions: { scope: scope.to_s },
12
- dependent: :destroy
13
-
14
- # has_one :photo_attachment_file, through: :photo_attachment, ...
15
- has_one :"#{scope}_attachment_file",
16
- through: :"#{scope}_attachment",
17
- source: :file
18
-
19
- # attr_accessible :photo_id, :photo_file
20
- attr_accessible :"#{scope}_id", :"#{scope}_file" if options[:accessible]
21
-
22
- # attr_writer :photo
23
- attr_writer :"#{scope}"
24
-
25
- # def photo
26
- # unless defined?(@photo)
27
- # @photo = photo_attachment_file
28
- # end
29
- # @photo
30
- # end
31
- define_method :"#{scope}" do
32
- unless instance_variable_defined? :"@#{scope}"
33
- instance_variable_set :"@#{scope}", send(:"#{scope}_attachment_file")
34
- end
35
- instance_variable_get :"@#{scope}"
36
- end
37
-
38
- # def photo_id=(id)
39
- # photo = ::Attachinary::File.find_by_id(id)
40
- # end
41
- define_method :"#{scope}_id=" do |id|
42
- send(:"#{scope}=", ::Attachinary::File.find_by_id(id))
43
- end
44
-
45
- # def photo_file=(f)
46
- # photo = ::Attachinary::File.upload!(f) if f.present?
47
- # end
48
- define_method :"#{scope}_file=" do |f|
49
- send(:"#{scope}=", ::Attachinary::File.upload!(f)) if f.present?
50
- end
51
-
52
- # def photo_id
53
- # photo.try(:id)
54
- # end
55
- define_method :"#{scope}_id" do
56
- send(:"#{scope}").try(:id)
57
- end
58
-
59
- # def photo?
60
- # photo.present?
61
- # end
62
- define_method :"#{scope}?" do
63
- send(:"#{scope}").present?
64
- end
65
-
66
- # before_save do
67
- # photo_attachment_file = photo
68
- # end
69
- before_save do
70
- send(:"#{scope}_attachment_file=", send(:"#{scope}"))
71
- end
72
-
73
- # def photo_options
74
- # options.merge({
75
- # field_name: "photo_id",
76
- # maximum: 1
77
- # })
78
- # end
79
- define_method :"#{scope}_options" do
80
- options.merge({
81
- field_name: "#{scope}_id",
82
- file_field_name: "#{scope}_file",
83
- single: true,
84
- maximum: 1
85
- })
86
- end
87
- end
88
-
89
- def has_attachments(scope, options={})
90
- attachinary_apply_defaults!(options)
91
- singular = scope.to_s.singularize
92
-
93
- # has_many :image_attachments
94
- has_many :"#{singular}_attachments",
95
- as: :parent,
96
- class_name: '::Attachinary::Attachment',
97
- conditions: { scope: scope.to_s },
98
- dependent: :destroy
99
-
100
- # has_many :image_attachment_files, through: :image_attachments
101
- has_many :"#{singular}_attachment_files",
102
- through: :"#{singular}_attachments",
103
- source: :file
104
-
105
- # attr_accessible :image_ids, :image_files
106
- attr_accessible :"#{singular}_ids", :"#{singular}_files" if options[:accessible]
107
-
108
- # attr_writer :images
109
- attr_writer :"#{scope}"
110
-
111
- # def images
112
- # unless defined?(@images)
113
- # @images = image_attachment_files
114
- # end
115
- # @images
116
- # end
117
- define_method :"#{scope}" do
118
- unless instance_variable_defined? :"@#{scope}"
119
- instance_variable_set :"@#{scope}", send(:"#{singular}_attachment_files")
120
- end
121
- instance_variable_get :"@#{scope}"
122
- end
123
-
124
- # def image_ids=(ids)
125
- # files = [ids].flatten.compact.uniq.reject(&:blank?) do |id|
126
- # ::Attachinary::File.find_by_id(id)
127
- # end.compact
128
- # images = files
129
- # end
130
- define_method :"#{singular}_ids=" do |ids|
131
- files = [ids].flatten.compact.uniq.reject(&:blank?).map do |id|
132
- ::Attachinary::File.find_by_id(id)
133
- end.compact
134
- send(:"#{scope}=", files)
135
- end
136
-
137
- # def image_files=(fs)
138
- # files = fs.map { |f| ::Attachinary::File.upload!(f) }
139
- # images = files
140
- # end
141
- define_method :"#{singular}_files=" do |fs|
142
- files = fs.map{ |f| ::Attachinary::File.upload!(f) }.compact
143
- send(:"#{scope}=", files)
144
- end
145
-
146
- # def image_ids
147
- # images.map(&:id)
148
- # end
149
- define_method :"#{singular}_ids" do
150
- send(:"#{scope}").map(&:id)
151
- end
152
-
153
- # def images?
154
- # images.present?
155
- # end
156
- define_method :"#{scope}?" do
157
- send(:"#{scope}").present?
158
- end
159
-
160
- # before_save do
161
- # image_attachment_files = images
162
- # end
163
- before_save do
164
- send(:"#{singular}_attachment_files=", send(:"#{scope}"))
165
- end
166
-
167
- # def image_options
168
- # options.merge({
169
- # field_name: "image_ids"
170
- # })
171
- # end
172
- define_method :"#{singular}_options" do
173
- options.merge({
174
- field_name: "#{singular}_ids",
175
- file_field_name: "#{singular}_files",
176
- single: false
177
- })
178
- end
179
- end
180
-
181
- private
182
- def attachinary_apply_defaults!(options)
183
- options.reverse_merge!({
184
- accessible: true
185
- })
186
- end
187
-
188
- end
189
- end