attachs 0.3.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +254 -0
  4. data/Rakefile +0 -2
  5. data/lib/attachs.rb +37 -10
  6. data/lib/attachs/active_record/base.rb +58 -95
  7. data/lib/attachs/active_record/connection_adapters.rb +40 -0
  8. data/lib/attachs/active_record/migration.rb +17 -0
  9. data/lib/attachs/active_record/validators.rb +7 -0
  10. data/lib/attachs/active_record/validators/attachment_content_type_validator.rb +30 -0
  11. data/lib/attachs/active_record/validators/attachment_presence_validator.rb +22 -0
  12. data/lib/attachs/active_record/validators/attachment_size_validator.rb +39 -0
  13. data/lib/attachs/attachment.rb +114 -0
  14. data/{config → lib/attachs}/locales/en.yml +2 -1
  15. data/{config → lib/attachs}/locales/es.yml +2 -1
  16. data/lib/attachs/railtie.rb +31 -11
  17. data/lib/attachs/storages/base.rb +68 -0
  18. data/lib/attachs/storages/local.rb +61 -37
  19. data/lib/attachs/storages/s3.rb +66 -57
  20. data/lib/attachs/tools/magick.rb +52 -0
  21. data/lib/attachs/types/base.rb +22 -0
  22. data/lib/attachs/types/default.rb +21 -0
  23. data/lib/attachs/types/regular.rb +21 -0
  24. data/lib/attachs/version.rb +1 -1
  25. data/lib/generators/attachs/install_generator.rb +13 -0
  26. data/lib/generators/attachs/templates/attachs.rb +2 -0
  27. data/lib/tasks/attachs.rake +30 -0
  28. data/test/attachment_test.rb +19 -0
  29. data/test/dummy/app/models/medium.rb +5 -0
  30. data/test/dummy/app/models/picture.rb +2 -0
  31. data/test/dummy/config/application.rb +1 -7
  32. data/test/dummy/config/initializers/attachs.rb +25 -0
  33. data/test/dummy/config/initializers/secret_token.rb +1 -0
  34. data/test/dummy/config/routes.rb +0 -1
  35. data/test/dummy/db/migrate/20140808012639_create_media.rb +11 -0
  36. data/test/dummy/db/schema.rb +14 -25
  37. data/test/dummy/log/development.log +9 -27
  38. data/test/dummy/log/test.log +52130 -11207
  39. data/test/dummy/test/fixtures/180x150.gif +0 -0
  40. data/test/generator_test.rb +18 -0
  41. data/test/local_storage_test.rb +57 -0
  42. data/test/magick_tool_test.rb +42 -0
  43. data/test/migration_test.rb +65 -0
  44. data/test/s3_storage_tes.rb +56 -0
  45. data/test/tasks_test.rb +61 -0
  46. data/test/test_helper.rb +17 -17
  47. data/test/validators_test.rb +100 -0
  48. metadata +85 -120
  49. data/README.rdoc +0 -104
  50. data/app/controllers/attachs/presets_controller.rb +0 -17
  51. data/config/routes.rb +0 -5
  52. data/lib/attachs/engine.rb +0 -7
  53. data/lib/attachs/magick/image.rb +0 -85
  54. data/lib/attachs/types/file.rb +0 -131
  55. data/lib/attachs/types/image.rb +0 -58
  56. data/lib/attachs/validators/attachment_content_type_validator.rb +0 -11
  57. data/lib/attachs/validators/attachment_presence_validator.rb +0 -9
  58. data/lib/attachs/validators/attachment_size_validator.rb +0 -21
  59. data/lib/attachs/validators/base.rb +0 -13
  60. data/lib/generators/attachs/s3/config_generator.rb +0 -13
  61. data/lib/generators/attachs/s3/templates/s3.yml +0 -16
  62. data/lib/tasks/attachs_tasks.rake +0 -71
  63. data/test/dummy/app/models/all_attached.rb +0 -16
  64. data/test/dummy/app/models/file_attached.rb +0 -3
  65. data/test/dummy/app/models/image_attached.rb +0 -3
  66. data/test/dummy/app/models/private_file_attached.rb +0 -3
  67. data/test/dummy/app/models/private_image_attached.rb +0 -3
  68. data/test/dummy/db/migrate/20130820222342_create_file_attacheds.rb +0 -9
  69. data/test/dummy/db/migrate/20130820222355_create_image_attacheds.rb +0 -9
  70. data/test/dummy/db/migrate/20130820222534_create_all_attacheds.rb +0 -19
  71. data/test/dummy/public/uploads/files/file.txt +0 -1
  72. data/test/dummy/public/uploads/images/big/image.jpg +0 -0
  73. data/test/dummy/public/uploads/images/original/image.jpg +0 -0
  74. data/test/dummy/public/uploads/images/small/image.jpg +0 -0
  75. data/test/dummy/test/fixtures/file_big.txt +0 -1
  76. data/test/dummy/test/fixtures/file_empty.txt +0 -1
  77. data/test/dummy/test/fixtures/image.jpg +0 -0
  78. data/test/dummy/test/fixtures/image_big.jpg +0 -0
  79. data/test/dummy/test/fixtures/image_empty.jpg +0 -0
  80. data/test/dummy/tmp/uploads/files/13898788260004868.txt +0 -1
  81. data/test/local_file_record_test.rb +0 -31
  82. data/test/local_file_string_test.rb +0 -23
  83. data/test/local_file_upload_test.rb +0 -30
  84. data/test/local_generate_test.rb +0 -25
  85. data/test/local_image_record_test.rb +0 -25
  86. data/test/local_image_string_test.rb +0 -34
  87. data/test/local_image_upload_test.rb +0 -27
  88. data/test/local_records_test.rb +0 -52
  89. data/test/local_validators_test.rb +0 -71
  90. data/test/s3_file_record_tes.rb +0 -21
  91. data/test/s3_file_string_tes.rb +0 -24
  92. data/test/s3_file_upload_tes.rb +0 -31
  93. data/test/s3_image_record_tes.rb +0 -26
  94. data/test/s3_image_string_tes.rb +0 -31
  95. data/test/s3_image_upload_tes.rb +0 -28
  96. data/test/s3_records_tes.rb +0 -56
  97. data/test/s3_validators_tes.rb +0 -72
@@ -1,86 +1,95 @@
1
1
  module Attachs
2
2
  module Storages
3
- class S3
4
-
5
- cattr_accessor :config
3
+ class S3 < Base
6
4
 
7
- def initialize(default, private)
8
- if (Rails.env.test? and !default)
9
- env = 'test'
10
- elsif default
11
- env = 'production'
12
- else
13
- env = Rails.env
5
+ def url(*args)
6
+ if attachment.url?
7
+ options = args.extract_options!
8
+ style = (args[0] || :original)
9
+ if Attachs.config.base_url.present?
10
+ Pathname.new(Attachs.config.base_url, path(style)).to_s
11
+ else
12
+ if options[:ssl].present?
13
+ secure = options[:ssl]
14
+ elsif attachment.options[:ssl].present?
15
+ secure = attachment.options[:ssl]
16
+ else
17
+ secure = Attachs.config.s3[:ssl]
18
+ end
19
+ object(style).public_url(secure: secure).to_s
20
+ end
14
21
  end
15
- AWS.config access_key_id: config[env]['access_key_id'], secret_access_key: config[env]['secret_access_key']
16
- @bucket = AWS::S3.new.buckets[config[env]['bucket']]
17
- @private = private
18
- end
19
-
20
- def exists?(path)
21
- object(path).exists?
22
22
  end
23
23
 
24
- def size(path)
25
- object(path).content_length
24
+ def process(force=false)
25
+ if attachment.upload?
26
+ stream attachment.upload, path
27
+ attachment.uploaded!
28
+ end
29
+ process_styles force
26
30
  end
27
31
 
28
- def url(path)
29
- base_url.present? ? ::File.join(base_url, path) : object(path).public_url(secure: false)
32
+ def process_styles(force=false)
33
+ if attachment.image?
34
+ unless cache[path]
35
+ download = Tempfile.new('s3')
36
+ object.read do |chunk|
37
+ download.write chunk
38
+ end
39
+ cache[path] = download
40
+ end
41
+ attachment.styles.each do |style|
42
+ if force == true
43
+ object(style).delete
44
+ end
45
+ unless object(style).exists?
46
+ tmp = Tempfile.new('s3')
47
+ resize cache[path].path, style, tmp.path
48
+ stream tmp, path(style)
49
+ end
50
+ end
51
+ end
30
52
  end
31
53
 
32
- def store(upload, path)
33
- bucket.objects.create(path, Pathname.new(upload.path)).acl = private? ? :private : :public_read
34
- end
35
-
36
- def delete(path)
37
- object(path).delete
54
+ def destroy
55
+ object.delete
56
+ destroy_styles
38
57
  end
39
58
 
40
- def magick(source, output, upload)
41
- if cache[source].blank?
42
- if upload.present?
43
- cache[source] = upload.path
44
- else
45
- remote = create_tmp
46
- remote.binmode
47
- object(source).read { |chunk| remote.write(chunk) }
48
- remote.close
49
- remote.open
50
- cache[source] = remote.path
59
+ def destroy_styles
60
+ if attachment.image?
61
+ attachment.styles.each do |style|
62
+ object(style).delete
51
63
  end
52
64
  end
53
- tmp = create_tmp
54
- yield Attachs::Magick::Image.new(cache[source], tmp.path)
55
- store tmp, output
56
65
  end
57
66
 
58
67
  protected
59
68
 
60
- attr_reader :bucket
61
-
62
- def config
63
- self.class.config
69
+ def move(origin, destination)
70
+ bucket.objects[origin].move_to(destination)
64
71
  end
65
72
 
66
- def base_url
67
- Rails.application.config.attachs.base_url
73
+ def cache
74
+ @cache ||= {}
68
75
  end
69
76
 
70
- def private?
71
- @private
72
- end
73
-
74
- def create_tmp
75
- Tempfile.new 's3'
77
+ def bucket
78
+ @bucket ||= AWS::S3.new.buckets[Attachs.config.s3[:bucket]]
76
79
  end
77
80
 
78
- def cache
79
- @cache ||= {}
81
+ def object(style=:original)
82
+ bucket.objects[path(style)]
80
83
  end
81
84
 
82
- def object(path)
83
- bucket.objects[path]
85
+ def stream(file, path)
86
+ object = bucket.objects.create(path, File.open(file.path, 'rb'))
87
+ if attachment.private?
88
+ object.acl = :private
89
+ else
90
+ object.acl = :public_read
91
+ end
92
+ cache[path] = file
84
93
  end
85
94
 
86
95
  end
@@ -0,0 +1,52 @@
1
+ module Attachs
2
+ module Tools
3
+ module Magick
4
+ class << self
5
+
6
+ def dimensions(source)
7
+ if output = run("identify -format %wx%h '#{source}'")
8
+ output.split('x').map(&:to_i)
9
+ end
10
+ end
11
+
12
+ def resize(source, style, destination=nil)
13
+ size = Attachs.config.styles[style]
14
+ new_width, new_height = size.scan(/[^x]+/).map(&:to_i)
15
+ case strategy = size[/!|#/]
16
+ when '#'
17
+ width, height = dimensions(source)
18
+ ratio = [new_width.to_f/width, new_height.to_f/height].max
19
+ options = "-resize #{(ratio*width).ceil}x#{(ratio*height).ceil} -gravity center -crop #{new_width}x#{new_height}+0+0"
20
+ when '!'
21
+ options = "-resize #{new_width}x#{new_height}\!"
22
+ else
23
+ options = "-resize #{new_width}x#{new_height}"
24
+ end
25
+ if global_options = Attachs.config.global_convert_options
26
+ options << " #{global_options}"
27
+ end
28
+ if style_options = Attachs.config.convert_options[style]
29
+ options << " #{style_options}"
30
+ end
31
+ if destination
32
+ run "convert '#{source}' #{options} '#{destination}'"
33
+ else
34
+ run "mogrify #{options} '#{source}'"
35
+ end
36
+ end
37
+
38
+ protected
39
+
40
+ def run(cmd)
41
+ stdout, stderr, status = Open3.capture3(cmd)
42
+ if status.success?
43
+ stdout.strip
44
+ else
45
+ false
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,22 @@
1
+ module Attachs
2
+ module Types
3
+ class Base
4
+
5
+ def initialize(attachment)
6
+ @attachment = attachment
7
+ end
8
+
9
+ protected
10
+
11
+ attr_reader :attachment
12
+
13
+ def storage
14
+ @storage ||= begin
15
+ klass = (attachment.options[:storage] || Attachs.config.default_storage).to_s.classify
16
+ Attachs::Storages.const_get(klass).new(attachment)
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ module Attachs
2
+ module Types
3
+ class Default < Base
4
+
5
+ %w(basename extension process process_styles destroy destroy_styles update).each do |name|
6
+ define_method(name) {}
7
+ end
8
+
9
+ def image?
10
+ false
11
+ end
12
+
13
+ def url(*args)
14
+ if attachment.default?
15
+ storage.url *args
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Attachs
2
+ module Types
3
+ class Regular < Base
4
+
5
+ delegate :url, :process, :process_styles, :destroy, :destroy_styles, :update, to: :storage
6
+
7
+ def basename
8
+ @basename ||= File.basename(attachment.filename, ".#{extension}")
9
+ end
10
+
11
+ def extension
12
+ @extension ||= File.extname(attachment.filename).from(1)
13
+ end
14
+
15
+ def image?
16
+ @image ||= attachment.content_type.start_with?('image')
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -1,5 +1,5 @@
1
1
  module Attachs
2
2
 
3
- VERSION = '0.3.5'
3
+ VERSION = '0.4.0'
4
4
 
5
5
  end
@@ -0,0 +1,13 @@
1
+ require 'rails/generators'
2
+
3
+ module Attachs
4
+ class InstallGenerator < Rails::Generators::Base
5
+
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+ def create_initializer
9
+ copy_file 'attachs.rb', 'config/initializers/attachs.rb'
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,2 @@
1
+ Attachs.configure do |config|
2
+ end
@@ -0,0 +1,30 @@
1
+ module Attachs
2
+ module Task
3
+ def self.process(force)
4
+ model = ENV['CLASS'].classify.constantize
5
+ attachment = ENV['ATTACHMENT'].to_sym
6
+ model.find_each do |record|
7
+ model.attachments.each do |attr, options|
8
+ if attr == attachment
9
+ record.send(attr).process(force)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ namespace :attachs do
18
+ namespace :refresh do
19
+ desc 'Refreshs all styles.'
20
+ task all: :environment do
21
+ Attachs::Task.process true
22
+ Rails.logger.info 'All styles regenerated successfully.'
23
+ end
24
+ desc 'Refreshs missing styles.'
25
+ task missing: :environment do
26
+ Attachs::Task.process false
27
+ Rails.logger.info 'Missing styles regenerated successfully.'
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ require 'test_helper'
2
+
3
+ class AttachmentTest < ActiveSupport::TestCase
4
+
5
+ test 'attachment source' do
6
+ original_medium = Medium.create(attach: file_upload)
7
+ copied_medium = Medium.create(attach: original_medium.attach)
8
+ %w(filename content_type size updated_at).each do |attr|
9
+ assert_equal original_medium.attach.send(attr), copied_medium.attach.send(attr)
10
+ end
11
+ end
12
+
13
+ test 'uri source' do
14
+ medium = Medium.create(attach: URI('https://s3.amazonaws.com/attachs-test/file.txt'))
15
+ file_path = Rails.root.join('public/original/file.txt')
16
+ assert File.exist?(file_path)
17
+ end
18
+
19
+ end
@@ -0,0 +1,5 @@
1
+ class Medium < ActiveRecord::Base
2
+ has_attached_file :attach, path: '/:style/:filename'
3
+ has_attached_file :local_attach, storage: 'local', path: '/storage/:type/:size/:style/:month/:basename.:extension', styles: [:small]
4
+ has_attached_file :s3_attach, storage: 's3', path: '/storage/:type/:size/:style/:month/:basename.:extension', styles: [:small]
5
+ end
@@ -0,0 +1,2 @@
1
+ class Picture < ActiveRecord::Base
2
+ end
@@ -18,13 +18,7 @@ module Dummy
18
18
  # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
19
19
  # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
20
20
  # config.i18n.default_locale = :de
21
- I18n.enforce_available_locales = false
22
-
23
- config.attachs.default_presets = :small
24
- config.attachs.presets = {
25
- small: { method: :fit, width: 120, height: 120 },
26
- big: { method: :fill, width: 1024, height: 768 }
27
- }
21
+ I18n.enforce_available_locales = false
28
22
  end
29
23
  end
30
24
 
@@ -0,0 +1,25 @@
1
+ Attachs.configure do |config|
2
+
3
+ config.global_styles = [:small, :medium, :big]
4
+ config.global_convert_options = '-quality 75'
5
+ config.styles = {
6
+ small: '120x120!',
7
+ medium: '300x250#',
8
+ big: '1024x768',
9
+ big_contain: '160x130',
10
+ small_contain: '140x110',
11
+ big_cover: '160x130#',
12
+ small_cover: '140x110#',
13
+ big_force: '160x130!',
14
+ small_force: '140x110!'
15
+ }
16
+ config.convert_options = {
17
+ big_contain: '-trim',
18
+ big_cover: '-trim',
19
+ big_force: '-trim'
20
+ }
21
+ config.interpolations = {
22
+ month: lambda { |attachment| attachment.updated_at.month }
23
+ }
24
+
25
+ end
@@ -9,4 +9,5 @@
9
9
 
10
10
  # Make sure your secret_key_base is kept private
11
11
  # if you're sharing your code publicly.
12
+ Dummy::Application.config.secret_token = '325198aee80c035ddba090e3307afe68ef17c12a25b60f987d673febf4164119cb1ce3772aa21fb6b16ede3b9362c0030f7d934f31f1f01f96dd61f289bc6f8c'
12
13
  Dummy::Application.config.secret_key_base = '325198aee80c035ddba090e3307afe68ef17c12a25b60f987d673febf4164119cb1ce3772aa21fb6b16ede3b9362c0030f7d934f31f1f01f96dd61f289bc6f8c'
@@ -1,3 +1,2 @@
1
1
  Rails.application.routes.draw do
2
- mount Attachs::Engine => '/'
3
2
  end
@@ -0,0 +1,11 @@
1
+ class CreateMedia < ActiveRecord::Migration
2
+ def change
3
+ create_table :media do |t|
4
+ t.attachment :attach
5
+ t.attachment :local_attach
6
+ t.attachment :s3_attach
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -11,32 +11,21 @@
11
11
  #
12
12
  # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(version: 20130820222534) do
14
+ ActiveRecord::Schema.define(version: 20140808012639) do
15
15
 
16
- create_table "all_attacheds", force: true do |t|
17
- t.string "image"
18
- t.string "file_presence"
19
- t.string "file_content_type"
20
- t.string "file_size"
21
- t.string "file_all"
22
- t.string "file_default"
23
- t.string "image_presence"
24
- t.string "image_content_type"
25
- t.string "image_size"
26
- t.string "image_all"
27
- t.string "image_default"
28
- t.datetime "created_at"
29
- t.datetime "updated_at"
30
- end
31
-
32
- create_table "file_attacheds", force: true do |t|
33
- t.string "file"
34
- t.datetime "created_at"
35
- t.datetime "updated_at"
36
- end
37
-
38
- create_table "image_attacheds", force: true do |t|
39
- t.string "image"
16
+ create_table "media", force: true do |t|
17
+ t.string "attach_filename"
18
+ t.string "attach_content_type"
19
+ t.integer "attach_size"
20
+ t.datetime "attach_updated_at"
21
+ t.string "local_attach_filename"
22
+ t.string "local_attach_content_type"
23
+ t.integer "local_attach_size"
24
+ t.datetime "local_attach_updated_at"
25
+ t.string "s3_attach_filename"
26
+ t.string "s3_attach_content_type"
27
+ t.integer "s3_attach_size"
28
+ t.datetime "s3_attach_updated_at"
40
29
  t.datetime "created_at"
41
30
  t.datetime "updated_at"
42
31
  end