parlement 0.2 → 0.3

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 (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