GithubHelp home page GithubHelp logo

tomasc / simple_form_attachments Goto Github PK

View Code? Open in Web Editor NEW
2.0 4.0 2.0 34.15 MB

A Rails engine which takes care of creating Attachments using the jQuery File Upload plugin.

License: MIT License

Ruby 69.69% HTML 29.27% JavaScript 0.13% CoffeeScript 0.56% CSS 0.34%
ruby-on-rails attachments engine uploads

simple_form_attachments's Introduction

SimpleFormAttachments

Build Status Gem Version Coverage Status

A Rails engine for creating Attachments using the jQuery File Upload plugin.

Dependencies:

Installation

Add this line to your application's Gemfile:

gem 'simple_form_attachments'

And then execute:

$ bundle

Or install it yourself as:

$ gem install simple_form_attachments

Add simple_form_attachments to application.js:

//= require simple_form_attachments

Usage

Attachment model

Create a model for your attachments that includes the SimpleFormAttachments::Attachment concern:

class AttachmentImage
  include SimpleFormAttachments::Attachment
end

This adds a :temporary Boolean field (set to false by default) and two scopes: temporary and permanent to the model.

Note that the concern does not include any specific accessors (ie Dragonfly). You need to define those yourself in your model.

Validations

Standard Rails validation errors are displayed in the uploader, should the uploaded file not pass the validations defined on the attachment model.

Owner model

Add the SimpleFormAttachments::HasAttachments concern to the attachment owner. This includes the mongoid relation macros has_one_attachment and has_many_attachments:

class Parent
  include Mongoid::Document
  include SimpleFormAttachments::HasAttachments

  has_one_attachment :attachment_pdf, class_name: 'AttachmentPdf'
  has_many_attachments :attachment_images, class_name: 'AttachmentImage'
end

Relations between the parent and Attachment are referenced, not embedded. The gem uses belongs_to for 1–1 relations and has_and_belongs_to_many for 1–n relations. This is to ensure that relations work with embedded parents, since the relations need to be stored on the owner's side.

You could use for example the before_save callback to embed the attachments yourself.

Callbacks

Since attachments can be added/removed dynamically in the form, we need to indicate which ones are actually submitted in the end, so that we can – immediately or later (left up to you) – delete the temporary ones.

For that each relation defines a method named after the relation name, which can be called for example via a callback:

after_save :mark_attachment_pdf_permanent
after_save :mark_attachment_images_permanent

These methods (atomically) set the attachment's :temporary attribute to false.

Alternatively the mark_all_attachments_permanent method can be used to loop through all attachment relations, triggering individual above-mentioned methods. This means the two after_save callbacks above can be replaced with:

after_save :mark_all_attachments_permanent

Validations

If you want to validate the number of attachments allowed on the owner, you can use for example the following validation. Eventual validation errors will be displayed to the user when submitting the parent form.

validates :attachment_pdfs, length: { maximum: 2 }

Controller

The default UploadController receives the uploaded attachment, sets its temporary to true and creates corresponding record in the database.

Remember, you need to mark attachments as permanent (ie temporary to false) yourself. See Callbacks above.

Routes

Mount the engine in your routes:

mount SimpleFormAttachments::Engine => "/"

This will include a default /attachments route.

Forms

Now you can use the attachment input in your SimpleForm forms:

= form.input :attachment_images, as: :attachment

Configuration

Sorting

Sorting of attachments is turned on by default for has_many_attachments relations. But it's possible to disable sorting on a field:

= f.input :attachment_images, as: :attachment, sortable: false

Views

Uploaded attachments are displayed in a div. The gem will look for a partial using the normal rails partial lookup (with a fallback to the default partial), this makes it easy to define a partial for each of your attachment models.

Here an example partial for a model called AttachmentImage:

/ app/views/simple_form_attachments/attachment_images/_attachment_image.html.slim
/ (Note the nesting in 'simple_form_attachments' directory.)

div class=SimpleFormAttachments.dom_class(:attachment, [:col, :thumb])
  = image_tag attachment.thumb_url

div class=SimpleFormAttachments.dom_class(:attachment, [:col, :file_info])
  span class=SimpleFormAttachments.dom_class(:attachment, :col, :file_info, :name)
    = link_to attachment.file_name, attachment.file.url
  span class=SimpleFormAttachments.dom_class(:attachment, :col, :file_info, :mime_type)
    = attachment.file_mime_type
  span class=SimpleFormAttachments.dom_class(:attachment, :col, :file_info, :size) data-filesize=attachment.file_size
    = attachment.file_size

- if attachment.errors.to_a.any?
  div class=SimpleFormAttachments.dom_class(:attachment, [:col, :errors])
    = render 'simple_form_attachments/errors', errors: attachment.errors.to_a

- else
  div class=SimpleFormAttachments.dom_class(:attachment, [:col, :fields])
    = fields.input :caption

Dom class and CSS

The component is isolated in the simple_form_attachments namespace. This gem comes with its own helper that generates corresponding class names – for example SimpleFormAttachments.dom_class(:attachment, :file_info) would generate simple_form_attachments__file_info.

Basic styling can be achieved by including simple_form_attachments css in your app:

/*
*= require simple_form_attachments
*/

Validation errors

If you want to show validation errors in your partial, simply include the simple_form_attachments/errors partial as shown above.

Route

You can customize the engine to use a different controller and route, for example should you want to preprocess the uploaded files or determine the attachment model based on file mime/extension/format, ….

Setup a controller for your attachments:

class AttachmentsController < SimpleFormAttachments::UploadController
  # The default controller uses the attachment type passed from the form input
  # to determine which Attachment class to create.
  #
  # This can be easily overridden, for instance if you want to infer
  # the class from the `mime type` of the file:
  #
  def attachment_class
    mime_type = params[:attachment][:file].content_type
  end
end

Add route as usual:

resources :attachments, only: [:create]

Specify the route either app wide in an initializer:

# config/initializers/simple_form_attachments.rb

require 'simple_form_attachments'
# Configuration

SimpleFormAttachments::AttachmentInput.configure do |c|
  c.route = attachments_path
  # can be also specified as a lambda to be evaluated at runtime: -> { attachments_path }
end

Or per input:

= f.input :attachment_images, as: :attachment, route: attachments_path

Testing

Besides a MiniTest test suite, the gem has a dummy app located at test/dummy. Simply cd into the directory and run bin/rails s to test the uploader.

Contributing

  1. Fork it ( https://github.com/tomasc/simple_form_attachments/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

simple_form_attachments's People

Contributors

asgerb avatar sebastianlyserena avatar tomasc avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

simple_form_attachments's Issues

Lack of Rails anti-CSRF defaults when Controller is inherited from ActionController::Base

Description of Issue

The default configuration for Rails’ ActionController::Base does not automatically include the anti-CSRF mechanism, protect_from_forgery. This leaves affected many Rails applications vulnerable to CSRF.

The issue has been mentioned in a Pull Request in Rails. Subsequently, a blog post was published to explore the extent of the impact. More technical information about the issue can be viewed here.

Fix

A reasonable fix has been mentioned both in the Pull Request made in Rails, as well as in this blog post.

An example fix for most Rails Application would be as follows:

class ApplicationController < ActionController::Base
+    protect_from_forgery with: :exception
+

And for Rails Applications which are APIs:

class ApplicationController < ActionController::Base
+    protect_from_forgery with: :null_session
+

Suggestion for to_simple_form_partial_path

Hey @tomasc, I have an idea for a small change, but wanted to throw it your way first, before making a PR 😉

My suggestion is to get the attachment's form partial path (to_simple_form_partial_path) by way of model_name, instead of relying on to_partial_path.

I often find myself overriding to_partial_path on my attachments, for various reasons. As we are currently relying on to_partial_path to get the partial for simple_form_attachments, this means I need to have the same directory structure here as well. Although this works fine, it somehow feels a bit strange to me.

Basically I think we could change this:

def to_simple_form_partial_path
  File.join('simple_form_attachments', to_partial_path)
end

To this:

def to_simple_form_partial_path
  File.join('simple_form_attachments', model_name.collection, model_name.element)
end

And get a more consistent naming/directory structure in return. What do you think, does it make sense?

Form input ignores collection attribute

sorted method works only on decorated/presented objects. But doesn't work as a collection attribute in form (where I'm not able to have decorated object).

= form.input :images, as: :attachment, collection: form.object.images.sorted

However when skipping as: :attachment I can see that sorted works. So it seems as a bug with simple form custom input?

add System tests

We could test that the uploader really works through the new Rails 5.1 system tests (Capybara). What do you think @asgerb ?

an option to store uploaded into `/tmp` folder

By default the uploaded file is passed to the controller, who takes care storing it into database and marking it as temporary / permanent.

It might be worth to implement optional mechanism (service?) for the uploaded files to be stored in the /tmp folder and another one that let's one find and copy the files from the /tmp folder wherever they should be stored permanently.

This gem has been extracted from Modulor, where the temporary files assigned to modules need to be stored in the db to be previewed and stored in drafts, and then marked permanent when a page is published.

However in uploaders to standard records a simpler solution would be to store the uploaded files to /tmp folder and then copy to its permanent location (database, S3, file system) once the record is saved. This is useful especially when storing files into object storage such as S3 etc. as it would save ourselves unnecessary uploads to S3.

What do you think @asgerb ?

Upload Controller fails – Encoding::UndefinedConversionError - "\xFF" from ASCII-8BIT to UTF-8:

Getting this error for every upload

Started POST "/attachments" for ::1 at 2015-07-19 11:33:28 +0200
Processing by SimpleFormAttachments::UploadController#create as JSON
  Parameters: {"attachment_type"=>"AttachmentImage", "attachment_parent"=>{"name"=>"slideshow_particle", "class"=>"SlideshowParticle"}, "attachment_relation"=>{"type"=>"Mongoid::Relations::Referenced::ManyToMany", "key"=>"attachment_image_ids", "name"=>"attachment_images", "multiple"=>"true"}, "authenticity_token"=>"6rID9+hxjLwY8/JbuQbKI8fKVF5qUxXesAmVXweK5bgLnSxmiFDzKRFHGtOJczJtJGpo/cSffFEuBOw6f7W0Gw==", "attachment"=>{"file"=>#<ActionDispatch::Http::UploadedFile:0x0000010bbcef10 @tempfile=#<Tempfile:/var/folders/g6/pbc73nwj4lxd_d3n2mvby1w80000gn/T/RackMultipart20150719-6478-1yj19nv.jpg>, @original_filename="4311725657_c89514586b_o.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"attachment[file]\"; filename=\"4311725657_c89514586b_o.jpg\"\r\nContent-Type: image/jpeg\r\n">}}
DRAGONFLY: shell command: 'identify' '-ping' '-format' '%m %w %h' '/var/folders/g6/pbc73nwj4lxd_d3n2mvby1w80000gn/T/RackMultipart20150719-6478-1yj19nv.jpg'
  MOPED: 127.0.0.1:27017 COMMAND      database=admin command={:ismaster=>1} runtime: 0.9860ms
  MOPED: 127.0.0.1:27017 INSERT       database=db_development collection=fs.chunks documents=[{"_id"=><BSON::ObjectId:0x2262436260 data=55ab6ee84a616b194e000005>, "n"=>0, "files_id"=><BSON::ObjectId:0x2262440020 data=55ab6ee84a616b194e000004>, "data"=><BSON::Binary:0x2262434960 type=generic data=0xffd8ffe000104a46...>}] flags=[]
                         COMMAND      database=db_development command={:getlasterror=>1, :w=>1} runtime: 3.1770ms
Completed 500 Internal Server Error in 28ms

Encoding::UndefinedConversionError - "\xFF" from ASCII-8BIT to UTF-8:
  bson (3.1.2) lib/bson/binary.rb:137:in `block in to_bson'
  bson (3.1.2) lib/bson/encodable.rb:81:in `encode_binary_data_with_placeholder'
  bson (3.1.2) lib/bson/binary.rb:134:in `to_bson'
  bson (3.1.2) lib/bson/hash.rb:46:in `block (2 levels) in to_bson'
  bson (3.1.2) lib/bson/hash.rb:43:in `block in to_bson'
  bson (3.1.2) lib/bson/encodable.rb:57:in `encode_with_placeholder_and_null'
  bson (3.1.2) lib/bson/hash.rb:42:in `to_bson'
  moped (2.0.6) lib/moped/protocol/message.rb:166:in `block in serialize_documents'
  moped (2.0.6) lib/moped/protocol/message.rb:165:in `serialize_documents'
  moped (2.0.6) lib/moped/protocol/message.rb:323:in `serialize'
  moped (2.0.6) lib/moped/connection.rb:171:in `block in write'
  moped (2.0.6) lib/moped/connection.rb:169:in `write'
  moped (2.0.6) lib/moped/node.rb:590:in `block (2 levels) in flush'
  moped (2.0.6) lib/moped/node.rb:182:in `block in ensure_connected'
  moped (2.0.6) lib/moped/node.rb:115:in `block in connection'
  connection_pool (2.2.0) lib/connection_pool.rb:64:in `block (2 levels) in with'
  connection_pool (2.2.0) lib/connection_pool.rb:63:in `block in with'
  connection_pool (2.2.0) lib/connection_pool.rb:60:in `with'
  moped (2.0.6) lib/moped/node.rb:114:in `connection'
  moped (2.0.6) lib/moped/node.rb:178:in `ensure_connected'
  moped (2.0.6) lib/moped/node.rb:589:in `block in flush'
  moped (2.0.6) lib/moped/node.rb:617:in `block in logging'
  activesupport (4.2.0) lib/active_support/notifications.rb:164:in `block in instrument'
  activesupport (4.2.0) lib/active_support/notifications/instrumenter.rb:20:in `instrument'
  activesupport (4.2.0) lib/active_support/notifications.rb:164:in `instrument'
  moped (2.0.6) lib/moped/instrumentable.rb:31:in `instrument'
  moped (2.0.6) lib/moped/node.rb:616:in `logging'
  moped (2.0.6) lib/moped/node.rb:587:in `flush'
  moped (2.0.6) lib/moped/node.rb:358:in `pipeline'
  moped (2.0.6) lib/moped/operation/write.rb:47:in `execute'
  moped (2.0.6) lib/moped/node.rb:665:in `write'
  moped (2.0.6) lib/moped/node.rb:273:in `insert'
  moped (2.0.6) lib/moped/collection.rb:128:in `block (2 levels) in insert'
  moped (2.0.6) lib/moped/cluster.rb:249:in `block in with_primary'
  moped (2.0.6) lib/moped/node.rb:204:in `block in ensure_primary'
  moped (2.0.6) lib/moped/executable.rb:25:in `execute'
  moped (2.0.6) lib/moped/node.rb:203:in `ensure_primary'
  moped (2.0.6) lib/moped/cluster.rb:248:in `with_primary'
  moped (2.0.6) lib/moped/collection.rb:127:in `block in insert'
  moped (2.0.6) lib/moped/retryable.rb:30:in `with_retry'
  moped (2.0.6) lib/moped/collection.rb:125:in `insert'
  mongoid (4.0.2) lib/mongoid/query_cache.rb:117:in `insert_with_clear_cache'
  mongoid (4.0.2) lib/mongoid/persistable/creatable.rb:79:in `insert_as_root'
  mongoid (4.0.2) lib/mongoid/persistable/creatable.rb:27:in `block in insert'
  mongoid (4.0.2) lib/mongoid/persistable/creatable.rb:118:in `block (2 levels) in prepare_insert'
  activesupport (4.2.0) lib/active_support/callbacks.rb:88:in `_run_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:734:in `_run_create_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:81:in `run_callbacks'
  mongoid (4.0.2) lib/mongoid/interceptable.rb:138:in `run_callbacks'
  mongoid (4.0.2) lib/mongoid/persistable/creatable.rb:117:in `block in prepare_insert'
  activesupport (4.2.0) lib/active_support/callbacks.rb:88:in `_run_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:734:in `_run_save_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:81:in `run_callbacks'
  mongoid (4.0.2) lib/mongoid/interceptable.rb:138:in `run_callbacks'
  mongoid (4.0.2) lib/mongoid/persistable/creatable.rb:116:in `prepare_insert'
  mongoid (4.0.2) lib/mongoid/persistable/creatable.rb:23:in `insert'
  mongoid (4.0.2) lib/mongoid/persistable/savable.rb:23:in `save'
  mongoid (4.0.2) lib/mongoid/persistable/savable.rb:44:in `save!'
  mongoid-grid_fs (2.1.0) lib/mongoid/grid_fs.rb:154:in `block (2 levels) in put'
  mongoid-grid_fs (2.1.0) lib/mongoid/grid_fs.rb:479:in `chunking'
  mongoid-grid_fs (2.1.0) lib/mongoid/grid_fs.rb:147:in `block in put'
  mongoid-grid_fs (2.1.0) lib/mongoid/grid_fs.rb:454:in `block in reading'
  mongoid-grid_fs (2.1.0) lib/mongoid/grid_fs.rb:494:in `rewind'
  mongoid-grid_fs (2.1.0) lib/mongoid/grid_fs.rb:453:in `reading'
  mongoid-grid_fs (2.1.0) lib/mongoid/grid_fs.rb:136:in `put'
  (eval):2:in `put'
  dragonfly-mongoid_data_store (0.0.1) lib/dragonfly/mongoid_data_store.rb:29:in `block in write'
  dragonfly (1.0.10) lib/dragonfly/temp_object.rb:89:in `file'
  /Users/Jakub/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/forwardable.rb:183:in `file'
  dragonfly-mongoid_data_store (0.0.1) lib/dragonfly/mongoid_data_store.rb:28:in `write'
  dragonfly (1.0.10) lib/dragonfly/content.rb:180:in `store'
  /Users/Jakub/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/forwardable.rb:183:in `store'
  dragonfly (1.0.10) lib/dragonfly/model/attachment.rb:179:in `store_job!'
  dragonfly (1.0.10) lib/dragonfly/model/attachment.rb:77:in `save!'
  dragonfly (1.0.10) lib/dragonfly/model/instance_methods.rb:16:in `block in save_dragonfly_attachments'
  dragonfly (1.0.10) lib/dragonfly/model/instance_methods.rb:15:in `save_dragonfly_attachments'
  activesupport (4.2.0) lib/active_support/callbacks.rb:427:in `block in make_lambda'
  activesupport (4.2.0) lib/active_support/callbacks.rb:163:in `block in halting'
  activesupport (4.2.0) lib/active_support/callbacks.rb:92:in `_run_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:734:in `_run_save_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:81:in `run_callbacks'
  mongoid (4.0.2) lib/mongoid/interceptable.rb:138:in `run_callbacks'
  mongoid (4.0.2) lib/mongoid/persistable/creatable.rb:116:in `prepare_insert'
  mongoid (4.0.2) lib/mongoid/persistable/creatable.rb:23:in `insert'
  mongoid (4.0.2) lib/mongoid/persistable/savable.rb:23:in `save'
  simple_form_attachments (0.0.7) app/controllers/simple_form_attachments/upload_controller.rb:5:in `create'
  actionpack (4.2.0) lib/action_controller/metal/implicit_render.rb:4:in `send_action'
  actionpack (4.2.0) lib/abstract_controller/base.rb:198:in `process_action'
  actionpack (4.2.0) lib/action_controller/metal/rendering.rb:10:in `process_action'
  actionpack (4.2.0) lib/abstract_controller/callbacks.rb:20:in `block in process_action'
  activesupport (4.2.0) lib/active_support/callbacks.rb:117:in `call'
  activesupport (4.2.0) lib/active_support/callbacks.rb:234:in `block in halting'
  activesupport (4.2.0) lib/active_support/callbacks.rb:169:in `block in halting'
  activesupport (4.2.0) lib/active_support/callbacks.rb:169:in `block in halting'
  activesupport (4.2.0) lib/active_support/callbacks.rb:169:in `block in halting'
  activesupport (4.2.0) lib/active_support/callbacks.rb:169:in `block in halting'
  activesupport (4.2.0) lib/active_support/callbacks.rb:92:in `_run_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:734:in `_run_process_action_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:81:in `run_callbacks'
  actionpack (4.2.0) lib/abstract_controller/callbacks.rb:19:in `process_action'
  actionpack (4.2.0) lib/action_controller/metal/rescue.rb:29:in `process_action'
  actionpack (4.2.0) lib/action_controller/metal/instrumentation.rb:31:in `block in process_action'
  activesupport (4.2.0) lib/active_support/notifications.rb:164:in `block in instrument'
  activesupport (4.2.0) lib/active_support/notifications/instrumenter.rb:20:in `instrument'
  activesupport (4.2.0) lib/active_support/notifications.rb:164:in `instrument'
  actionpack (4.2.0) lib/action_controller/metal/instrumentation.rb:30:in `process_action'
  actionpack (4.2.0) lib/action_controller/metal/params_wrapper.rb:250:in `process_action'
  actionpack (4.2.0) lib/abstract_controller/base.rb:137:in `process'
  actionview (4.2.0) lib/action_view/rendering.rb:30:in `process'
  actionpack (4.2.0) lib/action_controller/metal.rb:195:in `dispatch'
  actionpack (4.2.0) lib/action_controller/metal/rack_delegation.rb:13:in `dispatch'
  actionpack (4.2.0) lib/action_controller/metal.rb:236:in `block in action'
  actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:73:in `dispatch'
  actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:42:in `serve'
  actionpack (4.2.0) lib/action_dispatch/journey/router.rb:43:in `block in serve'
  actionpack (4.2.0) lib/action_dispatch/journey/router.rb:30:in `serve'
  actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:802:in `call'
  railties (4.2.0) lib/rails/engine.rb:518:in `call'
  railties (4.2.0) lib/rails/railtie.rb:194:in `method_missing'
  actionpack (4.2.0) lib/action_dispatch/routing/mapper.rb:51:in `serve'
  actionpack (4.2.0) lib/action_dispatch/journey/router.rb:43:in `block in serve'
  actionpack (4.2.0) lib/action_dispatch/journey/router.rb:30:in `serve'
  actionpack (4.2.0) lib/action_dispatch/routing/route_set.rb:802:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  dragonfly (1.0.10) lib/dragonfly/middleware.rb:14:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  dragonfly (1.0.10) lib/dragonfly/middleware.rb:14:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  dragonfly (1.0.10) lib/dragonfly/middleware.rb:14:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/rack/agent_hooks.rb:30:in `traced_call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/rack/browser_monitoring.rb:32:in `traced_call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/rack/developer_mode.rb:48:in `traced_call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  rack (1.6.4) lib/rack/etag.rb:24:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  rack (1.6.4) lib/rack/conditionalget.rb:38:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  rack (1.6.4) lib/rack/head.rb:13:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/flash.rb:260:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  rack (1.6.4) lib/rack/session/abstract/id.rb:225:in `context'
  rack (1.6.4) lib/rack/session/abstract/id.rb:220:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/cookies.rb:560:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
  activesupport (4.2.0) lib/active_support/callbacks.rb:88:in `_run_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:734:in `_run_call_callbacks'
  activesupport (4.2.0) lib/active_support/callbacks.rb:81:in `run_callbacks'
  actionpack (4.2.0) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/reloader.rb:73:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/remote_ip.rb:78:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  better_errors (2.1.1) lib/better_errors/middleware.rb:84:in `protected_app_call'
  better_errors (2.1.1) lib/better_errors/middleware.rb:79:in `better_errors_call'
  better_errors (2.1.1) lib/better_errors/middleware.rb:57:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
  rollbar (1.1.0) lib/rollbar/middleware/rails/show_exceptions.rb:19:in `call_with_rollbar'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  railties (4.2.0) lib/rails/rack/logger.rb:38:in `call_app'
  railties (4.2.0) lib/rails/rack/logger.rb:20:in `block in call'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in `block in tagged'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:26:in `tagged'
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in `tagged'
  railties (4.2.0) lib/rails/rack/logger.rb:20:in `call'
  quiet_assets (1.1.0) lib/quiet_assets.rb:27:in `call_with_quiet_assets'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  request_store (1.2.0) lib/request_store/middleware.rb:8:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/request_id.rb:21:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  rack (1.6.4) lib/rack/methodoverride.rb:22:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  rack (1.6.4) lib/rack/runtime.rb:18:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  activesupport (4.2.0) lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  dragonfly (1.0.10) lib/dragonfly/cookie_monster.rb:9:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  rack (1.6.4) lib/rack/lock.rb:17:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  actionpack (4.2.0) lib/action_dispatch/middleware/static.rb:113:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  rack (1.6.4) lib/rack/sendfile.rb:113:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  railties (4.2.0) lib/rails/engine.rb:518:in `call'
  railties (4.2.0) lib/rails/application.rb:164:in `call'
  newrelic_rpm (3.12.1.298) lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call'
  rack (1.6.4) lib/rack/lock.rb:17:in `call'
  rack (1.6.4) lib/rack/content_length.rb:15:in `call'
  rack (1.6.4) lib/rack/handler/webrick.rb:88:in `service'
  /Users/Jakub/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/webrick/httpserver.rb:138:in `service'
  /Users/Jakub/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/webrick/httpserver.rb:94:in `run'
  /Users/Jakub/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/webrick/server.rb:295:in `block in start_thread'

Choose file button opens file selector in first visible attachment input on page in Chrome

Hey hey Tomas,
I noticed a bug recently when having multiple attachment inputs open on a page (Chrome, haven't tried other browsers yet).

What happens is that clicking the "Choose file" button (or on the containing label element) triggers the very first attachment file input on the page. So when you click and select a file in any input but the first, the file gets added to the very first file input on the page.

The problem is that the input's id is the same for all inputs: attachment_file. The label's for attribute is therefore also attachment_file across all labels. From an html standpoint this is also not valid – every id should be unique – but that is a problem in rails forms in general.

I think the best way to solve this would be to amend the attachment_file_field method and give the file input a more specific id (see below), but I wanted to get your thoughts on it first...

My solution doesn't solve the situation where we have two "identical" attachment inputs open (meaning with similar parent/child relations, like two image modules for instace), where the bug would occur as well, seeing as the id would be the same for both inputs. One way to solve that would be to make the input's id field contain the id of the parent.

# lib/simple_form_attachments/attachment_input.rb, line 185
def attachment_file_field
  input_html_options = {
    multiple: multiple?,
    accept: accepted_file_types,
    class: 'file',
    id: "#{parent_name}_#{relation_key}_file"
  }
  template.label_tag(input_html_options[:id) do
    template.file_field_tag('attachment[file]', input_html_options) +
    template.content_tag(:span, I18n.t(:choose_file, scope: 'simple_form_attachments.links', count: (multiple? ? 2 : 1)), class: SimpleFormAttachments.dom_class(:label, [:choose_file]))
  end
end

publish the JS as NPM package

@asgerb we need to make this gem (and the Markdown editor one among others) pluggable into latest shiny Rails with webpack support. The JS should be published as NPM package.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.