parlement 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/CHANGES +14 -4
  2. data/README +25 -5
  3. data/Rakefile +21 -21
  4. data/app/controllers/account_controller.rb +5 -1
  5. data/app/controllers/elt_controller.rb +7 -10
  6. data/app/controllers/person_controller.rb +9 -0
  7. data/app/controllers/subscriber_controller.rb +21 -0
  8. data/app/helpers/elt_helper.rb +25 -13
  9. data/app/helpers/mailman.rb +9 -92
  10. data/app/helpers/subscriber_helper.rb +2 -0
  11. data/app/models/attachment.rb +2 -0
  12. data/app/models/elt.rb +64 -2
  13. data/app/models/mail.rb +198 -0
  14. data/app/models/mail_notify.rb +63 -0
  15. data/app/models/person.rb +8 -1
  16. data/app/views/account/_login.rhtml +31 -28
  17. data/app/views/account/_show.rhtml +4 -4
  18. data/app/views/elt/_elt.rhtml +23 -28
  19. data/app/views/elt/_list.rhtml +6 -2
  20. data/app/views/elt/new.rhtml +1 -1
  21. data/app/views/elt/show.rhtml +32 -10
  22. data/app/views/layouts/top.rhtml +16 -10
  23. data/app/views/mail_notify/publish.text.html.rhtml +46 -0
  24. data/app/views/mail_notify/publish.text.plain.rhtml +2 -0
  25. data/app/views/person/_listElts.rhtml +33 -0
  26. data/app/views/person/show.rhtml +21 -19
  27. data/config/boot.rb +2 -0
  28. data/config/environment.rb +19 -13
  29. data/config/environments/development.rb +3 -1
  30. data/config/environments/production.rb +2 -0
  31. data/config/environments/test.rb +2 -0
  32. data/config/routes.rb +5 -2
  33. data/db/ROOT/mail.txt +2 -0
  34. data/db/ROOT/parlement/news/release0.2.txt +8 -0
  35. data/db/ROOT/parlement/news/release0.3.txt +11 -0
  36. data/db/ROOT/parlement/test.txt +6 -1
  37. data/db/ROOT/parlement.txt +23 -30
  38. data/db/ROOT/perso.txt +17 -18
  39. data/db/development_structure.sql +133 -217
  40. data/db/schema.rb +83 -0
  41. data/db/schema.sql +11 -15
  42. data/lib/data_import.rb +3 -1
  43. data/public/attachment/file/architecture.png +0 -0
  44. data/public/attachment/file/architecture.svg +8972 -0
  45. data/public/attachment/file/security.svg +8960 -0
  46. data/public/images/Sleep-Deprivation-5.JPG +0 -0
  47. data/public/images/eltBackground.png +0 -0
  48. data/public/images/eltBackground.svg +89 -0
  49. data/public/images/orange_by_darren_Hester_350o.jpg +0 -0
  50. data/public/images/rails.png +0 -0
  51. data/public/images/smile.png +0 -0
  52. data/public/images/smile.svg +257 -0
  53. data/public/images/world.png +0 -0
  54. data/public/images/world.svg +170 -0
  55. data/public/javascripts/controls.js +30 -1
  56. data/public/javascripts/dragdrop.js +210 -145
  57. data/public/javascripts/effects.js +261 -399
  58. data/public/javascripts/ie7.js +6 -0
  59. data/public/javascripts/prototype.js +131 -72
  60. data/public/oldindex.html +270 -71
  61. data/public/stylesheets/default.css +189 -215
  62. data/script/about +1 -1
  63. data/script/breakpointer +1 -1
  64. data/script/console +1 -1
  65. data/script/destroy +1 -1
  66. data/script/generate +1 -1
  67. data/script/performance/benchmarker +1 -1
  68. data/script/performance/profiler +1 -1
  69. data/script/plugin +1 -1
  70. data/script/process/reaper +1 -1
  71. data/script/process/spawner +1 -1
  72. data/script/process/spinner +1 -1
  73. data/script/runner +1 -1
  74. data/script/server +1 -1
  75. data/test/fixtures/elts.yml +2 -0
  76. data/test/fixtures/mail/mail_ruby +27 -0
  77. data/test/fixtures/mail/mail_rubyChild +28 -0
  78. data/test/fixtures/mail/mail_rubyWithAttachment +7932 -0
  79. data/test/fixtures/mail/mail_rubyWithSubject +27 -0
  80. data/test/fixtures/mails.yml +7 -1
  81. data/test/fixtures/people.yml +5 -0
  82. data/test/fixtures/subscribers.yml +11 -0
  83. data/test/functional/account_controller_test.rb +38 -37
  84. data/test/functional/subscriber_controller_test.rb +128 -0
  85. data/test/test_helper.rb +44 -0
  86. data/test/unit/attachment_test.rb +1 -1
  87. data/test/unit/elt_test.rb +3 -2
  88. data/test/unit/mail_notify_test.rb +37 -0
  89. data/test/unit/mail_test.rb +124 -1
  90. data/test/unit/notifier_test.rb +0 -14
  91. data/test/unit/person_test.rb +2 -1
  92. data/test/unit/subscriber_test.rb +35 -0
  93. data/test/unit/user_test.rb +3 -3
  94. data/vendor/plugins/file_column/CHANGELOG +64 -0
  95. data/vendor/plugins/file_column/README +54 -0
  96. data/vendor/plugins/file_column/Rakefile +36 -0
  97. data/vendor/plugins/file_column/TODO +6 -0
  98. data/vendor/plugins/file_column/init.rb +12 -0
  99. data/vendor/plugins/file_column/lib/file_column.rb +719 -0
  100. data/vendor/plugins/file_column/lib/file_column_helper.rb +145 -0
  101. data/vendor/plugins/file_column/lib/file_compat.rb +28 -0
  102. data/vendor/plugins/file_column/lib/magick_file_column.rb +188 -0
  103. data/vendor/plugins/file_column/lib/validations.rb +112 -0
  104. data/vendor/plugins/file_column/test/abstract_unit.rb +90 -0
  105. data/vendor/plugins/file_column/test/connection.rb +17 -0
  106. data/vendor/plugins/file_column/test/file_column_helper_test.rb +97 -0
  107. data/vendor/plugins/file_column/test/file_column_test.rb +630 -0
  108. data/vendor/plugins/file_column/test/fixtures/entry.rb +32 -0
  109. data/vendor/plugins/file_column/test/fixtures/invalid-image.jpg +1 -0
  110. data/vendor/plugins/file_column/test/fixtures/kerb.jpg +0 -0
  111. data/vendor/plugins/file_column/test/fixtures/mysql.sql +25 -0
  112. data/vendor/plugins/file_column/test/fixtures/schema.rb +10 -0
  113. data/vendor/plugins/file_column/test/fixtures/skanthak.png +0 -0
  114. data/vendor/plugins/file_column/test/magick_test.rb +251 -0
  115. data/vendor/plugins/file_column/test/magick_view_only_test.rb +21 -0
  116. data/vendor/plugins/guid/README.TXT +19 -0
  117. data/vendor/plugins/guid/init.rb +23 -0
  118. data/vendor/plugins/guid/lib/usesguid.rb +37 -0
  119. data/vendor/plugins/guid/lib/uuid22.rb +43 -0
  120. data/vendor/plugins/guid/lib/uuidtools.rb +565 -0
  121. metadata +83 -15
  122. data/db/ROOT/CV.txt +0 -166
  123. data/lib/file_column.rb +0 -263
  124. data/lib/file_column_helper.rb +0 -45
  125. /data/{lib → vendor/plugins/file_column/lib}/rails_file_column.rb +0 -0
@@ -0,0 +1,145 @@
1
+ # This module contains helper methods for displaying and uploading files
2
+ # for attributes created by +FileColumn+'s +file_column+ method. It will be
3
+ # automatically included into ActionView::Base, thereby making this module's
4
+ # methods available in all your views.
5
+ module FileColumnHelper
6
+
7
+ # Use this helper to create an upload field for a file_column attribute. This will generate
8
+ # an additional hidden field to keep uploaded files during form-redisplays. For example,
9
+ # when called with
10
+ #
11
+ # <%= file_column_field("entry", "image") %>
12
+ #
13
+ # the following HTML will be generated (assuming the form is redisplayed and something has
14
+ # already been uploaded):
15
+ #
16
+ # <input type="hidden" name="entry[image_temp]" value="..." />
17
+ # <input type="file" name="entry[image]" />
18
+ #
19
+ # You can use the +option+ argument to pass additional options to the file-field tag.
20
+ #
21
+ # Be sure to set the enclosing form's encoding to 'multipart/form-data', by
22
+ # using something like this:
23
+ #
24
+ # <%= form_tag {:action => "create", ...}, :multipart => true %>
25
+ def file_column_field(object, method, options={})
26
+ result = ActionView::Helpers::InstanceTag.new(object.dup, method.to_s+"_temp", self).to_input_field_tag("hidden", {})
27
+ result << ActionView::Helpers::InstanceTag.new(object.dup, method, self).to_input_field_tag("file", options)
28
+ end
29
+
30
+ # Creates an URL where an uploaded file can be accessed. When called for an Entry object with
31
+ # id 42 (stored in <tt>@entry</tt>) like this
32
+ #
33
+ # <%= url_for_file_column(@entry, "image")
34
+ #
35
+ # the following URL will be produced, assuming the file "test.png" has been stored in
36
+ # the "image"-column of an Entry object stored in <tt>@entry</tt>:
37
+ #
38
+ # /entry/image/42/test.png
39
+ #
40
+ # This will produce a valid URL even for temporary uploaded files, e.g. files where the object
41
+ # they are belonging to has not been saved in the database yet.
42
+ #
43
+ # The URL produces, although starting with a slash, will be relative
44
+ # to your app's root. If you pass it to one rails' +image_tag+
45
+ # helper, rails will properly convert it to an absolute
46
+ # URL. However, this will not be the case, if you create a link with
47
+ # the +link_to+ helper. In this case, you can pass <tt>:absolute =>
48
+ # true</tt> to +options+, which will make sure, the generated URL is
49
+ # absolute on your server. Examples:
50
+ #
51
+ # <%= image_tag url_for_file_column(@entry, "image") %>
52
+ # <%= link_to "Download", url_for_file_column(@entry, "image", :absolute => true) %>
53
+ #
54
+ # If there is currently no uploaded file stored in the object's column this method will
55
+ # return +nil+.
56
+ def url_for_file_column(object, method, options=nil)
57
+ case object
58
+ when String, Symbol
59
+ object = instance_variable_get("@#{object.to_s}")
60
+ end
61
+
62
+ # parse options
63
+ subdir = nil
64
+ absolute = false
65
+ if options
66
+ case options
67
+ when Hash
68
+ subdir = options[:subdir]
69
+ absolute = options[:absolute]
70
+ when String, Symbol
71
+ subdir = options
72
+ end
73
+ end
74
+
75
+ relative_path = object.send("#{method}_relative_path", subdir)
76
+ return nil unless relative_path
77
+
78
+ url = ""
79
+ url << request.relative_url_root.to_s if absolute
80
+ url << "/"
81
+ url << object.send("#{method}_options")[:base_url] << "/"
82
+ url << relative_path
83
+ end
84
+
85
+ # Same as +url_for_file_colum+ but allows you to access different versions
86
+ # of the image that have been processed by RMagick.
87
+ #
88
+ # If your +options+ parameter is non-nil this will
89
+ # access a different version of an image that will be produced by
90
+ # RMagick. You can use the following types for +options+:
91
+ #
92
+ # * a <tt>:symbol</tt> will select a version defined in the model
93
+ # via FileColumn::Magick's <tt>:versions</tt> feature.
94
+ # * a <tt>geometry_string</tt> will dynamically create an
95
+ # image resized as specified by <tt>geometry_string</tt>. The image will
96
+ # be stored so that it does not have to be recomputed the next time the
97
+ # same version string is used.
98
+ # * <tt>some_hash</tt> will dynamically create an image
99
+ # that is created according to the options in <tt>some_hash</tt>. This
100
+ # accepts exactly the same options as Magick's version feature.
101
+ #
102
+ # The version produced by RMagick will be stored in a special sub-directory.
103
+ # The directory's name will be derived from the options you specified
104
+ # (via a hash function) but if you want
105
+ # to set it yourself, you can use the <tt>:name => name</tt> option.
106
+ #
107
+ # Examples:
108
+ #
109
+ # <%= url_for_image_column @entry, "image", "640x480" %>
110
+ #
111
+ # will produce an URL like this
112
+ #
113
+ # /entry/image/42/bdn19n/filename.jpg
114
+ # # "640x480".hash.abs.to_s(36) == "bdn19n"
115
+ #
116
+ # and
117
+ #
118
+ # <%= url_for_image_column @entry, "image",
119
+ # :size => "50x50", :crop => "1:1", :name => "thumb" %>
120
+ #
121
+ # will produce something like this:
122
+ #
123
+ # /entry/image/42/thumb/filename.jpg
124
+ #
125
+ # Hint: If you are using the same geometry string / options hash multiple times, you should
126
+ # define it in a helper to stay with DRY. Another option is to define it in the model via
127
+ # FileColumn::Magick's <tt>:versions</tt> feature and then refer to it via a symbol.
128
+ #
129
+ # The URL produced by this method is relative to your application's root URL,
130
+ # although it will start with a slash.
131
+ # If you pass this URL to rails' +image_tag+ helper, it will be converted to an
132
+ # absolute URL automatically.
133
+ # If there is currently no image uploaded, this method will return +nil+.
134
+ def url_for_image_column(object, method, options=nil)
135
+ case object
136
+ when String, Symbol
137
+ object = instance_variable_get("@#{object.to_s}")
138
+ end
139
+ subdir = nil
140
+ if options
141
+ subdir = object.send("#{method}_state").create_magick_version_if_needed(options)
142
+ end
143
+ url_for_file_column(object, method, subdir)
144
+ end
145
+ end
@@ -0,0 +1,28 @@
1
+ module FileColumn
2
+
3
+ # This bit of code allows you to pass regular old files to
4
+ # file_column. file_column depends on a few extra methods that the
5
+ # CGI uploaded file class adds. We will add the equivalent methods
6
+ # to file objects if necessary by extending them with this module. This
7
+ # avoids opening up the standard File class which might result in
8
+ # naming conflicts.
9
+
10
+ module FileCompat # :nodoc:
11
+ def original_filename
12
+ File.basename(path)
13
+ end
14
+
15
+ def size
16
+ File.size(path)
17
+ end
18
+
19
+ def local_path
20
+ path
21
+ end
22
+
23
+ def content_type
24
+ nil
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,188 @@
1
+ module FileColumn # :nodoc:
2
+
3
+ class BaseUploadedFile # :nodoc:
4
+ def transform_with_magick
5
+ if needs_resize?
6
+ begin
7
+ img = ::Magick::Image::read(absolute_path).first
8
+ rescue ::Magick::ImageMagickError
9
+ @magick_errors ||= []
10
+ @magick_errors << "invalid image"
11
+ return
12
+ end
13
+
14
+ if options[:magick][:versions]
15
+ options[:magick][:versions].each_pair do |version, version_options|
16
+ next if version_options[:lazy]
17
+ dirname = version_options[:name]
18
+ FileUtils.mkdir File.join(@dir, dirname)
19
+ resize_image(img, version_options, absolute_path(dirname))
20
+ end
21
+ end
22
+ if options[:magick][:size] or options[:magick][:crop]
23
+ resize_image(img, options[:magick], absolute_path)
24
+ end
25
+
26
+ GC.start
27
+ end
28
+ end
29
+
30
+ def create_magick_version_if_needed(version)
31
+ # RMagick might not have been loaded so far.
32
+ # We do not want to require it on every call of this method
33
+ # as this might be fairly expensive, so we just try if ::Magick
34
+ # exists and require it if not.
35
+ begin
36
+ ::Magick
37
+ rescue NameError
38
+ require 'RMagick'
39
+ end
40
+
41
+ if version.is_a?(Symbol)
42
+ version_options = options[:magick][:versions][version]
43
+ else
44
+ version_options = MagickExtension::process_options(version)
45
+ end
46
+
47
+ unless File.exists?(absolute_path(version_options[:name]))
48
+ img = ::Magick::Image::read(absolute_path).first
49
+ dirname = version_options[:name]
50
+ FileUtils.mkdir File.join(@dir, dirname)
51
+ resize_image(img, version_options, absolute_path(dirname))
52
+ end
53
+
54
+ version_options[:name]
55
+ end
56
+
57
+ attr_reader :magick_errors
58
+
59
+ def has_magick_errors?
60
+ @magick_errors and !@magick_errors.empty?
61
+ end
62
+
63
+ private
64
+
65
+ def needs_resize?
66
+ options[:magick] and just_uploaded? and
67
+ (options[:magick][:size] or options[:magick][:versions])
68
+ end
69
+
70
+ def resize_image(img, img_options, dest_path)
71
+ begin
72
+ if img_options[:crop]
73
+ dx, dy = img_options[:crop].split(':').map { |x| x.to_f }
74
+ w, h = (img.rows * dx / dy), (img.columns * dy / dx)
75
+ img = img.crop(::Magick::CenterGravity, [img.columns, w].min,
76
+ [img.rows, h].min)
77
+ end
78
+
79
+ if img_options[:size]
80
+ img = img.change_geometry(img_options[:size]) do |c, r, i|
81
+ i.resize(c, r)
82
+ end
83
+ end
84
+ ensure
85
+ img.write dest_path
86
+ File.chmod options[:permissions], dest_path
87
+ end
88
+ end
89
+ end
90
+
91
+ # If you are using file_column to upload images, you can
92
+ # directly process the images with RMagick,
93
+ # a ruby extension
94
+ # for accessing the popular imagemagick libraries. You can find
95
+ # more information about RMagick at http://rmagick.rubyforge.org.
96
+ #
97
+ # You can control what to do by adding a <tt>:magick</tt> option
98
+ # to your options hash. All operations are performed immediately
99
+ # after a new file is assigned to the file_column attribute (i.e.,
100
+ # when a new file has been uploaded).
101
+ #
102
+ # To resize the uploaded image according to an imagemagick geometry
103
+ # string, just use the <tt>:size</tt> option:
104
+ #
105
+ # file_column :image, :magick => {:size => "800x600>"}
106
+ #
107
+ # You can also create additional versions of your image, for example
108
+ # thumb-nails, like this:
109
+ # file_column :image, :magick => {:versions => {
110
+ # :thumb => {:size => "50x50"},
111
+ # :medium => {:size => "640x480>"}
112
+ # }
113
+ #
114
+ # If you wish to crop your images with a size ratio before scaling
115
+ # them according to your version geometry, you can use the :crop directive.
116
+ # file_column :image, :magick => {:versions => {
117
+ # :square => {:crop => "1:1", :size => "50x50", :name => "thumb"},
118
+ # :screen => {:crop => "4:3", :size => "640x480>"},
119
+ # :widescreen => {:crop => "16:9", :size => "640x360!"},
120
+ # }
121
+ # }
122
+ #
123
+ # These versions will be stored in separate sub-directories, named like the
124
+ # symbol you used to identify the version. So in the previous example, the
125
+ # image versions will be stored in "thumb", "screen" and "widescreen"
126
+ # directories, resp.
127
+ # A name different from the symbol can be set via the <tt>:name</tt> option.
128
+ #
129
+ # These versions can be accessed via FileColumnHelper's +url_for_image_column+
130
+ # method like this:
131
+ #
132
+ # <%= url_for_image_column "entry", "image", :thumb %>
133
+ #
134
+ # <b>Note:</b> You'll need the
135
+ # RMagick extension being installed in order to use file_column's
136
+ # imagemagick integration.
137
+ module MagickExtension
138
+
139
+ def self.file_column(klass, attr, options) # :nodoc:
140
+ require 'RMagick'
141
+ options[:magick] = process_options(options[:magick],false) if options[:magick]
142
+ if options[:magick][:versions]
143
+ options[:magick][:versions].each_pair do |name, value|
144
+ options[:magick][:versions][name] = process_options(value, name.to_s)
145
+ end
146
+ end
147
+ state_method = "#{attr}_state".to_sym
148
+ after_assign_method = "#{attr}_magick_after_assign".to_sym
149
+
150
+ klass.send(:define_method, after_assign_method) do
151
+ self.send(state_method).transform_with_magick
152
+ end
153
+
154
+ options[:after_upload] ||= []
155
+ options[:after_upload] << after_assign_method
156
+
157
+ klass.validate do |record|
158
+ state = record.send(state_method)
159
+ if state.has_magick_errors?
160
+ state.magick_errors.each do |error|
161
+ record.errors.add attr, error
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+
168
+ def self.process_options(options,create_name=true)
169
+ options = {:size => options } if options.kind_of?(String)
170
+ if options[:geometry]
171
+ options[:size] = options.delete(:geometry)
172
+ end
173
+ if options[:name].nil? and create_name
174
+ if create_name == true
175
+ hash = 0
176
+ for key in [:size, :crop]
177
+ hash = hash ^ options[key].hash if options[key]
178
+ end
179
+ options[:name] = hash.abs.to_s(36)
180
+ else
181
+ options[:name] = create_name
182
+ end
183
+ end
184
+ options
185
+ end
186
+
187
+ end
188
+ end
@@ -0,0 +1,112 @@
1
+ module FileColumn
2
+ module Validations #:nodoc:
3
+
4
+ def self.append_features(base)
5
+ super
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ # This module contains methods to create validations of uploaded files. All methods
10
+ # in this module will be included as class methods into <tt>ActiveRecord::Base</tt>
11
+ # so that you can use them in your models like this:
12
+ #
13
+ # class Entry < ActiveRecord::Base
14
+ # file_column :image
15
+ # validates_filesize_of :image, :in => 0..1.megabyte
16
+ # end
17
+ module ClassMethods
18
+ EXT_REGEXP = /\.([A-z0-9]+)$/
19
+
20
+ # This validates the file type of one or more file_columns. A list of file columns
21
+ # should be given followed by an options hash.
22
+ #
23
+ # Required options:
24
+ # * <tt>:in</tt> => list of extensions or mime types. If mime types are used they
25
+ # will be mapped into an extension via FileColumn::ClassMethods::MIME_EXTENSIONS.
26
+ #
27
+ # Examples:
28
+ # validates_file_format_of :field, :in => ["gif", "png", "jpg"]
29
+ # validates_file_format_of :field, :in => ["image/jpeg"]
30
+ def validates_file_format_of(*attrs)
31
+
32
+ options = attrs.pop if attrs.last.is_a?Hash
33
+ raise ArgumentError, "Please include the :in option." if !options || !options[:in]
34
+ options[:in] = [options[:in]] if options[:in].is_a?String
35
+ raise ArgumentError, "Invalid value for option :in" unless options[:in].is_a?Array
36
+
37
+ validates_each(attrs, options) do |record, attr, value|
38
+ unless value.blank?
39
+ mime_extensions = record.send("#{attr}_options")[:mime_extensions]
40
+ extensions = options[:in].map{|o| mime_extensions[o] || o }
41
+ record.errors.add attr, "is not a valid format." unless extensions.include?(value.scan(EXT_REGEXP).flatten.first)
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ # This validates the file size of one or more file_columns. A list of file columns
48
+ # should be given followed by an options hash.
49
+ #
50
+ # Required options:
51
+ # * <tt>:in</tt> => A size range. Note that you can use ActiveSupport's
52
+ # numeric extensions for kilobytes, etc.
53
+ #
54
+ # Examples:
55
+ # validates_filesize_of :field, :in => 0..100.megabytes
56
+ # validates_filesize_of :field, :in => 15.kilobytes..1.megabyte
57
+ def validates_filesize_of(*attrs)
58
+
59
+ options = attrs.pop if attrs.last.is_a?Hash
60
+ raise ArgumentError, "Please include the :in option." if !options || !options[:in]
61
+ raise ArgumentError, "Invalid value for option :in" unless options[:in].is_a?Range
62
+
63
+ validates_each(attrs, options) do |record, attr, value|
64
+ unless value.blank?
65
+ size = File.size(value)
66
+ record.errors.add attr, "is smaller than the allowed size range." if size < options[:in].first
67
+ record.errors.add attr, "is larger than the allowed size range." if size > options[:in].last
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ IMAGE_SIZE_REGEXP = /^(\d+)x(\d+)$/
74
+
75
+ # Validates the image size of one or more file_columns. A list of file columns
76
+ # should be given followed by an options hash. The validation will pass
77
+ # if both image dimensions (rows and columns) are at least as big as
78
+ # given in the <tt>:min</tt> option.
79
+ #
80
+ # Required options:
81
+ # * <tt>:min</tt> => minimum image dimension string, in the format NNxNN
82
+ # (columns x rows).
83
+ #
84
+ # Example:
85
+ # validates_image_size :field, :min => "1200x1800"
86
+ #
87
+ # This validation requires RMagick to be installed on your system
88
+ # to check the image's size.
89
+ def validates_image_size(*attrs)
90
+ options = attrs.pop if attrs.last.is_a?Hash
91
+ raise ArgumentError, "Please include a :min option." if !options || !options[:min]
92
+ minimums = options[:min].scan(IMAGE_SIZE_REGEXP).first.collect{|n| n.to_i} rescue []
93
+ raise ArgumentError, "Invalid value for option :min (should be 'XXxYY')" unless minimums.size == 2
94
+
95
+ require 'RMagick'
96
+
97
+ validates_each(attrs, options) do |record, attr, value|
98
+ unless value.blank?
99
+ begin
100
+ img = ::Magick::Image::read(value).first
101
+ record.errors.add('image', "is too small, must be at least #{minimums[0]}x#{minimums[1]}") if ( img.rows < minimums[1] || img.columns < minimums[0] )
102
+ rescue ::Magick::ImageMagickError
103
+ record.errors.add('image', "invalid image")
104
+ end
105
+ img = nil
106
+ GC.start
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,90 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'active_support'
4
+ require 'active_record'
5
+ require 'action_view'
6
+ require File.dirname(__FILE__) + '/connection'
7
+ require 'stringio'
8
+
9
+ RAILS_ROOT = File.dirname(__FILE__)
10
+
11
+ $: << "../lib"
12
+
13
+ require 'file_column'
14
+ require 'file_compat'
15
+ require 'validations'
16
+
17
+ # do not use the file executable normally in our tests as
18
+ # it may not be present on the machine we are running on
19
+ FileColumn::ClassMethods::DEFAULT_OPTIONS =
20
+ FileColumn::ClassMethods::DEFAULT_OPTIONS.merge({:file_exec => nil})
21
+
22
+ class ActiveRecord::Base
23
+ include FileColumn
24
+ include FileColumn::Validations
25
+ end
26
+
27
+
28
+ class RequestMock
29
+ attr_accessor :relative_url_root
30
+
31
+ def initialize
32
+ @relative_url_root = ""
33
+ end
34
+ end
35
+
36
+ class Test::Unit::TestCase
37
+ def assert_equal_paths(expected_path, path)
38
+ assert_equal normalize_path(expected_path), normalize_path(path)
39
+ end
40
+
41
+
42
+ private
43
+
44
+ def normalize_path(path)
45
+ Pathname.new(path).realpath
46
+ end
47
+
48
+ def uploaded_file(path, content_type, filename, type=:tempfile)
49
+ if type == :tempfile
50
+ t = Tempfile.new(File.basename(filename))
51
+ FileUtils.copy_file(path, t.path)
52
+ else
53
+ if path
54
+ t = StringIO.new(IO.read(path))
55
+ else
56
+ t = StringIO.new
57
+ end
58
+ end
59
+ (class << t; self; end).class_eval do
60
+ alias local_path path if type == :tempfile
61
+ define_method(:local_path) { "" } if type == :stringio
62
+ define_method(:original_filename) {filename}
63
+ define_method(:content_type) {content_type}
64
+ end
65
+ return t
66
+ end
67
+
68
+ def upload(basename, content_type=:guess, file_type=:tempfile)
69
+ if content_type == :guess
70
+ case basename
71
+ when /\.jpg$/ then content_type = "image/jpeg"
72
+ when /\.png$/ then content_type = "image/png"
73
+ else content_type = nil
74
+ end
75
+ end
76
+ uploaded_file(file_path(basename), content_type, basename, file_type)
77
+ end
78
+
79
+ def clear_validations
80
+ [:validate, :validate_on_create, :validate_on_update].each do |attr|
81
+ Entry.write_inheritable_attribute attr, []
82
+ Movie.write_inheritable_attribute attr, []
83
+ end
84
+ end
85
+
86
+ def file_path(filename)
87
+ File.expand_path("#{File.dirname(__FILE__)}/fixtures/#{filename}")
88
+ end
89
+
90
+ end
@@ -0,0 +1,17 @@
1
+ print "Using native MySQL\n"
2
+ require 'logger'
3
+
4
+ ActiveRecord::Base.logger = Logger.new("debug.log")
5
+
6
+ db = 'file_column_test'
7
+
8
+ ActiveRecord::Base.establish_connection(
9
+ :adapter => "mysql",
10
+ :host => "localhost",
11
+ :username => "rails",
12
+ :password => "",
13
+ :database => db,
14
+ :socket => "/var/run/mysqld/mysqld.sock"
15
+ )
16
+
17
+ load File.dirname(__FILE__) + "/fixtures/schema.rb"
@@ -0,0 +1,97 @@
1
+ require File.dirname(__FILE__) + '/abstract_unit'
2
+ require File.dirname(__FILE__) + '/fixtures/entry'
3
+
4
+ class UrlForFileColumnTest < Test::Unit::TestCase
5
+ include FileColumnHelper
6
+
7
+ def setup
8
+ Entry.file_column :image
9
+ @request = RequestMock.new
10
+ end
11
+
12
+ def test_url_for_file_column_with_temp_entry
13
+ @e = Entry.new(:image => upload("skanthak.png"))
14
+ url = url_for_file_column("e", "image")
15
+ assert_match %r{^/entry/image/tmp/\d+(\.\d+)+/skanthak.png$}, url
16
+ end
17
+
18
+ def test_url_for_file_column_with_saved_entry
19
+ @e = Entry.new(:image => upload("skanthak.png"))
20
+ assert @e.save
21
+
22
+ url = url_for_file_column("e", "image")
23
+ assert_equal "/entry/image/#{@e.id}/skanthak.png", url
24
+ end
25
+
26
+ def test_url_for_file_column_works_with_symbol
27
+ @e = Entry.new(:image => upload("skanthak.png"))
28
+ assert @e.save
29
+
30
+ url = url_for_file_column(:e, :image)
31
+ assert_equal "/entry/image/#{@e.id}/skanthak.png", url
32
+ end
33
+
34
+ def test_url_for_file_column_works_with_object
35
+ e = Entry.new(:image => upload("skanthak.png"))
36
+ assert e.save
37
+
38
+ url = url_for_file_column(e, "image")
39
+ assert_equal "/entry/image/#{e.id}/skanthak.png", url
40
+ end
41
+
42
+ def test_url_for_file_column_should_return_nil_on_no_uploaded_file
43
+ e = Entry.new
44
+ assert_nil url_for_file_column(e, "image")
45
+ end
46
+
47
+ def test_url_for_file_column_without_extension
48
+ e = Entry.new
49
+ e.image = uploaded_file(file_path("kerb.jpg"), "something/unknown", "local_filename")
50
+ assert e.save
51
+ assert_equal "/entry/image/#{e.id}/local_filename", url_for_file_column(e, "image")
52
+ end
53
+ end
54
+
55
+ class UrlForFileColumnTest < Test::Unit::TestCase
56
+ include FileColumnHelper
57
+ include ActionView::Helpers::AssetTagHelper
58
+ include ActionView::Helpers::TagHelper
59
+ include ActionView::Helpers::UrlHelper
60
+
61
+ def setup
62
+ Entry.file_column :image
63
+
64
+ # mock up some request data structures for AssetTagHelper
65
+ @request = RequestMock.new
66
+ @request.relative_url_root = "/foo/bar"
67
+ @controller = self
68
+ end
69
+
70
+ def request
71
+ @request
72
+ end
73
+
74
+ IMAGE_URL = %r{^/foo/bar/entry/image/.+/skanthak.png$}
75
+ def test_with_image_tag
76
+ e = Entry.new(:image => upload("skanthak.png"))
77
+ html = image_tag url_for_file_column(e, "image")
78
+ url = html.scan(/src=\"(.+)\"/).first.first
79
+
80
+ assert_match IMAGE_URL, url
81
+ end
82
+
83
+ def test_with_link_to_tag
84
+ e = Entry.new(:image => upload("skanthak.png"))
85
+ html = link_to "Download", url_for_file_column(e, "image", :absolute => true)
86
+ url = html.scan(/href=\"(.+)\"/).first.first
87
+
88
+ assert_match IMAGE_URL, url
89
+ end
90
+
91
+ def test_relative_url_root_not_modified
92
+ e = Entry.new(:image => upload("skanthak.png"))
93
+ url_for_file_column(e, "image", :absolute => true)
94
+
95
+ assert_equal "/foo/bar", @request.relative_url_root
96
+ end
97
+ end