brightcontent-attachments 2.0.33 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/app/assets/javascripts/brightcontent/attachments.js.coffee +42 -0
  4. data/app/assets/stylesheets/brightcontent/attachments.css.scss +3 -0
  5. data/app/controllers/brightcontent/attachments_controller.rb +9 -6
  6. data/app/models/brightcontent/attachment.rb +17 -1
  7. data/app/views/brightcontent/attachments/_attachment.html.erb +16 -10
  8. data/app/views/brightcontent/base/_form_field_attachments.html.erb +9 -11
  9. data/app/views/brightcontent/base/_list_field_attachments.html.erb +4 -0
  10. data/brightcontent-attachments.gemspec +1 -1
  11. data/config/locales/brightcontent_attachements.en.yml +8 -0
  12. data/config/locales/brightcontent_attachements.nl.yml +8 -0
  13. data/config/routes.rb +1 -1
  14. data/db/migrate/20140203140819_add_position_to_attachments.rb +5 -0
  15. data/lib/brightcontent/attachments/attachable.rb +16 -0
  16. data/lib/brightcontent/attachments/engine.rb +1 -1
  17. data/lib/brightcontent/attachments/model_extensions.rb +16 -0
  18. data/lib/brightcontent/attachments.rb +6 -2
  19. data/lib/generators/brightcontent/attachments/install_generator.rb +0 -3
  20. data/script/rails +2 -2
  21. data/spec/dummy/config/application.rb +2 -6
  22. data/spec/dummy/config/environments/development.rb +1 -10
  23. data/spec/dummy/config/environments/production.rb +0 -4
  24. data/spec/dummy/config/environments/test.rb +1 -6
  25. data/spec/dummy/config/initializers/secret_token.rb +1 -1
  26. data/spec/dummy/db/migrate/20121206121725_create_brightcontent_admin_users.rb +1 -1
  27. data/spec/dummy/db/schema.rb +14 -13
  28. data/spec/features/attachments_spec.rb +5 -6
  29. data/spec/models/brightcontent/attachable_spec.rb +10 -8
  30. data/spec/models/brightcontent/attachment_spec.rb +19 -0
  31. data/vendor/assets/javascripts/spin.js +353 -0
  32. metadata +33 -28
  33. data/app/assets/javascripts/brightcontent/attachments.js +0 -14
  34. data/app/helpers/brightcontent/attachments_helper.rb +0 -4
  35. data/lib/brightcontent/attachable.rb +0 -15
  36. data/lib/brightcontent/attachment_model_extensions.rb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31453eae643abc77863b353258667588ca9cbfb8
4
- data.tar.gz: 61d12130d8c1db3cec30990c81b5bb784437097e
3
+ metadata.gz: 9fe1f577211b1bff0afc4b534bcea4542074de68
4
+ data.tar.gz: 325702f3a132bf2655a27b0bc013699b191e4927
5
5
  SHA512:
6
- metadata.gz: 011ccfec559cfbf7d72d9f7f61b82ef56cffd5a84d136882a98889538615f37ec3a0ca91aff53b1bb08f3ae95092015400c6b02f8dd322570f41840f4bc0dc2a
7
- data.tar.gz: 714478bfc864a6842b5ec97e892cc3596e2697e32cd6ad98eeef9cf999f10f1552ea1b42f5425a0d652e9b14f65564e03266522cd94af79bf3c9f7cd3cbcad66
6
+ metadata.gz: 66247189162509cd49e45c686ae5ad59a260053110da12da42bb3f5ce975fef2d97918d8f2b1f0079ba8881c06c87676a33944c200d1fced267d188cec991c54
7
+ data.tar.gz: 83d89ffac4c3f21c62f0818c7d73ad35e3cbe1f32fa0cac70c302e2972ee58b644e80f70594eaad9bb641160b8a7800004dca1ac369ca85e7197e682cf578298
data/.gitignore CHANGED
@@ -8,3 +8,4 @@ spec/dummy/log/*.log
8
8
  spec/dummy/tmp/
9
9
  spec/dummy/.sass-cache
10
10
  vendor/bundle
11
+ bin/
@@ -0,0 +1,42 @@
1
+ #= require jquery-fileupload/basic
2
+ #= require spin
3
+
4
+ $ ->
5
+ request_url = $("#attachable_url").val()
6
+
7
+ spinner = new Spinner().spin()
8
+ $('#fileupload').fileupload
9
+ method: "PUT"
10
+ send: ->
11
+ spinner.spin $("#attachments-controls")[0]
12
+ $("#attachments *").attr disabled: true
13
+ $("#fileupload").attr disabled: true
14
+ $("#attachments-controls").css opacity: 0.5
15
+
16
+ done: (e, data) ->
17
+ $.get request_url, (attachments_view) ->
18
+ $('#attachments').html attachments_view
19
+ spinner.stop()
20
+ $("#fileupload").attr disabled: false
21
+ $("#attachments-controls").css opacity: 1
22
+
23
+
24
+ $("#attachments").on "click", ".delete", ->
25
+ el = $(this).parents(".attachment")
26
+ spinner.spin el[0]
27
+ el.css opacity: 0.5
28
+ el.find("*").attr disabled: true
29
+ $.ajax
30
+ method: "DELETE"
31
+ url: $(this).attr "href"
32
+ success: -> el.remove()
33
+ false
34
+
35
+ $("#attachments").sortable
36
+ axis: "y"
37
+ cancel: "input,textarea,button,select,option,a"
38
+ distance: 5
39
+ update: ->
40
+ $.post "#{request_url}/reposition", positions: ($("#attachments > *").map -> $(this).data "attachment-id").toArray()
41
+
42
+ $("#attachments").disableSelection()
@@ -0,0 +1,3 @@
1
+ #attachments .attachment {
2
+ cursor: move;
3
+ }
@@ -2,9 +2,8 @@ require_dependency "brightcontent/application_controller"
2
2
 
3
3
  module Brightcontent
4
4
  class AttachmentsController < ApplicationController
5
-
6
5
  def show
7
- @attachments = Attachment.where(attachable_type: params[:type].classify, attachable_id: params[:id])
6
+ @attachments = Attachment.for_attachable(params[:type], params[:id])
8
7
  render layout: false
9
8
  end
10
9
 
@@ -18,15 +17,19 @@ module Brightcontent
18
17
  end
19
18
 
20
19
  def destroy
21
- attachment = Attachment.destroy(params[:id])
22
- redirect_to attachment.attachable
20
+ Attachment.destroy params[:id]
21
+ head :no_content
22
+ end
23
+
24
+ def reposition
25
+ Attachment.for_attachable(params[:type], params[:id]).reposition! params[:positions]
26
+ head :no_content
23
27
  end
24
28
 
25
29
  private
26
30
 
27
31
  def attachment_params
28
- params.permit(:attachable_id, :attachable_type, :asset)
32
+ params.require(:attachment).permit(:attachable_id, :attachable_type, :asset)
29
33
  end
30
-
31
34
  end
32
35
  end
@@ -3,13 +3,26 @@ module Brightcontent
3
3
  belongs_to :attachable, polymorphic: true, :inverse_of => :attachments
4
4
  has_attached_file :asset, :styles => lambda {|attachment| attachment.instance.attachment_styles }
5
5
  before_post_process :resize_images
6
+ before_create :set_position
6
7
 
7
- default_scope order("id")
8
+ default_scope { order("position, id") }
9
+ scope :for_attachable, -> type, id { where(attachable_type: type.classify, attachable_id: id) }
8
10
 
9
11
  delegate :url, to: :asset
10
12
 
13
+ validates_attachment_content_type :asset, content_type: Brightcontent.attachment_content_types
14
+ validates :attachable, presence: true
11
15
  validates :asset, attachment_presence: true
12
16
 
17
+ def self.reposition!(positions)
18
+ positions.map! &:to_i
19
+ transaction do
20
+ all.each do |attachment|
21
+ attachment.update_column :position, positions.index(attachment.id)
22
+ end
23
+ end
24
+ end
25
+
13
26
  def attachment_styles
14
27
  default_attachment_styles.merge(user_defined_styles)
15
28
  end
@@ -34,5 +47,8 @@ module Brightcontent
34
47
  return false unless image?
35
48
  end
36
49
 
50
+ def set_position
51
+ self.position ||= self.class.for_attachable(attachable_type, attachable_id).maximum(:position).to_i + 1
52
+ end
37
53
  end
38
54
  end
@@ -1,10 +1,16 @@
1
- <%= div_for attachment do %>
2
- <% if attachment.image? %>
3
- <%= image_tag attachment.url(:brightcontent) %>
4
- <%= link_to "Insert", nil, class: "insert insert_image btn btn-mini", data: { insert_image: attachment.url(:main) } %>
5
- <% else %>
6
- <div class="attachment-file"><%= attachment.asset_file_name %></div>
7
- <%= link_to "Insert", nil, class: "insert insert_link btn btn-mini", data: { insert_url: attachment.url(:original), insert_name: attachment.asset_file_name } %>
8
- <% end %>
9
- <%= link_to "Delete", destroy_attachment_path(attachment.id), method: :delete, confirm: "Are you sure?", class: "delete btn btn-danger btn-mini" %>
10
- <% end %>
1
+ <li class="list-group-item attachment" data-attachment-id="<%= attachment.id %>">
2
+ <div class="media">
3
+ <% if attachment.image? %>
4
+ <%= image_tag attachment.url(:brightcontent), class: "media-object pull-left" %>
5
+ <% end %>
6
+ <div class="media-body">
7
+ <h4 class="attachment-file media-heading"><%= attachment.asset_file_name %></h4>
8
+ <% if attachment.image? %>
9
+ <%= link_to "Insert", nil, class: "insert insert_image btn btn-primary btn-xs", data: { insert_image: attachment.url(:main) } %>
10
+ <% else %>
11
+ <%= link_to "Insert", nil, class: "insert insert_link btn btn-primary btn-xs", data: { insert_url: attachment.url(:original), insert_name: attachment.asset_file_name } %>
12
+ <% end %>
13
+ <%= link_to "Delete", destroy_attachment_path(attachment.id), class: "delete btn btn-danger btn-xs", method: "delete" %>
14
+ </div>
15
+ </div>
16
+ </li>
@@ -1,18 +1,16 @@
1
- <div class="control-group optional">
2
- <label class="optional control-label">Attachments</label>
3
- <div class="controls">
1
+ <div class="control-group">
2
+ <label class="control-label"><%=Brightcontent::Attachment.model_name.human.pluralize%></label>
3
+ <div class="controls" id="attachments-controls">
4
4
  <% if item.persisted? %>
5
- <div id="attachments" class="clearfix">
5
+ <ul class="media-list" id="attachments">
6
6
  <%= render item.attachments %>
7
- </div>
8
-
9
- <input id="fileupload" type="file" name="asset" data-url="<%= new_attachment_path %>" multiple>
10
- <%= hidden_field_tag "attachable_id", item.id %>
11
- <%= hidden_field_tag "attachable_type", item.class.to_s %>
7
+ </ul>
8
+ <input id="fileupload" type="file" name="attachment[asset]" data-url="<%= new_attachment_path %>" class="form-control" multiple>
9
+ <%= hidden_field_tag "attachment[attachable_id]", item.id %>
10
+ <%= hidden_field_tag "attachment[attachable_type]", item.class.to_s %>
12
11
  <%= hidden_field_tag "attachable_url", attachments_path(item.class.to_s, item.id) %>
13
-
14
12
  <% else %>
15
- First save to enable uploads
13
+ <p class="form-control-static"><%= t('brightcontent.save_first')%></p>
16
14
  <% end %>
17
15
  </div>
18
16
  </div>
@@ -0,0 +1,4 @@
1
+ <% if item.attachments.any? %>
2
+ <span class="glyphicon glyphicon-paperclip"></span>
3
+ <%= item.attachments.count %>
4
+ <% end %>
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.require_paths = ["lib"]
17
17
 
18
18
  s.add_dependency "brightcontent-core", version
19
- s.add_dependency "paperclip"
19
+ s.add_dependency "paperclip", ">= 3.0.0"
20
20
  s.add_dependency "jquery-fileupload-rails"
21
21
 
22
22
  s.add_development_dependency "sqlite3"
@@ -0,0 +1,8 @@
1
+ en:
2
+ activerecord:
3
+ models:
4
+ brightcontent/attachment: Attachment
5
+
6
+ brightcontent:
7
+ save_first: First save to enable uploads
8
+ insert: Insert
@@ -0,0 +1,8 @@
1
+ nl:
2
+ activerecord:
3
+ models:
4
+ brightcontent/attachment: Bijlage
5
+
6
+ brightcontent:
7
+ save_first: Bewaar het item eerst voordat je kunt uploaden.
8
+ insert: Invoegen
data/config/routes.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  Brightcontent::Engine.routes.draw do
2
- # TODO: put instead of create
3
2
  get 'attachments/:type/:id' => 'attachments#show', as: :attachments
3
+ post 'attachments/:type/:id/reposition' => 'attachments#reposition'
4
4
  put 'attachments' => 'attachments#create', as: :new_attachment
5
5
  delete 'attachments/:id' => 'attachments#destroy', as: :destroy_attachment
6
6
  end
@@ -0,0 +1,5 @@
1
+ class AddPositionToAttachments < ActiveRecord::Migration
2
+ def change
3
+ add_column :brightcontent_attachments, :position, :integer
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ module Brightcontent
2
+ module Attachments
3
+ module Attachable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ add_brightcontent_column :attachments
8
+ has_many :attachments, as: :attachable, dependent: :destroy, class_name: Brightcontent::Attachment, inverse_of: :attachable
9
+ end
10
+
11
+ def attachment
12
+ attachments.first
13
+ end
14
+ end
15
+ end
16
+ end
@@ -3,7 +3,7 @@ module Brightcontent
3
3
  class Engine < ::Rails::Engine
4
4
  initializer "Add attachments class method" do
5
5
  ActiveSupport.on_load(:active_record) do
6
- include AttachmentModelExtensions
6
+ include ModelExtensions
7
7
  end
8
8
  end
9
9
  end
@@ -0,0 +1,16 @@
1
+ module Brightcontent
2
+ module Attachments
3
+ module ModelExtensions
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ attr_reader :attachment_styles
8
+
9
+ def has_attached_files(styles)
10
+ include ::Brightcontent::Attachments::Attachable
11
+ @attachment_styles = styles
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -5,9 +5,13 @@ require "jquery-fileupload-rails"
5
5
  require "brightcontent/attachments/engine"
6
6
 
7
7
  module Brightcontent
8
- autoload :Attachable, 'brightcontent/attachable'
9
- autoload :AttachmentModelExtensions, 'brightcontent/attachment_model_extensions'
8
+ register_extension 'attachments'
9
+
10
+ mattr_reader :attachment_content_types
11
+ @@attachment_content_types = [/\Aimage/, "application/pdf"]
10
12
 
11
13
  module Attachments
14
+ autoload :Attachable, 'brightcontent/attachments/attachable'
15
+ autoload :ModelExtensions, 'brightcontent/attachments/model_extensions'
12
16
  end
13
17
  end
@@ -2,13 +2,10 @@ module Brightcontent
2
2
  module Attachments
3
3
  module Generators
4
4
  class InstallGenerator < Rails::Generators::Base
5
-
6
5
  desc "Install migrations for Brightcontent Attachments"
7
-
8
6
  def copy_migrations
9
7
  rake "brightcontent_attachments_engine:install:migrations"
10
8
  end
11
-
12
9
  end
13
10
  end
14
11
  end
data/script/rails CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
3
 
4
- ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
- ENGINE_PATH = File.expand_path('../../lib/brightcontent/assets/engine', __FILE__)
4
+ ENGINE_ROOT = File.expand_path('../../../core', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../../core/lib/brightcontent/engine', __FILE__)
6
6
 
7
7
  require 'rails/all'
8
8
  require 'rails/engine/commands'
@@ -43,15 +43,11 @@ module Dummy
43
43
  # like if you have constraints or database-specific column types
44
44
  # config.active_record.schema_format = :sql
45
45
 
46
- # Enforce whitelist mode for mass assignment.
47
- # This will create an empty whitelist of attributes available for mass-assignment for all models
48
- # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
49
- # parameters by using an attr_accessible or attr_protected declaration.
50
- config.active_record.whitelist_attributes = false
51
-
52
46
  # Enable the asset pipeline
53
47
  config.assets.enabled = true
54
48
 
49
+ # config.i18n.enforce_available_locales = true
50
+
55
51
  # Version of your assets, change this if you want to expire all your assets
56
52
  config.assets.version = '1.0'
57
53
  end
@@ -1,14 +1,12 @@
1
1
  Dummy::Application.configure do
2
2
  # Settings specified here will take precedence over those in config/application.rb
3
+ config.eager_load = false
3
4
 
4
5
  # In the development environment your application's code is reloaded on
5
6
  # every request. This slows down response time but is perfect for development
6
7
  # since you don't have to restart the web server when you make code changes.
7
8
  config.cache_classes = false
8
9
 
9
- # Log error messages when you accidentally call methods on nil.
10
- config.whiny_nils = true
11
-
12
10
  # Show full error reports and disable caching
13
11
  config.consider_all_requests_local = true
14
12
  config.action_controller.perform_caching = false
@@ -22,13 +20,6 @@ Dummy::Application.configure do
22
20
  # Only use best-standards-support built into browsers
23
21
  config.action_dispatch.best_standards_support = :builtin
24
22
 
25
- # Raise exception on mass assignment protection for Active Record models
26
- config.active_record.mass_assignment_sanitizer = :strict
27
-
28
- # Log the query plan for queries taking more than this (works
29
- # with SQLite, MySQL, and PostgreSQL)
30
- config.active_record.auto_explain_threshold_in_seconds = 0.5
31
-
32
23
  # Do not compress assets
33
24
  config.assets.compress = false
34
25
 
@@ -60,8 +60,4 @@ Dummy::Application.configure do
60
60
 
61
61
  # Send deprecation notices to registered listeners
62
62
  config.active_support.deprecation = :notify
63
-
64
- # Log the query plan for queries taking more than this (works
65
- # with SQLite, MySQL, and PostgreSQL)
66
- # config.active_record.auto_explain_threshold_in_seconds = 0.5
67
63
  end
@@ -1,5 +1,6 @@
1
1
  Dummy::Application.configure do
2
2
  # Settings specified here will take precedence over those in config/application.rb
3
+ config.eager_load = false
3
4
 
4
5
  # The test environment is used exclusively to run your application's
5
6
  # test suite. You never need to work with it otherwise. Remember that
@@ -11,9 +12,6 @@ Dummy::Application.configure do
11
12
  config.serve_static_assets = true
12
13
  config.static_cache_control = "public, max-age=3600"
13
14
 
14
- # Log error messages when you accidentally call methods on nil
15
- config.whiny_nils = true
16
-
17
15
  # Show full error reports and disable caching
18
16
  config.consider_all_requests_local = true
19
17
  config.action_controller.perform_caching = false
@@ -29,9 +27,6 @@ Dummy::Application.configure do
29
27
  # ActionMailer::Base.deliveries array.
30
28
  config.action_mailer.delivery_method = :test
31
29
 
32
- # Raise exception on mass assignment protection for Active Record models
33
- config.active_record.mass_assignment_sanitizer = :strict
34
-
35
30
  # Print deprecation notices to the stderr
36
31
  config.active_support.deprecation = :stderr
37
32
  end
@@ -4,4 +4,4 @@
4
4
  # If you change this key, all old signed cookies will become invalid!
5
5
  # Make sure the secret is at least 30 characters and all random,
6
6
  # no regular words or you'll be exposed to dictionary attacks.
7
- Dummy::Application.config.secret_token = '1582af16879d84e4319aa02ea51964c7c27576e6468838ea1bcadf1286623d44e9117613a747386fde1bc2253a9a49c291a2c100fec64ae5c2cc5c75efa553ef'
7
+ Dummy::Application.config.secret_key_base = '1582af16879d84e4319aa02ea51964c7c27576e6468838ea1bcadf1286623d44e9117613a747386fde1bc2253a9a49c291a2c100fec64ae5c2cc5c75efa553ef'
@@ -7,6 +7,6 @@ class CreateBrightcontentAdminUsers < ActiveRecord::Migration
7
7
  t.timestamps
8
8
  end
9
9
 
10
- Brightcontent::AdminUser.create!(:email => 'admin@example.com', :password => 'password')
10
+ Brightcontent::AdminUser.create!(:email => 'admin@example.com', :password => 'password', :password_confirmation => 'password')
11
11
  end
12
12
  end
@@ -9,36 +9,37 @@
9
9
  # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
10
  # you'll amass, the slower it'll run and the greater likelihood for issues).
11
11
  #
12
- # It's strongly recommended to check this file into your version control system.
12
+ # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(:version => 20121222172513) do
14
+ ActiveRecord::Schema.define(version: 20140203140819) do
15
15
 
16
- create_table "blogs", :force => true do |t|
16
+ create_table "blogs", force: true do |t|
17
17
  t.string "name"
18
18
  t.text "body"
19
- t.datetime "created_at", :null => false
20
- t.datetime "updated_at", :null => false
19
+ t.datetime "created_at", null: false
20
+ t.datetime "updated_at", null: false
21
21
  end
22
22
 
23
- create_table "brightcontent_admin_users", :force => true do |t|
23
+ create_table "brightcontent_admin_users", force: true do |t|
24
24
  t.string "email"
25
25
  t.string "password_digest"
26
- t.datetime "created_at", :null => false
27
- t.datetime "updated_at", :null => false
26
+ t.datetime "created_at", null: false
27
+ t.datetime "updated_at", null: false
28
28
  end
29
29
 
30
- create_table "brightcontent_attachments", :force => true do |t|
30
+ create_table "brightcontent_attachments", force: true do |t|
31
31
  t.integer "attachable_id"
32
32
  t.string "attachable_type"
33
33
  t.string "asset_file_name"
34
34
  t.string "asset_content_type"
35
35
  t.integer "asset_file_size"
36
36
  t.datetime "asset_updated_at"
37
- t.datetime "created_at", :null => false
38
- t.datetime "updated_at", :null => false
37
+ t.datetime "created_at", null: false
38
+ t.datetime "updated_at", null: false
39
+ t.integer "position"
39
40
  end
40
41
 
41
- add_index "brightcontent_attachments", ["attachable_id"], :name => "index_brightcontent_attachments_on_attachable_id"
42
- add_index "brightcontent_attachments", ["attachable_type"], :name => "index_brightcontent_attachments_on_attachable_type"
42
+ add_index "brightcontent_attachments", ["attachable_id"], name: "index_brightcontent_attachments_on_attachable_id"
43
+ add_index "brightcontent_attachments", ["attachable_type"], name: "index_brightcontent_attachments_on_attachable_type"
43
44
 
44
45
  end
@@ -7,7 +7,7 @@ feature "Attachments" do
7
7
 
8
8
  scenario "show message if item is not saved" do
9
9
  click_link "Blogs"
10
- click_link "Create new Blog"
10
+ click_link "Create new blog"
11
11
  page.should have_content "First save to enable uploads"
12
12
  end
13
13
 
@@ -21,21 +21,20 @@ feature "Attachments" do
21
21
  context "with image" do
22
22
  background do
23
23
  blog = create(:blog)
24
- @attachment = Brightcontent::Attachment.create!(:asset => Rails.root.join("public/rails.png").open)
25
- blog.attachments << @attachment
24
+ @attachment = blog.attachments.create!(:asset => Rails.root.join("public/rails.png").open)
26
25
  click_link "Blogs"
27
26
  click_link "Edit"
28
27
  end
29
28
 
30
29
  scenario "shows images" do
31
- page.should have_selector "#attachment_#{@attachment.id}"
30
+ page.should have_selector "[data-attachment-id=\"#{@attachment.id}\"]"
32
31
  end
33
32
 
34
33
  scenario "delete image" do
35
- within "#attachment_#{@attachment.id}" do
34
+ within "[data-attachment-id=\"#{@attachment.id}\"]" do
36
35
  click_link "Delete"
37
36
  end
38
- page.should_not have_selector "#attachment_#{@attachment.id}"
37
+ page.should_not have_selector "[data-attachment-id=\"#{@attachment.id}\"]"
39
38
  end
40
39
  end
41
40
  end
@@ -1,16 +1,18 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Brightcontent
4
- describe Attachable do
5
- subject { Blog }
6
- its(:brightcontent_columns) { should include "attachments" }
4
+ module Attachments
5
+ describe Attachable do
6
+ subject { Blog }
7
+ its(:brightcontent_columns) { should include "attachments" }
7
8
 
8
- it "should have a has_many relation to attachments" do
9
- Blog.reflect_on_association(:attachments).macro.should eq :has_many
10
- end
9
+ it "should have a has_many relation to attachments" do
10
+ Blog.reflect_on_association(:attachments).macro.should eq :has_many
11
+ end
11
12
 
12
- it "should have .attachment method" do
13
- Blog.new.should respond_to :attachment
13
+ it "should have .attachment method" do
14
+ Blog.new.should respond_to :attachment
15
+ end
14
16
  end
15
17
  end
16
18
  end
@@ -15,5 +15,24 @@ module Brightcontent
15
15
  its(:attachment_styles) { should == { test: "200x200", brightcontent: "100x100#", main: "200x200#" } }
16
16
  end
17
17
  end
18
+
19
+ describe ".reposition!" do
20
+ let!(:blog) { build_stubbed(:blog) }
21
+ let!(:attachment_1) { blog.attachments.create!(:asset => Rails.root.join("public/rails.png").open) }
22
+ let!(:attachment_2) { blog.attachments.create!(:asset => Rails.root.join("public/rails.png").open) }
23
+ let!(:attachment_3) { blog.attachments.create!(:asset => Rails.root.join("public/rails.png").open) }
24
+
25
+ it "reorders the attachments as specified" do
26
+ ids = [attachment_2.id, attachment_3.id, attachment_1.id]
27
+ Attachment.reposition! ids
28
+ blog.attachments.pluck(:id).should == ids
29
+ end
30
+
31
+ it "sets unspecified attachments' positions to nil" do
32
+ ids = [attachment_3.id, attachment_2.id]
33
+ Attachment.reposition! ids
34
+ attachment_1.reload.position.should be_nil
35
+ end
36
+ end
18
37
  end
19
38
  end
@@ -0,0 +1,353 @@
1
+ /**
2
+ * Copyright (c) 2011-2013 Felix Gnass
3
+ * Licensed under the MIT license
4
+ */
5
+ (function(root, factory) {
6
+
7
+ /* CommonJS */
8
+ if (typeof exports == 'object') module.exports = factory()
9
+
10
+ /* AMD module */
11
+ else if (typeof define == 'function' && define.amd) define(factory)
12
+
13
+ /* Browser global */
14
+ else root.Spinner = factory()
15
+ }
16
+ (this, function() {
17
+ "use strict";
18
+
19
+ var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */
20
+ , animations = {} /* Animation rules keyed by their name */
21
+ , useCssAnimations /* Whether to use CSS animations or setTimeout */
22
+
23
+ /**
24
+ * Utility function to create elements. If no tag name is given,
25
+ * a DIV is created. Optionally properties can be passed.
26
+ */
27
+ function createEl(tag, prop) {
28
+ var el = document.createElement(tag || 'div')
29
+ , n
30
+
31
+ for(n in prop) el[n] = prop[n]
32
+ return el
33
+ }
34
+
35
+ /**
36
+ * Appends children and returns the parent.
37
+ */
38
+ function ins(parent /* child1, child2, ...*/) {
39
+ for (var i=1, n=arguments.length; i<n; i++)
40
+ parent.appendChild(arguments[i])
41
+
42
+ return parent
43
+ }
44
+
45
+ /**
46
+ * Insert a new stylesheet to hold the @keyframe or VML rules.
47
+ */
48
+ var sheet = (function() {
49
+ var el = createEl('style', {type : 'text/css'})
50
+ ins(document.getElementsByTagName('head')[0], el)
51
+ return el.sheet || el.styleSheet
52
+ }())
53
+
54
+ /**
55
+ * Creates an opacity keyframe animation rule and returns its name.
56
+ * Since most mobile Webkits have timing issues with animation-delay,
57
+ * we create separate rules for each line/segment.
58
+ */
59
+ function addAnimation(alpha, trail, i, lines) {
60
+ var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-')
61
+ , start = 0.01 + i/lines * 100
62
+ , z = Math.max(1 - (1-alpha) / trail * (100-start), alpha)
63
+ , prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase()
64
+ , pre = prefix && '-' + prefix + '-' || ''
65
+
66
+ if (!animations[name]) {
67
+ sheet.insertRule(
68
+ '@' + pre + 'keyframes ' + name + '{' +
69
+ '0%{opacity:' + z + '}' +
70
+ start + '%{opacity:' + alpha + '}' +
71
+ (start+0.01) + '%{opacity:1}' +
72
+ (start+trail) % 100 + '%{opacity:' + alpha + '}' +
73
+ '100%{opacity:' + z + '}' +
74
+ '}', sheet.cssRules.length)
75
+
76
+ animations[name] = 1
77
+ }
78
+
79
+ return name
80
+ }
81
+
82
+ /**
83
+ * Tries various vendor prefixes and returns the first supported property.
84
+ */
85
+ function vendor(el, prop) {
86
+ var s = el.style
87
+ , pp
88
+ , i
89
+
90
+ prop = prop.charAt(0).toUpperCase() + prop.slice(1)
91
+ for(i=0; i<prefixes.length; i++) {
92
+ pp = prefixes[i]+prop
93
+ if(s[pp] !== undefined) return pp
94
+ }
95
+ if(s[prop] !== undefined) return prop
96
+ }
97
+
98
+ /**
99
+ * Sets multiple style properties at once.
100
+ */
101
+ function css(el, prop) {
102
+ for (var n in prop)
103
+ el.style[vendor(el, n)||n] = prop[n]
104
+
105
+ return el
106
+ }
107
+
108
+ /**
109
+ * Fills in default values.
110
+ */
111
+ function merge(obj) {
112
+ for (var i=1; i < arguments.length; i++) {
113
+ var def = arguments[i]
114
+ for (var n in def)
115
+ if (obj[n] === undefined) obj[n] = def[n]
116
+ }
117
+ return obj
118
+ }
119
+
120
+ /**
121
+ * Returns the absolute page-offset of the given element.
122
+ */
123
+ function pos(el) {
124
+ var o = { x:el.offsetLeft, y:el.offsetTop }
125
+ while((el = el.offsetParent))
126
+ o.x+=el.offsetLeft, o.y+=el.offsetTop
127
+
128
+ return o
129
+ }
130
+
131
+ /**
132
+ * Returns the line color from the given string or array.
133
+ */
134
+ function getColor(color, idx) {
135
+ return typeof color == 'string' ? color : color[idx % color.length]
136
+ }
137
+
138
+ // Built-in defaults
139
+
140
+ var defaults = {
141
+ lines: 12, // The number of lines to draw
142
+ length: 7, // The length of each line
143
+ width: 5, // The line thickness
144
+ radius: 10, // The radius of the inner circle
145
+ rotate: 0, // Rotation offset
146
+ corners: 1, // Roundness (0..1)
147
+ color: '#000', // #rgb or #rrggbb
148
+ direction: 1, // 1: clockwise, -1: counterclockwise
149
+ speed: 1, // Rounds per second
150
+ trail: 100, // Afterglow percentage
151
+ opacity: 1/4, // Opacity of the lines
152
+ fps: 20, // Frames per second when using setTimeout()
153
+ zIndex: 2e9, // Use a high z-index by default
154
+ className: 'spinner', // CSS class to assign to the element
155
+ top: 'auto', // center vertically
156
+ left: 'auto', // center horizontally
157
+ position: 'relative' // element position
158
+ }
159
+
160
+ /** The constructor */
161
+ function Spinner(o) {
162
+ if (typeof this == 'undefined') return new Spinner(o)
163
+ this.opts = merge(o || {}, Spinner.defaults, defaults)
164
+ }
165
+
166
+ // Global defaults that override the built-ins:
167
+ Spinner.defaults = {}
168
+
169
+ merge(Spinner.prototype, {
170
+
171
+ /**
172
+ * Adds the spinner to the given target element. If this instance is already
173
+ * spinning, it is automatically removed from its previous target b calling
174
+ * stop() internally.
175
+ */
176
+ spin: function(target) {
177
+ this.stop()
178
+
179
+ var self = this
180
+ , o = self.opts
181
+ , el = self.el = css(createEl(0, {className: o.className}), {position: o.position, width: 0, zIndex: o.zIndex})
182
+ , mid = o.radius+o.length+o.width
183
+ , ep // element position
184
+ , tp // target position
185
+
186
+ if (target) {
187
+ target.insertBefore(el, target.firstChild||null)
188
+ tp = pos(target)
189
+ ep = pos(el)
190
+ css(el, {
191
+ left: (o.left == 'auto' ? tp.x-ep.x + (target.offsetWidth >> 1) : parseInt(o.left, 10) + mid) + 'px',
192
+ top: (o.top == 'auto' ? tp.y-ep.y + (target.offsetHeight >> 1) : parseInt(o.top, 10) + mid) + 'px'
193
+ })
194
+ }
195
+
196
+ el.setAttribute('role', 'progressbar')
197
+ self.lines(el, self.opts)
198
+
199
+ if (!useCssAnimations) {
200
+ // No CSS animation support, use setTimeout() instead
201
+ var i = 0
202
+ , start = (o.lines - 1) * (1 - o.direction) / 2
203
+ , alpha
204
+ , fps = o.fps
205
+ , f = fps/o.speed
206
+ , ostep = (1-o.opacity) / (f*o.trail / 100)
207
+ , astep = f/o.lines
208
+
209
+ ;(function anim() {
210
+ i++;
211
+ for (var j = 0; j < o.lines; j++) {
212
+ alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity)
213
+
214
+ self.opacity(el, j * o.direction + start, alpha, o)
215
+ }
216
+ self.timeout = self.el && setTimeout(anim, ~~(1000/fps))
217
+ })()
218
+ }
219
+ return self
220
+ },
221
+
222
+ /**
223
+ * Stops and removes the Spinner.
224
+ */
225
+ stop: function() {
226
+ var el = this.el
227
+ if (el) {
228
+ clearTimeout(this.timeout)
229
+ if (el.parentNode) el.parentNode.removeChild(el)
230
+ this.el = undefined
231
+ }
232
+ return this
233
+ },
234
+
235
+ /**
236
+ * Internal method that draws the individual lines. Will be overwritten
237
+ * in VML fallback mode below.
238
+ */
239
+ lines: function(el, o) {
240
+ var i = 0
241
+ , start = (o.lines - 1) * (1 - o.direction) / 2
242
+ , seg
243
+
244
+ function fill(color, shadow) {
245
+ return css(createEl(), {
246
+ position: 'absolute',
247
+ width: (o.length+o.width) + 'px',
248
+ height: o.width + 'px',
249
+ background: color,
250
+ boxShadow: shadow,
251
+ transformOrigin: 'left',
252
+ transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)',
253
+ borderRadius: (o.corners * o.width>>1) + 'px'
254
+ })
255
+ }
256
+
257
+ for (; i < o.lines; i++) {
258
+ seg = css(createEl(), {
259
+ position: 'absolute',
260
+ top: 1+~(o.width/2) + 'px',
261
+ transform: o.hwaccel ? 'translate3d(0,0,0)' : '',
262
+ opacity: o.opacity,
263
+ animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1/o.speed + 's linear infinite'
264
+ })
265
+
266
+ if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'}))
267
+ ins(el, ins(seg, fill(getColor(o.color, i), '0 0 1px rgba(0,0,0,.1)')))
268
+ }
269
+ return el
270
+ },
271
+
272
+ /**
273
+ * Internal method that adjusts the opacity of a single line.
274
+ * Will be overwritten in VML fallback mode below.
275
+ */
276
+ opacity: function(el, i, val) {
277
+ if (i < el.childNodes.length) el.childNodes[i].style.opacity = val
278
+ }
279
+
280
+ })
281
+
282
+
283
+ function initVML() {
284
+
285
+ /* Utility function to create a VML tag */
286
+ function vml(tag, attr) {
287
+ return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr)
288
+ }
289
+
290
+ // No CSS transforms but VML support, add a CSS rule for VML elements:
291
+ sheet.addRule('.spin-vml', 'behavior:url(#default#VML)')
292
+
293
+ Spinner.prototype.lines = function(el, o) {
294
+ var r = o.length+o.width
295
+ , s = 2*r
296
+
297
+ function grp() {
298
+ return css(
299
+ vml('group', {
300
+ coordsize: s + ' ' + s,
301
+ coordorigin: -r + ' ' + -r
302
+ }),
303
+ { width: s, height: s }
304
+ )
305
+ }
306
+
307
+ var margin = -(o.width+o.length)*2 + 'px'
308
+ , g = css(grp(), {position: 'absolute', top: margin, left: margin})
309
+ , i
310
+
311
+ function seg(i, dx, filter) {
312
+ ins(g,
313
+ ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}),
314
+ ins(css(vml('roundrect', {arcsize: o.corners}), {
315
+ width: r,
316
+ height: o.width,
317
+ left: o.radius,
318
+ top: -o.width>>1,
319
+ filter: filter
320
+ }),
321
+ vml('fill', {color: getColor(o.color, i), opacity: o.opacity}),
322
+ vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change
323
+ )
324
+ )
325
+ )
326
+ }
327
+
328
+ if (o.shadow)
329
+ for (i = 1; i <= o.lines; i++)
330
+ seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)')
331
+
332
+ for (i = 1; i <= o.lines; i++) seg(i)
333
+ return ins(el, g)
334
+ }
335
+
336
+ Spinner.prototype.opacity = function(el, i, val, o) {
337
+ var c = el.firstChild
338
+ o = o.shadow && o.lines || 0
339
+ if (c && i+o < c.childNodes.length) {
340
+ c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild
341
+ if (c) c.opacity = val
342
+ }
343
+ }
344
+ }
345
+
346
+ var probe = css(createEl('group'), {behavior: 'url(#default#VML)'})
347
+
348
+ if (!vendor(probe, 'transform') && probe.adj) initVML()
349
+ else useCssAnimations = vendor(probe, 'animation')
350
+
351
+ return Spinner
352
+
353
+ }));
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brightcontent-attachments
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.33
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Developers at Brightin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-07 00:00:00.000000000 Z
11
+ date: 2014-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: brightcontent-core
@@ -16,110 +16,110 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.33
19
+ version: 2.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.33
26
+ version: 2.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: paperclip
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 3.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 3.0.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: jquery-fileupload-rails
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: sqlite3
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec-rails
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: capybara
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: launchy
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - '>='
101
+ - - ">="
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - '>='
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: factory_girl_rails
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - '>='
115
+ - - ">="
116
116
  - !ruby/object:Gem::Version
117
117
  version: '0'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - '>='
122
+ - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  description: Separate attachment resource for brightcontent
@@ -128,26 +128,30 @@ executables: []
128
128
  extensions: []
129
129
  extra_rdoc_files: []
130
130
  files:
131
- - .gitignore
131
+ - ".gitignore"
132
132
  - Gemfile
133
133
  - README.md
134
134
  - Rakefile
135
135
  - app/assets/javascripts/brightcontent/.gitkeep
136
- - app/assets/javascripts/brightcontent/attachments.js
136
+ - app/assets/javascripts/brightcontent/attachments.js.coffee
137
+ - app/assets/stylesheets/brightcontent/attachments.css.scss
137
138
  - app/controllers/brightcontent/attachments_controller.rb
138
- - app/helpers/brightcontent/attachments_helper.rb
139
139
  - app/models/brightcontent/attachment.rb
140
140
  - app/views/brightcontent/attachments/_attachment.html.erb
141
141
  - app/views/brightcontent/attachments/show.html.erb
142
142
  - app/views/brightcontent/base/_form_field_attachments.html.erb
143
+ - app/views/brightcontent/base/_list_field_attachments.html.erb
143
144
  - brightcontent-attachments.gemspec
145
+ - config/locales/brightcontent_attachements.en.yml
146
+ - config/locales/brightcontent_attachements.nl.yml
144
147
  - config/routes.rb
145
148
  - db/migrate/20121222172513_create_attachments.rb
149
+ - db/migrate/20140203140819_add_position_to_attachments.rb
146
150
  - lib/brightcontent-attachments.rb
147
- - lib/brightcontent/attachable.rb
148
- - lib/brightcontent/attachment_model_extensions.rb
149
151
  - lib/brightcontent/attachments.rb
152
+ - lib/brightcontent/attachments/attachable.rb
150
153
  - lib/brightcontent/attachments/engine.rb
154
+ - lib/brightcontent/attachments/model_extensions.rb
151
155
  - lib/generators/brightcontent/attachments/install_generator.rb
152
156
  - script/rails
153
157
  - spec/dummy/README.rdoc
@@ -208,6 +212,7 @@ files:
208
212
  - spec/models/brightcontent/attachment_spec.rb
209
213
  - spec/spec_helper.rb
210
214
  - spec/support/acceptance_helper.rb
215
+ - vendor/assets/javascripts/spin.js
211
216
  homepage: http://brightin.nl
212
217
  licenses: []
213
218
  metadata: {}
@@ -217,17 +222,17 @@ require_paths:
217
222
  - lib
218
223
  required_ruby_version: !ruby/object:Gem::Requirement
219
224
  requirements:
220
- - - '>='
225
+ - - ">="
221
226
  - !ruby/object:Gem::Version
222
227
  version: '0'
223
228
  required_rubygems_version: !ruby/object:Gem::Requirement
224
229
  requirements:
225
- - - '>='
230
+ - - ">="
226
231
  - !ruby/object:Gem::Version
227
232
  version: '0'
228
233
  requirements: []
229
234
  rubyforge_project:
230
- rubygems_version: 2.0.2
235
+ rubygems_version: 2.2.2
231
236
  signing_key:
232
237
  specification_version: 4
233
238
  summary: Assets resource for brightcontent
@@ -1,14 +0,0 @@
1
- //= require jquery-fileupload/basic
2
-
3
- $(function () {
4
-
5
- var request_url = $("#attachable_url").val();
6
-
7
- $('#fileupload').fileupload({
8
- done: function (e, data) {
9
- $.get(request_url, function(data) {
10
- $('#attachments').html(data);
11
- });
12
- }
13
- });
14
- });
@@ -1,4 +0,0 @@
1
- module Brightcontent
2
- module AttachmentsHelper
3
- end
4
- end
@@ -1,15 +0,0 @@
1
- module Brightcontent
2
- module Attachable
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- add_brightcontent_column :attachments
7
- has_many :attachments, as: :attachable, dependent: :destroy, class_name: Brightcontent::Attachment, :inverse_of => :attachable
8
- end
9
-
10
- def attachment
11
- attachments.first
12
- end
13
-
14
- end
15
- end
@@ -1,13 +0,0 @@
1
- module Brightcontent
2
- module AttachmentModelExtensions
3
- extend ActiveSupport::Concern
4
-
5
- module ClassMethods
6
- attr_reader :attachment_styles
7
- def has_attached_files(styles)
8
- include Brightcontent::Attachable
9
- @attachment_styles = styles
10
- end
11
- end
12
- end
13
- end