has_media 0.2.5 → 0.2.6

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.
data/README.rdoc CHANGED
@@ -90,6 +90,9 @@ Configuration take place in config/initializers/has_media.rb
90
90
  require uploader
91
91
  end
92
92
 
93
+ # Change CarrierWave root path
94
+ CarrierWave.root = HasMedia.directory_path
95
+
93
96
  == Routes
94
97
 
95
98
  Add routes for media in your config/routes.rb
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.5
1
+ 0.2.6
@@ -1,5 +1,15 @@
1
1
  module HasMediaHelper
2
2
 
3
+ ##
4
+ # Generate a unique uid used in forms
5
+ #
6
+ # @param [Hash] available keys :
7
+ # - context: the context to link medium
8
+ # - object : the medium related object
9
+ # - medium : the medium object
10
+ #
11
+ # @return [String]
12
+ #
3
13
  def generate_uid(*opts)
4
14
  opts = opts.first
5
15
  object = opts[:object]||nil
@@ -16,21 +26,61 @@ module HasMediaHelper
16
26
  end
17
27
  end
18
28
 
29
+ ##
30
+ # Create a link to add medium file field with ajax
31
+ #
32
+ # @params [Hash] available keys :
33
+ # - object : the meduim related object (mandatory)
34
+ # - context: the context to link medium (mandatory)
35
+ # - text: link test label (optional)
36
+ #
37
+ # @return [String]
38
+ #
19
39
  def add_medium_link(opts)
40
+ unless opts.keys.include?(:object) ||
41
+ opts.keys.include?(:context)
42
+ raise "Must give object and context"
43
+ end
44
+ klass = opts[:object].class.to_s.underscore
45
+ opts[:text]||= I18n.t('add_link',
46
+ :medium_label => I18n.t(opts[:context], :scope => [:activerecord, :attributes, klass]),
47
+ :scope => [:has_media, :form])
20
48
  link_to_function opts[:text] do |page|
21
- page.insert_html :bottom, generate_uid(:object => opts[:object], :context => opts[:context]),
22
- :partial => 'has_media/medium_field',
23
- :locals => {
24
- :object => opts[:object],
25
- :context => opts[:context]
26
- }
49
+ page.insert_html :bottom, generate_uid(
50
+ :object => opts[:object],
51
+ :context => opts[:context]
52
+ ),
53
+ :partial => 'has_media/medium_field',
54
+ :locals => {
55
+ :object => opts[:object],
56
+ :context => opts[:context]
57
+ }
27
58
  end
28
59
  end
29
60
 
61
+ ##
62
+ # Create a remove medium link using Ajax
63
+ # Compatible with jQuery and Prototype
64
+ #
65
+ # @param [Hash] available keys :
66
+ # - medium : the medium to destroy
67
+ # - text : link text label (optional)
68
+ #
69
+ # @return [String]
70
+ #
30
71
  def remove_medium_link(opts)
72
+ opts[:text] ||= I18n.t('remove_link', :scope => [:has_media, :form])
31
73
  link_to opts[:text], medium_url(opts[:medium]), :remote => true, :method => :delete
32
74
  end
33
75
 
76
+ ##
77
+ # Create a file field for a medium
78
+ #
79
+ # @param [Medium] model, A medium object
80
+ # @param [String] context, the context to link medium
81
+ #
82
+ # @return [String]
83
+ #
34
84
  def has_media_field(model, context)
35
85
  render :partial => "has_media/media_fields", :locals => {
36
86
  :object => model,
@@ -8,8 +8,7 @@
8
8
  <%= link_to medium.original_file_uri, medium.original_file_uri %>
9
9
 
10
10
  <%- if object.send(context).is_a?(Array) and !readonly %>
11
- <%= remove_medium_link :text => "Remove",
12
- :object => object,
11
+ <%= remove_medium_link :object => object,
13
12
  :context => context,
14
13
  :medium => medium %>
15
14
  <%- end %>
@@ -14,9 +14,7 @@
14
14
  </div>
15
15
  <%- if object.send(context).is_a?(Array) %>
16
16
  <p>
17
- <%= add_medium_link :text => "Add #{context}",
18
- :object => object,
19
- :context => context %>
17
+ <%= add_medium_link :object => object, :context => context %>
20
18
  </p>
21
19
  <%- end %>
22
20
 
@@ -1,12 +1,14 @@
1
1
  <%- klass = object.class.to_s.underscore %>
2
2
  <%- if object.send(context).is_a?(Array) %>
3
3
  <p>
4
- <%= label_tag "#{klass}[#{context}][]" %>
4
+ <%= label_tag "#{klass}[#{context}][]",
5
+ t(context, :scope => [:activerecord, :attributes, klass]) %>
5
6
  <%= file_field_tag "#{klass}[#{context}][]" %>
6
7
  </p>
7
8
  <%- else %>
8
9
  <p>
9
- <%= label_tag "#{klass}[#{context}]" %>
10
+ <%= label_tag "#{klass}[#{context}]",
11
+ t(context, :scope => [:activerecord, :attributes, klass]) %>
10
12
  <%= file_field_tag "#{klass}[#{context}]" %>
11
13
  </p>
12
14
  <%- end %>
@@ -1 +1,5 @@
1
- $("#<%= generate_uid(:medium => @medium ) %>").remove();
1
+ if (typeof jQuery == 'undefined') {
2
+ $("<%= generate_uid(:medium => @medium ) %>").remove();
3
+ } else {
4
+ $("#<%= generate_uid(:medium => @medium ) %>").remove();
5
+ }
@@ -2,3 +2,6 @@ en:
2
2
  has_media:
3
3
  errors:
4
4
  type_error: "File type error"
5
+ form:
6
+ add_link: "Add %{medium_label}"
7
+ remove_link: "Remove"
data/has_media.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{has_media}
8
- s.version = "0.2.5"
8
+ s.version = "0.2.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["klacointe", "spk"]
12
- s.date = %q{2010-11-23}
12
+ s.date = %q{2010-12-03}
13
13
  s.description = %q{Media Managment Library for ActiveRecord and Carrierwave}
14
14
  s.email = %q{kevinlacointe@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -24,3 +24,6 @@ HasMedia.directory_uri = "/media"
24
24
  Dir.glob(File.dirname(__FILE__) + '/../app/uploaders/*.rb').each do |uploader|
25
25
  require uploader
26
26
  end
27
+
28
+ # Change CarrierWave root path
29
+ CarrierWave.root = HasMedia.directory_path
data/lib/has_media.rb CHANGED
@@ -24,30 +24,86 @@ module HasMedia
24
24
  :video => 'flv'
25
25
  }
26
26
 
27
+ ##
28
+ # medium_types
29
+ #
30
+ # Used to configure available medium types
31
+ #
32
+ # Each medium type id representing with its class name and contain an Array
33
+ # of possible mime types. An empty Array means no limitation on mime type
34
+ #
35
+ # Example :
36
+ # HasMedia.medium_types = {
37
+ # "Image" => ["image/jpeg", "image/png"],
38
+ # "Video" => ["video/mp4"],
39
+ # "Audio" => ["audio/mp3"],
40
+ # "Document" => []
41
+ # }
42
+ #
27
43
  def self.medium_types=(value)
28
44
  @@medium_types = value
29
45
  end
30
46
  def self.medium_types
31
47
  @@medium_types
32
48
  end
49
+
50
+ ##
51
+ # encoded_extensions
52
+ #
53
+ # Used to configure output format if you use a custom encoder
54
+ #
55
+ # Example :
56
+ # HasMedia.encoded_extensions = {
57
+ # :image => 'png',
58
+ # :audio => 'ogg',
59
+ # :video => 'flv'
60
+ # }
61
+ #
33
62
  def self.encoded_extensions=(value)
34
63
  @@encoded_extensions = value
35
64
  end
36
65
  def self.encoded_extensions
37
66
  @@encoded_extensions
38
67
  end
68
+
69
+ ##
70
+ # directory_path
71
+ #
72
+ # Used to configure directory_path to store media on filesystem
73
+ #
74
+ # Example :
75
+ # HasMedia.directory_path = Rails.root + "media"
76
+ #
39
77
  def self.directory_path=(value)
40
78
  @@store_dir = value
41
79
  end
42
80
  def self.directory_path
43
81
  @@store_dir
44
82
  end
83
+
84
+ ##
85
+ # directory_uri
86
+ #
87
+ # Used to store www access to your media
88
+ #
89
+ # Example :
90
+ # HasMedia.directory_path = Rails.root + "media"
91
+ #
45
92
  def self.directory_uri=(value)
46
93
  @@directory_uri = value
47
94
  end
48
95
  def self.directory_uri
49
96
  @@directory_uri
50
97
  end
98
+
99
+ ##
100
+ # errors_messages
101
+ #
102
+ # Used to store custom error messages
103
+ #
104
+ # Example :
105
+ # HasMedia.errors_messages = {:type_error => "Le format du logo n'est pas correct"}
106
+ #
51
107
  def self.errors_messages
52
108
  @@errors_messages
53
109
  end
@@ -59,20 +115,54 @@ module HasMedia
59
115
  mod.extend ClassMethods
60
116
  end
61
117
 
118
+ ##
119
+ # Sanitize file name
120
+ # @param [String] name
121
+ # @return [String]
122
+ #
123
+ def self.sanitize_file_name(name)
124
+ name = name.gsub("\\", "/") # work-around for IE
125
+ name = File.basename(name)
126
+ name = name.gsub(/[^a-zA-Z0-9\.\-\+_]/,"_")
127
+ name = "_#{name}" if name =~ /\A\.+\z/
128
+ name = "unnamed" if name.size == 0
129
+ return name.downcase
130
+ end
131
+
132
+
62
133
  module ClassMethods
63
134
 
135
+ ##
136
+ # has_one_medium
137
+ # Define a class method to link to a medium
138
+ #
139
+ # @param [String] context, the context (or accessor) to link medium
140
+ # @param [Hash] options, can be one of : encode, only
141
+ #
64
142
  def has_one_medium(context, options = {})
65
143
  set_relations(context, :has_one)
66
144
  set_general_methods
67
145
  create_one_accessors(context, options)
68
146
  end
69
147
 
148
+ ##
149
+ # has_many_media
150
+ # Define a class method to link to several media
151
+ #
152
+ # @param [String] context, the context (or accessor) to link media
153
+ # @param [Hash] options, can be one of : encode, only
154
+ #
70
155
  def has_many_media(context, options = {})
71
156
  set_relations(context, :has_many)
72
157
  set_general_methods
73
158
  create_many_accessors(context, options)
74
159
  end
75
160
 
161
+ ##
162
+ # set_general_methods
163
+ # Add generic methods for has_one_medium and has_many_media
164
+ # Including media_links relation, accessors, callbacks, validation ...
165
+ #
76
166
  def set_general_methods
77
167
  @methods_present ||= false
78
168
  unless @methods_present
@@ -84,6 +174,14 @@ module HasMedia
84
174
  @methods_present = true
85
175
  end
86
176
 
177
+ ##
178
+ # set_relations
179
+ # add relation on medium if not exists
180
+ # Also check if a class has a duplicate context
181
+ #
182
+ # @param [String] context
183
+ # @param [String] relation type, one of :has_many, :has_one
184
+ #
87
185
  def set_relations(context, relation)
88
186
  @contexts ||= {}
89
187
  @contexts[relation] ||= []
@@ -98,13 +196,30 @@ module HasMedia
98
196
  @media_relation_set << self
99
197
  end
100
198
 
199
+ ##
200
+ # set_callbacks
201
+ # Add callbacks to :
202
+ # - merge medium errors to class related errors
203
+ # - destroy medium
204
+ #
101
205
  def set_callbacks
102
206
  validate :merge_media_errors
103
207
  before_save :remove_old_media
104
208
  end
209
+
210
+ ##
211
+ # set_attributes
212
+ # Add media_errors attributes to store medium errors
213
+ #
105
214
  def set_attributes
106
215
  attr_accessor :media_errors
107
216
  end
217
+
218
+ ##
219
+ # set_validate_methods
220
+ # Define merge_media_errors to merge medium errors with errors given
221
+ # on master object.
222
+ #
108
223
  def set_validate_methods
109
224
  module_eval <<-"end;", __FILE__, __LINE__
110
225
  def merge_media_errors
@@ -115,15 +230,22 @@ module HasMedia
115
230
  end
116
231
  end;
117
232
  end
118
-
233
+
234
+ ##
235
+ # set_media_links_relation
236
+ # Declare media_links relation
119
237
  def set_media_links_relation
120
238
  has_many :media_links, :as => :mediated, :dependent => :destroy
121
239
  end
122
240
 
241
+ ##
242
+ # create_one_accessors
243
+ # Create needed accessors on master object for unique relation
244
+ #
245
+ # @param [String] context
246
+ # @param [Hash] options
247
+ #
123
248
  def create_one_accessors(context, options)
124
- #check_conditions = ''
125
- #check_conditions << "return unless medium.is_a? #{options[:only].to_s.capitalize}" if options.has_key? :only
126
-
127
249
  define_method(context) do
128
250
  media.with_context(context.to_sym).first
129
251
  end
@@ -141,10 +263,14 @@ module HasMedia
141
263
  end;
142
264
  end
143
265
 
266
+ ##
267
+ # create_many_accessors
268
+ # Create needed accessors on master object for multiple relation
269
+ #
270
+ # @param [String] context
271
+ # @param [Hash] options
272
+ #
144
273
  def create_many_accessors(context, options)
145
- #check_conditions = ''
146
- #check_conditions << "return unless medium.is_a? #{options[:only].to_s.capitalize}" if options.has_key? :only
147
-
148
274
  define_method(context.to_s.pluralize) do
149
275
  media.with_context(context.to_sym).uniq
150
276
  end
@@ -162,6 +288,9 @@ module HasMedia
162
288
  end
163
289
  end
164
290
 
291
+ ##
292
+ # Remove old media before saving
293
+ #
165
294
  def remove_old_media
166
295
  (@old_media || []).each do |medium|
167
296
  medium.destroy if medium
@@ -170,18 +299,22 @@ module HasMedia
170
299
 
171
300
  end
172
301
 
302
+ # Include HasMedia in all ActiveRecord::Base Object
173
303
  class ActiveRecord::Base
174
304
  include HasMedia
175
305
  end
306
+ # Include HasMediaHelper in all ActiveRecord::Base Object
176
307
  class ActionController::Base
177
308
  helper HasMediaHelper
178
309
  end
179
310
 
311
+ # Require generic medium uploader
180
312
  require File.dirname(__FILE__) + '/has_media/uploaders/medium_uploader'
181
313
  Dir.glob(File.dirname(__FILE__) + '/has_media/uploaders/*.rb').each do |uploader|
182
314
  require uploader
183
315
  end
184
316
 
317
+ # Require generic medium model
185
318
  require File.dirname(__FILE__) + '/has_media/models/medium'
186
319
  Dir.glob(File.dirname(__FILE__) + '/has_media/models/*.rb').each do |model|
187
320
  require model
@@ -22,15 +22,6 @@ class Medium < ActiveRecord::Base
22
22
  { :conditions => { :context => context.to_s} }
23
23
  }
24
24
 
25
- def self.sanitize(name)
26
- name = name.gsub("\\", "/") # work-around for IE
27
- name = File.basename(name)
28
- name = name.gsub(/[^a-zA-Z0-9\.\-\+_]/,"_")
29
- name = "_#{name}" if name =~ /\A\.+\z/
30
- name = "unnamed" if name.size == 0
31
- return name.downcase
32
- end
33
-
34
25
  def self.new_from_value(object, value, context, encode, only)
35
26
  if value.respond_to?(:content_type)
36
27
  mime_type = value.content_type
@@ -51,9 +42,9 @@ class Medium < ActiveRecord::Base
51
42
  end
52
43
  medium = klass.new
53
44
  if value.respond_to?(:original_filename)
54
- medium.filename = self.sanitize(value.original_filename)
45
+ medium.filename = HasMedia.sanitize_file_name(value.original_filename)
55
46
  else
56
- medium.filename = self.sanitize(File.basename(value.path))
47
+ medium.filename = HasMedia.sanitize_file_name(File.basename(value.path))
57
48
  end
58
49
  medium.file = value
59
50
  medium.content_type = mime_type
@@ -116,6 +107,13 @@ class Medium < ActiveRecord::Base
116
107
  File.join(directory_uri, encoded_file_name(version))
117
108
  end
118
109
 
110
+ ##
111
+ # encoded_file_name
112
+ # Return the encoded file name for a medium
113
+ # This use the HasMedia.encoded_extensions configuration
114
+ #
115
+ # @param [String] version, the string identifier for a specific encoded version
116
+ # FIXME duplicate with HasMedia::sanitize_file_name
119
117
  def encoded_file_name(version = nil)
120
118
  # remove original extension and add the encoded extension
121
119
  final_name = filename.gsub(/\.[^.]{1,4}$/, "") + '.' + file_extension
@@ -130,10 +128,25 @@ class Medium < ActiveRecord::Base
130
128
  self.id.to_s)
131
129
  end
132
130
 
131
+ ##
132
+ # file_exists?
133
+ # Is the medium for the current format exists
134
+ #
135
+ # @param [String] version to test
136
+ #
137
+ # @return [Boolean]
138
+ #
133
139
  def file_exists?(thumbnail = nil)
134
140
  File.exist?(File.join(Rails.root, 'public', file_uri(thumbnail)))
135
141
  end
136
142
 
143
+ ##
144
+ # file_extension
145
+ # Return the file extension for the current medium type
146
+ # This use the HasMedia.encoded_extensions configuration
147
+ #
148
+ # @return [String]
149
+ #
137
150
  def file_extension
138
151
  sym = type.underscore.to_sym
139
152
  unless HasMedia.encoded_extensions.keys.include?(sym)
@@ -145,14 +158,15 @@ class Medium < ActiveRecord::Base
145
158
  private
146
159
 
147
160
  ##
161
+ # set_default_encoding_status
148
162
  # Set encode_status value to notify the encoder of a new file
149
163
  def set_default_encoding_status
150
164
  self.encode_status = ENCODE_NOT_READY if filename_changed?
151
165
  end
152
166
 
153
167
  ##
168
+ # remove_file_from_fs
154
169
  # Unlink the folder containing the files
155
- # TODO : remove all files, not only the original one
156
170
  def remove_file_from_fs
157
171
  require 'fileutils'
158
172
  FileUtils.rm_rf(self.directory_path)
@@ -34,7 +34,7 @@ describe "HasMedia" do
34
34
  }
35
35
  end
36
36
 
37
- describe "fonctionalities" do
37
+ describe "basic fonctionalities" do
38
38
 
39
39
  before :each do
40
40
  @medium = MediumRelatedTest.new
@@ -202,9 +202,73 @@ describe "HasMedia" do
202
202
  @medium.save.should be_false
203
203
  @medium.errors.full_messages.include?(HasMedia.errors_messages[:type_error])
204
204
  end
205
+
206
+ it "should sanitize filename" do
207
+ @pdf = stub_temp_file('Conversational_Capital _Explained.pdf', 'application/pdf')
208
+ @medium = MediumRelatedTest.new
209
+ @medium.pdf = @pdf
210
+ @medium.save
211
+ @medium.pdf.filename.should == "conversational_capital__explained.pdf"
212
+ @medium.pdf.original_file_uri.should == "/media/pdf/#{@medium.pdf.id}/conversational_capital__explained.pdf"
213
+ end
205
214
  end
206
215
 
207
216
  describe "Configuration" do
217
+
218
+ it "should configure medium_types" do
219
+ old_conf = HasMedia.medium_types
220
+ HasMedia.medium_types = {
221
+ "Image" => ["image/jpeg"]
222
+ }
223
+ HasMedia.medium_types.should == {
224
+ "Image" => ["image/jpeg"]
225
+ }
226
+ HasMedia.medium_types = old_conf
227
+ end
228
+
229
+ it "should configure encoded_extensions" do
230
+ old_conf = HasMedia.encoded_extensions
231
+ HasMedia.encoded_extensions = {
232
+ :image => "png"
233
+ }
234
+ HasMedia.encoded_extensions.should == {
235
+ :image => "png"
236
+ }
237
+ HasMedia.encoded_extensions = old_conf
238
+ end
239
+
240
+ it "should configure directory_path" do
241
+ old_conf = HasMedia.directory_path
242
+ HasMedia.directory_path = "/tmp"
243
+ HasMedia.directory_path.should == "/tmp"
244
+ HasMedia.directory_path = old_conf
245
+ end
246
+
247
+ it "should configure directory_uri" do
248
+ old_uri = HasMedia.directory_uri
249
+ HasMedia.directory_uri = "/tmp"
250
+ HasMedia.directory_uri.should == "/tmp"
251
+ HasMedia.directory_uri = old_uri
252
+ end
253
+
254
+ it "should configure/merge errors_messages" do
255
+ old_conf = HasMedia.errors_messages
256
+ HasMedia.errors_messages = {
257
+ :type_error => "wtf?"
258
+ }
259
+ HasMedia.errors_messages.should == {
260
+ :type_error => "wtf?"
261
+ }
262
+ HasMedia.errors_messages = {
263
+ :another_error => "warning!"
264
+ }
265
+ HasMedia.errors_messages.should == {
266
+ :type_error => "wtf?",
267
+ :another_error => "warning!"
268
+ }
269
+ HasMedia.errors_messages = old_conf
270
+ end
271
+
208
272
  it "should check allowed medium types if no :only option given" do
209
273
  HasMedia.medium_types = {
210
274
  "Image" => ["image/jpeg"],
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has_media
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 5
10
- version: 0.2.5
9
+ - 6
10
+ version: 0.2.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - klacointe
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-11-23 00:00:00 +01:00
19
+ date: 2010-12-03 00:00:00 +01:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency