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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d4b7e169e9f30ad2bf78cb7f234ff71814200ad6
4
- data.tar.gz: b8a1b2b599f222bf759809834c9e492389629ef0
3
+ metadata.gz: 174763ef00e837f637a02dadcf2c6e732304c911
4
+ data.tar.gz: 543f20a4cfbc0097daa613ec66bf31459191c260
5
5
  SHA512:
6
- metadata.gz: 2ab3ab663652618fbe9bf500def726a1f101bbb695d7a7bd6ba30b57963fe1b4d2f6b3892d974764b850c53c5f9b0c08b6903acdd782a6e8ad3e258372a27c02
7
- data.tar.gz: 45bf708582744d4c012e5b739f921a3c1fc72e4ec2824516dbf9dfa0be08d185956e952f7159f9f25755ca977a343534e3854567be38a598ab8ffaf50e68eb20
6
+ metadata.gz: 5fc324cf2d3df019d8d37501e9af50eb1169d3e88b6ba11c6be97afdfd22377d3dd8391e03fb170d051f852b1116da68ce195de93d22bab1f98edf5986bee561
7
+ data.tar.gz: a57ec77ecc61acebed6512bce22a18c0152fc26b9f488f86daeed88f055e44b4284388644bba3ef7500eeecb7f99469e86f94435a356c627b1f77f99800f4c94
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2013 Mattways
1
+ Copyright 2014 Museways
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,254 @@
1
+ [![Gem Version](https://badge.fury.io/rb/attachs.svg)](http://badge.fury.io/rb/attachs) [![Code Climate](https://codeclimate.com/github/museways/attachs/badges/gpa.svg)](https://codeclimate.com/github/museways/attachs) [![Build Status](https://travis-ci.org/museways/attachs.svg?branch=master)](https://travis-ci.org/museways/attachs)
2
+
3
+ # Attachs
4
+
5
+ Minimalistic toolkit to attach files to records.
6
+
7
+ ## Install
8
+
9
+ Put this line in your Gemfile:
10
+ ```ruby
11
+ gem 'attachs'
12
+ ```
13
+
14
+ Then bundle:
15
+ ```
16
+ $ bundle
17
+ ```
18
+
19
+ NOTE: ImageMagick is needed.
20
+
21
+ ## Configuration
22
+
23
+ Generate the configuration file:
24
+ ```
25
+ rails g attachs:install
26
+ ```
27
+
28
+ The defaults values are:
29
+ ```ruby
30
+ Attachs.configure do |config|
31
+ config.styles = {}
32
+ config.interpolations = {}
33
+ config.global_styles = []
34
+ config.global_convert_options= ''
35
+ config.convert_options = {}
36
+ config.default_storage = :local
37
+ config.default_path = '/:timestamp-:filename'
38
+ config.base_url = ''
39
+ config.s3 = { ssl: false }
40
+ end
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ Add the column to your table:
46
+ ```ruby
47
+ create_table :users do |t|
48
+ t.attachment :avatar
49
+ end
50
+ ```
51
+
52
+ Define the attachment in your model:
53
+ ```ruby
54
+ class User < ActiveRecord::Base
55
+ has_attached_file :avatar
56
+ end
57
+ ```
58
+
59
+ ## Paths
60
+
61
+ To customize the path to some model:
62
+ ```ruby
63
+ class User < ActiveRecord::Base
64
+ has_attached_file :avatar, path: '/:type/:timestamp/:filename'
65
+ end
66
+ ```
67
+
68
+ To create custom interpolations:
69
+ ```ruby
70
+ Attachs.configure do |config|
71
+ config.interpolations = {
72
+ category: -> (attachment) { attachment.record.category }
73
+ }
74
+ end
75
+ ```
76
+
77
+ NOTE: Look into lib/attachs/storages/base.rb to find a list of the system interpolations.
78
+
79
+ ## Styles
80
+
81
+ Define the styles of the attachment:
82
+ ```ruby
83
+ Attachs.configure do |config|
84
+ config.styles = {
85
+ small: '120x120!', # forces the exact size
86
+ medium: '240x240#', # resizes and crops to fill the desire space
87
+ big: '360x360' # resizes to contain the imagen in the desire space
88
+ }
89
+ end
90
+ ```
91
+
92
+ Then reference them in your model:
93
+ ```ruby
94
+ class User < ActiveRecord::Base
95
+ has_attached_file :avatar, styles: [:small, :medium]
96
+ end
97
+ ```
98
+
99
+ To set styles for all attachments:
100
+ ```ruby
101
+ Attachs.configure do |config|
102
+ config.global_styles = [:big]
103
+ end
104
+ ```
105
+
106
+ ## Convert
107
+
108
+ To define global convert options:
109
+ ```ruby
110
+ Attachs.configure do |config|
111
+ config.global_convert_options = '-quality 75 -strip'
112
+ end
113
+ ```
114
+
115
+ To set convert options to some styles only:
116
+ ```ruby
117
+ Attachs.configure do |config|
118
+ config.convert_options = {
119
+ medium: '-trim'
120
+ }
121
+ end
122
+ ```
123
+
124
+ ## Security
125
+
126
+ To make the attachment private:
127
+ ```ruby
128
+ class User < ActiveRecord::Base
129
+ has_attached_file :avatar, private: true
130
+ end
131
+ ```
132
+
133
+ NOTE: Private attachments will be saved into /private folder.
134
+
135
+ ## Validations
136
+
137
+ To validate the presence of the attachment:
138
+ ```ruby
139
+ class User < ActiveRecord::Base
140
+ has_attached_file :avatar
141
+ validates_attachment_presence_of :avatar
142
+ end
143
+ ```
144
+
145
+ To validate the size of the attachment:
146
+ ```ruby
147
+ class User < ActiveRecord::Base
148
+ has_attached_file :avatar
149
+ validates_attachment_size_of :avatar, in: 1..5.megabytes
150
+ end
151
+ ```
152
+
153
+ To validate the content type of the attachment:
154
+ ```ruby
155
+ class User < ActiveRecord::Base
156
+ has_attached_file :avatar
157
+ validates_attachment_content_type_of :avatar, with: /\Aimage/
158
+ # Or using a list
159
+ validates_attachment_content_type_of :avatar, in: %w(image/jpg image/png)
160
+ end
161
+ ```
162
+
163
+ ## I18n
164
+
165
+ To translate the messages the keys are:
166
+ ```
167
+ errors.messages.attachment_presence
168
+ errors.messages.attachment_size_in
169
+ errors.messages.attachment_size_less_than
170
+ errors.messages.attachment_size_greater_than
171
+ errors.messages.attachment_content_type_with
172
+ errors.messages.attachment_content_type_in
173
+ ```
174
+
175
+ NOTE: Look into lib/attachs/locales yamls.
176
+
177
+ ## Forms
178
+
179
+ Your forms continue to work the same:
180
+ ```erb
181
+ <%= form_for @user do |f| %>
182
+ <%= f.file_field :avatar %>
183
+ <% end %>
184
+ ```
185
+
186
+ ## Urls
187
+
188
+ The url method points to the original file:
189
+ ```erb
190
+ <%= image_tag user.avatar.url %>
191
+ ```
192
+
193
+ To point to some particular style:
194
+ ```erb
195
+ <%= image_tag user.avatar.url(:small) %>
196
+ ```
197
+
198
+ The defauft url is used when there is no upload:
199
+ ```ruby
200
+ class User < ActiveRecord::Base
201
+ has_attached_file :avatar, default_url: '/missing.png'
202
+ end
203
+ ```
204
+
205
+ NOTE: If storage is s3 you can pass ssl: true to force https.
206
+
207
+ ## Storage
208
+
209
+ To override the storage in the model:
210
+ ```ruby
211
+ class User < ActiveRecord::Base
212
+ has_attached_file :avatar, storage: :s3
213
+ end
214
+ ```
215
+
216
+ To configure the s3 credentials:
217
+ ```ruby
218
+ Attachs.configure do |config|
219
+ config.s3 = {
220
+ bucket: 'xxx',
221
+ access_key_id: 'xxx',
222
+ secret_access_key: 'xxx'
223
+ }
224
+ end
225
+ ```
226
+
227
+ ## CDN
228
+
229
+ To configure a cdn:
230
+ ```ruby
231
+ Attachs.configure do |config|
232
+ config.base_url = 'http://cdn.example.com'
233
+ end
234
+ ```
235
+
236
+ ## Tasks
237
+
238
+ To refresh all the styles of some attachment:
239
+ ```
240
+ bundle exec rake attachs:refresh:all CLASS=User ATTACHMENT=avatar
241
+ ```
242
+
243
+ To refresh missing styles of some attachment:
244
+ ```
245
+ bundle exec rake attachs:refresh:missing CLASS=User ATTACHMENT=avatar
246
+ ```
247
+
248
+ ## Credits
249
+
250
+ This gem is maintained and funded by [museways](http://museways.com).
251
+
252
+ ## License
253
+
254
+ It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
data/Rakefile CHANGED
@@ -14,8 +14,6 @@ RDoc::Task.new(:rdoc) do |rdoc|
14
14
  rdoc.rdoc_files.include('lib/**/*.rb')
15
15
  end
16
16
 
17
- APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
- load 'rails/tasks/engine.rake'
19
17
 
20
18
 
21
19
 
data/lib/attachs.rb CHANGED
@@ -1,16 +1,43 @@
1
- require 'attachs/active_record/base'
2
- require 'attachs/magick/image'
1
+ require 'open3'
2
+ require 'attachs/attachment'
3
+ require 'attachs/tools/magick'
4
+ require 'attachs/storages/base'
3
5
  require 'attachs/storages/local'
4
6
  require 'attachs/storages/s3'
5
- require 'attachs/types/file'
6
- require 'attachs/types/image'
7
- require 'attachs/validators/base'
8
- require 'attachs/validators/attachment_content_type_validator'
9
- require 'attachs/validators/attachment_presence_validator'
10
- require 'attachs/validators/attachment_size_validator'
11
- require 'attachs/engine'
7
+ require 'attachs/types/base'
8
+ require 'attachs/types/default'
9
+ require 'attachs/types/regular'
10
+ require 'attachs/active_record/base'
11
+ require 'attachs/active_record/connection_adapters'
12
+ require 'attachs/active_record/migration'
13
+ require 'attachs/active_record/validators'
14
+ require 'attachs/active_record/validators/attachment_content_type_validator'
15
+ require 'attachs/active_record/validators/attachment_presence_validator'
16
+ require 'attachs/active_record/validators/attachment_size_validator'
12
17
  require 'attachs/railtie'
13
- require 'attachs/version'
14
18
 
15
19
  module Attachs
20
+ class << self
21
+
22
+ def configure
23
+ yield config
24
+ end
25
+
26
+ def config
27
+ @config ||= begin
28
+ ActiveSupport::OrderedOptions.new.tap do |config|
29
+ config.styles = {}
30
+ config.interpolations = {}
31
+ config.global_styles = []
32
+ config.global_convert_options= ''
33
+ config.convert_options = {}
34
+ config.default_storage = :local
35
+ config.default_path = '/:timestamp-:filename'
36
+ config.base_url = ''
37
+ config.s3 = { ssl: false }
38
+ end
39
+ end
40
+ end
41
+
42
+ end
16
43
  end
@@ -5,136 +5,99 @@ module Attachs
5
5
 
6
6
  protected
7
7
 
8
- attr_reader :stored_attachments, :deleted_attachments
9
-
10
8
  def attachments
11
- @attachments ||= {}
12
- end
13
-
14
- def add_attachment(attr, value, options)
15
- attachment = build_attachment_instance(value, options)
16
- attachments[attr] = attachment
17
- write_attribute attr, attachment.filename
9
+ @attachments ||= {}.tap do |instances|
10
+ self.class.attachments.each do |attr, options|
11
+ instances[attr] = Attachs::Attachment.new(self, attr, options)
12
+ end
13
+ end
18
14
  end
19
15
 
20
- def get_attachment(attr, options)
21
- return attachments[attr] if attachments.has_key? attr
22
- return nil unless read_attribute(attr) or options.has_key? :default
23
- attachments[attr] = build_attachment_instance(read_attribute(attr), options)
16
+ def queued_attachments
17
+ @queued_attachments ||= { process: {}, destroy: {} }
24
18
  end
25
19
 
26
- def remove_attachment(attr)
27
- write_attribute attr, nil
20
+ def queue_attachment(attr, options, value)
21
+ unless queued_attachments[:destroy].has_key? attr
22
+ queued_attachments[:destroy][attr] = attachments[attr]
23
+ end
24
+ if queued_attachments[:process].has_key? attr and value.nil?
25
+ attachments[attr]
26
+ else
27
+ queued_attachments[:process][attr] = Attachs::Attachment.new(self, attr, options, value)
28
+ end
28
29
  end
29
30
 
30
- def build_attachment_instance(source, options)
31
- klass = options.has_key?(:type) ? options[:type].to_s.classify : 'File'
32
- Attachs::Types.const_get(klass).new source, options
33
- end
34
-
35
- def check_changed_attachments
36
- @stored_attachments = []
37
- @deleted_attachments = []
31
+ def commit_attachments
38
32
  self.class.attachments.each do |attr, options|
39
- if changed_attributes.has_key? attr.to_s
40
- stored = attributes[attr.to_s]
41
- deleted = changed_attributes[attr.to_s]
42
- add_changed_attachment stored, options, :stored if stored.present?
43
- add_changed_attachment deleted, options, :deleted if deleted.present?
44
- end
33
+ send "destroy_#{attr}=", nil
45
34
  end
46
- end
47
-
48
- def iterate_attachments
49
- self.class.attachments.each do |attr, options|
50
- next unless instance = send(attr)
51
- yield instance
35
+ queued_attachments[:destroy].each do |attr, attachment|
36
+ attachment.destroy
52
37
  end
53
- end
54
-
55
- def add_changed_attachment(source, options, type)
56
- (type == :stored ? stored_attachments : deleted_attachments) << (source.is_a?(String) ? build_attachment_instance(source, options) : source)
57
- end
58
-
59
- def store_attachments
60
- iterate_attachments { |a| a.store }
38
+ queued_attachments[:process].each do |attr, attachment|
39
+ attachment.process
40
+ end
41
+ queued_attachments[:destroy] = {}
42
+ queued_attachments[:process] = {}
61
43
  end
62
44
 
63
- def delete_attachments
64
- iterate_attachments { |a| a.delete }
65
- end
66
-
67
- def remove_stored_attachments
68
- stored_attachments.each { |a| a.delete }
45
+ def destroy_attachments
46
+ self.class.attachments.each do |attr, options|
47
+ send(attr).destroy
48
+ end
69
49
  end
70
-
71
- def remove_deleted_attachments
72
- deleted_attachments.each { |a| a.delete }
73
- end
74
50
 
75
51
  module ClassMethods
76
52
 
77
- [:file, :image].each do |type|
78
- name = :"has_attached_#{type}"
79
- define_method name do |*args|
80
- options = args.extract_options!
81
- options[:type] = type
82
- define_attachment *args.map(&:to_sym).append(options)
53
+ def has_attached_file(*args)
54
+ options = args.extract_options!
55
+ unless attachable?
56
+ make_attachable
57
+ end
58
+ args.each do |attr|
59
+ define_attachment_methods attr, options
60
+ attachments[attr] = options
83
61
  end
84
- alias_method :"attached_#{type}", name
85
62
  end
86
63
 
87
- attr_reader :attachments
88
-
89
- def inherited(subclass)
90
- subclass.instance_variable_set :@attachments, @attachments
91
- super
64
+ def attachments
65
+ @attachments ||= {}
92
66
  end
93
67
 
94
68
  def attachable?
95
- attachments.present?
69
+ attachments.any?
96
70
  end
97
71
 
98
- protected
99
-
100
- def define_attachment(*args)
101
- options = args.extract_options!
102
- make_attachable unless attachable?
103
- args.each do |attr|
104
- define_attachment_attribute_methods attr, options
105
- define_attachment_default_validations attr, options
106
- attachments[attr] = options
107
- end
72
+ def inherited(subclass)
73
+ subclass.instance_variable_set :@attachments, @attachments
74
+ super
108
75
  end
109
76
 
77
+ protected
78
+
110
79
  def make_attachable
111
- before_save :store_attachments, :check_changed_attachments
112
- after_save :remove_deleted_attachments
113
- before_destroy :delete_attachments
114
- after_rollback :remove_stored_attachments
115
- @attachments = {}
80
+ after_save :commit_attachments
81
+ after_destroy :destroy_attachments
116
82
  end
117
83
 
118
- def define_attachment_attribute_methods(attr, options)
84
+ def define_attachment_methods(attr, options)
119
85
  define_method "#{attr}=" do |value|
120
- add_attachment attr, value, options if value.is_a? ActionDispatch::Http::UploadedFile or value.is_a? Rack::Test::UploadedFile
86
+ attachments[attr] = queue_attachment(attr, options, value)
121
87
  end
122
88
  define_method attr do
123
- get_attachment attr, options
124
- end
125
- define_method "delete_#{attr}=" do |value|
126
- remove_attachment attr if value == '1'
127
- instance_variable_set :"@delete_#{attr}", value
89
+ attachments[attr]
90
+ end
91
+ attr_reader "destroy_#{attr}"
92
+ define_method "destroy_#{attr}=" do |value|
93
+ instance_variable_set "@destroy_#{attr}", value
94
+ if [1, '1', true].include?(value) and !send(attr).upload?
95
+ send "#{attr}=", nil
96
+ end
128
97
  end
129
- attr_reader :"delete_#{attr}"
130
- end
131
-
132
- def define_attachment_default_validations(attr, options)
133
- default_validations = Rails.application.config.attachs.default_validations[options[:type]]
134
- validates attr, default_validations.dup if default_validations.present?
135
98
  end
136
99
 
137
- end
100
+ end
138
101
  end
139
102
  end
140
103
  end