summaryrefslogtreecommitdiff
path: root/vendor
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-06-27 22:52:50 +0200
committererdgeist <erdgeist@erdgeist.org>2026-06-27 22:52:50 +0200
commit9a19a0494ef51cdac9a78e24d517ca48ba44c453 (patch)
tree8eaae12d8047a40e29d3ea7ff3116b5c869e04bd /vendor
parent85a01e35274b8d4d4165a7b26bd7986e211246bb (diff)
parent1853082fcd8c067390c246f9daa01a9b47387497 (diff)
Migration from Rails 2.3.5 to Rails 8.1 successful.
Merging dev branch.
Diffstat (limited to 'vendor')
-rw-r--r--vendor/plugins/acts_as_list/README23
-rw-r--r--vendor/plugins/acts_as_list/init.rb3
-rw-r--r--vendor/plugins/acts_as_list/lib/active_record/acts/list.rb256
-rw-r--r--vendor/plugins/acts_as_list/test/list_test.rb332
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/CHANGELOG212
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/MIT-LICENSE20
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/README151
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/Rakefile22
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/acts_as_taggable_on_steroids.gemspec54
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/acts_as_taggable_migration_generator.rb11
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/templates/migration.rb26
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/init.rb1
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/lib/acts_as_taggable.rb230
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/lib/tag.rb71
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/lib/tag_list.rb108
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/lib/tagging.rb12
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/lib/tags_helper.rb13
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/abstract_unit.rb102
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/acts_as_taggable_test.rb384
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazine.rb3
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazines.yml5
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photo.rb8
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photos.yml19
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/post.rb7
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/posts.yml27
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/special_post.rb2
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscription.rb4
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscriptions.yml3
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/taggings.yml107
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/tags.yml14
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/user.rb7
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/users.yml5
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/tag_list_test.rb119
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/tag_test.rb62
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/tagging_test.rb11
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/tags_helper_test.rb25
-rw-r--r--vendor/plugins/awesome_nested_set/.autotest13
-rw-r--r--vendor/plugins/awesome_nested_set/MIT-LICENSE20
-rw-r--r--vendor/plugins/awesome_nested_set/README.rdoc79
-rw-r--r--vendor/plugins/awesome_nested_set/Rakefile54
-rw-r--r--vendor/plugins/awesome_nested_set/VERSION1
-rw-r--r--vendor/plugins/awesome_nested_set/awesome_nested_set.gemspec68
-rw-r--r--vendor/plugins/awesome_nested_set/init.rb1
-rw-r--r--vendor/plugins/awesome_nested_set/lib/awesome_nested_set.rb577
-rw-r--r--vendor/plugins/awesome_nested_set/lib/awesome_nested_set/helper.rb40
-rw-r--r--vendor/plugins/awesome_nested_set/rails/init.rb12
-rw-r--r--vendor/plugins/awesome_nested_set/test/application.rb1
-rw-r--r--vendor/plugins/awesome_nested_set/test/awesome_nested_set/helper_test.rb41
-rw-r--r--vendor/plugins/awesome_nested_set/test/awesome_nested_set_test.rb748
-rw-r--r--vendor/plugins/awesome_nested_set/test/fixtures/categories.yml34
-rw-r--r--vendor/plugins/awesome_nested_set/test/fixtures/category.rb15
-rw-r--r--vendor/plugins/awesome_nested_set/test/fixtures/departments.yml3
-rw-r--r--vendor/plugins/awesome_nested_set/test/fixtures/notes.yml38
-rw-r--r--vendor/plugins/awesome_nested_set/test/test_helper.rb29
-rw-r--r--vendor/plugins/exception_notification/README111
-rw-r--r--vendor/plugins/exception_notification/init.rb4
-rw-r--r--vendor/plugins/exception_notification/lib/exception_notifiable.rb99
-rw-r--r--vendor/plugins/exception_notification/lib/exception_notifier.rb66
-rw-r--r--vendor/plugins/exception_notification/lib/exception_notifier_helper.rb78
-rw-r--r--vendor/plugins/exception_notification/test/exception_notifier_helper_test.rb61
-rw-r--r--vendor/plugins/exception_notification/test/test_helper.rb7
-rw-r--r--vendor/plugins/exception_notification/views/exception_notifier/_backtrace.rhtml1
-rw-r--r--vendor/plugins/exception_notification/views/exception_notifier/_environment.rhtml7
-rw-r--r--vendor/plugins/exception_notification/views/exception_notifier/_inspect_model.rhtml16
-rw-r--r--vendor/plugins/exception_notification/views/exception_notifier/_request.rhtml4
-rw-r--r--vendor/plugins/exception_notification/views/exception_notifier/_session.rhtml2
-rw-r--r--vendor/plugins/exception_notification/views/exception_notifier/_title.rhtml3
-rw-r--r--vendor/plugins/exception_notification/views/exception_notifier/exception_notification.rhtml6
-rw-r--r--vendor/plugins/globalize2/LICENSE21
-rw-r--r--vendor/plugins/globalize2/README.textile65
-rw-r--r--vendor/plugins/globalize2/Rakefile39
-rw-r--r--vendor/plugins/globalize2/VERSION1
-rw-r--r--vendor/plugins/globalize2/generators/db_backend.rb0
-rw-r--r--vendor/plugins/globalize2/generators/templates/db_backend_migration.rb25
-rw-r--r--vendor/plugins/globalize2/globalize2.gemspec82
-rw-r--r--vendor/plugins/globalize2/init.rb1
-rw-r--r--vendor/plugins/globalize2/lib/globalize.rb15
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record.rb194
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb80
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb25
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record/migration.rb40
-rw-r--r--vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb41
-rw-r--r--vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb25
-rw-r--r--vendor/plugins/globalize2/test/active_record/fallbacks_test.rb102
-rw-r--r--vendor/plugins/globalize2/test/active_record/migration_test.rb118
-rw-r--r--vendor/plugins/globalize2/test/active_record/sti_translated_test.rb49
-rw-r--r--vendor/plugins/globalize2/test/active_record/translates_test.rb87
-rw-r--r--vendor/plugins/globalize2/test/active_record/translation_class_test.rb30
-rw-r--r--vendor/plugins/globalize2/test/active_record/validation_tests.rb75
-rw-r--r--vendor/plugins/globalize2/test/active_record_test.rb442
-rw-r--r--vendor/plugins/globalize2/test/all.rb2
-rw-r--r--vendor/plugins/globalize2/test/data/models.rb51
-rw-r--r--vendor/plugins/globalize2/test/data/no_globalize_schema.rb11
-rw-r--r--vendor/plugins/globalize2/test/i18n/missing_translations_test.rb36
-rw-r--r--vendor/plugins/globalize2/test/test_helper.rb76
-rw-r--r--vendor/plugins/paperclip/LICENSE26
-rw-r--r--vendor/plugins/paperclip/README.rdoc172
-rw-r--r--vendor/plugins/paperclip/Rakefile77
-rw-r--r--vendor/plugins/paperclip/generators/paperclip/USAGE5
-rw-r--r--vendor/plugins/paperclip/generators/paperclip/paperclip_generator.rb27
-rw-r--r--vendor/plugins/paperclip/generators/paperclip/templates/paperclip_migration.rb.erb19
-rw-r--r--vendor/plugins/paperclip/init.rb1
-rw-r--r--vendor/plugins/paperclip/lib/paperclip.rb318
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/attachment.rb403
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/callback_compatability.rb33
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/geometry.rb115
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/iostream.rb58
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/matchers.rb4
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/matchers/have_attached_file_matcher.rb49
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb66
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_presence_matcher.rb48
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_size_matcher.rb83
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/processor.rb48
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/storage.rb236
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/thumbnail.rb70
-rw-r--r--vendor/plugins/paperclip/lib/paperclip/upfile.rb48
-rw-r--r--vendor/plugins/paperclip/paperclip.gemspec40
-rw-r--r--vendor/plugins/paperclip/shoulda_macros/paperclip.rb68
-rw-r--r--vendor/plugins/paperclip/tasks/paperclip_tasks.rake79
-rw-r--r--vendor/plugins/paperclip/test/.gitignore1
-rw-r--r--vendor/plugins/paperclip/test/attachment_test.rb742
-rw-r--r--vendor/plugins/paperclip/test/fixtures/12k.pngbin12093 -> 0 bytes
-rw-r--r--vendor/plugins/paperclip/test/fixtures/50x50.pngbin1615 -> 0 bytes
-rw-r--r--vendor/plugins/paperclip/test/fixtures/5k.pngbin4456 -> 0 bytes
-rw-r--r--vendor/plugins/paperclip/test/fixtures/bad.png1
-rw-r--r--vendor/plugins/paperclip/test/fixtures/text.txt0
-rw-r--r--vendor/plugins/paperclip/test/fixtures/twopage.pdfbin8775 -> 0 bytes
-rw-r--r--vendor/plugins/paperclip/test/geometry_test.rb168
-rw-r--r--vendor/plugins/paperclip/test/helper.rb82
-rw-r--r--vendor/plugins/paperclip/test/integration_test.rb481
-rw-r--r--vendor/plugins/paperclip/test/iostream_test.rb71
-rw-r--r--vendor/plugins/paperclip/test/matchers/have_attached_file_matcher_test.rb21
-rw-r--r--vendor/plugins/paperclip/test/matchers/validate_attachment_content_type_matcher_test.rb30
-rw-r--r--vendor/plugins/paperclip/test/matchers/validate_attachment_presence_matcher_test.rb21
-rw-r--r--vendor/plugins/paperclip/test/matchers/validate_attachment_size_matcher_test.rb50
-rw-r--r--vendor/plugins/paperclip/test/paperclip_test.rb233
-rw-r--r--vendor/plugins/paperclip/test/processor_test.rb10
-rw-r--r--vendor/plugins/paperclip/test/storage_test.rb277
-rw-r--r--vendor/plugins/paperclip/test/thumbnail_test.rb177
-rw-r--r--vendor/plugins/routing-filter/.gitignore1
-rw-r--r--vendor/plugins/routing-filter/MIT-LICENSE20
-rw-r--r--vendor/plugins/routing-filter/README.markdown123
-rw-r--r--vendor/plugins/routing-filter/init.rb1
-rw-r--r--vendor/plugins/routing-filter/lib/routing_filter.rb69
-rw-r--r--vendor/plugins/routing-filter/lib/routing_filter/base.rb15
-rw-r--r--vendor/plugins/routing-filter/lib/routing_filter/locale.rb35
-rw-r--r--vendor/plugins/routing-filter/lib/routing_filter/pagination.rb19
-rw-r--r--vendor/plugins/routing-filter/spec/generation_spec.rb283
-rw-r--r--vendor/plugins/routing-filter/spec/recognition_spec.rb76
-rw-r--r--vendor/plugins/routing-filter/spec/routing_filter_spec.rb70
-rw-r--r--vendor/plugins/routing-filter/spec/spec.opts4
-rw-r--r--vendor/plugins/routing-filter/spec/spec_helper.rb49
-rw-r--r--vendor/plugins/will_paginate/.gitignore4
-rw-r--r--vendor/plugins/will_paginate/.manifest49
-rw-r--r--vendor/plugins/will_paginate/CHANGELOG.rdoc110
-rw-r--r--vendor/plugins/will_paginate/LICENSE18
-rw-r--r--vendor/plugins/will_paginate/README.rdoc107
-rw-r--r--vendor/plugins/will_paginate/Rakefile62
-rw-r--r--vendor/plugins/will_paginate/examples/apple-circle.gifbin178 -> 0 bytes
-rw-r--r--vendor/plugins/will_paginate/examples/index.haml69
-rw-r--r--vendor/plugins/will_paginate/examples/index.html92
-rw-r--r--vendor/plugins/will_paginate/examples/pagination.css90
-rw-r--r--vendor/plugins/will_paginate/examples/pagination.sass91
-rw-r--r--vendor/plugins/will_paginate/init.rb1
-rw-r--r--vendor/plugins/will_paginate/lib/will_paginate.rb78
-rw-r--r--vendor/plugins/will_paginate/lib/will_paginate/array.rb16
-rw-r--r--vendor/plugins/will_paginate/lib/will_paginate/collection.rb146
-rw-r--r--vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb32
-rw-r--r--vendor/plugins/will_paginate/lib/will_paginate/finder.rb264
-rw-r--r--vendor/plugins/will_paginate/lib/will_paginate/named_scope.rb170
-rw-r--r--vendor/plugins/will_paginate/lib/will_paginate/named_scope_patch.rb37
-rw-r--r--vendor/plugins/will_paginate/lib/will_paginate/version.rb9
-rw-r--r--vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb389
-rw-r--r--vendor/plugins/will_paginate/test/boot.rb21
-rw-r--r--vendor/plugins/will_paginate/test/collection_test.rb143
-rwxr-xr-xvendor/plugins/will_paginate/test/console8
-rw-r--r--vendor/plugins/will_paginate/test/finder_test.rb476
-rw-r--r--vendor/plugins/will_paginate/test/fixtures/admin.rb3
-rw-r--r--vendor/plugins/will_paginate/test/fixtures/developer.rb14
-rw-r--r--vendor/plugins/will_paginate/test/fixtures/developers_projects.yml13
-rw-r--r--vendor/plugins/will_paginate/test/fixtures/project.rb15
-rw-r--r--vendor/plugins/will_paginate/test/fixtures/projects.yml6
-rw-r--r--vendor/plugins/will_paginate/test/fixtures/replies.yml29
-rw-r--r--vendor/plugins/will_paginate/test/fixtures/reply.rb7
-rw-r--r--vendor/plugins/will_paginate/test/fixtures/topic.rb10
-rw-r--r--vendor/plugins/will_paginate/test/fixtures/topics.yml30
-rw-r--r--vendor/plugins/will_paginate/test/fixtures/user.rb2
-rw-r--r--vendor/plugins/will_paginate/test/fixtures/users.yml35
-rw-r--r--vendor/plugins/will_paginate/test/helper.rb37
-rw-r--r--vendor/plugins/will_paginate/test/lib/activerecord_test_case.rb36
-rw-r--r--vendor/plugins/will_paginate/test/lib/activerecord_test_connector.rb75
-rw-r--r--vendor/plugins/will_paginate/test/lib/load_fixtures.rb11
-rw-r--r--vendor/plugins/will_paginate/test/lib/view_test_process.rb171
-rw-r--r--vendor/plugins/will_paginate/test/tasks.rake59
-rw-r--r--vendor/plugins/will_paginate/test/view_test.rb365
-rw-r--r--vendor/plugins/will_paginate/will_paginate.gemspec20
196 files changed, 0 insertions, 15074 deletions
diff --git a/vendor/plugins/acts_as_list/README b/vendor/plugins/acts_as_list/README
deleted file mode 100644
index 36ae318..0000000
--- a/vendor/plugins/acts_as_list/README
+++ /dev/null
@@ -1,23 +0,0 @@
1ActsAsList
2==========
3
4This acts_as extension provides the capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a +position+ column defined as an integer on the mapped database table.
5
6
7Example
8=======
9
10 class TodoList < ActiveRecord::Base
11 has_many :todo_items, :order => "position"
12 end
13
14 class TodoItem < ActiveRecord::Base
15 belongs_to :todo_list
16 acts_as_list :scope => :todo_list
17 end
18
19 todo_list.first.move_to_bottom
20 todo_list.last.move_higher
21
22
23Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license \ No newline at end of file
diff --git a/vendor/plugins/acts_as_list/init.rb b/vendor/plugins/acts_as_list/init.rb
deleted file mode 100644
index eb87e87..0000000
--- a/vendor/plugins/acts_as_list/init.rb
+++ /dev/null
@@ -1,3 +0,0 @@
1$:.unshift "#{File.dirname(__FILE__)}/lib"
2require 'active_record/acts/list'
3ActiveRecord::Base.class_eval { include ActiveRecord::Acts::List }
diff --git a/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb b/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb
deleted file mode 100644
index 00d8692..0000000
--- a/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb
+++ /dev/null
@@ -1,256 +0,0 @@
1module ActiveRecord
2 module Acts #:nodoc:
3 module List #:nodoc:
4 def self.included(base)
5 base.extend(ClassMethods)
6 end
7
8 # This +acts_as+ extension provides the capabilities for sorting and reordering a number of objects in a list.
9 # The class that has this specified needs to have a +position+ column defined as an integer on
10 # the mapped database table.
11 #
12 # Todo list example:
13 #
14 # class TodoList < ActiveRecord::Base
15 # has_many :todo_items, :order => "position"
16 # end
17 #
18 # class TodoItem < ActiveRecord::Base
19 # belongs_to :todo_list
20 # acts_as_list :scope => :todo_list
21 # end
22 #
23 # todo_list.first.move_to_bottom
24 # todo_list.last.move_higher
25 module ClassMethods
26 # Configuration options are:
27 #
28 # * +column+ - specifies the column name to use for keeping the position integer (default: +position+)
29 # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>
30 # (if it hasn't already been added) and use that as the foreign key restriction. It's also possible
31 # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
32 # Example: <tt>acts_as_list :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
33 def acts_as_list(options = {})
34 configuration = { :column => "position", :scope => "1 = 1" }
35 configuration.update(options) if options.is_a?(Hash)
36
37 configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
38
39 if configuration[:scope].is_a?(Symbol)
40 scope_condition_method = %(
41 def scope_condition
42 if #{configuration[:scope].to_s}.nil?
43 "#{configuration[:scope].to_s} IS NULL"
44 else
45 "#{configuration[:scope].to_s} = \#{#{configuration[:scope].to_s}}"
46 end
47 end
48 )
49 else
50 scope_condition_method = "def scope_condition() \"#{configuration[:scope]}\" end"
51 end
52
53 class_eval <<-EOV
54 include ActiveRecord::Acts::List::InstanceMethods
55
56 def acts_as_list_class
57 ::#{self.name}
58 end
59
60 def position_column
61 '#{configuration[:column]}'
62 end
63
64 #{scope_condition_method}
65
66 before_destroy :remove_from_list
67 before_create :add_to_list_bottom
68 EOV
69 end
70 end
71
72 # All the methods available to a record that has had <tt>acts_as_list</tt> specified. Each method works
73 # by assuming the object to be the item in the list, so <tt>chapter.move_lower</tt> would move that chapter
74 # lower in the list of all chapters. Likewise, <tt>chapter.first?</tt> would return +true+ if that chapter is
75 # the first in the list of all chapters.
76 module InstanceMethods
77 # Insert the item at the given position (defaults to the top position of 1).
78 def insert_at(position = 1)
79 insert_at_position(position)
80 end
81
82 # Swap positions with the next lower item, if one exists.
83 def move_lower
84 return unless lower_item
85
86 acts_as_list_class.transaction do
87 lower_item.decrement_position
88 increment_position
89 end
90 end
91
92 # Swap positions with the next higher item, if one exists.
93 def move_higher
94 return unless higher_item
95
96 acts_as_list_class.transaction do
97 higher_item.increment_position
98 decrement_position
99 end
100 end
101
102 # Move to the bottom of the list. If the item is already in the list, the items below it have their
103 # position adjusted accordingly.
104 def move_to_bottom
105 return unless in_list?
106 acts_as_list_class.transaction do
107 decrement_positions_on_lower_items
108 assume_bottom_position
109 end
110 end
111
112 # Move to the top of the list. If the item is already in the list, the items above it have their
113 # position adjusted accordingly.
114 def move_to_top
115 return unless in_list?
116 acts_as_list_class.transaction do
117 increment_positions_on_higher_items
118 assume_top_position
119 end
120 end
121
122 # Removes the item from the list.
123 def remove_from_list
124 if in_list?
125 decrement_positions_on_lower_items
126 update_attribute position_column, nil
127 end
128 end
129
130 # Increase the position of this item without adjusting the rest of the list.
131 def increment_position
132 return unless in_list?
133 update_attribute position_column, self.send(position_column).to_i + 1
134 end
135
136 # Decrease the position of this item without adjusting the rest of the list.
137 def decrement_position
138 return unless in_list?
139 update_attribute position_column, self.send(position_column).to_i - 1
140 end
141
142 # Return +true+ if this object is the first in the list.
143 def first?
144 return false unless in_list?
145 self.send(position_column) == 1
146 end
147
148 # Return +true+ if this object is the last in the list.
149 def last?
150 return false unless in_list?
151 self.send(position_column) == bottom_position_in_list
152 end
153
154 # Return the next higher item in the list.
155 def higher_item
156 return nil unless in_list?
157 acts_as_list_class.find(:first, :conditions =>
158 "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i - 1).to_s}"
159 )
160 end
161
162 # Return the next lower item in the list.
163 def lower_item
164 return nil unless in_list?
165 acts_as_list_class.find(:first, :conditions =>
166 "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i + 1).to_s}"
167 )
168 end
169
170 # Test if this record is in a list
171 def in_list?
172 !send(position_column).nil?
173 end
174
175 private
176 def add_to_list_top
177 increment_positions_on_all_items
178 end
179
180 def add_to_list_bottom
181 self[position_column] = bottom_position_in_list.to_i + 1
182 end
183
184 # Overwrite this method to define the scope of the list changes
185 def scope_condition() "1" end
186
187 # Returns the bottom position number in the list.
188 # bottom_position_in_list # => 2
189 def bottom_position_in_list(except = nil)
190 item = bottom_item(except)
191 item ? item.send(position_column) : 0
192 end
193
194 # Returns the bottom item
195 def bottom_item(except = nil)
196 conditions = scope_condition
197 conditions = "#{conditions} AND #{self.class.primary_key} != #{except.id}" if except
198 acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
199 end
200
201 # Forces item to assume the bottom position in the list.
202 def assume_bottom_position
203 update_attribute(position_column, bottom_position_in_list(self).to_i + 1)
204 end
205
206 # Forces item to assume the top position in the list.
207 def assume_top_position
208 update_attribute(position_column, 1)
209 end
210
211 # This has the effect of moving all the higher items up one.
212 def decrement_positions_on_higher_items(position)
213 acts_as_list_class.update_all(
214 "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} <= #{position}"
215 )
216 end
217
218 # This has the effect of moving all the lower items up one.
219 def decrement_positions_on_lower_items
220 return unless in_list?
221 acts_as_list_class.update_all(
222 "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
223 )
224 end
225
226 # This has the effect of moving all the higher items down one.
227 def increment_positions_on_higher_items
228 return unless in_list?
229 acts_as_list_class.update_all(
230 "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
231 )
232 end
233
234 # This has the effect of moving all the lower items down one.
235 def increment_positions_on_lower_items(position)
236 acts_as_list_class.update_all(
237 "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{position}"
238 )
239 end
240
241 # Increments position (<tt>position_column</tt>) of all items in the list.
242 def increment_positions_on_all_items
243 acts_as_list_class.update_all(
244 "#{position_column} = (#{position_column} + 1)", "#{scope_condition}"
245 )
246 end
247
248 def insert_at_position(position)
249 remove_from_list
250 increment_positions_on_lower_items(position)
251 self.update_attribute(position_column, position)
252 end
253 end
254 end
255 end
256end
diff --git a/vendor/plugins/acts_as_list/test/list_test.rb b/vendor/plugins/acts_as_list/test/list_test.rb
deleted file mode 100644
index e89cb8e..0000000
--- a/vendor/plugins/acts_as_list/test/list_test.rb
+++ /dev/null
@@ -1,332 +0,0 @@
1require 'test/unit'
2
3require 'rubygems'
4gem 'activerecord', '>= 1.15.4.7794'
5require 'active_record'
6
7require "#{File.dirname(__FILE__)}/../init"
8
9ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
10
11def setup_db
12 ActiveRecord::Schema.define(:version => 1) do
13 create_table :mixins do |t|
14 t.column :pos, :integer
15 t.column :parent_id, :integer
16 t.column :created_at, :datetime
17 t.column :updated_at, :datetime
18 end
19 end
20end
21
22def teardown_db
23 ActiveRecord::Base.connection.tables.each do |table|
24 ActiveRecord::Base.connection.drop_table(table)
25 end
26end
27
28class Mixin < ActiveRecord::Base
29end
30
31class ListMixin < Mixin
32 acts_as_list :column => "pos", :scope => :parent
33
34 def self.table_name() "mixins" end
35end
36
37class ListMixinSub1 < ListMixin
38end
39
40class ListMixinSub2 < ListMixin
41end
42
43class ListWithStringScopeMixin < ActiveRecord::Base
44 acts_as_list :column => "pos", :scope => 'parent_id = #{parent_id}'
45
46 def self.table_name() "mixins" end
47end
48
49
50class ListTest < Test::Unit::TestCase
51
52 def setup
53 setup_db
54 (1..4).each { |counter| ListMixin.create! :pos => counter, :parent_id => 5 }
55 end
56
57 def teardown
58 teardown_db
59 end
60
61 def test_reordering
62 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
63
64 ListMixin.find(2).move_lower
65 assert_equal [1, 3, 2, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
66
67 ListMixin.find(2).move_higher
68 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
69
70 ListMixin.find(1).move_to_bottom
71 assert_equal [2, 3, 4, 1], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
72
73 ListMixin.find(1).move_to_top
74 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
75
76 ListMixin.find(2).move_to_bottom
77 assert_equal [1, 3, 4, 2], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
78
79 ListMixin.find(4).move_to_top
80 assert_equal [4, 1, 3, 2], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
81 end
82
83 def test_move_to_bottom_with_next_to_last_item
84 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
85 ListMixin.find(3).move_to_bottom
86 assert_equal [1, 2, 4, 3], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
87 end
88
89 def test_next_prev
90 assert_equal ListMixin.find(2), ListMixin.find(1).lower_item
91 assert_nil ListMixin.find(1).higher_item
92 assert_equal ListMixin.find(3), ListMixin.find(4).higher_item
93 assert_nil ListMixin.find(4).lower_item
94 end
95
96 def test_injection
97 item = ListMixin.new(:parent_id => 1)
98 assert_equal "parent_id = 1", item.scope_condition
99 assert_equal "pos", item.position_column
100 end
101
102 def test_insert
103 new = ListMixin.create(:parent_id => 20)
104 assert_equal 1, new.pos
105 assert new.first?
106 assert new.last?
107
108 new = ListMixin.create(:parent_id => 20)
109 assert_equal 2, new.pos
110 assert !new.first?
111 assert new.last?
112
113 new = ListMixin.create(:parent_id => 20)
114 assert_equal 3, new.pos
115 assert !new.first?
116 assert new.last?
117
118 new = ListMixin.create(:parent_id => 0)
119 assert_equal 1, new.pos
120 assert new.first?
121 assert new.last?
122 end
123
124 def test_insert_at
125 new = ListMixin.create(:parent_id => 20)
126 assert_equal 1, new.pos
127
128 new = ListMixin.create(:parent_id => 20)
129 assert_equal 2, new.pos
130
131 new = ListMixin.create(:parent_id => 20)
132 assert_equal 3, new.pos
133
134 new4 = ListMixin.create(:parent_id => 20)
135 assert_equal 4, new4.pos
136
137 new4.insert_at(3)
138 assert_equal 3, new4.pos
139
140 new.reload
141 assert_equal 4, new.pos
142
143 new.insert_at(2)
144 assert_equal 2, new.pos
145
146 new4.reload
147 assert_equal 4, new4.pos
148
149 new5 = ListMixin.create(:parent_id => 20)
150 assert_equal 5, new5.pos
151
152 new5.insert_at(1)
153 assert_equal 1, new5.pos
154
155 new4.reload
156 assert_equal 5, new4.pos
157 end
158
159 def test_delete_middle
160 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
161
162 ListMixin.find(2).destroy
163
164 assert_equal [1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
165
166 assert_equal 1, ListMixin.find(1).pos
167 assert_equal 2, ListMixin.find(3).pos
168 assert_equal 3, ListMixin.find(4).pos
169
170 ListMixin.find(1).destroy
171
172 assert_equal [3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
173
174 assert_equal 1, ListMixin.find(3).pos
175 assert_equal 2, ListMixin.find(4).pos
176 end
177
178 def test_with_string_based_scope
179 new = ListWithStringScopeMixin.create(:parent_id => 500)
180 assert_equal 1, new.pos
181 assert new.first?
182 assert new.last?
183 end
184
185 def test_nil_scope
186 new1, new2, new3 = ListMixin.create, ListMixin.create, ListMixin.create
187 new2.move_higher
188 assert_equal [new2, new1, new3], ListMixin.find(:all, :conditions => 'parent_id IS NULL', :order => 'pos')
189 end
190
191
192 def test_remove_from_list_should_then_fail_in_list?
193 assert_equal true, ListMixin.find(1).in_list?
194 ListMixin.find(1).remove_from_list
195 assert_equal false, ListMixin.find(1).in_list?
196 end
197
198 def test_remove_from_list_should_set_position_to_nil
199 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
200
201 ListMixin.find(2).remove_from_list
202
203 assert_equal [2, 1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
204
205 assert_equal 1, ListMixin.find(1).pos
206 assert_equal nil, ListMixin.find(2).pos
207 assert_equal 2, ListMixin.find(3).pos
208 assert_equal 3, ListMixin.find(4).pos
209 end
210
211 def test_remove_before_destroy_does_not_shift_lower_items_twice
212 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
213
214 ListMixin.find(2).remove_from_list
215 ListMixin.find(2).destroy
216
217 assert_equal [1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5', :order => 'pos').map(&:id)
218
219 assert_equal 1, ListMixin.find(1).pos
220 assert_equal 2, ListMixin.find(3).pos
221 assert_equal 3, ListMixin.find(4).pos
222 end
223
224end
225
226class ListSubTest < Test::Unit::TestCase
227
228 def setup
229 setup_db
230 (1..4).each { |i| ((i % 2 == 1) ? ListMixinSub1 : ListMixinSub2).create! :pos => i, :parent_id => 5000 }
231 end
232
233 def teardown
234 teardown_db
235 end
236
237 def test_reordering
238 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
239
240 ListMixin.find(2).move_lower
241 assert_equal [1, 3, 2, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
242
243 ListMixin.find(2).move_higher
244 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
245
246 ListMixin.find(1).move_to_bottom
247 assert_equal [2, 3, 4, 1], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
248
249 ListMixin.find(1).move_to_top
250 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
251
252 ListMixin.find(2).move_to_bottom
253 assert_equal [1, 3, 4, 2], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
254
255 ListMixin.find(4).move_to_top
256 assert_equal [4, 1, 3, 2], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
257 end
258
259 def test_move_to_bottom_with_next_to_last_item
260 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
261 ListMixin.find(3).move_to_bottom
262 assert_equal [1, 2, 4, 3], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
263 end
264
265 def test_next_prev
266 assert_equal ListMixin.find(2), ListMixin.find(1).lower_item
267 assert_nil ListMixin.find(1).higher_item
268 assert_equal ListMixin.find(3), ListMixin.find(4).higher_item
269 assert_nil ListMixin.find(4).lower_item
270 end
271
272 def test_injection
273 item = ListMixin.new("parent_id"=>1)
274 assert_equal "parent_id = 1", item.scope_condition
275 assert_equal "pos", item.position_column
276 end
277
278 def test_insert_at
279 new = ListMixin.create("parent_id" => 20)
280 assert_equal 1, new.pos
281
282 new = ListMixinSub1.create("parent_id" => 20)
283 assert_equal 2, new.pos
284
285 new = ListMixinSub2.create("parent_id" => 20)
286 assert_equal 3, new.pos
287
288 new4 = ListMixin.create("parent_id" => 20)
289 assert_equal 4, new4.pos
290
291 new4.insert_at(3)
292 assert_equal 3, new4.pos
293
294 new.reload
295 assert_equal 4, new.pos
296
297 new.insert_at(2)
298 assert_equal 2, new.pos
299
300 new4.reload
301 assert_equal 4, new4.pos
302
303 new5 = ListMixinSub1.create("parent_id" => 20)
304 assert_equal 5, new5.pos
305
306 new5.insert_at(1)
307 assert_equal 1, new5.pos
308
309 new4.reload
310 assert_equal 5, new4.pos
311 end
312
313 def test_delete_middle
314 assert_equal [1, 2, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
315
316 ListMixin.find(2).destroy
317
318 assert_equal [1, 3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
319
320 assert_equal 1, ListMixin.find(1).pos
321 assert_equal 2, ListMixin.find(3).pos
322 assert_equal 3, ListMixin.find(4).pos
323
324 ListMixin.find(1).destroy
325
326 assert_equal [3, 4], ListMixin.find(:all, :conditions => 'parent_id = 5000', :order => 'pos').map(&:id)
327
328 assert_equal 1, ListMixin.find(3).pos
329 assert_equal 2, ListMixin.find(4).pos
330 end
331
332end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/CHANGELOG b/vendor/plugins/acts_as_taggable_on_steroids/CHANGELOG
deleted file mode 100644
index 5391c15..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/CHANGELOG
+++ /dev/null
@@ -1,212 +0,0 @@
1[11 Jun 09]
2
3* Remove deprecated TagCountsExtension.
4
5* Update tests to use foxy fixtures [Jonas Wagner]
6
7* Allow hash conditions to be passed to Tag.counts [Jonas Wagner]
8
9[3 Jun 09]
10
11* Upgrade tests for Rails 2.3
12
13[18 Mar 09]
14
15* Change callbacks used to save tags.
16
17[18 Feb 09]
18
19* Greatly improve speed when using find_tagged_with and :match_all [notonthehighstreet.com].
20
21[17 Sep 08]
22
23* Sanitize scope conditions in find_options_for_tag_counts [Rémy-Christophe Schermesser]
24
25[23 Aug 08]
26
27* Fix tag_counts instance method when no tags are present.
28
29* Make tag_counts instance_method merge any :conditions passed to it.
30
31[30 Mar 08]
32
33* Make TagList.from accept array arguments.
34
35[29 Mar 08]
36
37* Improve parsing of quotes inside tags [Arturas Slajus].
38
39* Add Tag.counts method.
40
41[28 Mar 08]
42
43* Make Tag#taggings :dependent => :destroy.
44
45[27 Mar 08]
46
47* Fix documentation for tag_counts.
48
49[18 Mar 08]
50
51* Add TagList#toggle [Pete Yandell].
52
53# Add find_related_tags method [Austin Foncaier].
54
55[30 Jan 08]
56
57* Fix Tag.destroy_unused on Rails 2.0.
58
59[23 October 2007]
60
61* Make find_options_for_tag_counts and find_options_for_tagged_with dup their options.
62
63* Apply conditions properly in find_options_for_tag_counts.
64
65* Fix tag_cloud when no tags are present.
66
67[22 October 2007]
68
69* Fix find_tagged_with using :match_all and :include.
70
71* Use inner joins instead of left outer joins.
72
73[15 October 2007]
74
75* Make find_tagged_with correctly apply :conditions
76
77* Add Tag.destroy_unused option.
78
79[11 October 2007]
80
81* Make tag_counts work correctly with STI.
82
83[3 October 2007]
84
85* Improve documentation.
86
87* Fix TagsHelper and test.
88
89[2 October 2007]
90
91* Remove TagList.parse, use TagList.from instead.
92
93* Add :parse option to TagList#new, TagList#add, and TagList#remove.
94
95 tag_list = TagList.new("One, Two", :parse => true) # ["One", "Two"]
96
97 tag_list # ["One", "Two"]
98 tag_list.add("Three, Four", :parse => true) # ["One", "Two", "Three", "Four"]
99
100* Remove TagList#names.
101
102[29 September 2007]
103
104* Add TagsHelper to assist with generating tag clouds and provide a simple example.
105
106[27 September 2007]
107
108* Add #tag_counts method to get tag counts for a specific object's tags.
109
110* BACKWARDS INCOMPATIBILITY: Rename #find_options_for_tagged_with to #find_options_for_find_tagged_with
111
112[17 September 2007]
113
114* Fix clearing of cached tag list when all tags removed.
115
116[12 September 2007]
117
118* Make the TagList class inherit from Array.
119
120* Deprecate obsolete TagList#names.
121
122[6 September 2007]
123
124* Add TagList#include? and TagList#empty?
125
126[26 August 2007]
127
128* Remove deprecated Tag.delimiter. Use TagList.delimiter instead.
129
130[25 August 2007]
131
132* Make tag_counts work with has_many :through
133
134[23 August 2007]
135
136* Make search comparisons case-insensitive across different databases. [Moisés Machado]
137
138* Improve compatiblity with STI. [Moisés Machado]
139
140[25 July 2007]
141
142* Respect custom table names for the Tag and Tagging classes.
143
144* Fix the :exclude option for find_tagged_with
145
146[17 July 2007]
147
148* Make the migration work on edge rails
149
150[8 July 2007]
151
152* find_options_for_tagged_with should not alter its arguments
153
154[1 July 2007]
155
156* Fix incorrect tagging when the case of the tag list is changed.
157
158* Fix deprecated Tag.delimiter accessor.
159
160[23 June 2007]
161
162* Add validation to Tag model.
163
164* find_options_for_tagged_with should always return a hash.
165
166* find_tagged_with passing in no tags should return an empty array.
167
168* Improve compatibility with PostgreSQL.
169
170[21 June 2007]
171
172* Remove extra .rb from generated migration file name.
173
174[15 June 2007]
175
176* Introduce TagList class.
177
178* Various cleanups and improvements.
179
180* Use TagList.delimiter now, not Tag.delimiter. Tag.delimiter will be removed at some stage.
181
182[11 June 2007]
183
184* Restructure the creation of the options for find_tagged_with [Thijs Cadier]
185
186* Add an example migration with a generator.
187
188* Add caching.
189
190* Fix compatibility with Ruby < 1.8.6
191
192[23 April 2007]
193
194* Make tag_list to respect Tag.delimiter
195
196[31 March 2007]
197
198* Add Tag.delimiter accessor to change how tags are parsed.
199
200* Fix :include => :tags when used with find_tagged_with
201
202[7 March 2007]
203
204* Fix tag_counts for SQLServer [Brad Young]
205
206[21 Feb 2007]
207
208* Use scoping instead of TagCountsExtension [Michael Schuerig]
209
210[7 Jan 2007]
211
212* Add :match_all to find_tagged_with [Michael Sheakoski]
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/MIT-LICENSE b/vendor/plugins/acts_as_taggable_on_steroids/MIT-LICENSE
deleted file mode 100644
index 602bda2..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/MIT-LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
1Copyright (c) 2006 Jonathan Viney
2
3Permission is hereby granted, free of charge, to any person obtaining
4a copy of this software and associated documentation files (the
5"Software"), to deal in the Software without restriction, including
6without limitation the rights to use, copy, modify, merge, publish,
7distribute, sublicense, and/or sell copies of the Software, and to
8permit persons to whom the Software is furnished to do so, subject to
9the following conditions:
10
11The above copyright notice and this permission notice shall be
12included in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/README b/vendor/plugins/acts_as_taggable_on_steroids/README
deleted file mode 100644
index b794030..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/README
+++ /dev/null
@@ -1,151 +0,0 @@
1= acts_as_taggable_on_steroids
2
3If you find this plugin useful, please consider a donation to show your support!
4
5 http://www.paypal.com/cgi-bin/webscr?cmd=_send-money
6
7 Email address: jonathan.viney@gmail.com
8
9== Instructions
10
11This plugin is based on acts_as_taggable by DHH but includes extras
12such as tests, smarter tag assignment, and tag cloud calculations.
13
14== Installation
15
16 ruby script/plugin install git://github.com/jviney/acts_as_taggable_on_steroids.git
17
18== Usage
19
20=== Prepare database
21
22Generate and apply the migration:
23
24 ruby script/generate acts_as_taggable_migration
25 rake db:migrate
26
27=== Basic tagging
28
29Let's suppose users have many posts and we want those posts to have tags.
30The first step is to add +acts_as_taggable+ to the Post class:
31
32 class Post < ActiveRecord::Base
33 acts_as_taggable
34
35 belongs_to :user
36 end
37
38We can now use the tagging methods provided by acts_as_taggable, <tt>#tag_list</tt> and <tt>#tag_list=</tt>. Both these
39methods work like regular attribute accessors.
40
41 p = Post.find(:first)
42 p.tag_list # []
43 p.tag_list = "Funny, Silly"
44 p.save
45 p.tag_list # ["Funny", "Silly"]
46
47You can also add or remove arrays of tags.
48
49 p.tag_list.add("Great", "Awful")
50 p.tag_list.remove("Funny")
51
52In your views you should use something like the following:
53
54 <%= f.label :tag_list %>
55 <%= f.text_field :tag_list, :size => 80 %>
56
57=== Finding tagged objects
58
59To retrieve objects tagged with a certain tag, use find_tagged_with.
60
61 Post.find_tagged_with('Funny, Silly')
62
63By default, find_tagged_with will find objects that have any of the given tags. To
64find only objects that are tagged with all the given tags, use match_all.
65
66 Post.find_tagged_with('Funny, Silly', :match_all => true)
67
68See <tt>ActiveRecord::Acts::Taggable::InstanceMethods</tt> for more methods and options.
69
70=== Tag cloud calculations
71
72To construct tag clouds, the frequency of each tag needs to be calculated.
73Because we specified +acts_as_taggable+ on the <tt>Post</tt> class, we can
74get a calculation of all the tag counts by using <tt>Post.tag_counts</tt>. But what if we wanted a tag count for
75an single user's posts? To achieve this we call tag_counts on the association:
76
77 User.find(:first).posts.tag_counts
78
79A helper is included to assist with generating tag clouds. Include it in your helper file:
80
81 module ApplicationHelper
82 include TagsHelper
83 end
84
85You can also use the <tt>counts</tt> method on <tt>Tag</tt> to get the counts for all tags in the database.
86
87 Tag.counts
88
89Here is an example that generates a tag cloud.
90
91Controller:
92
93 class PostController < ApplicationController
94 def tag_cloud
95 @tags = Post.tag_counts
96 end
97 end
98
99View:
100 <% tag_cloud @tags, %w(css1 css2 css3 css4) do |tag, css_class| %>
101 <%= link_to tag.name, { :action => :tag, :id => tag.name }, :class => css_class %>
102 <% end %>
103
104CSS:
105
106 .css1 { font-size: 1.0em; }
107 .css2 { font-size: 1.2em; }
108 .css3 { font-size: 1.4em; }
109 .css4 { font-size: 1.6em; }
110
111=== Caching
112
113It is useful to cache the list of tags to reduce the number of queries executed. To do this,
114add a column named <tt>cached_tag_list</tt> to the model which is being tagged. The column should be long enough to hold
115the full tag list and must have a default value of null, not an empty string.
116
117 class CachePostTagList < ActiveRecord::Migration
118 def self.up
119 add_column :posts, :cached_tag_list, :string
120 end
121 end
122
123 class Post < ActiveRecord::Base
124 acts_as_taggable
125
126 # The caching column defaults to cached_tag_list, but can be changed:
127 #
128 # set_cached_tag_list_column_name "my_caching_column_name"
129 end
130
131The details of the caching are handled for you. Just continue to use the tag_list accessor as you normally would.
132Note that the cached tag list will not be updated if you directly create Tagging objects or manually append to the
133<tt>tags</tt> or <tt>taggings</tt> associations. To update the cached tag list you should call <tt>save_cached_tag_list</tt> manually.
134
135=== Delimiter
136
137If you want to change the delimiter used to parse and present tags, set TagList.delimiter.
138For example, to use spaces instead of commas, add the following to config/environment.rb:
139
140 TagList.delimiter = " "
141
142=== Unused tags
143
144Set Tag.destroy_unused to remove tags when they are no longer being
145used to tag any objects. Defaults to false.
146
147 Tag.destroy_unused = true
148
149=== Other
150
151Problems, comments, and suggestions all welcome. jonathan.viney@gmail.com
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/Rakefile b/vendor/plugins/acts_as_taggable_on_steroids/Rakefile
deleted file mode 100644
index 93c2706..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/Rakefile
+++ /dev/null
@@ -1,22 +0,0 @@
1require 'rake'
2require 'rake/testtask'
3require 'rake/rdoctask'
4
5desc 'Default: run unit tests.'
6task :default => :test
7
8desc 'Test the acts_as_taggable_on_steroids plugin.'
9Rake::TestTask.new(:test) do |t|
10 t.libs << 'test'
11 t.pattern = 'test/**/*_test.rb'
12 t.verbose = true
13end
14
15desc 'Generate documentation for the acts_as_taggable_on_steroids plugin.'
16Rake::RDocTask.new(:rdoc) do |rdoc|
17 rdoc.rdoc_dir = 'rdoc'
18 rdoc.title = 'Acts As Taggable On Steroids'
19 rdoc.options << '--line-numbers' << '--inline-source'
20 rdoc.rdoc_files.include('README')
21 rdoc.rdoc_files.include('lib/**/*.rb')
22end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/acts_as_taggable_on_steroids.gemspec b/vendor/plugins/acts_as_taggable_on_steroids/acts_as_taggable_on_steroids.gemspec
deleted file mode 100644
index f91ad4d..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/acts_as_taggable_on_steroids.gemspec
+++ /dev/null
@@ -1,54 +0,0 @@
1Gem::Specification.new do |s|
2 s.name = "acts_as_taggable_on_steroids"
3 s.version = "1.1"
4 s.date = "2009-06-11"
5 s.summary = "Rails plugin that is based on acts_as_taggable by DHH but includes extras such as tests, smarter tag assignment, and tag cloud calculations."
6 s.email = "jonathan.viney@gmail.com"
7 s.homepage = "http://github.com/jviney/acts_as_taggable_on_steroids"
8 s.description = "Rails plugin that is based on acts_as_taggable by DHH but includes extras such as tests, smarter tag assignment, and tag cloud calculations."
9 s.has_rdoc = true
10 s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
11 s.rubyforge_project = "acts_as_taggable_on_steroids"
12 s.authors = "Jonathan Viney"
13 s.files = [
14 "acts_as_taggable_on_steroids.gemspec",
15 "CHANGELOG",
16 "generators/acts_as_taggable_migration",
17 "generators/acts_as_taggable_migration/acts_as_taggable_migration_generator.rb",
18 "generators/acts_as_taggable_migration/templates",
19 "generators/acts_as_taggable_migration/templates/migration.rb",
20 "init.rb",
21 "lib/acts_as_taggable.rb",
22 "lib/tag.rb",
23 "lib/tag_list.rb",
24 "lib/tagging.rb",
25 "lib/tags_helper.rb",
26 "MIT-LICENSE",
27 "Rakefile",
28 "README",
29 ]
30 s.test_files = [
31 "test/abstract_unit.rb",
32 "test/acts_as_taggable_test.rb",
33 "test/database.yml",
34 "test/fixtures",
35 "test/fixtures/magazine.rb",
36 "test/fixtures/magazines.yml",
37 "test/fixtures/photo.rb",
38 "test/fixtures/photos.yml",
39 "test/fixtures/post.rb",
40 "test/fixtures/posts.yml",
41 "test/fixtures/special_post.rb",
42 "test/fixtures/subscription.rb",
43 "test/fixtures/subscriptions.yml",
44 "test/fixtures/taggings.yml",
45 "test/fixtures/tags.yml",
46 "test/fixtures/user.rb",
47 "test/fixtures/users.yml",
48 "test/schema.rb",
49 "test/tag_list_test.rb",
50 "test/tag_test.rb",
51 "test/tagging_test.rb",
52 "test/tags_helper_test.rb"
53 ]
54end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/acts_as_taggable_migration_generator.rb b/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/acts_as_taggable_migration_generator.rb
deleted file mode 100644
index be9b39c..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/acts_as_taggable_migration_generator.rb
+++ /dev/null
@@ -1,11 +0,0 @@
1class ActsAsTaggableMigrationGenerator < Rails::Generator::Base
2 def manifest
3 record do |m|
4 m.migration_template 'migration.rb', 'db/migrate'
5 end
6 end
7
8 def file_name
9 "acts_as_taggable_migration"
10 end
11end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/templates/migration.rb b/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/templates/migration.rb
deleted file mode 100644
index ea0c2cc..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/templates/migration.rb
+++ /dev/null
@@ -1,26 +0,0 @@
1class ActsAsTaggableMigration < ActiveRecord::Migration
2 def self.up
3 create_table :tags do |t|
4 t.column :name, :string
5 end
6
7 create_table :taggings do |t|
8 t.column :tag_id, :integer
9 t.column :taggable_id, :integer
10
11 # You should make sure that the column created is
12 # long enough to store the required class names.
13 t.column :taggable_type, :string
14
15 t.column :created_at, :datetime
16 end
17
18 add_index :taggings, :tag_id
19 add_index :taggings, [:taggable_id, :taggable_type]
20 end
21
22 def self.down
23 drop_table :taggings
24 drop_table :tags
25 end
26end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/init.rb b/vendor/plugins/acts_as_taggable_on_steroids/init.rb
deleted file mode 100644
index 64505b9..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/init.rb
+++ /dev/null
@@ -1 +0,0 @@
1require File.dirname(__FILE__) + '/lib/acts_as_taggable'
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/lib/acts_as_taggable.rb b/vendor/plugins/acts_as_taggable_on_steroids/lib/acts_as_taggable.rb
deleted file mode 100644
index 7a96ef0..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/lib/acts_as_taggable.rb
+++ /dev/null
@@ -1,230 +0,0 @@
1module ActiveRecord #:nodoc:
2 module Acts #:nodoc:
3 module Taggable #:nodoc:
4 def self.included(base)
5 base.extend(ClassMethods)
6 end
7
8 module ClassMethods
9 def acts_as_taggable
10 has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
11 has_many :tags, :through => :taggings
12
13 before_save :save_cached_tag_list
14
15 after_create :save_tags
16 after_update :save_tags
17
18 include ActiveRecord::Acts::Taggable::InstanceMethods
19 extend ActiveRecord::Acts::Taggable::SingletonMethods
20
21 alias_method_chain :reload, :tag_list
22 end
23
24 def cached_tag_list_column_name
25 "cached_tag_list"
26 end
27
28 def set_cached_tag_list_column_name(value = nil, &block)
29 define_attr_method :cached_tag_list_column_name, value, &block
30 end
31 end
32
33 module SingletonMethods
34 # Returns an array of related tags.
35 # Related tags are all the other tags that are found on the models tagged with the provided tags.
36 #
37 # Pass either a tag, string, or an array of strings or tags.
38 #
39 # Options:
40 # :order - SQL Order how to order the tags. Defaults to "count DESC, tags.name".
41 def find_related_tags(tags, options = {})
42 tags = tags.is_a?(Array) ? TagList.new(tags.map(&:to_s)) : TagList.from(tags)
43
44 related_models = find_tagged_with(tags)
45
46 return [] if related_models.blank?
47
48 related_ids = related_models.to_s(:db)
49
50 Tag.find(:all, options.merge({
51 :select => "#{Tag.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
52 :joins => "JOIN #{Tagging.table_name} ON #{Tagging.table_name}.taggable_type = '#{base_class.name}'
53 AND #{Tagging.table_name}.taggable_id IN (#{related_ids})
54 AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id",
55 :order => options[:order] || "count DESC, #{Tag.table_name}.name",
56 :group => "#{Tag.table_name}.id, #{Tag.table_name}.name HAVING #{Tag.table_name}.name NOT IN (#{tags.map { |n| quote_value(n) }.join(",")})"
57 }))
58 end
59
60 # Pass either a tag, string, or an array of strings or tags.
61 #
62 # Options:
63 # :exclude - Find models that are not tagged with the given tags
64 # :match_all - Find models that match all of the given tags, not just one
65 # :conditions - A piece of SQL conditions to add to the query
66 def find_tagged_with(*args)
67 options = find_options_for_find_tagged_with(*args)
68 options.blank? ? [] : find(:all, options)
69 end
70
71 def find_options_for_find_tagged_with(tags, options = {})
72 tags = tags.is_a?(Array) ? TagList.new(tags.map(&:to_s)) : TagList.from(tags)
73 options = options.dup
74
75 return {} if tags.empty?
76
77 conditions = []
78 conditions << sanitize_sql(options.delete(:conditions)) if options[:conditions]
79
80 taggings_alias, tags_alias = "#{table_name}_taggings", "#{table_name}_tags"
81
82 joins = [
83 "INNER JOIN #{Tagging.table_name} #{taggings_alias} ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key} AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}",
84 "INNER JOIN #{Tag.table_name} #{tags_alias} ON #{tags_alias}.id = #{taggings_alias}.tag_id"
85 ]
86
87 if options.delete(:exclude)
88 conditions << <<-END
89 #{table_name}.id NOT IN
90 (SELECT #{Tagging.table_name}.taggable_id FROM #{Tagging.table_name}
91 INNER JOIN #{Tag.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id
92 WHERE #{tags_condition(tags)} AND #{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})
93 END
94 else
95 if options.delete(:match_all)
96 joins << joins_for_match_all_tags(tags)
97 else
98 conditions << tags_condition(tags, tags_alias)
99 end
100 end
101
102 { :select => "DISTINCT #{table_name}.*",
103 :joins => joins.join(" "),
104 :conditions => conditions.join(" AND ")
105 }.reverse_merge!(options)
106 end
107
108 def joins_for_match_all_tags(tags)
109 joins = []
110
111 tags.each_with_index do |tag, index|
112 taggings_alias, tags_alias = "taggings_#{index}", "tags_#{index}"
113
114 join = <<-END
115 INNER JOIN #{Tagging.table_name} #{taggings_alias} ON
116 #{taggings_alias}.taggable_id = #{table_name}.#{primary_key} AND
117 #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}
118
119 INNER JOIN #{Tag.table_name} #{tags_alias} ON
120 #{taggings_alias}.tag_id = #{tags_alias}.id AND
121 #{tags_alias}.name = ?
122 END
123
124 joins << sanitize_sql([join, tag])
125 end
126
127 joins.join(" ")
128 end
129
130 # Calculate the tag counts for all tags.
131 #
132 # See Tag.counts for available options.
133 def tag_counts(options = {})
134 Tag.find(:all, find_options_for_tag_counts(options))
135 end
136
137 def find_options_for_tag_counts(options = {})
138 options = options.dup
139 scope = scope(:find)
140
141 conditions = []
142 conditions << send(:sanitize_conditions, options.delete(:conditions)) if options[:conditions]
143 conditions << send(:sanitize_conditions, scope[:conditions]) if scope && scope[:conditions]
144 conditions << "#{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)}"
145 conditions << type_condition unless descends_from_active_record?
146 conditions.compact!
147 conditions = conditions.join(" AND ")
148
149 joins = ["INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"]
150 joins << options.delete(:joins) if options[:joins]
151 joins << scope[:joins] if scope && scope[:joins]
152 joins = joins.join(" ")
153
154 options = { :conditions => conditions, :joins => joins }.update(options)
155
156 Tag.options_for_counts(options)
157 end
158
159 def caching_tag_list?
160 column_names.include?(cached_tag_list_column_name)
161 end
162
163 private
164 def tags_condition(tags, table_name = Tag.table_name)
165 condition = tags.map { |t| sanitize_sql(["#{table_name}.name LIKE ?", t]) }.join(" OR ")
166 "(" + condition + ")" unless condition.blank?
167 end
168 end
169
170 module InstanceMethods
171 def tag_list
172 return @tag_list if @tag_list
173
174 if self.class.caching_tag_list? and !(cached_value = send(self.class.cached_tag_list_column_name)).nil?
175 @tag_list = TagList.from(cached_value)
176 else
177 @tag_list = TagList.new(*tags.map(&:name))
178 end
179 end
180
181 def tag_list=(value)
182 @tag_list = TagList.from(value)
183 end
184
185 def save_cached_tag_list
186 if self.class.caching_tag_list?
187 self[self.class.cached_tag_list_column_name] = tag_list.to_s
188 end
189 end
190
191 def save_tags
192 return unless @tag_list
193
194 new_tag_names = @tag_list - tags.map(&:name)
195 old_tags = tags.reject { |tag| @tag_list.include?(tag.name) }
196
197 self.class.transaction do
198 if old_tags.any?
199 taggings.find(:all, :conditions => ["tag_id IN (?)", old_tags.map(&:id)]).each(&:destroy)
200 taggings.reset
201 end
202
203 new_tag_names.each do |new_tag_name|
204 tags << Tag.find_or_create_with_like_by_name(new_tag_name)
205 end
206 end
207
208 true
209 end
210
211 # Calculate the tag counts for the tags used by this model.
212 #
213 # The possible options are the same as the tag_counts class method.
214 def tag_counts(options = {})
215 return [] if tag_list.blank?
216
217 options[:conditions] = self.class.send(:merge_conditions, options[:conditions], self.class.send(:tags_condition, tag_list))
218 self.class.tag_counts(options)
219 end
220
221 def reload_with_tag_list(*args) #:nodoc:
222 @tag_list = nil
223 reload_without_tag_list(*args)
224 end
225 end
226 end
227 end
228end
229
230ActiveRecord::Base.send(:include, ActiveRecord::Acts::Taggable)
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/lib/tag.rb b/vendor/plugins/acts_as_taggable_on_steroids/lib/tag.rb
deleted file mode 100644
index 44f0991..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/lib/tag.rb
+++ /dev/null
@@ -1,71 +0,0 @@
1class Tag < ActiveRecord::Base
2 has_many :taggings, :dependent => :destroy
3
4 validates_presence_of :name
5 validates_uniqueness_of :name
6
7 cattr_accessor :destroy_unused
8 self.destroy_unused = false
9
10 # LIKE is used for cross-database case-insensitivity
11 def self.find_or_create_with_like_by_name(name)
12 find(:first, :conditions => ["name LIKE ?", name]) || create(:name => name)
13 end
14
15 def ==(object)
16 super || (object.is_a?(Tag) && name == object.name)
17 end
18
19 def to_s
20 name
21 end
22
23 def count
24 read_attribute(:count).to_i
25 end
26
27 class << self
28 # Calculate the tag counts for all tags.
29 # :start_at - Restrict the tags to those created after a certain time
30 # :end_at - Restrict the tags to those created before a certain time
31 # :conditions - A piece of SQL conditions to add to the query
32 # :limit - The maximum number of tags to return
33 # :order - A piece of SQL to order by. Eg 'count desc' or 'taggings.created_at desc'
34 # :at_least - Exclude tags with a frequency less than the given value
35 # :at_most - Exclude tags with a frequency greater than the given value
36 def counts(options = {})
37 find(:all, options_for_counts(options))
38 end
39
40 def options_for_counts(options = {})
41 options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit, :joins
42 options = options.dup
43
44 start_at = sanitize_sql(["#{Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
45 end_at = sanitize_sql(["#{Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
46
47 conditions = [
48 (sanitize_sql(options.delete(:conditions)) if options[:conditions]),
49 start_at,
50 end_at
51 ].compact
52
53 conditions = conditions.join(' AND ') if conditions.any?
54
55 joins = ["INNER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
56 joins << options.delete(:joins) if options[:joins]
57
58 at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
59 at_most = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
60 having = [at_least, at_most].compact.join(' AND ')
61 group_by = "#{Tag.table_name}.id, #{Tag.table_name}.name HAVING COUNT(*) > 0"
62 group_by << " AND #{having}" unless having.blank?
63
64 { :select => "#{Tag.table_name}.id, #{Tag.table_name}.name, COUNT(*) AS count",
65 :joins => joins.join(" "),
66 :conditions => conditions,
67 :group => group_by
68 }.update(options)
69 end
70 end
71end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/lib/tag_list.rb b/vendor/plugins/acts_as_taggable_on_steroids/lib/tag_list.rb
deleted file mode 100644
index 700399b..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/lib/tag_list.rb
+++ /dev/null
@@ -1,108 +0,0 @@
1class TagList < Array
2 cattr_accessor :delimiter
3 self.delimiter = ','
4
5 def initialize(*args)
6 add(*args)
7 end
8
9 # Add tags to the tag_list. Duplicate or blank tags will be ignored.
10 #
11 # tag_list.add("Fun", "Happy")
12 #
13 # Use the <tt>:parse</tt> option to add an unparsed tag string.
14 #
15 # tag_list.add("Fun, Happy", :parse => true)
16 def add(*names)
17 extract_and_apply_options!(names)
18 concat(names)
19 clean!
20 self
21 end
22
23 # Remove specific tags from the tag_list.
24 #
25 # tag_list.remove("Sad", "Lonely")
26 #
27 # Like #add, the <tt>:parse</tt> option can be used to remove multiple tags in a string.
28 #
29 # tag_list.remove("Sad, Lonely", :parse => true)
30 def remove(*names)
31 extract_and_apply_options!(names)
32 delete_if { |name| names.include?(name) }
33 self
34 end
35
36 # Toggle the presence of the given tags.
37 # If a tag is already in the list it is removed, otherwise it is added.
38 def toggle(*names)
39 extract_and_apply_options!(names)
40
41 names.each do |name|
42 include?(name) ? delete(name) : push(name)
43 end
44
45 clean!
46 self
47 end
48
49 # Transform the tag_list into a tag string suitable for edting in a form.
50 # The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
51 #
52 # tag_list = TagList.new("Round", "Square,Cube")
53 # tag_list.to_s # 'Round, "Square,Cube"'
54 def to_s
55 clean!
56
57 map do |name|
58 name.include?(delimiter) ? "\"#{name}\"" : name
59 end.join(delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ")
60 end
61
62 private
63 # Remove whitespace, duplicates, and blanks.
64 def clean!
65 reject!(&:blank?)
66 map!(&:strip)
67 uniq!
68 end
69
70 def extract_and_apply_options!(args)
71 options = args.last.is_a?(Hash) ? args.pop : {}
72 options.assert_valid_keys :parse
73
74 if options[:parse]
75 args.map! { |a| self.class.from(a) }
76 end
77
78 args.flatten!
79 end
80
81 class << self
82 # Returns a new TagList using the given tag string.
83 #
84 # tag_list = TagList.from("One , Two, Three")
85 # tag_list # ["One", "Two", "Three"]
86 def from(source)
87 returning new do |tag_list|
88
89 case source
90 when Array
91 tag_list.add(source)
92 else
93 string = source.to_s.dup
94
95 # Parse the quoted tags
96 [
97 /\s*#{delimiter}\s*(['"])(.*?)\1\s*/,
98 /^\s*(['"])(.*?)\1\s*#{delimiter}?/
99 ].each do |re|
100 string.gsub!(re) { tag_list << $2; "" }
101 end
102
103 tag_list.add(string.split(delimiter))
104 end
105 end
106 end
107 end
108end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/lib/tagging.rb b/vendor/plugins/acts_as_taggable_on_steroids/lib/tagging.rb
deleted file mode 100644
index 87bc44d..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/lib/tagging.rb
+++ /dev/null
@@ -1,12 +0,0 @@
1class Tagging < ActiveRecord::Base #:nodoc:
2 belongs_to :tag
3 belongs_to :taggable, :polymorphic => true
4
5 def after_destroy
6 if Tag.destroy_unused
7 if tag.taggings.count.zero?
8 tag.destroy
9 end
10 end
11 end
12end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/lib/tags_helper.rb b/vendor/plugins/acts_as_taggable_on_steroids/lib/tags_helper.rb
deleted file mode 100644
index d5644b7..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/lib/tags_helper.rb
+++ /dev/null
@@ -1,13 +0,0 @@
1module TagsHelper
2 # See the README for an example using tag_cloud.
3 def tag_cloud(tags, classes)
4 return if tags.empty?
5
6 max_count = tags.sort_by(&:count).last.count.to_f
7
8 tags.each do |tag|
9 index = ((tag.count / max_count) * (classes.size - 1)).round
10 yield tag, classes[index]
11 end
12 end
13end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/abstract_unit.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/abstract_unit.rb
deleted file mode 100644
index f480387..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/abstract_unit.rb
+++ /dev/null
@@ -1,102 +0,0 @@
1require 'test/unit'
2
3begin
4 require File.dirname(__FILE__) + '/../../../../config/environment'
5rescue LoadError
6 require 'rubygems'
7 gem 'activerecord'
8 gem 'actionpack'
9 require 'active_record'
10 require 'action_controller'
11end
12
13# Search for fixtures first
14fixture_path = File.dirname(__FILE__) + '/fixtures/'
15ActiveSupport::Dependencies.load_paths.insert(0, fixture_path)
16
17require "active_record/test_case"
18require "active_record/fixtures"
19
20require File.dirname(__FILE__) + '/../lib/acts_as_taggable'
21require_dependency File.dirname(__FILE__) + '/../lib/tag_list'
22require_dependency File.dirname(__FILE__) + '/../lib/tags_helper'
23
24ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/debug.log')
25ActiveRecord::Base.configurations = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
26ActiveRecord::Base.establish_connection(ENV['DB'] || 'mysql')
27
28load(File.dirname(__FILE__) + '/schema.rb')
29
30class ActiveSupport::TestCase #:nodoc:
31 include ActiveRecord::TestFixtures
32
33 self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
34
35 self.use_transactional_fixtures = true
36 self.use_instantiated_fixtures = false
37
38 fixtures :all
39
40 def assert_equivalent(expected, actual, message = nil)
41 if expected.first.is_a?(ActiveRecord::Base)
42 assert_equal expected.sort_by(&:id), actual.sort_by(&:id), message
43 else
44 assert_equal expected.sort, actual.sort, message
45 end
46 end
47
48 def assert_tag_counts(tags, expected_values)
49 # Map the tag fixture names to real tag names
50 expected_values = expected_values.inject({}) do |hash, (tag, count)|
51 hash[tags(tag).name] = count
52 hash
53 end
54
55 tags.each do |tag|
56 value = expected_values.delete(tag.name)
57
58 assert_not_nil value, "Expected count for #{tag.name} was not provided"
59 assert_equal value, tag.count, "Expected value of #{value} for #{tag.name}, but was #{tag.count}"
60 end
61
62 unless expected_values.empty?
63 assert false, "The following tag counts were not present: #{expected_values.inspect}"
64 end
65 end
66
67 def assert_queries(num = 1)
68 $query_count = 0
69 yield
70 ensure
71 assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed."
72 end
73
74 def assert_no_queries(&block)
75 assert_queries(0, &block)
76 end
77
78 # From Rails trunk
79 def assert_difference(expressions, difference = 1, message = nil, &block)
80 expression_evaluations = [expressions].flatten.collect{|expression| lambda { eval(expression, block.binding) } }
81
82 original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression.call }
83 yield
84 expression_evaluations.each_with_index do |expression, i|
85 assert_equal original_values[i] + difference, expression.call, message
86 end
87 end
88
89 def assert_no_difference(expressions, message = nil, &block)
90 assert_difference expressions, 0, message, &block
91 end
92end
93
94ActiveRecord::Base.connection.class.class_eval do
95 def execute_with_counting(sql, name = nil, &block)
96 $query_count ||= 0
97 $query_count += 1
98 execute_without_counting(sql, name, &block)
99 end
100
101 alias_method_chain :execute, :counting
102end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/acts_as_taggable_test.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/acts_as_taggable_test.rb
deleted file mode 100644
index 9766d34..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/acts_as_taggable_test.rb
+++ /dev/null
@@ -1,384 +0,0 @@
1require File.dirname(__FILE__) + '/abstract_unit'
2
3class ActsAsTaggableOnSteroidsTest < ActiveSupport::TestCase
4 def test_find_related_tags_with
5 assert_equivalent [tags(:good), tags(:bad), tags(:question)], Post.find_related_tags("nature")
6 assert_equivalent [tags(:nature)], Post.find_related_tags([tags(:good)])
7 assert_equivalent [tags(:bad), tags(:question)], Post.find_related_tags(["Very Good", "Nature"])
8 assert_equivalent [tags(:bad), tags(:question)], Post.find_related_tags([tags(:good), tags(:nature)])
9 end
10
11 def test_find_tagged_with_include_and_order
12 assert_equal photos(:sam_sky, :sam_flower, :jonathan_dog), Photo.find_tagged_with("Nature", :order => "photos.title DESC", :include => :user)
13 end
14
15 def test_find_related_tags_with_non_existent_tags
16 assert_equal [], Post.find_related_tags("ABCDEFG")
17 assert_equal [], Post.find_related_tags(['HIJKLM'])
18 end
19
20 def test_find_related_tags_with_nothing
21 assert_equal [], Post.find_related_tags("")
22 assert_equal [], Post.find_related_tags([])
23 end
24
25 def test_find_tagged_with
26 assert_equivalent [posts(:jonathan_sky), posts(:sam_flowers)], Post.find_tagged_with('"Very good"')
27 assert_equal Post.find_tagged_with('"Very good"'), Post.find_tagged_with(['Very good'])
28 assert_equal Post.find_tagged_with('"Very good"'), Post.find_tagged_with([tags(:good)])
29
30 assert_equivalent [photos(:jonathan_dog), photos(:sam_flower), photos(:sam_sky)], Photo.find_tagged_with('Nature')
31 assert_equal Photo.find_tagged_with('Nature'), Photo.find_tagged_with(['Nature'])
32 assert_equal Photo.find_tagged_with('Nature'), Photo.find_tagged_with([tags(:nature)])
33
34 assert_equivalent [photos(:jonathan_bad_cat), photos(:jonathan_dog), photos(:jonathan_questioning_dog)], Photo.find_tagged_with('"Crazy animal" Bad')
35 assert_equal Photo.find_tagged_with('"Crazy animal" Bad'), Photo.find_tagged_with(['Crazy animal', 'Bad'])
36 assert_equal Photo.find_tagged_with('"Crazy animal" Bad'), Photo.find_tagged_with([tags(:animal), tags(:bad)])
37 end
38
39 def test_find_tagged_with_nothing
40 assert_equal [], Post.find_tagged_with("")
41 assert_equal [], Post.find_tagged_with([])
42 end
43
44 def test_find_tagged_with_nonexistant_tags
45 assert_equal [], Post.find_tagged_with('ABCDEFG')
46 assert_equal [], Photo.find_tagged_with(['HIJKLM'])
47 assert_equal [], Photo.find_tagged_with([Tag.new(:name => 'unsaved tag')])
48 end
49
50 def test_find_tagged_with_match_all
51 assert_equivalent [photos(:jonathan_dog)], Photo.find_tagged_with('Crazy animal, "Nature"', :match_all => true)
52 end
53
54 def test_find_tagged_with_match_all_and_include
55 assert_equivalent [posts(:jonathan_sky), posts(:sam_flowers)], Post.find_tagged_with(['Very good', 'Nature'], :match_all => true, :include => :tags)
56 end
57
58 def test_find_tagged_with_conditions
59 assert_equal [], Post.find_tagged_with('"Very good", Nature', :conditions => '1=0')
60 end
61
62 def test_find_tagged_with_duplicates_options_hash
63 options = { :conditions => '1=1' }.freeze
64 assert_nothing_raised { Post.find_tagged_with("Nature", options) }
65 end
66
67 def test_find_tagged_with_exclusions
68 assert_equivalent [photos(:jonathan_questioning_dog), photos(:jonathan_bad_cat)], Photo.find_tagged_with("Nature", :exclude => true)
69 assert_equivalent [posts(:jonathan_grass), posts(:jonathan_rain), posts(:jonathan_cloudy), posts(:jonathan_still_cloudy)], Post.find_tagged_with("'Very good', Bad", :exclude => true)
70 end
71
72 def test_find_options_for_find_tagged_with_no_tags_returns_empty_hash
73 assert_equal Hash.new, Post.find_options_for_find_tagged_with("")
74 assert_equal Hash.new, Post.find_options_for_find_tagged_with([nil])
75 end
76
77 def test_find_options_for_find_tagged_with_leaves_arguments_unchanged
78 original_tags = photos(:jonathan_questioning_dog).tags.dup
79 Photo.find_options_for_find_tagged_with(photos(:jonathan_questioning_dog).tags)
80 assert_equal original_tags, photos(:jonathan_questioning_dog).tags
81 end
82
83 def test_find_options_for_find_tagged_with_respects_custom_table_name
84 Tagging.table_name = "categorisations"
85 Tag.table_name = "categories"
86
87 options = Photo.find_options_for_find_tagged_with("Hello")
88
89 assert_no_match(/ taggings /, options[:joins])
90 assert_no_match(/ tags /, options[:joins])
91
92 assert_match(/ categorisations /, options[:joins])
93 assert_match(/ categories /, options[:joins])
94 ensure
95 Tagging.table_name = "taggings"
96 Tag.table_name = "tags"
97 end
98
99 def test_include_tags_on_find_tagged_with
100 assert_nothing_raised do
101 Photo.find_tagged_with('Nature', :include => :tags)
102 Photo.find_tagged_with("Nature", :include => { :taggings => :tag })
103 end
104 end
105
106 def test_basic_tag_counts_on_class
107 assert_tag_counts Post.tag_counts, :good => 2, :nature => 7, :question => 1, :bad => 1
108 assert_tag_counts Photo.tag_counts, :good => 1, :nature => 3, :question => 1, :bad => 1, :animal => 3
109 end
110
111 def test_tag_counts_on_class_with_date_conditions
112 assert_tag_counts Post.tag_counts(:start_at => Date.new(2006, 8, 4)), :good => 1, :nature => 5, :question => 1, :bad => 1
113 assert_tag_counts Post.tag_counts(:end_at => Date.new(2006, 8, 6)), :good => 1, :nature => 4, :question => 1
114 assert_tag_counts Post.tag_counts(:start_at => Date.new(2006, 8, 5), :end_at => Date.new(2006, 8, 10)), :good => 1, :nature => 4, :bad => 1
115
116 assert_tag_counts Photo.tag_counts(:start_at => Date.new(2006, 8, 12), :end_at => Date.new(2006, 8, 19)), :good => 1, :nature => 2, :bad => 1, :question => 1, :animal => 3
117 end
118
119 def test_tag_counts_on_class_with_frequencies
120 assert_tag_counts Photo.tag_counts(:at_least => 2), :nature => 3, :animal => 3
121 assert_tag_counts Photo.tag_counts(:at_most => 2), :good => 1, :question => 1, :bad => 1
122 end
123
124 def test_tag_counts_on_class_with_frequencies_and_conditions
125 assert_tag_counts Photo.tag_counts(:at_least => 2, :conditions => '1=1'), :nature => 3, :animal => 3
126 end
127
128 def test_tag_counts_duplicates_options_hash
129 options = { :at_least => 2, :conditions => '1=1' }.freeze
130 assert_nothing_raised { Photo.tag_counts(options) }
131 end
132
133 def test_tag_counts_with_limit
134 assert_equal 2, Photo.tag_counts(:limit => 2).size
135 assert_equal 1, Post.tag_counts(:at_least => 4, :limit => 2).size
136 end
137
138 def test_tag_counts_with_limit_and_order
139 assert_equal [tags(:nature), tags(:good)], Post.tag_counts(:order => 'count desc', :limit => 2)
140 end
141
142 def test_tag_counts_on_association
143 assert_tag_counts users(:jonathan).posts.tag_counts, :good => 1, :nature => 5, :question => 1
144 assert_tag_counts users(:sam).posts.tag_counts, :good => 1, :nature => 2, :bad => 1
145
146 assert_tag_counts users(:jonathan).photos.tag_counts, :animal => 3, :nature => 1, :question => 1, :bad => 1
147 assert_tag_counts users(:sam).photos.tag_counts, :nature => 2, :good => 1
148 end
149
150 def test_tag_counts_on_association_with_options
151 assert_equal [], users(:jonathan).posts.tag_counts(:conditions => '1=0')
152 assert_tag_counts users(:jonathan).posts.tag_counts(:at_most => 2), :good => 1, :question => 1
153 end
154
155 def test_tag_counts_on_has_many_through
156 assert_tag_counts users(:jonathan).magazines.tag_counts, :good => 1
157 end
158
159 def test_tag_counts_on_model_instance
160 assert_tag_counts photos(:jonathan_dog).tag_counts, :animal => 3, :nature => 3
161 end
162
163 def test_tag_counts_on_model_instance_merges_conditions
164 assert_tag_counts photos(:jonathan_dog).tag_counts(:conditions => "tags.name = 'Crazy animal'"), :animal => 3
165 end
166
167 def test_tag_counts_on_model_instance_with_no_tags
168 photo = Photo.create!
169
170 assert_tag_counts photo.tag_counts, {}
171 end
172
173 def test_tag_counts_should_sanitize_scope_conditions
174 Photo.send :with_scope, :find => { :conditions => ["tags.id = ?", tags(:animal).id] } do
175 assert_tag_counts Photo.tag_counts, :animal => 3
176 end
177 end
178
179 def test_tag_counts_respects_custom_table_names
180 Tagging.table_name = "categorisations"
181 Tag.table_name = "categories"
182
183 options = Photo.find_options_for_tag_counts(:start_at => 2.weeks.ago, :end_at => Date.today)
184 sql = options.values.join(' ')
185
186 assert_no_match /taggings/, sql
187 assert_no_match /tags/, sql
188
189 assert_match /categorisations/, sql
190 assert_match /categories/, sql
191 ensure
192 Tagging.table_name = "taggings"
193 Tag.table_name = "tags"
194 end
195
196 def test_tag_list_reader
197 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
198 assert_equivalent ["Bad", "Crazy animal"], photos(:jonathan_bad_cat).tag_list
199 end
200
201 def test_reassign_tag_list
202 assert_equivalent ["Nature", "Question"], posts(:jonathan_rain).tag_list
203 posts(:jonathan_rain).taggings.reload
204
205 # Only an update of the posts table should be executed, the other two queries are for savepoints
206 assert_queries 3 do
207 posts(:jonathan_rain).update_attributes!(:tag_list => posts(:jonathan_rain).tag_list.to_s)
208 end
209
210 assert_equivalent ["Nature", "Question"], posts(:jonathan_rain).tag_list
211 end
212
213 def test_new_tags
214 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
215 posts(:jonathan_sky).update_attributes!(:tag_list => "#{posts(:jonathan_sky).tag_list}, One, Two")
216 assert_equivalent ["Very good", "Nature", "One", "Two"], posts(:jonathan_sky).tag_list
217 end
218
219 def test_remove_tag
220 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
221 posts(:jonathan_sky).update_attributes!(:tag_list => "Nature")
222 assert_equivalent ["Nature"], posts(:jonathan_sky).tag_list
223 end
224
225 def test_change_case_of_tags
226 original_tag_names = photos(:jonathan_questioning_dog).tag_list
227 photos(:jonathan_questioning_dog).update_attributes!(:tag_list => photos(:jonathan_questioning_dog).tag_list.to_s.upcase)
228
229 # The new tag list is not uppercase becuase the AR finders are not case-sensitive
230 # and find the old tags when re-tagging with the uppercase tags.
231 assert_equivalent original_tag_names, photos(:jonathan_questioning_dog).reload.tag_list
232 end
233
234 def test_remove_and_add_tag
235 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
236 posts(:jonathan_sky).update_attributes!(:tag_list => "Nature, Beautiful")
237 assert_equivalent ["Nature", "Beautiful"], posts(:jonathan_sky).tag_list
238 end
239
240 def test_tags_not_saved_if_validation_fails
241 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
242 assert !posts(:jonathan_sky).update_attributes(:tag_list => "One, Two", :text => "")
243 assert_equivalent ["Very good", "Nature"], Post.find(posts(:jonathan_sky).id).tag_list
244 end
245
246 def test_tag_list_accessors_on_new_record
247 p = Post.new(:text => 'Test')
248
249 assert p.tag_list.blank?
250 p.tag_list = "One, Two"
251 assert_equal "One, Two", p.tag_list.to_s
252 end
253
254 def test_clear_tag_list_with_nil
255 p = photos(:jonathan_questioning_dog)
256
257 assert !p.tag_list.blank?
258 assert p.update_attributes(:tag_list => nil)
259 assert p.tag_list.blank?
260
261 assert p.reload.tag_list.blank?
262 end
263
264 def test_clear_tag_list_with_string
265 p = photos(:jonathan_questioning_dog)
266
267 assert !p.tag_list.blank?
268 assert p.update_attributes(:tag_list => ' ')
269 assert p.tag_list.blank?
270
271 assert p.reload.tag_list.blank?
272 end
273
274 def test_tag_list_reset_on_reload
275 p = photos(:jonathan_questioning_dog)
276 assert !p.tag_list.blank?
277 p.tag_list = nil
278 assert p.tag_list.blank?
279 assert !p.reload.tag_list.blank?
280 end
281
282 def test_instance_tag_counts
283 assert_tag_counts posts(:jonathan_sky).tag_counts, :good => 2, :nature => 7
284 end
285
286 def test_tag_list_populated_when_cache_nil
287 assert_nil posts(:jonathan_sky).cached_tag_list
288 posts(:jonathan_sky).save!
289 assert_equal posts(:jonathan_sky).tag_list.to_s, posts(:jonathan_sky).cached_tag_list
290 end
291
292 def test_cached_tag_list_used
293 posts(:jonathan_sky).save!
294 posts(:jonathan_sky).reload
295
296 assert_no_queries do
297 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
298 end
299 end
300
301 def test_cached_tag_list_not_used
302 # Load fixture and column information
303 posts(:jonathan_sky).taggings(:reload)
304
305 assert_queries 1 do
306 # Tags association will be loaded
307 posts(:jonathan_sky).tag_list
308 end
309 end
310
311 def test_cached_tag_list_updated
312 assert_nil posts(:jonathan_sky).cached_tag_list
313 posts(:jonathan_sky).save!
314 assert_equivalent ["Very good", "Nature"], TagList.from(posts(:jonathan_sky).cached_tag_list)
315 posts(:jonathan_sky).update_attributes!(:tag_list => "None")
316
317 assert_equal 'None', posts(:jonathan_sky).cached_tag_list
318 assert_equal 'None', posts(:jonathan_sky).reload.cached_tag_list
319 end
320
321 def test_clearing_cached_tag_list
322 # Generate the cached tag list
323 posts(:jonathan_sky).save!
324
325 posts(:jonathan_sky).update_attributes!(:tag_list => "")
326 assert_equal "", posts(:jonathan_sky).cached_tag_list
327 end
328
329 def test_find_tagged_with_using_sti
330 special_post = SpecialPost.create!(:text => "Test", :tag_list => "Random")
331
332 assert_equal [special_post], SpecialPost.find_tagged_with("Random")
333 assert Post.find_tagged_with("Random").include?(special_post)
334 end
335
336 def test_tag_counts_using_sti
337 SpecialPost.create!(:text => "Test", :tag_list => "Nature")
338
339 assert_tag_counts SpecialPost.tag_counts, :nature => 1
340 end
341
342 def test_case_insensitivity
343 assert_difference "Tag.count", 1 do
344 Post.create!(:text => "Test", :tag_list => "one")
345 Post.create!(:text => "Test", :tag_list => "One")
346 end
347
348 assert_equal Post.find_tagged_with("Nature"), Post.find_tagged_with("nature")
349 end
350
351 def test_tag_not_destroyed_when_unused
352 posts(:jonathan_sky).tag_list.add("Random")
353 posts(:jonathan_sky).save!
354
355 assert_no_difference 'Tag.count' do
356 posts(:jonathan_sky).tag_list.remove("Random")
357 posts(:jonathan_sky).save!
358 end
359 end
360
361 def test_tag_destroyed_when_unused
362 Tag.destroy_unused = true
363
364 posts(:jonathan_sky).tag_list.add("Random")
365 posts(:jonathan_sky).save!
366
367 assert_difference 'Tag.count', -1 do
368 posts(:jonathan_sky).tag_list.remove("Random")
369 posts(:jonathan_sky).save!
370 end
371 ensure
372 Tag.destroy_unused = false
373 end
374end
375
376class ActsAsTaggableOnSteroidsFormTest < ActiveSupport::TestCase
377 include ActionView::Helpers::FormHelper
378
379 def test_tag_list_contents
380 fields_for :post, posts(:jonathan_sky) do |f|
381 assert_match /Nature, Very good/, f.text_field(:tag_list)
382 end
383 end
384end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazine.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazine.rb
deleted file mode 100644
index 554afe4..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazine.rb
+++ /dev/null
@@ -1,3 +0,0 @@
1class Magazine < ActiveRecord::Base
2 acts_as_taggable
3end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazines.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazines.yml
deleted file mode 100644
index 7124fd9..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazines.yml
+++ /dev/null
@@ -1,5 +0,0 @@
1ruby:
2 name: Ruby
3
4rails:
5 name: Rails
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photo.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photo.rb
deleted file mode 100644
index 224957f..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photo.rb
+++ /dev/null
@@ -1,8 +0,0 @@
1class Photo < ActiveRecord::Base
2 acts_as_taggable
3
4 belongs_to :user
5end
6
7class SpecialPhoto < Photo
8end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photos.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photos.yml
deleted file mode 100644
index bf078cf..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photos.yml
+++ /dev/null
@@ -1,19 +0,0 @@
1jonathan_dog:
2 user: jonathan
3 title: A small dog
4
5jonathan_questioning_dog:
6 user: jonathan
7 title: What does this dog want?
8
9jonathan_bad_cat:
10 user: jonathan
11 title: Bad cat
12
13sam_flower:
14 user: sam
15 title: Flower
16
17sam_sky:
18 user: sam
19 title: Sky
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/post.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/post.rb
deleted file mode 100644
index bee100a..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/post.rb
+++ /dev/null
@@ -1,7 +0,0 @@
1class Post < ActiveRecord::Base
2 acts_as_taggable
3
4 belongs_to :user
5
6 validates_presence_of :text
7end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/posts.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/posts.yml
deleted file mode 100644
index 50e91bc..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/posts.yml
+++ /dev/null
@@ -1,27 +0,0 @@
1jonathan_sky:
2 user: jonathan
3 text: The sky is particularly blue today
4
5jonathan_grass:
6 user: jonathan
7 text: The grass seems very green
8
9jonathan_rain:
10 user: jonathan
11 text: Why does the rain fall?
12
13jonathan_cloudy:
14 user: jonathan
15 text: Is it cloudy?
16
17jonathan_still_cloudy:
18 user: jonathan
19 text: Is it still cloudy?
20
21sam_ground:
22 user: sam
23 text: The ground is looking too brown
24
25sam_flowers:
26 user: sam
27 text: Why are the flowers dead?
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/special_post.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/special_post.rb
deleted file mode 100644
index 366a0d5..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/special_post.rb
+++ /dev/null
@@ -1,2 +0,0 @@
1class SpecialPost < Post
2end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscription.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscription.rb
deleted file mode 100644
index e975cb7..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscription.rb
+++ /dev/null
@@ -1,4 +0,0 @@
1class Subscription < ActiveRecord::Base
2 belongs_to :user
3 belongs_to :magazine
4end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscriptions.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscriptions.yml
deleted file mode 100644
index d294e51..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscriptions.yml
+++ /dev/null
@@ -1,3 +0,0 @@
1jonathan_rails:
2 user: jonathan
3 magazine: ruby
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/taggings.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/taggings.yml
deleted file mode 100644
index 1193b56..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/taggings.yml
+++ /dev/null
@@ -1,107 +0,0 @@
1# Posts
2jonathan_sky_good:
3 tag: good
4 taggable: jonathan_sky (Post)
5 created_at: 2006-08-01
6
7jonathan_sky_nature:
8 tag: nature
9 taggable: jonathan_sky (Post)
10 created_at: 2006-08-02
11
12jonathan_grass_nature:
13 tag: nature
14 taggable: jonathan_grass (Post)
15 created_at: 2006-08-03
16
17jonathan_rain_question:
18 tag: question
19 taggable: jonathan_rain (Post)
20 created_at: 2006-08-04
21
22jonathan_rain_nature:
23 tag: nature
24 taggable: jonathan_rain (Post)
25 created_at: 2006-08-05
26
27jonathan_cloudy_nature:
28 tag: nature
29 taggable: jonathan_cloudy (Post)
30 created_at: 2006-08-06
31
32jonathan_still_cloudy_nature:
33 tag: nature
34 taggable: jonathan_still_cloudy (Post)
35 created_at: 2006-08-07
36
37sam_ground_nature:
38 tag: nature
39 taggable: sam_ground (Post)
40 created_at: 2006-08-08
41
42sam_ground_bad:
43 tag: bad
44 taggable: sam_ground (Post)
45 created_at: 2006-08-09
46
47sam_flowers_good:
48 tag: good
49 taggable: sam_flowers (Post)
50 created_at: 2006-08-10
51
52sam_flowers_nature:
53 tag: nature
54 taggable: sam_flowers (Post)
55 created_at: 2006-08-11
56
57# Photos
58jonathan_dog_animal:
59 tag: animal
60 taggable: jonathan_dog (Photo)
61 created_at: 2006-08-12
62
63jonathan_dog_nature:
64 tag: nature
65 taggable: jonathan_dog (Photo)
66 created_at: 2006-08-13
67
68jonathan_questioning_dog_animal:
69 tag: animal
70 taggable: jonathan_questioning_dog (Photo)
71 created_at: 2006-08-14
72
73jonathan_questioning_dog_question:
74 tag: question
75 taggable: jonathan_questioning_dog (Photo)
76 created_at: 2006-08-15
77
78jonathan_bad_cat_bad:
79 tag: bad
80 taggable: jonathan_bad_cat (Photo)
81 created_at: 2006-08-16
82
83jonathan_bad_cat_animal:
84 tag: animal
85 taggable: jonathan_bad_cat (Photo)
86 created_at: 2006-08-17
87
88sam_flower_nature:
89 tag: nature
90 taggable: sam_flower (Photo)
91 created_at: 2006-08-18
92
93sam_flower_good:
94 tag: good
95 taggable: sam_flower (Photo)
96 created_at: 2006-08-19
97
98sam_sky_nature:
99 tag: nature
100 taggable: sam_sky (Photo)
101 created_at: 2006-08-20
102
103# Magazines
104ruby_good:
105 tag: good
106 taggable: ruby (Magazine)
107 created_at: 2007-08-25
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/tags.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/tags.yml
deleted file mode 100644
index a3c2aa5..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/tags.yml
+++ /dev/null
@@ -1,14 +0,0 @@
1good:
2 name: Very good
3
4bad:
5 name: Bad
6
7nature:
8 name: Nature
9
10question:
11 name: Question
12
13animal:
14 name: Crazy animal
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/user.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/user.rb
deleted file mode 100644
index 8c0f787..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/user.rb
+++ /dev/null
@@ -1,7 +0,0 @@
1class User < ActiveRecord::Base
2 has_many :posts
3 has_many :photos
4
5 has_many :subscriptions
6 has_many :magazines, :through => :subscriptions
7end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/users.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/users.yml
deleted file mode 100644
index e6f22d3..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/users.yml
+++ /dev/null
@@ -1,5 +0,0 @@
1jonathan:
2 name: Jonathan
3
4sam:
5 name: Sam
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/tag_list_test.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/tag_list_test.rb
deleted file mode 100644
index 17d4ab9..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/tag_list_test.rb
+++ /dev/null
@@ -1,119 +0,0 @@
1require File.dirname(__FILE__) + '/abstract_unit'
2
3class TagListTest < ActiveSupport::TestCase
4 def test_from_leaves_string_unchanged
5 tags = '"One ", Two'
6 original = tags.dup
7 TagList.from(tags)
8 assert_equal tags, original
9 end
10
11 def test_from_single_name
12 assert_equal %w(Fun), TagList.from("Fun")
13 assert_equal %w(Fun), TagList.from('"Fun"')
14 end
15
16 def test_from_blank
17 assert_equal [], TagList.from(nil)
18 assert_equal [], TagList.from("")
19 end
20
21 def test_from_single_quoted_tag
22 assert_equal ['with, comma'], TagList.from('"with, comma"')
23 end
24
25 def test_spaces_do_not_delineate
26 assert_equal ['A B', 'C'], TagList.from('A B, C')
27 end
28
29 def test_from_multiple_tags
30 assert_equivalent %w(Alpha Beta Delta Gamma), TagList.from("Alpha, Beta, Delta, Gamma")
31 end
32
33 def test_from_multiple_tags_with_quotes
34 assert_equivalent %w(Alpha Beta Delta Gamma), TagList.from('Alpha, "Beta", Gamma , "Delta"')
35 end
36
37 def test_from_with_single_quotes
38 assert_equivalent ['A B', 'C'], TagList.from("'A B', C")
39 end
40
41 def test_from_multiple_tags_with_quote_and_commas
42 assert_equivalent ['Alpha, Beta', 'Delta', 'Gamma, something'], TagList.from('"Alpha, Beta", Delta, "Gamma, something"')
43 end
44
45 def test_from_with_inner_quotes
46 assert_equivalent ["House", "Drum 'n' Bass", "Trance"], TagList.from("House, Drum 'n' Bass, Trance")
47 assert_equivalent ["House", "Drum'n'Bass", "Trance"], TagList.from("House, Drum'n'Bass, Trance")
48 end
49
50 def test_from_removes_white_space
51 assert_equivalent %w(Alpha Beta), TagList.from('" Alpha ", "Beta "')
52 assert_equivalent %w(Alpha Beta), TagList.from(' Alpha, Beta ')
53 end
54
55 def test_from_and_new_treat_both_accept_arrays
56 tags = ["One", "Two"]
57
58 assert_equal TagList.from(tags), TagList.new(tags)
59 end
60
61 def test_alternative_delimiter
62 TagList.delimiter = " "
63
64 assert_equal %w(One Two), TagList.from("One Two")
65 assert_equal ['One two', 'three', 'four'], TagList.from('"One two" three four')
66 ensure
67 TagList.delimiter = ","
68 end
69
70 def test_duplicate_tags_removed
71 assert_equal %w(One), TagList.from("One, One")
72 end
73
74 def test_to_s_with_commas
75 assert_equal "Question, Crazy Animal", TagList.new("Question", "Crazy Animal").to_s
76 end
77
78 def test_to_s_with_alternative_delimiter
79 TagList.delimiter = " "
80
81 assert_equal '"Crazy Animal" Question', TagList.new("Crazy Animal", "Question").to_s
82 ensure
83 TagList.delimiter = ","
84 end
85
86 def test_add
87 tag_list = TagList.new("One")
88 assert_equal %w(One), tag_list
89
90 assert_equal %w(One Two), tag_list.add("Two")
91 assert_equal %w(One Two Three), tag_list.add(["Three"])
92 end
93
94 def test_remove
95 tag_list = TagList.new("One", "Two")
96 assert_equal %w(Two), tag_list.remove("One")
97 assert_equal %w(), tag_list.remove(["Two"])
98 end
99
100 def test_new_with_parsing
101 assert_equal %w(One Two), TagList.new("One, Two", :parse => true)
102 end
103
104 def test_add_with_parsing
105 assert_equal %w(One Two), TagList.new.add("One, Two", :parse => true)
106 end
107
108 def test_remove_with_parsing
109 tag_list = TagList.from("Three, Four, Five")
110 assert_equal %w(Four), tag_list.remove("Three, Five", :parse => true)
111 end
112
113 def test_toggle
114 tag_list = TagList.new("One", "Two")
115 assert_equal %w(One Three), tag_list.toggle("Two", "Three")
116 assert_equal %w(), tag_list.toggle("One", "Three")
117 assert_equal %w(Four), tag_list.toggle("Four")
118 end
119end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/tag_test.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/tag_test.rb
deleted file mode 100644
index 97dd249..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/tag_test.rb
+++ /dev/null
@@ -1,62 +0,0 @@
1require File.dirname(__FILE__) + '/abstract_unit'
2
3class TagTest < ActiveSupport::TestCase
4 def test_name_required
5 t = Tag.create
6 assert_match /blank/, t.errors[:name].to_s
7 end
8
9 def test_name_unique
10 t = Tag.create!(:name => "My tag")
11 duplicate = t.clone
12
13 assert !duplicate.save
14 assert_match /taken/, duplicate.errors[:name].to_s
15 end
16
17 def test_taggings
18 assert_equivalent [taggings(:jonathan_sky_good), taggings(:sam_flowers_good), taggings(:sam_flower_good), taggings(:ruby_good)], tags(:good).taggings
19 assert_equivalent [taggings(:sam_ground_bad), taggings(:jonathan_bad_cat_bad)], tags(:bad).taggings
20 end
21
22 def test_to_s
23 assert_equal tags(:good).name, tags(:good).to_s
24 end
25
26 def test_equality
27 assert_equal tags(:good), tags(:good)
28 assert_equal Tag.find(tags(:good).id), Tag.find(tags(:good).id)
29 assert_equal Tag.new(:name => 'A'), Tag.new(:name => 'A')
30 assert_not_equal Tag.new(:name => 'A'), Tag.new(:name => 'B')
31 end
32
33 def test_taggings_removed_when_tag_destroyed
34 assert_difference "Tagging.count", -Tagging.count(:conditions => { :tag_id => tags(:good).id }) do
35 assert tags(:good).destroy
36 end
37 end
38
39 def test_all_counts
40 assert_tag_counts Tag.counts, :good => 4, :bad => 2, :nature => 10, :question => 2, :animal => 3
41 end
42
43 def test_all_counts_with_string_conditions
44 assert_tag_counts Tag.counts(:conditions => 'taggings.created_at >= \'2006-08-15\''),
45 :question => 1, :bad => 1, :animal => 1, :nature => 2, :good => 2
46 end
47
48 def test_all_counts_with_array_conditions
49 assert_tag_counts Tag.counts(:conditions => ['taggings.created_at >= ?', '2006-08-15']),
50 :question => 1, :bad => 1, :animal => 1, :nature => 2, :good => 2
51 end
52
53 def test_all_counts_with_hash_conditions
54 tag_counts = Tag.counts(
55 :conditions => {
56 :taggings => { :created_at => (DateTime.parse('2006-08-14 23:59') .. DateTime.parse('4000-01-01')) }
57 }
58 )
59
60 assert_tag_counts tag_counts, :question => 1, :bad => 1, :animal => 1, :nature => 2, :good => 2
61 end
62end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/tagging_test.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/tagging_test.rb
deleted file mode 100644
index bea4583..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/tagging_test.rb
+++ /dev/null
@@ -1,11 +0,0 @@
1require File.dirname(__FILE__) + '/abstract_unit'
2
3class TaggingTest < ActiveSupport::TestCase
4 def test_tag
5 assert_equal tags(:good), taggings(:jonathan_sky_good).tag
6 end
7
8 def test_taggable
9 assert_equal posts(:jonathan_sky), taggings(:jonathan_sky_good).taggable
10 end
11end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/tags_helper_test.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/tags_helper_test.rb
deleted file mode 100644
index c482909..0000000
--- a/vendor/plugins/acts_as_taggable_on_steroids/test/tags_helper_test.rb
+++ /dev/null
@@ -1,25 +0,0 @@
1require File.dirname(__FILE__) + '/abstract_unit'
2
3class TagsHelperTest < ActiveSupport::TestCase
4 include TagsHelper
5
6 def test_tag_cloud
7 cloud_elements = []
8
9 tag_cloud Post.tag_counts, %w(css1 css2 css3 css4) do |tag, css_class|
10 cloud_elements << [tag, css_class]
11 end
12
13 assert cloud_elements.include?([tags(:good), "css2"])
14 assert cloud_elements.include?([tags(:bad), "css1"])
15 assert cloud_elements.include?([tags(:nature), "css4"])
16 assert cloud_elements.include?([tags(:question), "css1"])
17 assert_equal 4, cloud_elements.size
18 end
19
20 def test_tag_cloud_when_no_tags
21 tag_cloud SpecialPost.tag_counts, %w(css1) do
22 assert false, "tag_cloud should not yield"
23 end
24 end
25end
diff --git a/vendor/plugins/awesome_nested_set/.autotest b/vendor/plugins/awesome_nested_set/.autotest
deleted file mode 100644
index 54518a4..0000000
--- a/vendor/plugins/awesome_nested_set/.autotest
+++ /dev/null
@@ -1,13 +0,0 @@
1Autotest.add_hook :initialize do |at|
2 at.clear_mappings
3
4 at.add_mapping %r%^lib/(.*)\.rb$% do |_, m|
5 at.files_matching %r%^test/#{m[1]}_test.rb$%
6 end
7
8 at.add_mapping(%r%^test/.*\.rb$%) {|filename, _| filename }
9
10 at.add_mapping %r%^test/fixtures/(.*)s.yml% do |_, _|
11 at.files_matching %r%^test/.*\.rb$%
12 end
13end \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/MIT-LICENSE b/vendor/plugins/awesome_nested_set/MIT-LICENSE
deleted file mode 100644
index 570ecf8..0000000
--- a/vendor/plugins/awesome_nested_set/MIT-LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
1Copyright (c) 2007 [name of plugin creator]
2
3Permission is hereby granted, free of charge, to any person obtaining
4a copy of this software and associated documentation files (the
5"Software"), to deal in the Software without restriction, including
6without limitation the rights to use, copy, modify, merge, publish,
7distribute, sublicense, and/or sell copies of the Software, and to
8permit persons to whom the Software is furnished to do so, subject to
9the following conditions:
10
11The above copyright notice and this permission notice shall be
12included in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/plugins/awesome_nested_set/README.rdoc b/vendor/plugins/awesome_nested_set/README.rdoc
deleted file mode 100644
index 884016d..0000000
--- a/vendor/plugins/awesome_nested_set/README.rdoc
+++ /dev/null
@@ -1,79 +0,0 @@
1= AwesomeNestedSet
2
3Awesome Nested Set is an implementation of the nested set pattern for ActiveRecord models. It is replacement for acts_as_nested_set and BetterNestedSet, but awesomer. It supports Rails 2.1 and later.
4
5== What makes this so awesome?
6
7This is a new implementation of nested set based off of BetterNestedSet that fixes some bugs, removes tons of duplication, adds a few useful methods, and adds STI support.
8
9== Installation
10
11Install as a plugin:
12
13 script/plugin install git://github.com/collectiveidea/awesome_nested_set.git
14
15== Usage
16
17To make use of awesome_nested_set, your model needs to have 3 fields: lft, rgt, and parent_id:
18
19 class CreateCategories < ActiveRecord::Migration
20 def self.up
21 create_table :categories do |t|
22 t.string :name
23 t.integer :parent_id
24 t.integer :lft
25 t.integer :rgt
26 end
27 end
28
29 def self.down
30 drop_table :categories
31 end
32 end
33
34Enable the nested set functionality by declaring acts_as_nested_set on your model
35
36 class Category < ActiveRecord::Base
37 acts_as_nested_set
38 end
39
40Run `rake rdoc` to generate the API docs and see CollectiveIdea::Acts::NestedSet::SingletonMethods for more info.
41
42== View Helper
43
44The view helper is called #nested_set_options.
45
46Example usage:
47
48 <%= f.select :parent_id, nested_set_options(Category, @category) {|i| "#{'-' * i.level} #{i.name}" } %>
49
50 <%= select_tag 'parent_id', options_for_select(nested_set_options(Category) {|i| "#{'-' * i.level} #{i.name}" } ) %>
51
52See CollectiveIdea::Acts::NestedSet::Helper for more information about the helpers.
53
54== References
55
56You can learn more about nested sets at:
57
58 http://www.dbmsmag.com/9603d06.html
59 http://threebit.net/tutorials/nestedset/tutorial1.html
60 http://api.rubyonrails.com/classes/ActiveRecord/Acts/NestedSet/ClassMethods.html
61 http://opensource.symetrie.com/trac/better_nested_set/
62
63== How to contribute
64
65If you find what you might think is a bug:
66
671. Check the GitHub issue tracker to see if anyone else has had the same issue.
68 http://github.com/collectiveidea/awesome_nested_set/issues/
692. If you don't see anything, create an issue with information on how to reproduce it.
70
71If you want to contribute an enhancement or a fix:
72
731. Fork the project on github.
74 http://github.com/collectiveidea/awesome_nested_set/
752. Make your changes with tests.
763. Commit the changes without making changes to the Rakefile, VERSION, or any other files that aren't related to your enhancement or fix
774. Send a pull request.
78
79Copyright ©2008 Collective Idea, released under the MIT license \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/Rakefile b/vendor/plugins/awesome_nested_set/Rakefile
deleted file mode 100644
index ce70813..0000000
--- a/vendor/plugins/awesome_nested_set/Rakefile
+++ /dev/null
@@ -1,54 +0,0 @@
1begin
2 require 'jeweler'
3rescue LoadError
4 puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
5 exit 1
6end
7require 'rake/testtask'
8require 'rake/rdoctask'
9require 'rcov/rcovtask'
10require "load_multi_rails_rake_tasks"
11
12Jeweler::Tasks.new do |s|
13 s.name = "awesome_nested_set"
14 s.summary = "An awesome nested set implementation for Active Record"
15 s.description = s.summary
16 s.email = "info@collectiveidea.com"
17 s.homepage = "http://github.com/collectiveidea/awesome_nested_set"
18 s.authors = ["Brandon Keepers", "Daniel Morrison"]
19 s.add_dependency "activerecord", ['>= 1.1']
20 s.has_rdoc = true
21 s.extra_rdoc_files = [ "README.rdoc"]
22 s.rdoc_options = ["--main", "README.rdoc", "--inline-source", "--line-numbers"]
23 s.test_files = Dir['test/**/*.{yml,rb}']
24end
25
26desc 'Default: run unit tests.'
27task :default => :test
28
29desc 'Test the awesome_nested_set plugin.'
30Rake::TestTask.new(:test) do |t|
31 t.libs += ['lib', 'test']
32 t.pattern = 'test/**/*_test.rb'
33 t.verbose = true
34end
35
36desc 'Generate documentation for the awesome_nested_set plugin.'
37Rake::RDocTask.new(:rdoc) do |rdoc|
38 rdoc.rdoc_dir = 'rdoc'
39 rdoc.title = 'AwesomeNestedSet'
40 rdoc.options << '--line-numbers' << '--inline-source'
41 rdoc.rdoc_files.include('README.rdoc')
42 rdoc.rdoc_files.include('lib/**/*.rb')
43end
44
45namespace :test do
46 desc "just rcov minus html output"
47 Rcov::RcovTask.new(:coverage) do |t|
48 t.libs << 'test'
49 t.test_files = FileList['test/**/*_test.rb']
50 t.output_dir = 'coverage'
51 t.verbose = true
52 t.rcov_opts = %w(--exclude test,/usr/lib/ruby,/Library/Ruby,lib/awesome_nested_set/named_scope.rb --sort coverage)
53 end
54end \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/VERSION b/vendor/plugins/awesome_nested_set/VERSION
deleted file mode 100644
index 9df886c..0000000
--- a/vendor/plugins/awesome_nested_set/VERSION
+++ /dev/null
@@ -1 +0,0 @@
11.4.2
diff --git a/vendor/plugins/awesome_nested_set/awesome_nested_set.gemspec b/vendor/plugins/awesome_nested_set/awesome_nested_set.gemspec
deleted file mode 100644
index 81108c5..0000000
--- a/vendor/plugins/awesome_nested_set/awesome_nested_set.gemspec
+++ /dev/null
@@ -1,68 +0,0 @@
1# -*- encoding: utf-8 -*-
2
3Gem::Specification.new do |s|
4 s.name = %q{awesome_nested_set}
5 s.version = "1.4.2"
6
7 s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8 s.authors = ["Brandon Keepers", "Daniel Morrison"]
9 s.date = %q{2009-09-08}
10 s.description = %q{An awesome nested set implementation for Active Record}
11 s.email = %q{info@collectiveidea.com}
12 s.extra_rdoc_files = [
13 "README.rdoc"
14 ]
15 s.files = [
16 ".autotest",
17 ".gitignore",
18 "MIT-LICENSE",
19 "README.rdoc",
20 "Rakefile",
21 "VERSION",
22 "awesome_nested_set.gemspec",
23 "init.rb",
24 "lib/awesome_nested_set.rb",
25 "lib/awesome_nested_set/helper.rb",
26 "rails/init.rb",
27 "test/application.rb",
28 "test/awesome_nested_set/helper_test.rb",
29 "test/awesome_nested_set_test.rb",
30 "test/db/database.yml",
31 "test/db/schema.rb",
32 "test/fixtures/categories.yml",
33 "test/fixtures/category.rb",
34 "test/fixtures/departments.yml",
35 "test/fixtures/notes.yml",
36 "test/test_helper.rb"
37 ]
38 s.homepage = %q{http://github.com/collectiveidea/awesome_nested_set}
39 s.rdoc_options = ["--main", "README.rdoc", "--inline-source", "--line-numbers"]
40 s.require_paths = ["lib"]
41 s.rubygems_version = %q{1.3.3}
42 s.summary = %q{An awesome nested set implementation for Active Record}
43 s.test_files = [
44 "test/db/database.yml",
45 "test/fixtures/categories.yml",
46 "test/fixtures/departments.yml",
47 "test/fixtures/notes.yml",
48 "test/application.rb",
49 "test/awesome_nested_set/helper_test.rb",
50 "test/awesome_nested_set_test.rb",
51 "test/db/schema.rb",
52 "test/fixtures/category.rb",
53 "test/test_helper.rb"
54 ]
55
56 if s.respond_to? :specification_version then
57 current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
58 s.specification_version = 3
59
60 if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
61 s.add_runtime_dependency(%q<activerecord>, [">= 1.1"])
62 else
63 s.add_dependency(%q<activerecord>, [">= 1.1"])
64 end
65 else
66 s.add_dependency(%q<activerecord>, [">= 1.1"])
67 end
68end
diff --git a/vendor/plugins/awesome_nested_set/init.rb b/vendor/plugins/awesome_nested_set/init.rb
deleted file mode 100644
index 43dc7c2..0000000
--- a/vendor/plugins/awesome_nested_set/init.rb
+++ /dev/null
@@ -1 +0,0 @@
1require File.dirname(__FILE__) + "/rails/init"
diff --git a/vendor/plugins/awesome_nested_set/lib/awesome_nested_set.rb b/vendor/plugins/awesome_nested_set/lib/awesome_nested_set.rb
deleted file mode 100644
index 015aa0f..0000000
--- a/vendor/plugins/awesome_nested_set/lib/awesome_nested_set.rb
+++ /dev/null
@@ -1,577 +0,0 @@
1module CollectiveIdea #:nodoc:
2 module Acts #:nodoc:
3 module NestedSet #:nodoc:
4 def self.included(base)
5 base.extend(SingletonMethods)
6 end
7
8 # This acts provides Nested Set functionality. Nested Set is a smart way to implement
9 # an _ordered_ tree, with the added feature that you can select the children and all of their
10 # descendants with a single query. The drawback is that insertion or move need some complex
11 # sql queries. But everything is done here by this module!
12 #
13 # Nested sets are appropriate each time you want either an orderd tree (menus,
14 # commercial categories) or an efficient way of querying big trees (threaded posts).
15 #
16 # == API
17 #
18 # Methods names are aligned with acts_as_tree as much as possible to make replacment from one
19 # by another easier.
20 #
21 # item.children.create(:name => "child1")
22 #
23 module SingletonMethods
24 # Configuration options are:
25 #
26 # * +:parent_column+ - specifies the column name to use for keeping the position integer (default: parent_id)
27 # * +:left_column+ - column name for left boundry data, default "lft"
28 # * +:right_column+ - column name for right boundry data, default "rgt"
29 # * +:scope+ - restricts what is to be considered a list. Given a symbol, it'll attach "_id"
30 # (if it hasn't been already) and use that as the foreign key restriction. You
31 # can also pass an array to scope by multiple attributes.
32 # Example: <tt>acts_as_nested_set :scope => [:notable_id, :notable_type]</tt>
33 # * +:dependent+ - behavior for cascading destroy. If set to :destroy, all the
34 # child objects are destroyed alongside this object by calling their destroy
35 # method. If set to :delete_all (default), all the child objects are deleted
36 # without calling their destroy method.
37 #
38 # See CollectiveIdea::Acts::NestedSet::ClassMethods for a list of class methods and
39 # CollectiveIdea::Acts::NestedSet::InstanceMethods for a list of instance methods added
40 # to acts_as_nested_set models
41 def acts_as_nested_set(options = {})
42 options = {
43 :parent_column => 'parent_id',
44 :left_column => 'lft',
45 :right_column => 'rgt',
46 :dependent => :delete_all, # or :destroy
47 }.merge(options)
48
49 if options[:scope].is_a?(Symbol) && options[:scope].to_s !~ /_id$/
50 options[:scope] = "#{options[:scope]}_id".intern
51 end
52
53 write_inheritable_attribute :acts_as_nested_set_options, options
54 class_inheritable_reader :acts_as_nested_set_options
55
56 unless self.is_a?(ClassMethods)
57 include Comparable
58 include Columns
59 include InstanceMethods
60 extend Columns
61 extend ClassMethods
62
63 belongs_to :parent, :class_name => self.base_class.class_name,
64 :foreign_key => parent_column_name
65 has_many :children, :class_name => self.base_class.class_name,
66 :foreign_key => parent_column_name, :order => quoted_left_column_name
67
68 attr_accessor :skip_before_destroy
69
70 # no bulk assignment
71 attr_protected left_column_name.intern,
72 right_column_name.intern
73
74 before_create :set_default_left_and_right
75 before_save :store_new_parent
76 after_save :move_to_new_parent
77 before_destroy :destroy_descendants
78
79 # no assignment to structure fields
80 [left_column_name, right_column_name].each do |column|
81 module_eval <<-"end_eval", __FILE__, __LINE__
82 def #{column}=(x)
83 raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{column}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
84 end
85 end_eval
86 end
87
88 named_scope :roots, :conditions => {parent_column_name => nil}, :order => quoted_left_column_name
89 named_scope :leaves, :conditions => "#{quoted_right_column_name} - #{quoted_left_column_name} = 1", :order => quoted_left_column_name
90
91 define_callbacks("before_move", "after_move") if self.respond_to?(:define_callbacks)
92 end
93
94 end
95
96 end
97
98 module ClassMethods
99
100 # Returns the first root
101 def root
102 roots.find(:first)
103 end
104
105 def valid?
106 left_and_rights_valid? && no_duplicates_for_columns? && all_roots_valid?
107 end
108
109 def left_and_rights_valid?
110 count(
111 :joins => "LEFT OUTER JOIN #{quoted_table_name} AS parent ON " +
112 "#{quoted_table_name}.#{quoted_parent_column_name} = parent.#{primary_key}",
113 :conditions =>
114 "#{quoted_table_name}.#{quoted_left_column_name} IS NULL OR " +
115 "#{quoted_table_name}.#{quoted_right_column_name} IS NULL OR " +
116 "#{quoted_table_name}.#{quoted_left_column_name} >= " +
117 "#{quoted_table_name}.#{quoted_right_column_name} OR " +
118 "(#{quoted_table_name}.#{quoted_parent_column_name} IS NOT NULL AND " +
119 "(#{quoted_table_name}.#{quoted_left_column_name} <= parent.#{quoted_left_column_name} OR " +
120 "#{quoted_table_name}.#{quoted_right_column_name} >= parent.#{quoted_right_column_name}))"
121 ) == 0
122 end
123
124 def no_duplicates_for_columns?
125 scope_string = Array(acts_as_nested_set_options[:scope]).map do |c|
126 connection.quote_column_name(c)
127 end.push(nil).join(", ")
128 [quoted_left_column_name, quoted_right_column_name].all? do |column|
129 # No duplicates
130 find(:first,
131 :select => "#{scope_string}#{column}, COUNT(#{column})",
132 :group => "#{scope_string}#{column}
133 HAVING COUNT(#{column}) > 1").nil?
134 end
135 end
136
137 # Wrapper for each_root_valid? that can deal with scope.
138 def all_roots_valid?
139 if acts_as_nested_set_options[:scope]
140 roots(:group => scope_column_names).group_by{|record| scope_column_names.collect{|col| record.send(col.to_sym)}}.all? do |scope, grouped_roots|
141 each_root_valid?(grouped_roots)
142 end
143 else
144 each_root_valid?(roots)
145 end
146 end
147
148 def each_root_valid?(roots_to_validate)
149 left = right = 0
150 roots_to_validate.all? do |root|
151 returning(root.left > left && root.right > right) do
152 left = root.left
153 right = root.right
154 end
155 end
156 end
157
158 # Rebuilds the left & rights if unset or invalid. Also very useful for converting from acts_as_tree.
159 def rebuild!
160 # Don't rebuild a valid tree.
161 return true if valid?
162
163 scope = lambda{|node|}
164 if acts_as_nested_set_options[:scope]
165 scope = lambda{|node|
166 scope_column_names.inject(""){|str, column_name|
167 str << "AND #{connection.quote_column_name(column_name)} = #{connection.quote(node.send(column_name.to_sym))} "
168 }
169 }
170 end
171 indices = {}
172
173 set_left_and_rights = lambda do |node|
174 # set left
175 node[left_column_name] = indices[scope.call(node)] += 1
176 # find
177 find(:all, :conditions => ["#{quoted_parent_column_name} = ? #{scope.call(node)}", node], :order => "#{quoted_left_column_name}, #{quoted_right_column_name}, id").each{|n| set_left_and_rights.call(n) }
178 # set right
179 node[right_column_name] = indices[scope.call(node)] += 1
180 node.save!
181 end
182
183 # Find root node(s)
184 root_nodes = find(:all, :conditions => "#{quoted_parent_column_name} IS NULL", :order => "#{quoted_left_column_name}, #{quoted_right_column_name}, id").each do |root_node|
185 # setup index for this scope
186 indices[scope.call(root_node)] ||= 0
187 set_left_and_rights.call(root_node)
188 end
189 end
190
191 # Iterates over tree elements and determines the current level in the tree.
192 # Only accepts default ordering, odering by an other column than lft
193 # does not work. This method is much more efficent than calling level
194 # because it doesn't require any additional database queries.
195 #
196 # Example:
197 # Category.each_with_level(Category.root.self_and_descendants) do |o, level|
198 #
199 def each_with_level(objects)
200 path = [nil]
201 objects.each do |o|
202 if o.parent_id != path.last
203 # we are on a new level, did we decent or ascent?
204 if path.include?(o.parent_id)
205 # remove wrong wrong tailing paths elements
206 path.pop while path.last != o.parent_id
207 else
208 path << o.parent_id
209 end
210 end
211 yield(o, path.length - 1)
212 end
213 end
214 end
215
216 # Mixed into both classes and instances to provide easy access to the column names
217 module Columns
218 def left_column_name
219 acts_as_nested_set_options[:left_column]
220 end
221
222 def right_column_name
223 acts_as_nested_set_options[:right_column]
224 end
225
226 def parent_column_name
227 acts_as_nested_set_options[:parent_column]
228 end
229
230 def scope_column_names
231 Array(acts_as_nested_set_options[:scope])
232 end
233
234 def quoted_left_column_name
235 connection.quote_column_name(left_column_name)
236 end
237
238 def quoted_right_column_name
239 connection.quote_column_name(right_column_name)
240 end
241
242 def quoted_parent_column_name
243 connection.quote_column_name(parent_column_name)
244 end
245
246 def quoted_scope_column_names
247 scope_column_names.collect {|column_name| connection.quote_column_name(column_name) }
248 end
249 end
250
251 # Any instance method that returns a collection makes use of Rails 2.1's named_scope (which is bundled for Rails 2.0), so it can be treated as a finder.
252 #
253 # category.self_and_descendants.count
254 # category.ancestors.find(:all, :conditions => "name like '%foo%'")
255 module InstanceMethods
256 # Value of the parent column
257 def parent_id
258 self[parent_column_name]
259 end
260
261 # Value of the left column
262 def left
263 self[left_column_name]
264 end
265
266 # Value of the right column
267 def right
268 self[right_column_name]
269 end
270
271 # Returns true if this is a root node.
272 def root?
273 parent_id.nil?
274 end
275
276 def leaf?
277 !new_record? && right - left == 1
278 end
279
280 # Returns true is this is a child node
281 def child?
282 !parent_id.nil?
283 end
284
285 # order by left column
286 def <=>(x)
287 left <=> x.left
288 end
289
290 # Redefine to act like active record
291 def ==(comparison_object)
292 comparison_object.equal?(self) ||
293 (comparison_object.instance_of?(self.class) &&
294 comparison_object.id == id &&
295 !comparison_object.new_record?)
296 end
297
298 # Returns root
299 def root
300 self_and_ancestors.find(:first)
301 end
302
303 # Returns the array of all parents and self
304 def self_and_ancestors
305 nested_set_scope.scoped :conditions => [
306 "#{self.class.quoted_table_name}.#{quoted_left_column_name} <= ? AND #{self.class.quoted_table_name}.#{quoted_right_column_name} >= ?", left, right
307 ]
308 end
309
310 # Returns an array of all parents
311 def ancestors
312 without_self self_and_ancestors
313 end
314
315 # Returns the array of all children of the parent, including self
316 def self_and_siblings
317 nested_set_scope.scoped :conditions => {parent_column_name => parent_id}
318 end
319
320 # Returns the array of all children of the parent, except self
321 def siblings
322 without_self self_and_siblings
323 end
324
325 # Returns a set of all of its nested children which do not have children
326 def leaves
327 descendants.scoped :conditions => "#{self.class.quoted_table_name}.#{quoted_right_column_name} - #{self.class.quoted_table_name}.#{quoted_left_column_name} = 1"
328 end
329
330 # Returns the level of this object in the tree
331 # root level is 0
332 def level
333 parent_id.nil? ? 0 : ancestors.count
334 end
335
336 # Returns a set of itself and all of its nested children
337 def self_and_descendants
338 nested_set_scope.scoped :conditions => [
339 "#{self.class.quoted_table_name}.#{quoted_left_column_name} >= ? AND #{self.class.quoted_table_name}.#{quoted_right_column_name} <= ?", left, right
340 ]
341 end
342
343 # Returns a set of all of its children and nested children
344 def descendants
345 without_self self_and_descendants
346 end
347
348 def is_descendant_of?(other)
349 other.left < self.left && self.left < other.right && same_scope?(other)
350 end
351
352 def is_or_is_descendant_of?(other)
353 other.left <= self.left && self.left < other.right && same_scope?(other)
354 end
355
356 def is_ancestor_of?(other)
357 self.left < other.left && other.left < self.right && same_scope?(other)
358 end
359
360 def is_or_is_ancestor_of?(other)
361 self.left <= other.left && other.left < self.right && same_scope?(other)
362 end
363
364 # Check if other model is in the same scope
365 def same_scope?(other)
366 Array(acts_as_nested_set_options[:scope]).all? do |attr|
367 self.send(attr) == other.send(attr)
368 end
369 end
370
371 # Find the first sibling to the left
372 def left_sibling
373 siblings.find(:first, :conditions => ["#{self.class.quoted_table_name}.#{quoted_left_column_name} < ?", left],
374 :order => "#{self.class.quoted_table_name}.#{quoted_left_column_name} DESC")
375 end
376
377 # Find the first sibling to the right
378 def right_sibling
379 siblings.find(:first, :conditions => ["#{self.class.quoted_table_name}.#{quoted_left_column_name} > ?", left])
380 end
381
382 # Shorthand method for finding the left sibling and moving to the left of it.
383 def move_left
384 move_to_left_of left_sibling
385 end
386
387 # Shorthand method for finding the right sibling and moving to the right of it.
388 def move_right
389 move_to_right_of right_sibling
390 end
391
392 # Move the node to the left of another node (you can pass id only)
393 def move_to_left_of(node)
394 move_to node, :left
395 end
396
397 # Move the node to the left of another node (you can pass id only)
398 def move_to_right_of(node)
399 move_to node, :right
400 end
401
402 # Move the node to the child of another node (you can pass id only)
403 def move_to_child_of(node)
404 move_to node, :child
405 end
406
407 # Move the node to root nodes
408 def move_to_root
409 move_to nil, :root
410 end
411
412 def move_possible?(target)
413 self != target && # Can't target self
414 same_scope?(target) && # can't be in different scopes
415 # !(left..right).include?(target.left..target.right) # this needs tested more
416 # detect impossible move
417 !((left <= target.left && right >= target.left) or (left <= target.right && right >= target.right))
418 end
419
420 def to_text
421 self_and_descendants.map do |node|
422 "#{'*'*(node.level+1)} #{node.id} #{node.to_s} (#{node.parent_id}, #{node.left}, #{node.right})"
423 end.join("\n")
424 end
425
426 protected
427
428 def without_self(scope)
429 scope.scoped :conditions => ["#{self.class.quoted_table_name}.#{self.class.primary_key} != ?", self]
430 end
431
432 # All nested set queries should use this nested_set_scope, which performs finds on
433 # the base ActiveRecord class, using the :scope declared in the acts_as_nested_set
434 # declaration.
435 def nested_set_scope
436 options = {:order => quoted_left_column_name}
437 scopes = Array(acts_as_nested_set_options[:scope])
438 options[:conditions] = scopes.inject({}) do |conditions,attr|
439 conditions.merge attr => self[attr]
440 end unless scopes.empty?
441 self.class.base_class.scoped options
442 end
443
444 def store_new_parent
445 @move_to_new_parent_id = send("#{parent_column_name}_changed?") ? parent_id : false
446 true # force callback to return true
447 end
448
449 def move_to_new_parent
450 if @move_to_new_parent_id.nil?
451 move_to_root
452 elsif @move_to_new_parent_id
453 move_to_child_of(@move_to_new_parent_id)
454 end
455 end
456
457 # on creation, set automatically lft and rgt to the end of the tree
458 def set_default_left_and_right
459 maxright = nested_set_scope.maximum(right_column_name) || 0
460 # adds the new node to the right of all existing nodes
461 self[left_column_name] = maxright + 1
462 self[right_column_name] = maxright + 2
463 end
464
465 # Prunes a branch off of the tree, shifting all of the elements on the right
466 # back to the left so the counts still work.
467 def destroy_descendants
468 return if right.nil? || left.nil? || skip_before_destroy
469
470 self.class.base_class.transaction do
471 if acts_as_nested_set_options[:dependent] == :destroy
472 descendants.each do |model|
473 model.skip_before_destroy = true
474 model.destroy
475 end
476 else
477 nested_set_scope.delete_all(
478 ["#{quoted_left_column_name} > ? AND #{quoted_right_column_name} < ?",
479 left, right]
480 )
481 end
482
483 # update lefts and rights for remaining nodes
484 diff = right - left + 1
485 nested_set_scope.update_all(
486 ["#{quoted_left_column_name} = (#{quoted_left_column_name} - ?)", diff],
487 ["#{quoted_left_column_name} > ?", right]
488 )
489 nested_set_scope.update_all(
490 ["#{quoted_right_column_name} = (#{quoted_right_column_name} - ?)", diff],
491 ["#{quoted_right_column_name} > ?", right]
492 )
493
494 # Don't allow multiple calls to destroy to corrupt the set
495 self.skip_before_destroy = true
496 end
497 end
498
499 # reload left, right, and parent
500 def reload_nested_set
501 reload(:select => "#{quoted_left_column_name}, " +
502 "#{quoted_right_column_name}, #{quoted_parent_column_name}")
503 end
504
505 def move_to(target, position)
506 raise ActiveRecord::ActiveRecordError, "You cannot move a new node" if self.new_record?
507 return if callback(:before_move) == false
508 transaction do
509 if target.is_a? self.class.base_class
510 target.reload_nested_set
511 elsif position != :root
512 # load object if node is not an object
513 target = nested_set_scope.find(target)
514 end
515 self.reload_nested_set
516
517 unless position == :root || move_possible?(target)
518 raise ActiveRecord::ActiveRecordError, "Impossible move, target node cannot be inside moved tree."
519 end
520
521 bound = case position
522 when :child; target[right_column_name]
523 when :left; target[left_column_name]
524 when :right; target[right_column_name] + 1
525 when :root; 1
526 else raise ActiveRecord::ActiveRecordError, "Position should be :child, :left, :right or :root ('#{position}' received)."
527 end
528
529 if bound > self[right_column_name]
530 bound = bound - 1
531 other_bound = self[right_column_name] + 1
532 else
533 other_bound = self[left_column_name] - 1
534 end
535
536 # there would be no change
537 return if bound == self[right_column_name] || bound == self[left_column_name]
538
539 # we have defined the boundaries of two non-overlapping intervals,
540 # so sorting puts both the intervals and their boundaries in order
541 a, b, c, d = [self[left_column_name], self[right_column_name], bound, other_bound].sort
542
543 new_parent = case position
544 when :child; target.id
545 when :root; nil
546 else target[parent_column_name]
547 end
548
549 self.class.base_class.update_all([
550 "#{quoted_left_column_name} = CASE " +
551 "WHEN #{quoted_left_column_name} BETWEEN :a AND :b " +
552 "THEN #{quoted_left_column_name} + :d - :b " +
553 "WHEN #{quoted_left_column_name} BETWEEN :c AND :d " +
554 "THEN #{quoted_left_column_name} + :a - :c " +
555 "ELSE #{quoted_left_column_name} END, " +
556 "#{quoted_right_column_name} = CASE " +
557 "WHEN #{quoted_right_column_name} BETWEEN :a AND :b " +
558 "THEN #{quoted_right_column_name} + :d - :b " +
559 "WHEN #{quoted_right_column_name} BETWEEN :c AND :d " +
560 "THEN #{quoted_right_column_name} + :a - :c " +
561 "ELSE #{quoted_right_column_name} END, " +
562 "#{quoted_parent_column_name} = CASE " +
563 "WHEN #{self.class.base_class.primary_key} = :id THEN :new_parent " +
564 "ELSE #{quoted_parent_column_name} END",
565 {:a => a, :b => b, :c => c, :d => d, :id => self.id, :new_parent => new_parent}
566 ], nested_set_scope.proxy_options[:conditions])
567 end
568 target.reload_nested_set if target
569 self.reload_nested_set
570 callback(:after_move)
571 end
572
573 end
574
575 end
576 end
577end
diff --git a/vendor/plugins/awesome_nested_set/lib/awesome_nested_set/helper.rb b/vendor/plugins/awesome_nested_set/lib/awesome_nested_set/helper.rb
deleted file mode 100644
index 09c803f..0000000
--- a/vendor/plugins/awesome_nested_set/lib/awesome_nested_set/helper.rb
+++ /dev/null
@@ -1,40 +0,0 @@
1module CollectiveIdea #:nodoc:
2 module Acts #:nodoc:
3 module NestedSet #:nodoc:
4 # This module provides some helpers for the model classes using acts_as_nested_set.
5 # It is included by default in all views.
6 #
7 module Helper
8 # Returns options for select.
9 # You can exclude some items from the tree.
10 # You can pass a block receiving an item and returning the string displayed in the select.
11 #
12 # == Params
13 # * +class_or_item+ - Class name or top level times
14 # * +mover+ - The item that is being move, used to exlude impossible moves
15 # * +&block+ - a block that will be used to display: { |item| ... item.name }
16 #
17 # == Usage
18 #
19 # <%= f.select :parent_id, nested_set_options(Category, @category) {|i|
20 # "#{'–' * i.level} #{i.name}"
21 # }) %>
22 #
23 def nested_set_options(class_or_item, mover = nil)
24 class_or_item = class_or_item.roots if class_or_item.is_a?(Class)
25 items = Array(class_or_item)
26 result = []
27 items.each do |root|
28 result += root.self_and_descendants.map do |i|
29 if mover.nil? || mover.new_record? || mover.move_possible?(i)
30 [yield(i), i.id]
31 end
32 end.compact
33 end
34 result
35 end
36
37 end
38 end
39 end
40end \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/rails/init.rb b/vendor/plugins/awesome_nested_set/rails/init.rb
deleted file mode 100644
index 2ff1336..0000000
--- a/vendor/plugins/awesome_nested_set/rails/init.rb
+++ /dev/null
@@ -1,12 +0,0 @@
1require 'awesome_nested_set'
2
3ActiveRecord::Base.class_eval do
4 include CollectiveIdea::Acts::NestedSet
5end
6
7if defined?(ActionView)
8 require 'awesome_nested_set/helper'
9 ActionView::Base.class_eval do
10 include CollectiveIdea::Acts::NestedSet::Helper
11 end
12end \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/test/application.rb b/vendor/plugins/awesome_nested_set/test/application.rb
deleted file mode 100644
index 0e6eacf..0000000
--- a/vendor/plugins/awesome_nested_set/test/application.rb
+++ /dev/null
@@ -1 +0,0 @@
1# This file is here to satisfy test_help from Rails < 2.3 \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/test/awesome_nested_set/helper_test.rb b/vendor/plugins/awesome_nested_set/test/awesome_nested_set/helper_test.rb
deleted file mode 100644
index 888323c..0000000
--- a/vendor/plugins/awesome_nested_set/test/awesome_nested_set/helper_test.rb
+++ /dev/null
@@ -1,41 +0,0 @@
1require 'test_helper'
2
3module CollectiveIdea
4 module Acts #:nodoc:
5 module NestedSet #:nodoc:
6 class AwesomeNestedSetTest < TestCaseClass
7 include Helper
8 fixtures :categories
9
10 def test_nested_set_options
11 expected = [
12 [" Top Level", 1],
13 ["- Child 1", 2],
14 ['- Child 2', 3],
15 ['-- Child 2.1', 4],
16 ['- Child 3', 5],
17 [" Top Level 2", 6]
18 ]
19 actual = nested_set_options(Category) do |c|
20 "#{'-' * c.level} #{c.name}"
21 end
22 assert_equal expected, actual
23 end
24
25 def test_nested_set_options_with_mover
26 expected = [
27 [" Top Level", 1],
28 ["- Child 1", 2],
29 ['- Child 3', 5],
30 [" Top Level 2", 6]
31 ]
32 actual = nested_set_options(Category, categories(:child_2)) do |c|
33 "#{'-' * c.level} #{c.name}"
34 end
35 assert_equal expected, actual
36 end
37
38 end
39 end
40 end
41end
diff --git a/vendor/plugins/awesome_nested_set/test/awesome_nested_set_test.rb b/vendor/plugins/awesome_nested_set/test/awesome_nested_set_test.rb
deleted file mode 100644
index 7b587de..0000000
--- a/vendor/plugins/awesome_nested_set/test/awesome_nested_set_test.rb
+++ /dev/null
@@ -1,748 +0,0 @@
1require 'test_helper'
2
3class Note < ActiveRecord::Base
4 acts_as_nested_set :scope => [:notable_id, :notable_type]
5end
6class Default < ActiveRecord::Base
7 acts_as_nested_set
8 set_table_name 'categories'
9end
10class ScopedCategory < ActiveRecord::Base
11 acts_as_nested_set :scope => :organization
12 set_table_name 'categories'
13end
14class RenamedColumns < ActiveRecord::Base
15 acts_as_nested_set :parent_column => 'mother_id', :left_column => 'red', :right_column => 'black'
16end
17
18class AwesomeNestedSetTest < TestCaseClass
19
20 def test_left_column_default
21 assert_equal 'lft', Default.acts_as_nested_set_options[:left_column]
22 end
23
24 def test_right_column_default
25 assert_equal 'rgt', Default.acts_as_nested_set_options[:right_column]
26 end
27
28 def test_parent_column_default
29 assert_equal 'parent_id', Default.acts_as_nested_set_options[:parent_column]
30 end
31
32 def test_scope_default
33 assert_nil Default.acts_as_nested_set_options[:scope]
34 end
35
36 def test_left_column_name
37 assert_equal 'lft', Default.left_column_name
38 assert_equal 'lft', Default.new.left_column_name
39 assert_equal 'red', RenamedColumns.left_column_name
40 assert_equal 'red', RenamedColumns.new.left_column_name
41 end
42
43 def test_right_column_name
44 assert_equal 'rgt', Default.right_column_name
45 assert_equal 'rgt', Default.new.right_column_name
46 assert_equal 'black', RenamedColumns.right_column_name
47 assert_equal 'black', RenamedColumns.new.right_column_name
48 end
49
50 def test_parent_column_name
51 assert_equal 'parent_id', Default.parent_column_name
52 assert_equal 'parent_id', Default.new.parent_column_name
53 assert_equal 'mother_id', RenamedColumns.parent_column_name
54 assert_equal 'mother_id', RenamedColumns.new.parent_column_name
55 end
56
57 def test_creation_with_altered_column_names
58 assert_nothing_raised do
59 RenamedColumns.create!()
60 end
61 end
62
63 def test_quoted_left_column_name
64 quoted = Default.connection.quote_column_name('lft')
65 assert_equal quoted, Default.quoted_left_column_name
66 assert_equal quoted, Default.new.quoted_left_column_name
67 end
68
69 def test_quoted_right_column_name
70 quoted = Default.connection.quote_column_name('rgt')
71 assert_equal quoted, Default.quoted_right_column_name
72 assert_equal quoted, Default.new.quoted_right_column_name
73 end
74
75 def test_left_column_protected_from_assignment
76 assert_raises(ActiveRecord::ActiveRecordError) { Category.new.lft = 1 }
77 end
78
79 def test_right_column_protected_from_assignment
80 assert_raises(ActiveRecord::ActiveRecordError) { Category.new.rgt = 1 }
81 end
82
83 def test_colums_protected_on_initialize
84 c = Category.new(:lft => 1, :rgt => 2)
85 assert_nil c.lft
86 assert_nil c.rgt
87 end
88
89 def test_scoped_appends_id
90 assert_equal :organization_id, ScopedCategory.acts_as_nested_set_options[:scope]
91 end
92
93 def test_roots_class_method
94 assert_equal Category.find_all_by_parent_id(nil), Category.roots
95 end
96
97 def test_root_class_method
98 assert_equal categories(:top_level), Category.root
99 end
100
101 def test_root
102 assert_equal categories(:top_level), categories(:child_3).root
103 end
104
105 def test_root?
106 assert categories(:top_level).root?
107 assert categories(:top_level_2).root?
108 end
109
110 def test_leaves_class_method
111 assert_equal Category.find(:all, :conditions => "#{Category.right_column_name} - #{Category.left_column_name} = 1"), Category.leaves
112 assert_equal Category.leaves.count, 4
113 assert (Category.leaves.include? categories(:child_1))
114 assert (Category.leaves.include? categories(:child_2_1))
115 assert (Category.leaves.include? categories(:child_3))
116 assert (Category.leaves.include? categories(:top_level_2))
117 end
118
119 def test_leaf
120 assert categories(:child_1).leaf?
121 assert categories(:child_2_1).leaf?
122 assert categories(:child_3).leaf?
123 assert categories(:top_level_2).leaf?
124
125 assert !categories(:top_level).leaf?
126 assert !categories(:child_2).leaf?
127 assert !Category.new.leaf?
128 end
129
130
131 def test_parent
132 assert_equal categories(:child_2), categories(:child_2_1).parent
133 end
134
135 def test_self_and_ancestors
136 child = categories(:child_2_1)
137 self_and_ancestors = [categories(:top_level), categories(:child_2), child]
138 assert_equal self_and_ancestors, child.self_and_ancestors
139 end
140
141 def test_ancestors
142 child = categories(:child_2_1)
143 ancestors = [categories(:top_level), categories(:child_2)]
144 assert_equal ancestors, child.ancestors
145 end
146
147 def test_self_and_siblings
148 child = categories(:child_2)
149 self_and_siblings = [categories(:child_1), child, categories(:child_3)]
150 assert_equal self_and_siblings, child.self_and_siblings
151 assert_nothing_raised do
152 tops = [categories(:top_level), categories(:top_level_2)]
153 assert_equal tops, categories(:top_level).self_and_siblings
154 end
155 end
156
157 def test_siblings
158 child = categories(:child_2)
159 siblings = [categories(:child_1), categories(:child_3)]
160 assert_equal siblings, child.siblings
161 end
162
163 def test_leaves
164 leaves = [categories(:child_1), categories(:child_2_1), categories(:child_3), categories(:top_level_2)]
165 assert categories(:top_level).leaves, leaves
166 end
167
168 def test_level
169 assert_equal 0, categories(:top_level).level
170 assert_equal 1, categories(:child_1).level
171 assert_equal 2, categories(:child_2_1).level
172 end
173
174 def test_has_children?
175 assert categories(:child_2_1).children.empty?
176 assert !categories(:child_2).children.empty?
177 assert !categories(:top_level).children.empty?
178 end
179
180 def test_self_and_descendents
181 parent = categories(:top_level)
182 self_and_descendants = [parent, categories(:child_1), categories(:child_2),
183 categories(:child_2_1), categories(:child_3)]
184 assert_equal self_and_descendants, parent.self_and_descendants
185 assert_equal self_and_descendants, parent.self_and_descendants.count
186 end
187
188 def test_descendents
189 lawyers = Category.create!(:name => "lawyers")
190 us = Category.create!(:name => "United States")
191 us.move_to_child_of(lawyers)
192 patent = Category.create!(:name => "Patent Law")
193 patent.move_to_child_of(us)
194 lawyers.reload
195
196 assert_equal 1, lawyers.children.size
197 assert_equal 1, us.children.size
198 assert_equal 2, lawyers.descendants.size
199 end
200
201 def test_self_and_descendents
202 parent = categories(:top_level)
203 descendants = [categories(:child_1), categories(:child_2),
204 categories(:child_2_1), categories(:child_3)]
205 assert_equal descendants, parent.descendants
206 end
207
208 def test_children
209 category = categories(:top_level)
210 category.children.each {|c| assert_equal category.id, c.parent_id }
211 end
212
213 def test_order_of_children
214 categories(:child_2).move_left
215 assert_equal categories(:child_2), categories(:top_level).children[0]
216 assert_equal categories(:child_1), categories(:top_level).children[1]
217 assert_equal categories(:child_3), categories(:top_level).children[2]
218 end
219
220 def test_is_or_is_ancestor_of?
221 assert categories(:top_level).is_or_is_ancestor_of?(categories(:child_1))
222 assert categories(:top_level).is_or_is_ancestor_of?(categories(:child_2_1))
223 assert categories(:child_2).is_or_is_ancestor_of?(categories(:child_2_1))
224 assert !categories(:child_2_1).is_or_is_ancestor_of?(categories(:child_2))
225 assert !categories(:child_1).is_or_is_ancestor_of?(categories(:child_2))
226 assert categories(:child_1).is_or_is_ancestor_of?(categories(:child_1))
227 end
228
229 def test_is_ancestor_of?
230 assert categories(:top_level).is_ancestor_of?(categories(:child_1))
231 assert categories(:top_level).is_ancestor_of?(categories(:child_2_1))
232 assert categories(:child_2).is_ancestor_of?(categories(:child_2_1))
233 assert !categories(:child_2_1).is_ancestor_of?(categories(:child_2))
234 assert !categories(:child_1).is_ancestor_of?(categories(:child_2))
235 assert !categories(:child_1).is_ancestor_of?(categories(:child_1))
236 end
237
238 def test_is_or_is_ancestor_of_with_scope
239 root = ScopedCategory.root
240 child = root.children.first
241 assert root.is_or_is_ancestor_of?(child)
242 child.update_attribute :organization_id, 'different'
243 assert !root.is_or_is_ancestor_of?(child)
244 end
245
246 def test_is_or_is_descendant_of?
247 assert categories(:child_1).is_or_is_descendant_of?(categories(:top_level))
248 assert categories(:child_2_1).is_or_is_descendant_of?(categories(:top_level))
249 assert categories(:child_2_1).is_or_is_descendant_of?(categories(:child_2))
250 assert !categories(:child_2).is_or_is_descendant_of?(categories(:child_2_1))
251 assert !categories(:child_2).is_or_is_descendant_of?(categories(:child_1))
252 assert categories(:child_1).is_or_is_descendant_of?(categories(:child_1))
253 end
254
255 def test_is_descendant_of?
256 assert categories(:child_1).is_descendant_of?(categories(:top_level))
257 assert categories(:child_2_1).is_descendant_of?(categories(:top_level))
258 assert categories(:child_2_1).is_descendant_of?(categories(:child_2))
259 assert !categories(:child_2).is_descendant_of?(categories(:child_2_1))
260 assert !categories(:child_2).is_descendant_of?(categories(:child_1))
261 assert !categories(:child_1).is_descendant_of?(categories(:child_1))
262 end
263
264 def test_is_or_is_descendant_of_with_scope
265 root = ScopedCategory.root
266 child = root.children.first
267 assert child.is_or_is_descendant_of?(root)
268 child.update_attribute :organization_id, 'different'
269 assert !child.is_or_is_descendant_of?(root)
270 end
271
272 def test_same_scope?
273 root = ScopedCategory.root
274 child = root.children.first
275 assert child.same_scope?(root)
276 child.update_attribute :organization_id, 'different'
277 assert !child.same_scope?(root)
278 end
279
280 def test_left_sibling
281 assert_equal categories(:child_1), categories(:child_2).left_sibling
282 assert_equal categories(:child_2), categories(:child_3).left_sibling
283 end
284
285 def test_left_sibling_of_root
286 assert_nil categories(:top_level).left_sibling
287 end
288
289 def test_left_sibling_without_siblings
290 assert_nil categories(:child_2_1).left_sibling
291 end
292
293 def test_left_sibling_of_leftmost_node
294 assert_nil categories(:child_1).left_sibling
295 end
296
297 def test_right_sibling
298 assert_equal categories(:child_3), categories(:child_2).right_sibling
299 assert_equal categories(:child_2), categories(:child_1).right_sibling
300 end
301
302 def test_right_sibling_of_root
303 assert_equal categories(:top_level_2), categories(:top_level).right_sibling
304 assert_nil categories(:top_level_2).right_sibling
305 end
306
307 def test_right_sibling_without_siblings
308 assert_nil categories(:child_2_1).right_sibling
309 end
310
311 def test_right_sibling_of_rightmost_node
312 assert_nil categories(:child_3).right_sibling
313 end
314
315 def test_move_left
316 categories(:child_2).move_left
317 assert_nil categories(:child_2).left_sibling
318 assert_equal categories(:child_1), categories(:child_2).right_sibling
319 assert Category.valid?
320 end
321
322 def test_move_right
323 categories(:child_2).move_right
324 assert_nil categories(:child_2).right_sibling
325 assert_equal categories(:child_3), categories(:child_2).left_sibling
326 assert Category.valid?
327 end
328
329 def test_move_to_left_of
330 categories(:child_3).move_to_left_of(categories(:child_1))
331 assert_nil categories(:child_3).left_sibling
332 assert_equal categories(:child_1), categories(:child_3).right_sibling
333 assert Category.valid?
334 end
335
336 def test_move_to_right_of
337 categories(:child_1).move_to_right_of(categories(:child_3))
338 assert_nil categories(:child_1).right_sibling
339 assert_equal categories(:child_3), categories(:child_1).left_sibling
340 assert Category.valid?
341 end
342
343 def test_move_to_root
344 categories(:child_2).move_to_root
345 assert_nil categories(:child_2).parent
346 assert_equal 0, categories(:child_2).level
347 assert_equal 1, categories(:child_2_1).level
348 assert_equal 1, categories(:child_2).left
349 assert_equal 4, categories(:child_2).right
350 assert Category.valid?
351 end
352
353 def test_move_to_child_of
354 categories(:child_1).move_to_child_of(categories(:child_3))
355 assert_equal categories(:child_3).id, categories(:child_1).parent_id
356 assert Category.valid?
357 end
358
359 def test_move_to_child_of_appends_to_end
360 child = Category.create! :name => 'New Child'
361 child.move_to_child_of categories(:top_level)
362 assert_equal child, categories(:top_level).children.last
363 end
364
365 def test_subtree_move_to_child_of
366 assert_equal 4, categories(:child_2).left
367 assert_equal 7, categories(:child_2).right
368
369 assert_equal 2, categories(:child_1).left
370 assert_equal 3, categories(:child_1).right
371
372 categories(:child_2).move_to_child_of(categories(:child_1))
373 assert Category.valid?
374 assert_equal categories(:child_1).id, categories(:child_2).parent_id
375
376 assert_equal 3, categories(:child_2).left
377 assert_equal 6, categories(:child_2).right
378 assert_equal 2, categories(:child_1).left
379 assert_equal 7, categories(:child_1).right
380 end
381
382 def test_slightly_difficult_move_to_child_of
383 assert_equal 11, categories(:top_level_2).left
384 assert_equal 12, categories(:top_level_2).right
385
386 # create a new top-level node and move single-node top-level tree inside it.
387 new_top = Category.create(:name => 'New Top')
388 assert_equal 13, new_top.left
389 assert_equal 14, new_top.right
390
391 categories(:top_level_2).move_to_child_of(new_top)
392
393 assert Category.valid?
394 assert_equal new_top.id, categories(:top_level_2).parent_id
395
396 assert_equal 12, categories(:top_level_2).left
397 assert_equal 13, categories(:top_level_2).right
398 assert_equal 11, new_top.left
399 assert_equal 14, new_top.right
400 end
401
402 def test_difficult_move_to_child_of
403 assert_equal 1, categories(:top_level).left
404 assert_equal 10, categories(:top_level).right
405 assert_equal 5, categories(:child_2_1).left
406 assert_equal 6, categories(:child_2_1).right
407
408 # create a new top-level node and move an entire top-level tree inside it.
409 new_top = Category.create(:name => 'New Top')
410 categories(:top_level).move_to_child_of(new_top)
411 categories(:child_2_1).reload
412 assert Category.valid?
413 assert_equal new_top.id, categories(:top_level).parent_id
414
415 assert_equal 4, categories(:top_level).left
416 assert_equal 13, categories(:top_level).right
417 assert_equal 8, categories(:child_2_1).left
418 assert_equal 9, categories(:child_2_1).right
419 end
420
421 #rebuild swaps the position of the 2 children when added using move_to_child twice onto same parent
422 def test_move_to_child_more_than_once_per_parent_rebuild
423 root1 = Category.create(:name => 'Root1')
424 root2 = Category.create(:name => 'Root2')
425 root3 = Category.create(:name => 'Root3')
426
427 root2.move_to_child_of root1
428 root3.move_to_child_of root1
429
430 output = Category.roots.last.to_text
431 Category.update_all('lft = null, rgt = null')
432 Category.rebuild!
433
434 assert_equal Category.roots.last.to_text, output
435 end
436
437 # doing move_to_child twice onto same parent from the furthest right first
438 def test_move_to_child_more_than_once_per_parent_outside_in
439 node1 = Category.create(:name => 'Node-1')
440 node2 = Category.create(:name => 'Node-2')
441 node3 = Category.create(:name => 'Node-3')
442
443 node2.move_to_child_of node1
444 node3.move_to_child_of node1
445
446 output = Category.roots.last.to_text
447 Category.update_all('lft = null, rgt = null')
448 Category.rebuild!
449
450 assert_equal Category.roots.last.to_text, output
451 end
452
453
454 def test_valid_with_null_lefts
455 assert Category.valid?
456 Category.update_all('lft = null')
457 assert !Category.valid?
458 end
459
460 def test_valid_with_null_rights
461 assert Category.valid?
462 Category.update_all('rgt = null')
463 assert !Category.valid?
464 end
465
466 def test_valid_with_missing_intermediate_node
467 # Even though child_2_1 will still exist, it is a sign of a sloppy delete, not an invalid tree.
468 assert Category.valid?
469 Category.delete(categories(:child_2).id)
470 assert Category.valid?
471 end
472
473 def test_valid_with_overlapping_and_rights
474 assert Category.valid?
475 categories(:top_level_2)['lft'] = 0
476 categories(:top_level_2).save
477 assert !Category.valid?
478 end
479
480 def test_rebuild
481 assert Category.valid?
482 before_text = Category.root.to_text
483 Category.update_all('lft = null, rgt = null')
484 Category.rebuild!
485 assert Category.valid?
486 assert_equal before_text, Category.root.to_text
487 end
488
489 def test_move_possible_for_sibling
490 assert categories(:child_2).move_possible?(categories(:child_1))
491 end
492
493 def test_move_not_possible_to_self
494 assert !categories(:top_level).move_possible?(categories(:top_level))
495 end
496
497 def test_move_not_possible_to_parent
498 categories(:top_level).descendants.each do |descendant|
499 assert !categories(:top_level).move_possible?(descendant)
500 assert descendant.move_possible?(categories(:top_level))
501 end
502 end
503
504 def test_is_or_is_ancestor_of?
505 [:child_1, :child_2, :child_2_1, :child_3].each do |c|
506 assert categories(:top_level).is_or_is_ancestor_of?(categories(c))
507 end
508 assert !categories(:top_level).is_or_is_ancestor_of?(categories(:top_level_2))
509 end
510
511 def test_left_and_rights_valid_with_blank_left
512 assert Category.left_and_rights_valid?
513 categories(:child_2)[:lft] = nil
514 categories(:child_2).save(false)
515 assert !Category.left_and_rights_valid?
516 end
517
518 def test_left_and_rights_valid_with_blank_right
519 assert Category.left_and_rights_valid?
520 categories(:child_2)[:rgt] = nil
521 categories(:child_2).save(false)
522 assert !Category.left_and_rights_valid?
523 end
524
525 def test_left_and_rights_valid_with_equal
526 assert Category.left_and_rights_valid?
527 categories(:top_level_2)[:lft] = categories(:top_level_2)[:rgt]
528 categories(:top_level_2).save(false)
529 assert !Category.left_and_rights_valid?
530 end
531
532 def test_left_and_rights_valid_with_left_equal_to_parent
533 assert Category.left_and_rights_valid?
534 categories(:child_2)[:lft] = categories(:top_level)[:lft]
535 categories(:child_2).save(false)
536 assert !Category.left_and_rights_valid?
537 end
538
539 def test_left_and_rights_valid_with_right_equal_to_parent
540 assert Category.left_and_rights_valid?
541 categories(:child_2)[:rgt] = categories(:top_level)[:rgt]
542 categories(:child_2).save(false)
543 assert !Category.left_and_rights_valid?
544 end
545
546 def test_moving_dirty_objects_doesnt_invalidate_tree
547 r1 = Category.create
548 r2 = Category.create
549 r3 = Category.create
550 r4 = Category.create
551 nodes = [r1, r2, r3, r4]
552
553 r2.move_to_child_of(r1)
554 assert Category.valid?
555
556 r3.move_to_child_of(r1)
557 assert Category.valid?
558
559 r4.move_to_child_of(r2)
560 assert Category.valid?
561 end
562
563 def test_multi_scoped_no_duplicates_for_columns?
564 assert_nothing_raised do
565 Note.no_duplicates_for_columns?
566 end
567 end
568
569 def test_multi_scoped_all_roots_valid?
570 assert_nothing_raised do
571 Note.all_roots_valid?
572 end
573 end
574
575 def test_multi_scoped
576 note1 = Note.create!(:body => "A", :notable_id => 2, :notable_type => 'Category')
577 note2 = Note.create!(:body => "B", :notable_id => 2, :notable_type => 'Category')
578 note3 = Note.create!(:body => "C", :notable_id => 2, :notable_type => 'Default')
579
580 assert_equal [note1, note2], note1.self_and_siblings
581 assert_equal [note3], note3.self_and_siblings
582 end
583
584 def test_multi_scoped_rebuild
585 root = Note.create!(:body => "A", :notable_id => 3, :notable_type => 'Category')
586 child1 = Note.create!(:body => "B", :notable_id => 3, :notable_type => 'Category')
587 child2 = Note.create!(:body => "C", :notable_id => 3, :notable_type => 'Category')
588
589 child1.move_to_child_of root
590 child2.move_to_child_of root
591
592 Note.update_all('lft = null, rgt = null')
593 Note.rebuild!
594
595 assert_equal Note.roots.find_by_body('A'), root
596 assert_equal [child1, child2], Note.roots.find_by_body('A').children
597 end
598
599 def test_same_scope_with_multi_scopes
600 assert_nothing_raised do
601 notes(:scope1).same_scope?(notes(:child_1))
602 end
603 assert notes(:scope1).same_scope?(notes(:child_1))
604 assert notes(:child_1).same_scope?(notes(:scope1))
605 assert !notes(:scope1).same_scope?(notes(:scope2))
606 end
607
608 def test_quoting_of_multi_scope_column_names
609 assert_equal ["\"notable_id\"", "\"notable_type\""], Note.quoted_scope_column_names
610 end
611
612 def test_equal_in_same_scope
613 assert_equal notes(:scope1), notes(:scope1)
614 assert_not_equal notes(:scope1), notes(:child_1)
615 end
616
617 def test_equal_in_different_scopes
618 assert_not_equal notes(:scope1), notes(:scope2)
619 end
620
621 def test_delete_does_not_invalidate
622 Category.acts_as_nested_set_options[:dependent] = :delete
623 categories(:child_2).destroy
624 assert Category.valid?
625 end
626
627 def test_destroy_does_not_invalidate
628 Category.acts_as_nested_set_options[:dependent] = :destroy
629 categories(:child_2).destroy
630 assert Category.valid?
631 end
632
633 def test_destroy_multiple_times_does_not_invalidate
634 Category.acts_as_nested_set_options[:dependent] = :destroy
635 categories(:child_2).destroy
636 categories(:child_2).destroy
637 assert Category.valid?
638 end
639
640 def test_assigning_parent_id_on_create
641 category = Category.create!(:name => "Child", :parent_id => categories(:child_2).id)
642 assert_equal categories(:child_2), category.parent
643 assert_equal categories(:child_2).id, category.parent_id
644 assert_not_nil category.left
645 assert_not_nil category.right
646 assert Category.valid?
647 end
648
649 def test_assigning_parent_on_create
650 category = Category.create!(:name => "Child", :parent => categories(:child_2))
651 assert_equal categories(:child_2), category.parent
652 assert_equal categories(:child_2).id, category.parent_id
653 assert_not_nil category.left
654 assert_not_nil category.right
655 assert Category.valid?
656 end
657
658 def test_assigning_parent_id_to_nil_on_create
659 category = Category.create!(:name => "New Root", :parent_id => nil)
660 assert_nil category.parent
661 assert_nil category.parent_id
662 assert_not_nil category.left
663 assert_not_nil category.right
664 assert Category.valid?
665 end
666
667 def test_assigning_parent_id_on_update
668 category = categories(:child_2_1)
669 category.parent_id = categories(:child_3).id
670 category.save
671 assert_equal categories(:child_3), category.parent
672 assert_equal categories(:child_3).id, category.parent_id
673 assert Category.valid?
674 end
675
676 def test_assigning_parent_on_update
677 category = categories(:child_2_1)
678 category.parent = categories(:child_3)
679 category.save
680 assert_equal categories(:child_3), category.parent
681 assert_equal categories(:child_3).id, category.parent_id
682 assert Category.valid?
683 end
684
685 def test_assigning_parent_id_to_nil_on_update
686 category = categories(:child_2_1)
687 category.parent_id = nil
688 category.save
689 assert_nil category.parent
690 assert_nil category.parent_id
691 assert Category.valid?
692 end
693
694 def test_creating_child_from_parent
695 category = categories(:child_2).children.create!(:name => "Child")
696 assert_equal categories(:child_2), category.parent
697 assert_equal categories(:child_2).id, category.parent_id
698 assert_not_nil category.left
699 assert_not_nil category.right
700 assert Category.valid?
701 end
702
703 def check_structure(entries, structure)
704 structure = structure.dup
705 Category.each_with_level(entries) do |category, level|
706 expected_level, expected_name = structure.shift
707 assert_equal expected_name, category.name, "wrong category"
708 assert_equal expected_level, level, "wrong level for #{category.name}"
709 end
710 end
711
712 def test_each_with_level
713 levels = [
714 [0, "Top Level"],
715 [1, "Child 1"],
716 [1, "Child 2"],
717 [2, "Child 2.1"],
718 [1, "Child 3" ]]
719
720 check_structure(Category.root.self_and_descendants, levels)
721
722 # test some deeper structures
723 category = Category.find_by_name("Child 1")
724 c1 = Category.new(:name => "Child 1.1")
725 c2 = Category.new(:name => "Child 1.1.1")
726 c3 = Category.new(:name => "Child 1.1.1.1")
727 c4 = Category.new(:name => "Child 1.2")
728 [c1, c2, c3, c4].each(&:save!)
729
730 c1.move_to_child_of(category)
731 c2.move_to_child_of(c1)
732 c3.move_to_child_of(c2)
733 c4.move_to_child_of(category)
734
735 levels = [
736 [0, "Top Level"],
737 [1, "Child 1"],
738 [2, "Child 1.1"],
739 [3, "Child 1.1.1"],
740 [4, "Child 1.1.1.1"],
741 [2, "Child 1.2"],
742 [1, "Child 2"],
743 [2, "Child 2.1"],
744 [1, "Child 3" ]]
745
746 check_structure(Category.root.self_and_descendants, levels)
747 end
748end
diff --git a/vendor/plugins/awesome_nested_set/test/fixtures/categories.yml b/vendor/plugins/awesome_nested_set/test/fixtures/categories.yml
deleted file mode 100644
index bc8e078..0000000
--- a/vendor/plugins/awesome_nested_set/test/fixtures/categories.yml
+++ /dev/null
@@ -1,34 +0,0 @@
1top_level:
2 id: 1
3 name: Top Level
4 lft: 1
5 rgt: 10
6child_1:
7 id: 2
8 name: Child 1
9 parent_id: 1
10 lft: 2
11 rgt: 3
12child_2:
13 id: 3
14 name: Child 2
15 parent_id: 1
16 lft: 4
17 rgt: 7
18child_2_1:
19 id: 4
20 name: Child 2.1
21 parent_id: 3
22 lft: 5
23 rgt: 6
24child_3:
25 id: 5
26 name: Child 3
27 parent_id: 1
28 lft: 8
29 rgt: 9
30top_level_2:
31 id: 6
32 name: Top Level 2
33 lft: 11
34 rgt: 12
diff --git a/vendor/plugins/awesome_nested_set/test/fixtures/category.rb b/vendor/plugins/awesome_nested_set/test/fixtures/category.rb
deleted file mode 100644
index 506b0da..0000000
--- a/vendor/plugins/awesome_nested_set/test/fixtures/category.rb
+++ /dev/null
@@ -1,15 +0,0 @@
1class Category < ActiveRecord::Base
2 acts_as_nested_set
3
4 def to_s
5 name
6 end
7
8 def recurse &block
9 block.call self, lambda{
10 self.children.each do |child|
11 child.recurse &block
12 end
13 }
14 end
15end \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/test/fixtures/departments.yml b/vendor/plugins/awesome_nested_set/test/fixtures/departments.yml
deleted file mode 100644
index e50a944..0000000
--- a/vendor/plugins/awesome_nested_set/test/fixtures/departments.yml
+++ /dev/null
@@ -1,3 +0,0 @@
1top:
2 id: 1
3 name: Top \ No newline at end of file
diff --git a/vendor/plugins/awesome_nested_set/test/fixtures/notes.yml b/vendor/plugins/awesome_nested_set/test/fixtures/notes.yml
deleted file mode 100644
index 004a533..0000000
--- a/vendor/plugins/awesome_nested_set/test/fixtures/notes.yml
+++ /dev/null
@@ -1,38 +0,0 @@
1scope1:
2 id: 1
3 body: Top Level
4 lft: 1
5 rgt: 10
6 notable_id: 1
7 notable_type: Category
8child_1:
9 id: 2
10 body: Child 1
11 parent_id: 1
12 lft: 2
13 rgt: 3
14 notable_id: 1
15 notable_type: Category
16child_2:
17 id: 3
18 body: Child 2
19 parent_id: 1
20 lft: 4
21 rgt: 7
22 notable_id: 1
23 notable_type: Category
24child_3:
25 id: 4
26 body: Child 3
27 parent_id: 1
28 lft: 8
29 rgt: 9
30 notable_id: 1
31 notable_type: Category
32scope2:
33 id: 5
34 body: Top Level 2
35 lft: 1
36 rgt: 2
37 notable_id: 1
38 notable_type: Departments
diff --git a/vendor/plugins/awesome_nested_set/test/test_helper.rb b/vendor/plugins/awesome_nested_set/test/test_helper.rb
deleted file mode 100644
index 05d8855..0000000
--- a/vendor/plugins/awesome_nested_set/test/test_helper.rb
+++ /dev/null
@@ -1,29 +0,0 @@
1$:.unshift(File.dirname(__FILE__) + '/../lib')
2plugin_test_dir = File.dirname(__FILE__)
3RAILS_ROOT = plugin_test_dir
4
5require 'rubygems'
6require 'test/unit'
7require 'multi_rails_init'
8require 'test_help'
9
10require plugin_test_dir + '/../init.rb'
11
12TestCaseClass = ActiveSupport::TestCase rescue Test::Unit::TestCase
13
14ActiveRecord::Base.logger = Logger.new(plugin_test_dir + "/debug.log")
15
16ActiveRecord::Base.configurations = YAML::load(IO.read(plugin_test_dir + "/db/database.yml"))
17ActiveRecord::Base.establish_connection(ENV["DB"] || "sqlite3mem")
18ActiveRecord::Migration.verbose = false
19load(File.join(plugin_test_dir, "db", "schema.rb"))
20
21Dir["#{plugin_test_dir}/fixtures/*.rb"].each {|file| require file }
22
23class TestCaseClass #:nodoc:
24 self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
25 self.use_transactional_fixtures = true
26 self.use_instantiated_fixtures = false
27
28 fixtures :categories, :notes, :departments
29end
diff --git a/vendor/plugins/exception_notification/README b/vendor/plugins/exception_notification/README
deleted file mode 100644
index 9a47c41..0000000
--- a/vendor/plugins/exception_notification/README
+++ /dev/null
@@ -1,111 +0,0 @@
1= Exception Notifier Plugin for Rails
2
3The Exception Notifier plugin provides a mailer object and a default set of
4templates for sending email notifications when errors occur in a Rails
5application. The plugin is configurable, allowing programmers to specify:
6
7* the sender address of the email
8* the recipient addresses
9* the text used to prefix the subject line
10
11The email includes information about the current request, session, and
12environment, and also gives a backtrace of the exception.
13
14== Usage
15
16First, include the ExceptionNotifiable mixin in whichever controller you want
17to generate error emails (typically ApplicationController):
18
19 class ApplicationController < ActionController::Base
20 include ExceptionNotifiable
21 ...
22 end
23
24Then, specify the email recipients in your environment:
25
26 ExceptionNotifier.exception_recipients = %w(joe@schmoe.com bill@schmoe.com)
27
28And that's it! The defaults take care of the rest.
29
30== Configuration
31
32You can tweak other values to your liking, as well. In your environment file,
33just set any or all of the following values:
34
35 # defaults to exception.notifier@default.com
36 ExceptionNotifier.sender_address =
37 %("Application Error" <app.error@myapp.com>)
38
39 # defaults to "[ERROR] "
40 ExceptionNotifier.email_prefix = "[APP] "
41
42Email notifications will only occur when the IP address is determined not to
43be local. You can specify certain addresses to always be local so that you'll
44get a detailed error instead of the generic error page. You do this in your
45controller (or even per-controller):
46
47 consider_local "64.72.18.143", "14.17.21.25"
48
49You can specify subnet masks as well, so that all matching addresses are
50considered local:
51
52 consider_local "64.72.18.143/24"
53
54The address "127.0.0.1" is always considered local. If you want to completely
55reset the list of all addresses (for instance, if you wanted "127.0.0.1" to
56NOT be considered local), you can simply do, somewhere in your controller:
57
58 local_addresses.clear
59
60== Customization
61
62By default, the notification email includes four parts: request, session,
63environment, and backtrace (in that order). You can customize how each of those
64sections are rendered by placing a partial named for that part in your
65app/views/exception_notifier directory (e.g., _session.rhtml). Each partial has
66access to the following variables:
67
68* @controller: the controller that caused the error
69* @request: the current request object
70* @exception: the exception that was raised
71* @host: the name of the host that made the request
72* @backtrace: a sanitized version of the exception's backtrace
73* @rails_root: a sanitized version of RAILS_ROOT
74* @data: a hash of optional data values that were passed to the notifier
75* @sections: the array of sections to include in the email
76
77You can reorder the sections, or exclude sections completely, by altering the
78ExceptionNotifier.sections variable. You can even add new sections that
79describe application-specific data--just add the section's name to the list
80(whereever you'd like), and define the corresponding partial. Then, if your
81new section requires information that isn't available by default, make sure
82it is made available to the email using the exception_data macro:
83
84 class ApplicationController < ActionController::Base
85 ...
86 protected
87 exception_data :additional_data
88
89 def additional_data
90 { :document => @document,
91 :person => @person }
92 end
93 ...
94 end
95
96In the above case, @document and @person would be made available to the email
97renderer, allowing your new section(s) to access and display them. See the
98existing sections defined by the plugin for examples of how to write your own.
99
100== Advanced Customization
101
102By default, the email notifier will only notify on critical errors. For
103ActiveRecord::RecordNotFound and ActionController::UnknownAction, it will
104simply render the contents of your public/404.html file. Other exceptions
105will render public/500.html and will send the email notification. If you want
106to use different rules for the notification, you will need to implement your
107own rescue_action_in_public method. You can look at the default implementation
108in ExceptionNotifiable for an example of how to go about that.
109
110
111Copyright (c) 2005 Jamis Buck, released under the MIT license \ No newline at end of file
diff --git a/vendor/plugins/exception_notification/init.rb b/vendor/plugins/exception_notification/init.rb
deleted file mode 100644
index 4d9d76e..0000000
--- a/vendor/plugins/exception_notification/init.rb
+++ /dev/null
@@ -1,4 +0,0 @@
1require "action_mailer"
2require "exception_notifier"
3require "exception_notifiable"
4require "exception_notifier_helper"
diff --git a/vendor/plugins/exception_notification/lib/exception_notifiable.rb b/vendor/plugins/exception_notification/lib/exception_notifiable.rb
deleted file mode 100644
index d5e28fc..0000000
--- a/vendor/plugins/exception_notification/lib/exception_notifiable.rb
+++ /dev/null
@@ -1,99 +0,0 @@
1require 'ipaddr'
2
3# Copyright (c) 2005 Jamis Buck
4#
5# Permission is hereby granted, free of charge, to any person obtaining
6# a copy of this software and associated documentation files (the
7# "Software"), to deal in the Software without restriction, including
8# without limitation the rights to use, copy, modify, merge, publish,
9# distribute, sublicense, and/or sell copies of the Software, and to
10# permit persons to whom the Software is furnished to do so, subject to
11# the following conditions:
12#
13# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23module ExceptionNotifiable
24 def self.included(target)
25 target.extend(ClassMethods)
26 end
27
28 module ClassMethods
29 def consider_local(*args)
30 local_addresses.concat(args.flatten.map { |a| IPAddr.new(a) })
31 end
32
33 def local_addresses
34 addresses = read_inheritable_attribute(:local_addresses)
35 unless addresses
36 addresses = [IPAddr.new("127.0.0.1")]
37 write_inheritable_attribute(:local_addresses, addresses)
38 end
39 addresses
40 end
41
42 def exception_data(deliverer=self)
43 if deliverer == self
44 read_inheritable_attribute(:exception_data)
45 else
46 write_inheritable_attribute(:exception_data, deliverer)
47 end
48 end
49
50 def exceptions_to_treat_as_404
51 exceptions = [ActiveRecord::RecordNotFound,
52 ActionController::UnknownController,
53 ActionController::UnknownAction]
54 exceptions << ActionController::RoutingError if ActionController.const_defined?(:RoutingError)
55 exceptions
56 end
57 end
58
59 private
60
61 def local_request?
62 remote = IPAddr.new(request.remote_ip)
63 !self.class.local_addresses.detect { |addr| addr.include?(remote) }.nil?
64 end
65
66 def render_404
67 respond_to do |type|
68 type.html { render :file => "#{RAILS_ROOT}/public/404.html", :status => "404 Not Found" }
69 type.all { render :nothing => true, :status => "404 Not Found" }
70 end
71 end
72
73 def render_500
74 respond_to do |type|
75 type.html { render :file => "#{RAILS_ROOT}/public/500.html", :status => "500 Error" }
76 type.all { render :nothing => true, :status => "500 Error" }
77 end
78 end
79
80 def rescue_action_in_public(exception)
81 case exception
82 when *self.class.exceptions_to_treat_as_404
83 render_404
84
85 else
86 render_500
87
88 deliverer = self.class.exception_data
89 data = case deliverer
90 when nil then {}
91 when Symbol then send(deliverer)
92 when Proc then deliverer.call(self)
93 end
94
95 ExceptionNotifier.deliver_exception_notification(exception, self,
96 request, data)
97 end
98 end
99end
diff --git a/vendor/plugins/exception_notification/lib/exception_notifier.rb b/vendor/plugins/exception_notification/lib/exception_notifier.rb
deleted file mode 100644
index 72e2e1a..0000000
--- a/vendor/plugins/exception_notification/lib/exception_notifier.rb
+++ /dev/null
@@ -1,66 +0,0 @@
1require 'pathname'
2
3# Copyright (c) 2005 Jamis Buck
4#
5# Permission is hereby granted, free of charge, to any person obtaining
6# a copy of this software and associated documentation files (the
7# "Software"), to deal in the Software without restriction, including
8# without limitation the rights to use, copy, modify, merge, publish,
9# distribute, sublicense, and/or sell copies of the Software, and to
10# permit persons to whom the Software is furnished to do so, subject to
11# the following conditions:
12#
13# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23class ExceptionNotifier < ActionMailer::Base
24 @@sender_address = %("Exception Notifier" <exception.notifier@default.com>)
25 cattr_accessor :sender_address
26
27 @@exception_recipients = []
28 cattr_accessor :exception_recipients
29
30 @@email_prefix = "[ERROR] "
31 cattr_accessor :email_prefix
32
33 @@sections = %w(request session environment backtrace)
34 cattr_accessor :sections
35
36 self.template_root = "#{File.dirname(__FILE__)}/../views"
37
38 def self.reloadable?() false end
39
40 def exception_notification(exception, controller, request, data={})
41 content_type "text/plain"
42
43 subject "#{email_prefix}#{controller.controller_name}##{controller.action_name} (#{exception.class}) #{exception.message.inspect}"
44
45 recipients exception_recipients
46 from sender_address
47
48 body data.merge({ :controller => controller, :request => request,
49 :exception => exception, :host => (request.env["HTTP_X_FORWARDED_HOST"] || request.env["HTTP_HOST"]),
50 :backtrace => sanitize_backtrace(exception.backtrace),
51 :rails_root => rails_root, :data => data,
52 :sections => sections })
53 end
54
55 private
56
57 def sanitize_backtrace(trace)
58 re = Regexp.new(/^#{Regexp.escape(rails_root)}/)
59 trace.map { |line| Pathname.new(line.gsub(re, "[RAILS_ROOT]")).cleanpath.to_s }
60 end
61
62 def rails_root
63 @rails_root ||= Pathname.new(RAILS_ROOT).cleanpath.to_s
64 end
65
66end
diff --git a/vendor/plugins/exception_notification/lib/exception_notifier_helper.rb b/vendor/plugins/exception_notification/lib/exception_notifier_helper.rb
deleted file mode 100644
index d3dc63a..0000000
--- a/vendor/plugins/exception_notification/lib/exception_notifier_helper.rb
+++ /dev/null
@@ -1,78 +0,0 @@
1require 'pp'
2
3# Copyright (c) 2005 Jamis Buck
4#
5# Permission is hereby granted, free of charge, to any person obtaining
6# a copy of this software and associated documentation files (the
7# "Software"), to deal in the Software without restriction, including
8# without limitation the rights to use, copy, modify, merge, publish,
9# distribute, sublicense, and/or sell copies of the Software, and to
10# permit persons to whom the Software is furnished to do so, subject to
11# the following conditions:
12#
13# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23module ExceptionNotifierHelper
24 VIEW_PATH = "views/exception_notifier"
25 APP_PATH = "#{RAILS_ROOT}/app/#{VIEW_PATH}"
26 PARAM_FILTER_REPLACEMENT = "[FILTERED]"
27
28 def render_section(section)
29 RAILS_DEFAULT_LOGGER.info("rendering section #{section.inspect}")
30 summary = render_overridable(section).strip
31 unless summary.blank?
32 title = render_overridable(:title, :locals => { :title => section }).strip
33 "#{title}\n\n#{summary.gsub(/^/, " ")}\n\n"
34 end
35 end
36
37 def render_overridable(partial, options={})
38 if File.exist?(path = "#{APP_PATH}/_#{partial}.rhtml")
39 render(options.merge(:file => path, :use_full_path => false))
40 elsif File.exist?(path = "#{File.dirname(__FILE__)}/../#{VIEW_PATH}/_#{partial}.rhtml")
41 render(options.merge(:file => path, :use_full_path => false))
42 else
43 ""
44 end
45 end
46
47 def inspect_model_object(model, locals={})
48 render_overridable(:inspect_model,
49 :locals => { :inspect_model => model,
50 :show_instance_variables => true,
51 :show_attributes => true }.merge(locals))
52 end
53
54 def inspect_value(value)
55 len = 512
56 result = object_to_yaml(value).gsub(/\n/, "\n ").strip
57 result = result[0,len] + "... (#{result.length-len} bytes more)" if result.length > len+20
58 result
59 end
60
61 def object_to_yaml(object)
62 object.to_yaml.sub(/^---\s*/m, "")
63 end
64
65 def exclude_raw_post_parameters?
66 @controller && @controller.respond_to?(:filter_parameters)
67 end
68
69 def filter_sensitive_post_data_parameters(parameters)
70 exclude_raw_post_parameters? ? @controller.__send__(:filter_parameters, parameters) : parameters
71 end
72
73 def filter_sensitive_post_data_from_env(env_key, env_value)
74 return env_value unless exclude_raw_post_parameters?
75 return PARAM_FILTER_REPLACEMENT if (env_key =~ /RAW_POST_DATA/i)
76 return @controller.__send__(:filter_parameters, {env_key => env_value}).values[0]
77 end
78end
diff --git a/vendor/plugins/exception_notification/test/exception_notifier_helper_test.rb b/vendor/plugins/exception_notification/test/exception_notifier_helper_test.rb
deleted file mode 100644
index dd47637..0000000
--- a/vendor/plugins/exception_notification/test/exception_notifier_helper_test.rb
+++ /dev/null
@@ -1,61 +0,0 @@
1require 'test_helper'
2require 'exception_notifier_helper'
3
4class ExceptionNotifierHelperTest < Test::Unit::TestCase
5
6 class ExceptionNotifierHelperIncludeTarget
7 include ExceptionNotifierHelper
8 end
9
10 def setup
11 @helper = ExceptionNotifierHelperIncludeTarget.new
12 end
13
14 # No controller
15
16 def test_should_not_exclude_raw_post_parameters_if_no_controller
17 assert !@helper.exclude_raw_post_parameters?
18 end
19
20 # Controller, no filtering
21
22 class ControllerWithoutFilterParameters; end
23
24 def test_should_not_filter_env_values_for_raw_post_data_keys_if_controller_can_not_filter_parameters
25 stub_controller(ControllerWithoutFilterParameters.new)
26 assert @helper.filter_sensitive_post_data_from_env("RAW_POST_DATA", "secret").include?("secret")
27 end
28 def test_should_not_exclude_raw_post_parameters_if_controller_can_not_filter_parameters
29 stub_controller(ControllerWithoutFilterParameters.new)
30 assert !@helper.exclude_raw_post_parameters?
31 end
32 def test_should_return_params_if_controller_can_not_filter_parameters
33 stub_controller(ControllerWithoutFilterParameters.new)
34 assert_equal :params, @helper.filter_sensitive_post_data_parameters(:params)
35 end
36
37 # Controller with filtering
38
39 class ControllerWithFilterParameters
40 def filter_parameters(params); :filtered end
41 end
42
43 def test_should_filter_env_values_for_raw_post_data_keys_if_controller_can_filter_parameters
44 stub_controller(ControllerWithFilterParameters.new)
45 assert !@helper.filter_sensitive_post_data_from_env("RAW_POST_DATA", "secret").include?("secret")
46 assert @helper.filter_sensitive_post_data_from_env("SOME_OTHER_KEY", "secret").include?("secret")
47 end
48 def test_should_exclude_raw_post_parameters_if_controller_can_filter_parameters
49 stub_controller(ControllerWithFilterParameters.new)
50 assert @helper.exclude_raw_post_parameters?
51 end
52 def test_should_delegate_param_filtering_to_controller_if_controller_can_filter_parameters
53 stub_controller(ControllerWithFilterParameters.new)
54 assert_equal :filtered, @helper.filter_sensitive_post_data_parameters(:params)
55 end
56
57 private
58 def stub_controller(controller)
59 @helper.instance_variable_set(:@controller, controller)
60 end
61end \ No newline at end of file
diff --git a/vendor/plugins/exception_notification/test/test_helper.rb b/vendor/plugins/exception_notification/test/test_helper.rb
deleted file mode 100644
index bbe6fc5..0000000
--- a/vendor/plugins/exception_notification/test/test_helper.rb
+++ /dev/null
@@ -1,7 +0,0 @@
1require 'test/unit'
2require 'rubygems'
3require 'active_support'
4
5$:.unshift File.join(File.dirname(__FILE__), '../lib')
6
7RAILS_ROOT = '.' unless defined?(RAILS_ROOT)
diff --git a/vendor/plugins/exception_notification/views/exception_notifier/_backtrace.rhtml b/vendor/plugins/exception_notification/views/exception_notifier/_backtrace.rhtml
deleted file mode 100644
index 7d13ba0..0000000
--- a/vendor/plugins/exception_notification/views/exception_notifier/_backtrace.rhtml
+++ /dev/null
@@ -1 +0,0 @@
1<%= @backtrace.join "\n" %>
diff --git a/vendor/plugins/exception_notification/views/exception_notifier/_environment.rhtml b/vendor/plugins/exception_notification/views/exception_notifier/_environment.rhtml
deleted file mode 100644
index 42dd803..0000000
--- a/vendor/plugins/exception_notification/views/exception_notifier/_environment.rhtml
+++ /dev/null
@@ -1,7 +0,0 @@
1<% max = @request.env.keys.max { |a,b| a.length <=> b.length } -%>
2<% @request.env.keys.sort.each do |key| -%>
3* <%= "%-*s: %s" % [max.length, key, filter_sensitive_post_data_from_env(key, @request.env[key].to_s.strip)] %>
4<% end -%>
5
6* Process: <%= $$ %>
7* Server : <%= `hostname -s`.chomp %>
diff --git a/vendor/plugins/exception_notification/views/exception_notifier/_inspect_model.rhtml b/vendor/plugins/exception_notification/views/exception_notifier/_inspect_model.rhtml
deleted file mode 100644
index e817847..0000000
--- a/vendor/plugins/exception_notification/views/exception_notifier/_inspect_model.rhtml
+++ /dev/null
@@ -1,16 +0,0 @@
1<% if show_attributes -%>
2[attributes]
3<% attrs = inspect_model.attributes -%>
4<% max = attrs.keys.max { |a,b| a.length <=> b.length } -%>
5<% attrs.keys.sort.each do |attr| -%>
6* <%= "%*-s: %s" % [max.length, attr, object_to_yaml(attrs[attr]).gsub(/\n/, "\n ").strip] %>
7<% end -%>
8<% end -%>
9
10<% if show_instance_variables -%>
11[instance variables]
12<% inspect_model.instance_variables.sort.each do |variable| -%>
13<%- next if variable == "@attributes" -%>
14* <%= variable %>: <%= inspect_value(inspect_model.instance_variable_get(variable)) %>
15<% end -%>
16<% end -%>
diff --git a/vendor/plugins/exception_notification/views/exception_notifier/_request.rhtml b/vendor/plugins/exception_notification/views/exception_notifier/_request.rhtml
deleted file mode 100644
index 2542309..0000000
--- a/vendor/plugins/exception_notification/views/exception_notifier/_request.rhtml
+++ /dev/null
@@ -1,4 +0,0 @@
1* URL : <%= @request.protocol %><%= @host %><%= @request.request_uri %>
2* IP address: <%= @request.env["HTTP_X_FORWARDED_FOR"] || @request.env["REMOTE_ADDR"] %>
3* Parameters: <%= filter_sensitive_post_data_parameters(@request.parameters).inspect %>
4* Rails root: <%= @rails_root %>
diff --git a/vendor/plugins/exception_notification/views/exception_notifier/_session.rhtml b/vendor/plugins/exception_notification/views/exception_notifier/_session.rhtml
deleted file mode 100644
index 283c862..0000000
--- a/vendor/plugins/exception_notification/views/exception_notifier/_session.rhtml
+++ /dev/null
@@ -1,2 +0,0 @@
1* session id: <%= @request.session.instance_variable_get(:@session_id).inspect %>
2* data: <%= PP.pp(@request.session.instance_variable_get(:@data),"").gsub(/\n/, "\n ").strip %>
diff --git a/vendor/plugins/exception_notification/views/exception_notifier/_title.rhtml b/vendor/plugins/exception_notification/views/exception_notifier/_title.rhtml
deleted file mode 100644
index 1ed5a3f..0000000
--- a/vendor/plugins/exception_notification/views/exception_notifier/_title.rhtml
+++ /dev/null
@@ -1,3 +0,0 @@
1-------------------------------
2<%= title.to_s.humanize %>:
3-------------------------------
diff --git a/vendor/plugins/exception_notification/views/exception_notifier/exception_notification.rhtml b/vendor/plugins/exception_notification/views/exception_notifier/exception_notification.rhtml
deleted file mode 100644
index ec30c4a..0000000
--- a/vendor/plugins/exception_notification/views/exception_notifier/exception_notification.rhtml
+++ /dev/null
@@ -1,6 +0,0 @@
1A <%= @exception.class %> occurred in <%= @controller.controller_name %>#<%= @controller.action_name %>:
2
3 <%= @exception.message %>
4 <%= @backtrace.first %>
5
6<%= @sections.map { |section| render_section(section) }.join %>
diff --git a/vendor/plugins/globalize2/LICENSE b/vendor/plugins/globalize2/LICENSE
deleted file mode 100644
index 94a6b81..0000000
--- a/vendor/plugins/globalize2/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
1The MIT License
2
3Copyright (c) 2008, 2009 Joshua Harvey
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21THE SOFTWARE. \ No newline at end of file
diff --git a/vendor/plugins/globalize2/README.textile b/vendor/plugins/globalize2/README.textile
deleted file mode 100644
index 7155a7c..0000000
--- a/vendor/plugins/globalize2/README.textile
+++ /dev/null
@@ -1,65 +0,0 @@
1h1. Globalize2
2
3Globalize2 is the successor of Globalize for Rails.
4
5It is compatible with and builds on the new "I18n api in Ruby on Rails":http://guides.rubyonrails.org/i18n.html. and adds model translations to ActiveRecord.
6
7Globalize2 is much more lightweight and compatible than its predecessor was. Model translations in Globalize2 use default ActiveRecord features and do not limit any ActiveRecord functionality any more.
8
9h2. Requirements
10
11ActiveRecord
12I18n
13
14(or Rails > 2.2)
15
16h2. Installation
17
18To install Globalize2 with its default setup just use:
19
20<pre><code>
21script/plugin install git://github.com/joshmh/globalize2.git
22</code></pre>
23
24h2. Model translations
25
26Model translations allow you to translate your models' attribute values. E.g.
27
28<pre><code>
29class Post < ActiveRecord::Base
30 translates :title, :text
31end
32</code></pre>
33
34Allows you to values for the attributes :title and :text per locale:
35
36<pre><code>
37I18n.locale = :en
38post.title # => Globalize2 rocks!
39
40I18n.locale = :he
41post.title # => גלובאלייז2 שולט!
42</code></pre>
43
44In order to make this work, you'll need to add the appropriate translation tables. Globalize2 comes with a handy helper method to help you do this. It's called @create_translation_table!@. Here's an example:
45
46<pre><code>
47class CreatePosts < ActiveRecord::Migration
48 def self.up
49 create_table :posts do |t|
50 t.timestamps
51 end
52 Post.create_translation_table! :title => :string, :text => :text
53 end
54 def self.down
55 drop_table :posts
56 Post.drop_translation_table!
57 end
58end
59</code></pre>
60
61Note that the ActiveRecord model @Post@ must already exist and have a @translates@ directive listing the translated fields.
62
63h2. Migration from Globalize
64
65See this script by Tomasz Stachewicz: http://gist.github.com/120867
diff --git a/vendor/plugins/globalize2/Rakefile b/vendor/plugins/globalize2/Rakefile
deleted file mode 100644
index ee80713..0000000
--- a/vendor/plugins/globalize2/Rakefile
+++ /dev/null
@@ -1,39 +0,0 @@
1require 'rake'
2require 'rake/testtask'
3require 'rake/rdoctask'
4
5desc 'Default: run unit tests.'
6task :default => :test
7
8desc 'Test the globalize2 plugin.'
9Rake::TestTask.new(:test) do |t|
10 t.libs << 'lib'
11 t.pattern = 'test/**/*_test.rb'
12 t.verbose = true
13end
14
15desc 'Generate documentation for the globalize2 plugin.'
16Rake::RDocTask.new(:rdoc) do |rdoc|
17 rdoc.rdoc_dir = 'rdoc'
18 rdoc.title = 'Globalize2'
19 rdoc.options << '--line-numbers' << '--inline-source'
20 rdoc.rdoc_files.include('README')
21 rdoc.rdoc_files.include('lib/**/*.rb')
22end
23
24begin
25 require 'jeweler'
26 Jeweler::Tasks.new do |s|
27 s.name = "globalize2"
28 s.summary = "Rails I18n: de-facto standard library for ActiveRecord data translation"
29 s.description = "Rails I18n: de-facto standard library for ActiveRecord data translation"
30 s.email = "joshmh@gmail.com"
31 s.homepage = "http://github.com/joshmh/globalize2"
32 # s.rubyforge_project = ''
33 s.authors = ["Sven Fuchs, Joshua Harvey, Clemens Kofler"]
34 # s.add_development_dependency ''
35 end
36 Jeweler::GemcutterTasks.new
37rescue LoadError
38 puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
39end
diff --git a/vendor/plugins/globalize2/VERSION b/vendor/plugins/globalize2/VERSION
deleted file mode 100644
index 0ea3a94..0000000
--- a/vendor/plugins/globalize2/VERSION
+++ /dev/null
@@ -1 +0,0 @@
10.2.0
diff --git a/vendor/plugins/globalize2/generators/db_backend.rb b/vendor/plugins/globalize2/generators/db_backend.rb
deleted file mode 100644
index e69de29..0000000
--- a/vendor/plugins/globalize2/generators/db_backend.rb
+++ /dev/null
diff --git a/vendor/plugins/globalize2/generators/templates/db_backend_migration.rb b/vendor/plugins/globalize2/generators/templates/db_backend_migration.rb
deleted file mode 100644
index 0f02611..0000000
--- a/vendor/plugins/globalize2/generators/templates/db_backend_migration.rb
+++ /dev/null
@@ -1,25 +0,0 @@
1class ActsAsTaggableMigration < ActiveRecord::Migration
2 def self.up
3 create_table :globalize_translations do |t|
4 t.string :locale, :null => false
5 t.string :key, :null => false
6 t.string :translation
7 t.timestamps
8 end
9
10# TODO: FINISH DOING MIGRATION -- stopped in the middle
11
12 create_table :globalize_translations_map do |t|
13 t.string :key, :null => false
14 t.integer :translation_id, :null => false
15 end
16
17 add_index :taggings, :tag_id
18 add_index :taggings, [:taggable_id, :taggable_type]
19 end
20
21 def self.down
22 drop_table :globalize_translations
23 drop_table :tags
24 end
25end
diff --git a/vendor/plugins/globalize2/globalize2.gemspec b/vendor/plugins/globalize2/globalize2.gemspec
deleted file mode 100644
index 6ad93d3..0000000
--- a/vendor/plugins/globalize2/globalize2.gemspec
+++ /dev/null
@@ -1,82 +0,0 @@
1# Generated by jeweler
2# DO NOT EDIT THIS FILE DIRECTLY
3# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4# -*- encoding: utf-8 -*-
5
6Gem::Specification.new do |s|
7 s.name = %q{globalize2}
8 s.version = "0.2.0"
9
10 s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11 s.authors = ["Sven Fuchs, Joshua Harvey, Clemens Kofler"]
12 s.date = %q{2009-12-25}
13 s.description = %q{Rails I18n: de-facto standard library for ActiveRecord data translation}
14 s.email = %q{joshmh@gmail.com}
15 s.extra_rdoc_files = [
16 "LICENSE",
17 "README.textile"
18 ]
19 s.files = [
20 ".gitignore",
21 "LICENSE",
22 "README.textile",
23 "Rakefile",
24 "VERSION",
25 "generators/db_backend.rb",
26 "generators/templates/db_backend_migration.rb",
27 "globalize2.gemspec",
28 "init.rb",
29 "lib/globalize.rb",
30 "lib/globalize/active_record.rb",
31 "lib/globalize/active_record/adapter.rb",
32 "lib/globalize/active_record/attributes.rb",
33 "lib/globalize/active_record/migration.rb",
34 "lib/i18n/missing_translations_log_handler.rb",
35 "lib/i18n/missing_translations_raise_handler.rb",
36 "notes.textile",
37 "test/active_record/fallbacks_test.rb",
38 "test/active_record/migration_test.rb",
39 "test/active_record/sti_translated_test.rb",
40 "test/active_record/translates_test.rb",
41 "test/active_record/translation_class_test.rb",
42 "test/active_record/validation_tests.rb",
43 "test/active_record_test.rb",
44 "test/all.rb",
45 "test/data/models.rb",
46 "test/data/no_globalize_schema.rb",
47 "test/data/schema.rb",
48 "test/i18n/missing_translations_test.rb",
49 "test/test_helper.rb"
50 ]
51 s.homepage = %q{http://github.com/joshmh/globalize2}
52 s.rdoc_options = ["--charset=UTF-8"]
53 s.require_paths = ["lib"]
54 s.rubygems_version = %q{1.3.5}
55 s.summary = %q{Rails I18n: de-facto standard library for ActiveRecord data translation}
56 s.test_files = [
57 "test/active_record/fallbacks_test.rb",
58 "test/active_record/migration_test.rb",
59 "test/active_record/sti_translated_test.rb",
60 "test/active_record/translates_test.rb",
61 "test/active_record/translation_class_test.rb",
62 "test/active_record/validation_tests.rb",
63 "test/active_record_test.rb",
64 "test/all.rb",
65 "test/data/models.rb",
66 "test/data/no_globalize_schema.rb",
67 "test/data/schema.rb",
68 "test/i18n/missing_translations_test.rb",
69 "test/test_helper.rb"
70 ]
71
72 if s.respond_to? :specification_version then
73 current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
74 s.specification_version = 3
75
76 if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
77 else
78 end
79 else
80 end
81end
82
diff --git a/vendor/plugins/globalize2/init.rb b/vendor/plugins/globalize2/init.rb
deleted file mode 100644
index a408925..0000000
--- a/vendor/plugins/globalize2/init.rb
+++ /dev/null
@@ -1 +0,0 @@
1require 'globalize'
diff --git a/vendor/plugins/globalize2/lib/globalize.rb b/vendor/plugins/globalize2/lib/globalize.rb
deleted file mode 100644
index 67c1878..0000000
--- a/vendor/plugins/globalize2/lib/globalize.rb
+++ /dev/null
@@ -1,15 +0,0 @@
1module Globalize
2 autoload :ActiveRecord, 'globalize/active_record'
3
4 class << self
5 def fallbacks?
6 I18n.respond_to?(:fallbacks)
7 end
8
9 def fallbacks(locale)
10 fallbacks? ? I18n.fallbacks[locale] : [locale.to_sym]
11 end
12 end
13end
14
15ActiveRecord::Base.send(:include, Globalize::ActiveRecord)
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record.rb b/vendor/plugins/globalize2/lib/globalize/active_record.rb
deleted file mode 100644
index d2a843f..0000000
--- a/vendor/plugins/globalize2/lib/globalize/active_record.rb
+++ /dev/null
@@ -1,194 +0,0 @@
1module Globalize
2 class MigrationError < StandardError; end
3 class MigrationMissingTranslatedField < MigrationError; end
4 class BadMigrationFieldType < MigrationError; end
5
6 module ActiveRecord
7 autoload :Adapter, 'globalize/active_record/adapter'
8 autoload :Attributes, 'globalize/active_record/attributes'
9 autoload :Migration, 'globalize/active_record/migration'
10
11 def self.included(base)
12 base.extend ActMacro
13 end
14
15 class << self
16 def build_translation_class(target, options)
17 options[:table_name] ||= "#{target.table_name.singularize}_translations"
18
19 klass = target.const_defined?(:Translation) ?
20 target.const_get(:Translation) :
21 target.const_set(:Translation, Class.new(::ActiveRecord::Base))
22
23 klass.class_eval do
24 set_table_name(options[:table_name])
25 belongs_to target.name.underscore.gsub('/', '_')
26 def locale; read_attribute(:locale).to_sym; end
27 def locale=(locale); write_attribute(:locale, locale.to_s); end
28 end
29
30 klass
31 end
32 end
33
34 module ActMacro
35 def locale
36 (defined?(@@locale) && @@locale)
37 end
38
39 def locale=(locale)
40 @@locale = locale
41 end
42
43 def translates(*attr_names)
44 return if translates?
45 options = attr_names.extract_options!
46
47 class_inheritable_accessor :translation_class, :translated_attribute_names
48 self.translation_class = ActiveRecord.build_translation_class(self, options)
49 self.translated_attribute_names = attr_names.map(&:to_sym)
50
51 include InstanceMethods
52 extend ClassMethods, Migration
53
54 after_save :save_translations!
55 has_many :translations, :class_name => translation_class.name,
56 :foreign_key => class_name.foreign_key,
57 :dependent => :delete_all,
58 :extend => HasManyExtensions
59
60 named_scope :with_translations, lambda { |locale|
61 conditions = required_attributes.map do |attribute|
62 "#{quoted_translation_table_name}.#{attribute} IS NOT NULL"
63 end
64 conditions << "#{quoted_translation_table_name}.locale = ?"
65 { :include => :translations, :conditions => [conditions.join(' AND '), locale] }
66 }
67
68 attr_names.each { |attr_name| translated_attr_accessor(attr_name) }
69 end
70
71 def translates?
72 included_modules.include?(InstanceMethods)
73 end
74 end
75
76 module HasManyExtensions
77 def by_locale(locale)
78 first(:conditions => { :locale => locale.to_s })
79 end
80
81 def by_locales(locales)
82 all(:conditions => { :locale => locales.map(&:to_s) })
83 end
84 end
85
86 module ClassMethods
87 delegate :set_translation_table_name, :to => :translation_class
88
89 def with_locale(locale)
90 previous_locale, self.locale = self.locale, locale
91 result = yield
92 self.locale = previous_locale
93 result
94 end
95
96 def translation_table_name
97 translation_class.table_name
98 end
99
100 def quoted_translation_table_name
101 translation_class.quoted_table_name
102 end
103
104 def required_attributes
105 validations = reflect_on_all_validations.select do |validation|
106 validation.macro == :validates_presence_of
107 end.map(&:name)
108 end
109
110 def respond_to?(method, *args, &block)
111 method.to_s =~ /^find_by_(\w+)$/ && translated_attribute_names.include?($1.to_sym) || super
112 end
113
114 def method_missing(method, *args)
115 if method.to_s =~ /^find_by_(\w+)$/ && translated_attribute_names.include?($1.to_sym)
116 find_first_by_translated_attr_and_locales($1, args.first)
117 else
118 super
119 end
120 end
121
122 protected
123
124 def find_first_by_translated_attr_and_locales(name, value)
125 query = "#{translated_attr_name(name)} = ? AND #{translated_attr_name('locale')} IN (?)"
126 locales = Globalize.fallbacks(locale || I18n.locale).map(&:to_s)
127 find(:first, :joins => :translations, :conditions => [query, value, locales])
128 end
129
130 def translated_attr_accessor(name)
131 define_method "#{name}=", lambda { |value|
132 globalize.write(self.class.locale || I18n.locale, name, value)
133 self[name] = value
134 }
135 define_method name, lambda { |*args|
136 globalize.fetch(args.first || self.class.locale || I18n.locale, name)
137 }
138 alias_method "#{name}_before_type_cast", name
139 end
140
141 def translated_attr_name(name)
142 "#{translation_class.table_name}.#{name}"
143 end
144 end
145
146 module InstanceMethods
147 def globalize
148 @globalize ||= Adapter.new self
149 end
150
151 def attributes=(attributes, *args)
152 if attributes.respond_to?(:delete) && locale = attributes.delete(:locale)
153 self.class.with_locale(locale) { super }
154 else
155 super
156 end
157 end
158
159 def available_locales
160 translations.scoped(:select => 'DISTINCT locale').map(&:locale)
161 end
162
163 def translated_locales
164 translations.map(&:locale)
165 end
166
167 def translated_attributes
168 translated_attribute_names.inject({}) do |attributes, name|
169 attributes.merge(name => send(name))
170 end
171 end
172
173 def set_translations(options)
174 options.keys.each do |locale|
175 translation = translations.find_by_locale(locale.to_s) ||
176 translations.build(:locale => locale.to_s)
177 translation.update_attributes!(options[locale])
178 end
179 end
180
181 def reload(options = nil)
182 translated_attribute_names.each { |name| @attributes.delete(name.to_s) }
183 globalize.reset
184 super(options)
185 end
186
187 protected
188
189 def save_translations!
190 globalize.save_translations!
191 end
192 end
193 end
194end
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb b/vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb
deleted file mode 100644
index 12f89ec..0000000
--- a/vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb
+++ /dev/null
@@ -1,80 +0,0 @@
1module Globalize
2 module ActiveRecord
3 class Adapter
4 # The cache caches attributes that already were looked up for read access.
5 # The stash keeps track of new or changed values that need to be saved.
6 attr_reader :record, :cache, :stash
7
8 def initialize(record)
9 @record = record
10 @cache = Attributes.new
11 @stash = Attributes.new
12 end
13
14 def fetch(locale, attr_name)
15 cache.contains?(locale, attr_name) ?
16 cache.read(locale, attr_name) :
17 cache.write(locale, attr_name, fetch_attribute(locale, attr_name))
18 end
19
20 def write(locale, attr_name, value)
21 stash.write(locale, attr_name, value)
22 cache.write(locale, attr_name, value)
23 end
24
25 def save_translations!
26 stash.each do |locale, attrs|
27 translation = record.translations.find_or_initialize_by_locale(locale.to_s)
28 attrs.each { |attr_name, value| translation[attr_name] = value }
29 translation.save!
30 end
31 stash.clear
32 end
33
34 def reset
35 cache.clear
36 # stash.clear
37 end
38
39 protected
40
41 def fetch_translation(locale)
42 locale = locale.to_sym
43 record.translations.loaded? ? record.translations.detect { |t| t.locale == locale } :
44 record.translations.by_locale(locale)
45 end
46
47 def fetch_translations(locale)
48 # only query if not already included with :include => translations
49 record.translations.loaded? ? record.translations :
50 record.translations.by_locales(Globalize.fallbacks(locale))
51 end
52
53 def fetch_attribute(locale, attr_name)
54 translations = fetch_translations(locale)
55 value, requested_locale = nil, locale
56
57 Globalize.fallbacks(locale).each do |fallback|
58 translation = translations.detect { |t| t.locale == fallback }
59 value = translation && translation.send(attr_name)
60 locale = fallback && break if value
61 end
62
63 set_metadata(value, :locale => locale, :requested_locale => requested_locale)
64 value
65 end
66
67 def set_metadata(object, metadata)
68 if object.respond_to?(:translation_metadata)
69 object.translation_metadata.merge!(meta_data)
70 end
71 end
72
73 def translation_metadata_accessor(object)
74 return if obj.respond_to?(:translation_metadata)
75 class << object; attr_accessor :translation_metadata end
76 object.translation_metadata ||= {}
77 end
78 end
79 end
80end
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb b/vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb
deleted file mode 100644
index 7bd923c..0000000
--- a/vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb
+++ /dev/null
@@ -1,25 +0,0 @@
1# Helper class for storing values per locale. Used by Globalize::Adapter
2# to stash and cache attribute values.
3module Globalize
4 module ActiveRecord
5 class Attributes < Hash
6 def [](locale)
7 locale = locale.to_sym
8 self[locale] = {} unless has_key?(locale)
9 self.fetch(locale)
10 end
11
12 def contains?(locale, attr_name)
13 self[locale].has_key?(attr_name)
14 end
15
16 def read(locale, attr_name)
17 self[locale][attr_name]
18 end
19
20 def write(locale, attr_name, value)
21 self[locale][attr_name] = value
22 end
23 end
24 end
25end
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record/migration.rb b/vendor/plugins/globalize2/lib/globalize/active_record/migration.rb
deleted file mode 100644
index ebff3b6..0000000
--- a/vendor/plugins/globalize2/lib/globalize/active_record/migration.rb
+++ /dev/null
@@ -1,40 +0,0 @@
1module Globalize
2 module ActiveRecord
3 module Migration
4 def create_translation_table!(fields)
5 translated_attribute_names.each do |f|
6 raise MigrationMissingTranslatedField, "Missing translated field #{f}" unless fields[f]
7 end
8
9 fields.each do |name, type|
10 if translated_attribute_names.include?(name) && ![:string, :text].include?(type)
11 raise BadMigrationFieldType, "Bad field type for #{name}, should be :string or :text"
12 end
13 end
14
15 self.connection.create_table(translation_table_name) do |t|
16 t.references self.table_name.singularize
17 t.string :locale
18 fields.each do |name, type|
19 t.column name, type
20 end
21 t.timestamps
22 end
23
24 self.connection.add_index(translation_table_name, "#{self.table_name.singularize}_id", :name => translation_index_name)
25 end
26
27 def translation_index_name
28 require 'digest/sha1'
29 # FIXME what's the max size of an index name?
30 index_name = "index_#{translation_table_name}_on_#{self.table_name.singularize}_id"
31 index_name.size < 50 ? index_name : "index_#{Digest::SHA1.hexdigest(index_name)}"
32 end
33
34 def drop_translation_table!
35 self.connection.remove_index(translation_table_name, :name => translation_index_name) rescue nil
36 self.connection.drop_table(translation_table_name)
37 end
38 end
39 end
40end
diff --git a/vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb b/vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb
deleted file mode 100644
index 24c1089..0000000
--- a/vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb
+++ /dev/null
@@ -1,41 +0,0 @@
1# A simple exception handler that behaves like the default exception handler
2# but additionally logs missing translations to a given log.
3#
4# Useful for identifying missing translations during testing.
5#
6# E.g.
7#
8# require 'globalize/i18n/missing_translations_log_handler'
9# I18n.missing_translations_logger = RAILS_DEFAULT_LOGGER
10# I18n.exception_handler = :missing_translations_log_handler
11#
12# To set up a different log file:
13#
14# logger = Logger.new("#{RAILS_ROOT}/log/missing_translations.log")
15# I18n.missing_translations_logger = logger
16
17module I18n
18 @@missing_translations_logger = nil
19
20 class << self
21 def missing_translations_logger
22 @@missing_translations_logger ||= begin
23 require 'logger' unless defined?(Logger)
24 Logger.new(STDOUT)
25 end
26 end
27
28 def missing_translations_logger=(logger)
29 @@missing_translations_logger = logger
30 end
31
32 def missing_translations_log_handler(exception, locale, key, options)
33 if MissingTranslationData === exception
34 missing_translations_logger.warn(exception.message)
35 return exception.message
36 else
37 raise exception
38 end
39 end
40 end
41end
diff --git a/vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb b/vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb
deleted file mode 100644
index 18237b1..0000000
--- a/vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb
+++ /dev/null
@@ -1,25 +0,0 @@
1# A simple exception handler that behaves like the default exception handler
2# but also raises on missing translations.
3#
4# Useful for identifying missing translations during testing.
5#
6# E.g.
7#
8# require 'globalize/i18n/missing_translations_raise_handler'
9# I18n.exception_handler = :missing_translations_raise_handler
10module I18n
11 class << self
12 def missing_translations_raise_handler(exception, locale, key, options)
13 raise exception
14 end
15 end
16end
17
18I18n.exception_handler = :missing_translations_raise_handler
19
20ActionView::Helpers::TranslationHelper.module_eval do
21 def translate(key, options = {})
22 I18n.translate(key, options)
23 end
24 alias :t :translate
25end
diff --git a/vendor/plugins/globalize2/test/active_record/fallbacks_test.rb b/vendor/plugins/globalize2/test/active_record/fallbacks_test.rb
deleted file mode 100644
index 449ec8b..0000000
--- a/vendor/plugins/globalize2/test/active_record/fallbacks_test.rb
+++ /dev/null
@@ -1,102 +0,0 @@
1require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2require File.expand_path(File.dirname(__FILE__) + '/../data/models')
3
4if I18n.respond_to?(:fallbacks)
5 class TranslatedTest < ActiveSupport::TestCase
6 def setup
7 I18n.locale = :'en-US'
8 I18n.fallbacks.clear
9 reset_db!
10 ActiveRecord::Base.locale = nil
11 end
12
13 def teardown
14 I18n.fallbacks.clear
15 end
16
17 test "keeping one field in new locale when other field is changed" do
18 I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
19 post = Post.create :subject => 'foo'
20 I18n.locale = 'de-DE'
21 post.content = 'bar'
22 assert_equal 'foo', post.subject
23 end
24
25 test "modifying non-required field in a new locale" do
26 I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
27 post = Post.create :subject => 'foo'
28 I18n.locale = 'de-DE'
29 post.content = 'bar'
30 assert post.save
31 end
32
33 test "resolves a simple fallback" do
34 I18n.locale = 'de-DE'
35 post = Post.create :subject => 'foo'
36 I18n.locale = 'de'
37 post.subject = 'baz'
38 post.content = 'bar'
39 post.save
40 I18n.locale = 'de-DE'
41 assert_equal 'foo', post.subject
42 assert_equal 'bar', post.content
43 end
44
45 test "resolves a simple fallback without reloading" do
46 I18n.locale = 'de-DE'
47 post = Post.new :subject => 'foo'
48 I18n.locale = 'de'
49 post.subject = 'baz'
50 post.content = 'bar'
51 I18n.locale = 'de-DE'
52 assert_equal 'foo', post.subject
53 assert_equal 'bar', post.content
54 end
55
56 test "resolves a complex fallback without reloading" do
57 I18n.fallbacks.map 'de' => %w(en he)
58 I18n.locale = 'de'
59 post = Post.new
60 I18n.locale = 'en'
61 post.subject = 'foo'
62 I18n.locale = 'he'
63 post.subject = 'baz'
64 post.content = 'bar'
65 I18n.locale = 'de'
66 assert_equal 'foo', post.subject
67 assert_equal 'bar', post.content
68 end
69
70 test 'fallbacks with lots of locale switching' do
71 I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
72 post = Post.create :subject => 'foo'
73
74 I18n.locale = :'de-DE'
75 assert_equal 'foo', post.subject
76
77 I18n.locale = :'en-US'
78 post.update_attribute :subject, 'bar'
79
80 I18n.locale = :'de-DE'
81 assert_equal 'bar', post.subject
82 end
83
84 test 'fallbacks with lots of locale switching' do
85 I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
86 child = Child.create :content => 'foo'
87
88 I18n.locale = :'de-DE'
89 assert_equal 'foo', child.content
90
91 I18n.locale = :'en-US'
92 child.update_attribute :content, 'bar'
93
94 I18n.locale = :'de-DE'
95 assert_equal 'bar', child.content
96 end
97 end
98end
99
100# TODO should validate_presence_of take fallbacks into account? maybe we need
101# an extra validation call, or more options for validate_presence_of.
102
diff --git a/vendor/plugins/globalize2/test/active_record/migration_test.rb b/vendor/plugins/globalize2/test/active_record/migration_test.rb
deleted file mode 100644
index 359d811..0000000
--- a/vendor/plugins/globalize2/test/active_record/migration_test.rb
+++ /dev/null
@@ -1,118 +0,0 @@
1require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2require File.expand_path(File.dirname(__FILE__) + '/../data/models')
3
4class MigrationTest < ActiveSupport::TestCase
5 def setup
6 reset_db!
7 Post.drop_translation_table!
8 end
9
10 test 'globalize table added' do
11 assert !Post.connection.table_exists?(:post_translations)
12 assert !Post.connection.index_exists?(:post_translations, :post_id)
13
14 Post.create_translation_table!(:subject => :string, :content => :text)
15 assert Post.connection.table_exists?(:post_translations)
16 assert Post.connection.index_exists?(:post_translations, :post_id)
17
18 columns = Post.connection.columns(:post_translations)
19 assert locale = columns.detect { |c| c.name == 'locale' }
20 assert_equal :string, locale.type
21 assert subject = columns.detect { |c| c.name == 'subject' }
22 assert_equal :string, subject.type
23 assert content = columns.detect { |c| c.name == 'content' }
24 assert_equal :text, content.type
25 assert post_id = columns.detect { |c| c.name == 'post_id' }
26 assert_equal :integer, post_id.type
27 assert created_at = columns.detect { |c| c.name == 'created_at' }
28 assert_equal :datetime, created_at.type
29 assert updated_at = columns.detect { |c| c.name == 'updated_at' }
30 assert_equal :datetime, updated_at.type
31 end
32
33 test 'globalize table dropped' do
34 assert !Post.connection.table_exists?( :post_translations )
35 assert !Post.connection.index_exists?( :post_translations, :post_id )
36 Post.create_translation_table! :subject => :string, :content => :text
37 assert Post.connection.table_exists?( :post_translations )
38 assert Post.connection.index_exists?( :post_translations, :post_id )
39 Post.drop_translation_table!
40 assert !Post.connection.table_exists?( :post_translations )
41 assert !Post.connection.index_exists?( :post_translations, :post_id )
42 end
43
44 test 'exception on missing field inputs' do
45 assert_raise Globalize::MigrationMissingTranslatedField do
46 Post.create_translation_table! :content => :text
47 end
48 end
49
50 test 'exception on bad input type' do
51 assert_raise Globalize::BadMigrationFieldType do
52 Post.create_translation_table! :subject => :string, :content => :integer
53 end
54 end
55
56 test "exception on bad input type isn't raised for untranslated fields" do
57 assert_nothing_raised do
58 Post.create_translation_table! :subject => :string, :content => :string, :views_count => :integer
59 end
60 end
61
62 test 'create_translation_table! should not be called on non-translated models' do
63 assert_raise NoMethodError do
64 Blog.create_translation_table! :name => :string
65 end
66 end
67
68 test 'drop_translation_table! should not be called on non-translated models' do
69 assert_raise NoMethodError do
70 Blog.drop_translation_table!
71 end
72 end
73
74 test "translation_index_name returns a readable index name when it's not longer than 50 characters" do
75 assert_equal 'index_post_translations_on_post_id', Post.send(:translation_index_name)
76 end
77
78 test "translation_index_name returns a hashed index name when it's longer than 50 characters" do
79 class UltraLongModelNameWithoutProper < ActiveRecord::Base
80 translates :foo
81 end
82 name = UltraLongModelNameWithoutProper.send(:translation_index_name)
83 assert_match /^index_[a-z0-9]{40}$/, name
84 end
85
86 test 'globalize table added when table has long name' do
87 UltraLongModelNameWithoutProper.create_translation_table!(
88 :subject => :string, :content => :text
89 )
90
91 assert UltraLongModelNameWithoutProper.connection.table_exists?(
92 :ultra_long_model_name_without_proper_translations
93 )
94 assert UltraLongModelNameWithoutProper.connection.index_exists?(
95 :ultra_long_model_name_without_proper_translations,
96 :name => UltraLongModelNameWithoutProper.send(
97 :translation_index_name
98 )
99 )
100 end
101
102 test 'globalize table dropped when table has long name' do
103 UltraLongModelNameWithoutProper.drop_translation_table!
104 UltraLongModelNameWithoutProper.create_translation_table!(
105 :subject => :string, :content => :text
106 )
107 UltraLongModelNameWithoutProper.drop_translation_table!
108
109 assert !UltraLongModelNameWithoutProper.connection.table_exists?(
110 :ultra_long_model_name_without_proper_translations
111 )
112 assert !UltraLongModelNameWithoutProper.connection.index_exists?(
113 :ultra_long_model_name_without_proper_translations,
114 :ultra_long_model_name_without_proper_id
115 )
116 end
117
118end
diff --git a/vendor/plugins/globalize2/test/active_record/sti_translated_test.rb b/vendor/plugins/globalize2/test/active_record/sti_translated_test.rb
deleted file mode 100644
index f529b8d..0000000
--- a/vendor/plugins/globalize2/test/active_record/sti_translated_test.rb
+++ /dev/null
@@ -1,49 +0,0 @@
1require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2require File.expand_path(File.dirname(__FILE__) + '/../data/models')
3
4class StiTranslatedTest < ActiveSupport::TestCase
5 def setup
6 I18n.locale = :'en-US'
7 reset_db!
8 end
9
10 test "works with simple dynamic finders" do
11 foo = Child.create :content => 'foo'
12 Child.create :content => 'bar'
13 child = Child.find_by_content('foo')
14 assert_equal foo, child
15 end
16
17 test 'change attribute on globalized model' do
18 child = Child.create :content => 'foo'
19 assert_equal [], child.changed
20 child.content = 'bar'
21 assert_equal [ 'content' ], child.changed
22 child.content = 'baz'
23 assert_member 'content', child.changed
24 end
25
26 test 'change attribute on globalized model after locale switching' do
27 child = Child.create :content => 'foo'
28 assert_equal [], child.changed
29 child.content = 'bar'
30 I18n.locale = :de
31 assert_equal [ 'content' ], child.changed
32 end
33
34 test "saves all locales, even after locale switching" do
35 child = Child.new :content => 'foo'
36 I18n.locale = 'de-DE'
37 child.content = 'bar'
38 I18n.locale = 'he-IL'
39 child.content = 'baz'
40 child.save
41 I18n.locale = 'en-US'
42 child = Child.first
43 assert_equal 'foo', child.content
44 I18n.locale = 'de-DE'
45 assert_equal 'bar', child.content
46 I18n.locale = 'he-IL'
47 assert_equal 'baz', child.content
48 end
49end
diff --git a/vendor/plugins/globalize2/test/active_record/translates_test.rb b/vendor/plugins/globalize2/test/active_record/translates_test.rb
deleted file mode 100644
index 4207bc4..0000000
--- a/vendor/plugins/globalize2/test/active_record/translates_test.rb
+++ /dev/null
@@ -1,87 +0,0 @@
1require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2require File.expand_path(File.dirname(__FILE__) + '/../data/models')
3
4class TranslatesTest < ActiveSupport::TestCase
5 def setup
6 I18n.locale = nil
7 ActiveRecord::Base.locale = nil
8 reset_db!
9 end
10
11 test 'defines a :locale accessors on ActiveRecord::Base' do
12 ActiveRecord::Base.locale = :de
13 assert_equal :de, ActiveRecord::Base.locale
14 end
15
16 test 'the :locale reader on ActiveRecord::Base does not default to I18n.locale (anymore)' do
17 I18n.locale = :en
18 assert_nil ActiveRecord::Base.locale
19 end
20
21 test 'ActiveRecord::Base.with_locale temporarily sets the given locale and yields the block' do
22 I18n.locale = :en
23 post = Post.with_locale(:de) do
24 Post.create!(:subject => 'Titel', :content => 'Inhalt')
25 end
26 assert_nil Post.locale
27 assert_equal :en, I18n.locale
28
29 I18n.locale = :de
30 assert_equal 'Titel', post.subject
31 end
32
33 test 'translation_class returns the Translation class' do
34 assert_equal Post::Translation, Post.translation_class
35 end
36
37 test 'defines a has_many association on the model class' do
38 assert_has_many Post, :translations
39 end
40
41 test 'defines a scope for retrieving locales that have complete translations' do
42 post = Post.create!(:subject => 'subject', :content => 'content')
43 assert_equal [:en], post.translated_locales
44 end
45
46 test 'sets the given attributes to translated_attribute_names' do
47 assert_equal [:subject, :content], Post.translated_attribute_names
48 end
49
50 test 'defines accessors for the translated attributes' do
51 post = Post.new
52 assert post.respond_to?(:subject)
53 assert post.respond_to?(:subject=)
54 end
55
56 test 'attribute reader without arguments will use the current locale on ActiveRecord::Base or I18n' do
57 post = Post.with_locale(:de) do
58 Post.create!(:subject => 'Titel', :content => 'Inhalt')
59 end
60 I18n.locale = :de
61 assert_equal 'Titel', post.subject
62
63 I18n.locale = :en
64 ActiveRecord::Base.locale = :de
65 assert_equal 'Titel', post.subject
66 end
67
68 test 'attribute reader when passed a locale will use the given locale' do
69 post = Post.with_locale(:de) do
70 Post.create!(:subject => 'Titel', :content => 'Inhalt')
71 end
72 assert_equal 'Titel', post.subject(:de)
73 end
74
75 test 'attribute reader will use the current locale on ActiveRecord::Base or I18n' do
76 post = Post.with_locale(:en) do
77 Post.create!(:subject => 'title', :content => 'content')
78 end
79 I18n.locale = :de
80 post.subject = 'Titel'
81 assert_equal 'Titel', post.subject
82
83 ActiveRecord::Base.locale = :en
84 post.subject = 'title'
85 assert_equal 'title', post.subject
86 end
87end
diff --git a/vendor/plugins/globalize2/test/active_record/translation_class_test.rb b/vendor/plugins/globalize2/test/active_record/translation_class_test.rb
deleted file mode 100644
index 1628416..0000000
--- a/vendor/plugins/globalize2/test/active_record/translation_class_test.rb
+++ /dev/null
@@ -1,30 +0,0 @@
1require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2require File.expand_path(File.dirname(__FILE__) + '/../data/models')
3
4class TranlationClassTest < ActiveSupport::TestCase
5 def setup
6 reset_db!
7 end
8
9 test 'defines a Translation class nested in the model class' do
10 assert Post.const_defined?(:Translation)
11 end
12
13 test 'defines a belongs_to association' do
14 assert_belongs_to Post::Translation, :post
15 end
16
17 test 'defines a reader for :locale that always returns a symbol' do
18 post = Post::Translation.new
19 post.write_attribute('locale', 'de')
20 assert_equal :de, post.locale
21 end
22
23 test 'defines a write for :locale that always writes a string' do
24 post = Post::Translation.new
25 post.locale = :de
26 assert_equal 'de', post.read_attribute('locale')
27 end
28end
29
30
diff --git a/vendor/plugins/globalize2/test/active_record/validation_tests.rb b/vendor/plugins/globalize2/test/active_record/validation_tests.rb
deleted file mode 100644
index 0148fa3..0000000
--- a/vendor/plugins/globalize2/test/active_record/validation_tests.rb
+++ /dev/null
@@ -1,75 +0,0 @@
1require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2require File.expand_path(File.dirname(__FILE__) + '/../data/models')
3
4class ValidationTest < ActiveSupport::TestCase
5 def setup
6 reset_db!
7 end
8
9 def teardown
10 Validatee.instance_variable_set(:@validate_callbacks, CallbackChain.new)
11 end
12
13 test "validates_presence_of" do
14 Validatee.class_eval { validates_presence_of :string }
15 assert !Validatee.new.valid?
16 assert Validatee.new(:string => 'foo').valid?
17 end
18
19 test "validates_confirmation_of" do
20 Validatee.class_eval { validates_confirmation_of :string }
21 assert !Validatee.new(:string => 'foo', :string_confirmation => 'bar').valid?
22 assert Validatee.new(:string => 'foo', :string_confirmation => 'foo').valid?
23 end
24
25 test "validates_acceptance_of" do
26 Validatee.class_eval { validates_acceptance_of :string, :accept => '1' }
27 assert !Validatee.new(:string => '0').valid?
28 assert Validatee.new(:string => '1').valid?
29 end
30
31 test "validates_length_of (:is)" do
32 Validatee.class_eval { validates_length_of :string, :is => 1 }
33 assert !Validatee.new(:string => 'aa').valid?
34 assert Validatee.new(:string => 'a').valid?
35 end
36
37 test "validates_format_of" do
38 Validatee.class_eval { validates_format_of :string, :with => /^\d+$/ }
39 assert !Validatee.new(:string => 'a').valid?
40 assert Validatee.new(:string => '1').valid?
41 end
42
43 test "validates_inclusion_of" do
44 Validatee.class_eval { validates_inclusion_of :string, :in => %(a) }
45 assert !Validatee.new(:string => 'b').valid?
46 assert Validatee.new(:string => 'a').valid?
47 end
48
49 test "validates_exclusion_of" do
50 Validatee.class_eval { validates_exclusion_of :string, :in => %(b) }
51 assert !Validatee.new(:string => 'b').valid?
52 assert Validatee.new(:string => 'a').valid?
53 end
54
55 test "validates_numericality_of" do
56 Validatee.class_eval { validates_numericality_of :string }
57 assert !Validatee.new(:string => 'a').valid?
58 assert Validatee.new(:string => '1').valid?
59 end
60
61 # This doesn't pass and Rails' validates_uniqueness_of implementation doesn't
62 # seem to be extensible easily. One can work around that by either defining
63 # a custom validation on the Validatee model itself, or by using validates_uniqueness_of
64 # on Validatee::Translation.
65 #
66 # test "validates_uniqueness_of" do
67 # Validatee.class_eval { validates_uniqueness_of :string }
68 # Validatee.create!(:string => 'a')
69 # assert !Validatee.new(:string => 'a').valid?
70 # assert Validatee.new(:string => 'b').valid?
71 # end
72
73 # test "validates_associated" do
74 # end
75end \ No newline at end of file
diff --git a/vendor/plugins/globalize2/test/active_record_test.rb b/vendor/plugins/globalize2/test/active_record_test.rb
deleted file mode 100644
index e6b54f0..0000000
--- a/vendor/plugins/globalize2/test/active_record_test.rb
+++ /dev/null
@@ -1,442 +0,0 @@
1require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2require File.expand_path(File.dirname(__FILE__) + '/data/models')
3
4# Higher level tests.
5
6class ActiveRecordTest < ActiveSupport::TestCase
7 def setup
8 I18n.locale = :en
9 reset_db!
10 ActiveRecord::Base.locale = nil
11 end
12
13 def assert_translated(locale, record, names, expected)
14 I18n.locale = locale
15 assert_equal Array(expected), Array(names).map { |name| record.send(name) }
16 end
17
18 test "a translated record has translations" do
19 assert_equal [], Post.new.translations
20 end
21
22 test "saves a translated version of the record for each locale" do
23 post = Post.create(:subject => 'title')
24 I18n.locale = :de
25 post.update_attributes(:subject => 'Titel')
26
27 assert_equal 2, post.translations.size
28 assert_equal %w(de en), post.translations.map(&:locale).map(&:to_s).sort
29 assert_equal %w(Titel title), post.translations.map(&:subject).sort
30 end
31
32 test "a translated record has German translations" do
33 I18n.locale = :de
34 post = Post.create(:subject => 'foo')
35 assert_equal 1, post.translations.size
36 assert_equal [:de], post.translations.map { |t| t.locale }
37 end
38
39 test "modifiying translated fields while switching locales" do
40 post = Post.create(:subject => 'title', :content => 'content')
41 assert_equal %w(title content), [post.subject, post.content]
42
43 I18n.locale = :de
44 post.subject, post.content = 'Titel', 'Inhalt'
45
46 assert_translated(:de, post, [:subject, :content], %w(Titel Inhalt))
47 assert_translated(:en, post, [:subject, :content], %w(title content))
48 assert_translated(:de, post, [:subject, :content], %w(Titel Inhalt))
49
50 post.save
51 post.reload
52
53 assert_translated(:en, post, [:subject, :content], %w(title content))
54 assert_translated(:de, post, [:subject, :content], %w(Titel Inhalt))
55 end
56
57 test "attribute writers do return their argument" do
58 value = Post.new.subject = 'foo'
59 assert_equal 'foo', value
60 end
61
62 test "update_attribute succeeds with valid values" do
63 post = Post.create(:subject => 'foo', :content => 'bar')
64 post.update_attribute(:subject, 'baz')
65 assert_equal 'baz', Post.first.subject
66 end
67
68 test "update_attributes fails with invalid values" do
69 post = Post.create(:subject => 'foo', :content => 'bar')
70 assert !post.update_attributes(:subject => '')
71 assert_nil post.reload.attributes['subject']
72 assert_equal 'foo', post.subject
73 end
74
75 test "passing the locale to create uses the given locale" do
76 post = Post.create(:subject => 'Titel', :content => 'Inhalt', :locale => :de)
77 assert_equal :en, I18n.locale
78 assert_nil ActiveRecord::Base.locale
79
80 I18n.locale = :de
81 assert_equal 'Titel', post.subject
82 end
83
84 test "passing the locale to attributes= uses the given locale" do
85 post = Post.create(:subject => 'title', :content => 'content')
86 post.update_attributes(:subject => 'Titel', :content => 'Inhalt', :locale => :de)
87 post.reload
88
89 assert_equal :en, I18n.locale
90 assert_nil ActiveRecord::Base.locale
91
92 assert_equal 'title', post.subject
93 I18n.locale = :de
94 assert_equal 'Titel', post.subject
95 end
96
97 test 'reload works' do
98 post = Post.create(:subject => 'foo', :content => 'bar')
99 post.subject = 'baz'
100 post.reload
101 assert_equal 'foo', post.subject
102 end
103
104 test "returns nil if no translations are found (unsaved record)" do
105 post = Post.new(:subject => 'foo')
106 assert_equal 'foo', post.subject
107 assert_nil post.content
108 end
109
110 test "returns nil if no translations are found (saved record)" do
111 post = Post.create(:subject => 'foo')
112 post.reload
113 assert_equal 'foo', post.subject
114 assert_nil post.content
115 end
116
117 test "finds a German post" do
118 post = Post.create(:subject => 'foo (en)', :content => 'bar')
119 I18n.locale = :de
120 post = Post.first
121 post.subject = 'baz (de)'
122 post.save
123 assert_equal 'baz (de)', Post.first.subject
124 I18n.locale = :en
125 assert_equal 'foo (en)', Post.first.subject
126 end
127
128 test "saves an English post and loads correctly" do
129 post = Post.create(:subject => 'foo', :content => 'bar')
130 assert post.save
131 post = Post.first
132 assert_equal 'foo', post.subject
133 assert_equal 'bar', post.content
134 end
135
136 test "returns the value for the correct locale, after locale switching" do
137 post = Post.create(:subject => 'foo')
138 I18n.locale = :de
139 post.subject = 'bar'
140 post.save
141 I18n.locale = :en
142 post = Post.first
143 assert_equal 'foo', post.subject
144 I18n.locale = :de
145 assert_equal 'bar', post.subject
146 end
147
148 test "returns the value for the correct locale, after locale switching, without saving" do
149 post = Post.create :subject => 'foo'
150 I18n.locale = :de
151 post.subject = 'bar'
152 I18n.locale = :en
153 assert_equal 'foo', post.subject
154 I18n.locale = :de
155 assert_equal 'bar', post.subject
156 end
157
158 test "saves all locales, even after locale switching" do
159 post = Post.new :subject => 'foo'
160 I18n.locale = :de
161 post.subject = 'bar'
162 I18n.locale = :he
163 post.subject = 'baz'
164 post.save
165 I18n.locale = :en
166 post = Post.first
167 assert_equal 'foo', post.subject
168 I18n.locale = :de
169 assert_equal 'bar', post.subject
170 I18n.locale = :he
171 assert_equal 'baz', post.subject
172 end
173
174 test "works with associations" do
175 blog = Blog.create
176 post1 = blog.posts.create(:subject => 'foo')
177
178 I18n.locale = :de
179 post2 = blog.posts.create(:subject => 'bar')
180 assert_equal 2, blog.posts.size
181
182 I18n.locale = :en
183 assert_equal 'foo', blog.posts.first.subject
184 assert_nil blog.posts.last.subject
185
186 I18n.locale = :de
187 assert_equal 'bar', blog.posts.last.subject
188 end
189
190 test "works with simple dynamic finders" do
191 foo = Post.create(:subject => 'foo')
192 Post.create(:subject => 'bar')
193 post = Post.find_by_subject('foo')
194 assert_equal foo, post
195 end
196
197 test 'change attribute on globalized model' do
198 post = Post.create(:subject => 'foo', :content => 'bar')
199 assert_equal [], post.changed
200 post.subject = 'baz'
201 assert_equal ['subject'], post.changed
202 post.content = 'quux'
203 assert_member 'subject', post.changed
204 assert_member 'content', post.changed
205 end
206
207 test 'change attribute on globalized model after locale switching' do
208 post = Post.create(:subject => 'foo', :content => 'bar')
209 assert_equal [], post.changed
210 post.subject = 'baz'
211 I18n.locale = :de
212 assert_equal ['subject'], post.changed
213 end
214
215 test 'complex writing and stashing' do
216 post = Post.create(:subject => 'foo', :content => 'bar')
217 post.subject = nil
218 assert_nil post.subject
219 assert !post.valid?
220 post.subject = 'stashed_foo'
221 assert_equal 'stashed_foo', post.subject
222 end
223
224 test 'translated class locale setting' do
225 assert ActiveRecord::Base.respond_to?(:locale)
226 assert_equal :en, I18n.locale
227 assert_nil ActiveRecord::Base.locale
228
229 I18n.locale = :de
230 assert_equal :de, I18n.locale
231 assert_nil ActiveRecord::Base.locale
232
233 ActiveRecord::Base.locale = :es
234 assert_equal :de, I18n.locale
235 assert_equal :es, ActiveRecord::Base.locale
236
237 I18n.locale = :fr
238 assert_equal :fr, I18n.locale
239 assert_equal :es, ActiveRecord::Base.locale
240 end
241
242 test "untranslated class responds to locale" do
243 assert Blog.respond_to?(:locale)
244 end
245
246 test "to ensure locales in different classes are the same" do
247 ActiveRecord::Base.locale = :de
248 assert_equal :de, ActiveRecord::Base.locale
249 assert_equal :de, Parent.locale
250
251 Parent.locale = :es
252 assert_equal :es, ActiveRecord::Base.locale
253 assert_equal :es, Parent.locale
254 end
255
256 test "attribute saving goes by content locale and not global locale" do
257 ActiveRecord::Base.locale = :de
258 assert_equal :en, I18n.locale
259 Post.create :subject => 'foo'
260 assert_equal :de, Post.first.translations.first.locale
261 end
262
263 test "attribute loading goes by content locale and not global locale" do
264 post = Post.create(:subject => 'foo')
265 assert_nil ActiveRecord::Base.locale
266
267 ActiveRecord::Base.locale = :de
268 assert_equal :en, I18n.locale
269 post.update_attribute(:subject, 'foo [de]')
270 assert_equal 'foo [de]', Post.first.subject
271
272 ActiveRecord::Base.locale = :en
273 assert_equal 'foo', Post.first.subject
274 end
275
276 test "access content locale before setting" do
277 Globalize::ActiveRecord::ActMacro.class_eval "remove_class_variable(:@@locale)"
278 assert_nothing_raised { ActiveRecord::Base.locale }
279 end
280
281 test "available_locales" do
282 Post.locale = :de
283 post = Post.create(:subject => 'foo')
284 Post.locale = :es
285 post.update_attribute(:subject, 'bar')
286 Post.locale = :fr
287 post.update_attribute(:subject, 'baz')
288 assert_equal [:de, :es, :fr], post.available_locales
289 assert_equal [:de, :es, :fr], Post.first.available_locales
290 end
291
292 test "saving record correctly after post-save reload" do
293 reloader = Reloader.create(:content => 'foo')
294 assert_equal 'foo', reloader.content
295 end
296
297 test "including translations" do
298 I18n.locale = :de
299 Post.create(:subject => "Foo1", :content => "Bar1")
300 Post.create(:subject => "Foo2", :content => "Bar2")
301
302 class << Post
303 def translations_included
304 self.all(:include => :translations)
305 end
306 end
307
308 default = Post.all.map { |x| [x.subject, x.content] }
309 with_include = Post.translations_included.map { |x| [x.subject, x.content] }
310 assert_equal default, with_include
311 end
312
313 test "setting multiple translations at once with options hash" do
314 Post.locale = :de
315 post = Post.create(:subject => "foo1", :content => "foo1")
316 Post.locale = :en
317 post.update_attributes(:subject => "bar1", :content => "bar1")
318
319 options = { :de => {:subject => "foo2", :content => "foo2"},
320 :en => {:subject => "bar2", :content => "bar2"} }
321 post.set_translations options
322 post.reload
323
324 assert ["bar2", "bar2"], [post.subject, post.content]
325 Post.locale = :de
326 assert ["foo2", "foo2"], [post.subject, post.content]
327 end
328
329 test "setting only one translation with set_translations" do
330 Post.locale = :de
331 post = Post.create(:subject => "foo1", :content => "foo1")
332 Post.locale = :en
333 post.update_attributes(:subject => "bar1", :content => "bar1")
334
335 options = { :en => { :subject => "bar2", :content => "bar2" } }
336 post.set_translations options
337 post.reload
338
339 assert ["bar2", "bar2"], [post.subject, post.content]
340 Post.locale = :de
341 assert ["foo1", "foo1"], [post.subject, post.content]
342 end
343
344 test "setting only selected attributes with set_translations" do
345 Post.locale = :de
346 post = Post.create(:subject => "foo1", :content => "foo1")
347 Post.locale = :en
348 post.update_attributes(:subject => "bar1", :content => "bar1")
349
350 options = { :de => { :content => "foo2" }, :en => { :subject => "bar2" } }
351 post.set_translations options
352 post.reload
353
354 assert ["bar2", "bar1"], [post.subject, post.content]
355 Post.locale = :de
356 assert ["foo1", "foo2"], [post.subject, post.content]
357 end
358
359 test "setting invalid attributes raises ArgumentError" do
360 Post.locale = :de
361 post = Post.create(:subject => "foo1", :content => "foo1")
362 Post.locale = :en
363 post.update_attributes(:subject => "bar1", :content => "bar1")
364
365 options = { :de => {:fake => "foo2"} }
366 exception = assert_raise(ActiveRecord::UnknownAttributeError) do
367 post.set_translations options
368 end
369 assert_equal "unknown attribute: fake", exception.message
370 end
371
372 test "reload accepting find options" do
373 p = Post.create(:subject => "Foo", :content => "Bar")
374 assert p.reload(:readonly => true, :lock => true)
375 assert_raise(ArgumentError) { p.reload(:foo => :bar) }
376 end
377
378 test "dependent destroy of translation" do
379 p = Post.create(:subject => "Foo", :content => "Bar")
380 assert_equal 1, PostTranslation.count
381 p.destroy
382 assert_equal 0, PostTranslation.count
383 end
384
385 test "translating subclass of untranslated comment model" do
386 translated_comment = TranslatedComment.create(:post => @post)
387 assert_nothing_raised { translated_comment.translations }
388 end
389
390 test "modifiying translated comments works as expected" do
391 I18n.locale = :en
392 translated_comment = TranslatedComment.create(:post => @post, :content => 'foo')
393 assert_equal 'foo', translated_comment.content
394
395 I18n.locale = :de
396 translated_comment.content = 'bar'
397 assert translated_comment.save
398 assert_equal 'bar', translated_comment.content
399
400 I18n.locale = :en
401 assert_equal 'foo', translated_comment.content
402
403 assert_equal 2, translated_comment.translations.size
404 end
405
406 test "can create a proxy class for a namespaced model" do
407 assert_nothing_raised do
408 module Foo
409 module Bar
410 class Baz < ActiveRecord::Base
411 translates :bumm
412 end
413 end
414 end
415 end
416 end
417
418 test "attribute translated before type cast" do
419 Post.locale = :en
420 post = Post.create(:subject => 'foo', :content => 'bar')
421 Post.locale = :de
422 post.update_attribute(:subject, "German foo")
423 assert_equal 'German foo', post.subject_before_type_cast
424 Post.locale = :en
425 assert_equal 'foo', post.subject_before_type_cast
426 end
427
428 test "don't override existing translation class" do
429 assert PostTranslation.new.respond_to?(:existing_method)
430 end
431
432 test "has_many and named scopes work with globalize" do
433 blog = Blog.create
434 assert_nothing_raised { blog.posts.foobar }
435 end
436end
437
438# TODO error checking for fields that exist in main table, don't exist in
439# proxy table, aren't strings or text
440#
441# TODO allow finding by translated attributes in conditions?
442# TODO generate advanced dynamic finders?
diff --git a/vendor/plugins/globalize2/test/all.rb b/vendor/plugins/globalize2/test/all.rb
deleted file mode 100644
index ff467a1..0000000
--- a/vendor/plugins/globalize2/test/all.rb
+++ /dev/null
@@ -1,2 +0,0 @@
1files = Dir[File.dirname(__FILE__) + '/**/*_test.rb']
2files.each { |file| require file } \ No newline at end of file
diff --git a/vendor/plugins/globalize2/test/data/models.rb b/vendor/plugins/globalize2/test/data/models.rb
deleted file mode 100644
index 553fca3..0000000
--- a/vendor/plugins/globalize2/test/data/models.rb
+++ /dev/null
@@ -1,51 +0,0 @@
1#require 'ruby2ruby'
2#require 'parse_tree'
3#require 'parse_tree_extensions'
4#require 'pp'
5
6class PostTranslation < ActiveRecord::Base
7 def existing_method ; end
8end
9
10class Post < ActiveRecord::Base
11 translates :subject, :content
12 validates_presence_of :subject
13 named_scope :foobar, :conditions => { :title => "foobar" }
14end
15
16class Blog < ActiveRecord::Base
17 has_many :posts, :order => 'id ASC'
18end
19
20class Parent < ActiveRecord::Base
21 translates :content
22end
23
24class Child < Parent
25end
26
27class Comment < ActiveRecord::Base
28 validates_presence_of :content
29 belongs_to :post
30end
31
32class TranslatedComment < Comment
33 translates :content
34end
35
36class UltraLongModelNameWithoutProper < ActiveRecord::Base
37 translates :subject, :content
38 validates_presence_of :subject
39end
40
41class Reloader < Parent
42 after_create :do_reload
43
44 def do_reload
45 reload
46 end
47end
48
49class Validatee < ActiveRecord::Base
50 translates :string
51end
diff --git a/vendor/plugins/globalize2/test/data/no_globalize_schema.rb b/vendor/plugins/globalize2/test/data/no_globalize_schema.rb
deleted file mode 100644
index 379455d..0000000
--- a/vendor/plugins/globalize2/test/data/no_globalize_schema.rb
+++ /dev/null
@@ -1,11 +0,0 @@
1# This schema creates tables without columns for the translated fields
2ActiveRecord::Schema.define do
3 create_table :blogs, :force => true do |t|
4 t.string :name
5 end
6
7 create_table :posts, :force => true do |t|
8 t.references :blog
9 end
10end
11
diff --git a/vendor/plugins/globalize2/test/i18n/missing_translations_test.rb b/vendor/plugins/globalize2/test/i18n/missing_translations_test.rb
deleted file mode 100644
index 5d0ecd6..0000000
--- a/vendor/plugins/globalize2/test/i18n/missing_translations_test.rb
+++ /dev/null
@@ -1,36 +0,0 @@
1require File.dirname(__FILE__) + '/../test_helper'
2require 'i18n/missing_translations_log_handler'
3
4class MissingTranslationsTest < ActiveSupport::TestCase
5 test "defines I18n.missing_translations_logger accessor" do
6 assert I18n.respond_to?(:missing_translations_logger)
7 end
8
9 test "defines I18n.missing_translations_logger= writer" do
10 assert I18n.respond_to?(:missing_translations_logger=)
11 end
12end
13
14class TestLogger < String
15 def warn(msg) self.concat msg; end
16end
17
18class LogMissingTranslationsTest < ActiveSupport::TestCase
19 def setup
20 @locale, @key, @options = :en, :foo, {}
21 @exception = I18n::MissingTranslationData.new(@locale, @key, @options)
22
23 @logger = TestLogger.new
24 I18n.missing_translations_logger = @logger
25 end
26
27 test "still returns the exception message for MissingTranslationData exceptions" do
28 result = I18n.send(:missing_translations_log_handler, @exception, @locale, @key, @options)
29 assert_equal 'translation missing: en, foo', result
30 end
31
32 test "logs the missing translation to I18n.missing_translations_logger" do
33 I18n.send(:missing_translations_log_handler, @exception, @locale, @key, @options)
34 assert_equal 'translation missing: en, foo', @logger
35 end
36end
diff --git a/vendor/plugins/globalize2/test/test_helper.rb b/vendor/plugins/globalize2/test/test_helper.rb
deleted file mode 100644
index 195907d..0000000
--- a/vendor/plugins/globalize2/test/test_helper.rb
+++ /dev/null
@@ -1,76 +0,0 @@
1$LOAD_PATH << File.expand_path( File.dirname(__FILE__) + '/../lib' )
2
3require 'rubygems'
4require 'test/unit'
5require 'active_record'
6require 'active_support'
7require 'active_support/test_case'
8require 'mocha'
9require 'globalize'
10require 'validation_reflection'
11
12config = { :adapter => 'sqlite3', :database => ':memory:' }
13ActiveRecord::Base.establish_connection(config)
14
15class ActiveSupport::TestCase
16 def reset_db!(schema_path = nil)
17 schema_path ||= File.expand_path(File.dirname(__FILE__) + '/data/schema.rb')
18 ActiveRecord::Migration.verbose = false
19 ActiveRecord::Base.silence { load(schema_path) }
20 end
21
22 def assert_member(item, array)
23 assert_block "Item #{item} is not in array #{array}" do
24 array.member?(item)
25 end
26 end
27
28 def assert_belongs_to(model, associated)
29 assert model.reflect_on_all_associations(:belongs_to).detect { |association|
30 association.name.to_s == associated.to_s
31 }
32 end
33
34 def assert_has_many(model, associated)
35 assert model.reflect_on_all_associations(:has_many).detect { |association|
36 association.name.to_s == associated.to_s
37 }
38 end
39end
40
41module ActiveRecord
42 module ConnectionAdapters
43 class AbstractAdapter
44 def index_exists?(table_name, column_name)
45 indexes(table_name).any? { |index| index.name == index_name(table_name, column_name) }
46 end
47 end
48 end
49end
50
51# module ActiveRecord
52# class BaseWithoutTable < Base
53# self.abstract_class = true
54#
55# def create_or_update
56# errors.empty?
57# end
58#
59# class << self
60# def columns()
61# @columns ||= []
62# end
63#
64# def column(name, sql_type = nil, default = nil, null = true)
65# columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
66# reset_column_information
67# end
68#
69# # Do not reset @columns
70# def reset_column_information
71# read_methods.each { |name| undef_method(name) }
72# @column_names = @columns_hash = @content_columns = @dynamic_methods_hash = @read_methods = nil
73# end
74# end
75# end
76# end \ No newline at end of file
diff --git a/vendor/plugins/paperclip/LICENSE b/vendor/plugins/paperclip/LICENSE
deleted file mode 100644
index 299b9ed..0000000
--- a/vendor/plugins/paperclip/LICENSE
+++ /dev/null
@@ -1,26 +0,0 @@
1
2LICENSE
3
4The MIT License
5
6Copyright (c) 2008 Jon Yurek and thoughtbot, inc.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14
15The above copyright notice and this permission notice shall be included in
16all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24THE SOFTWARE.
25
26
diff --git a/vendor/plugins/paperclip/README.rdoc b/vendor/plugins/paperclip/README.rdoc
deleted file mode 100644
index 85fcf4b..0000000
--- a/vendor/plugins/paperclip/README.rdoc
+++ /dev/null
@@ -1,172 +0,0 @@
1=Paperclip
2
3Paperclip is intended as an easy file attachment library for ActiveRecord. The
4intent behind it was to keep setup as easy as possible and to treat files as
5much like other attributes as possible. This means they aren't saved to their
6final locations on disk, nor are they deleted if set to nil, until
7ActiveRecord::Base#save is called. It manages validations based on size and
8presence, if required. It can transform its assigned image into thumbnails if
9needed, and the prerequisites are as simple as installing ImageMagick (which,
10for most modern Unix-based systems, is as easy as installing the right
11packages). Attached files are saved to the filesystem and referenced in the
12browser by an easily understandable specification, which has sensible and
13useful defaults.
14
15See the documentation for +has_attached_file+ in Paperclip::ClassMethods for
16more detailed options.
17
18==Quick Start
19
20In your model:
21
22 class User < ActiveRecord::Base
23 has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
24 end
25
26In your migrations:
27
28 class AddAvatarColumnsToUser < ActiveRecord::Migration
29 def self.up
30 add_column :users, :avatar_file_name, :string
31 add_column :users, :avatar_content_type, :string
32 add_column :users, :avatar_file_size, :integer
33 add_column :users, :avatar_updated_at, :datetime
34 end
35
36 def self.down
37 remove_column :users, :avatar_file_name
38 remove_column :users, :avatar_content_type
39 remove_column :users, :avatar_file_size
40 remove_column :users, :avatar_updated_at
41 end
42 end
43
44In your edit and new views:
45
46 <% form_for :user, @user, :url => user_path, :html => { :multipart => true } do |form| %>
47 <%= form.file_field :avatar %>
48 <% end %>
49
50In your controller:
51
52 def create
53 @user = User.create( params[:user] )
54 end
55
56In your show view:
57
58 <%= image_tag @user.avatar.url %>
59 <%= image_tag @user.avatar.url(:medium) %>
60 <%= image_tag @user.avatar.url(:thumb) %>
61
62==Usage
63
64The basics of paperclip are quite simple: Declare that your model has an
65attachment with the has_attached_file method, and give it a name. Paperclip
66will wrap up up to four attributes (all prefixed with that attachment's name,
67so you can have multiple attachments per model if you wish) and give the a
68friendly front end. The attributes are <attachment>_file_name,
69<attachment>_file_size, <attachment>_content_type, and <attachment>_updated_at.
70Only <attachment>_file_name is required for paperclip to operate. More
71information about the options to has_attached_file is available in the
72documentation of Paperclip::ClassMethods.
73
74Attachments can be validated with Paperclip's validation methods,
75validates_attachment_presence, validates_attachment_content_type, and
76validates_attachment_size.
77
78==Storage
79
80The files that are assigned as attachments are, by default, placed in the
81directory specified by the :path option to has_attached_file. By default, this
82location is
83":rails_root/public/system/:attachment/:id/:style/:basename.:extension". This
84location was chosen because on standard Capistrano deployments, the
85public/system directory is symlinked to the app's shared directory, meaning it
86will survive between deployments. For example, using that :path, you may have a
87file at
88
89 /data/myapp/releases/20081229172410/public/system/avatars/13/small/my_pic.png
90
91NOTE: This is a change from previous versions of Paperclip, but is overall a
92safer choice for the defaul file store.
93
94You may also choose to store your files using Amazon's S3 service. You can find
95more information about S3 storage at the description for
96Paperclip::Storage::S3.
97
98Files on the local filesystem (and in the Rails app's public directory) will be
99available to the internet at large. If you require access control, it's
100possible to place your files in a different location. You will need to change
101both the :path and :url options in order to make sure the files are unavailable
102to the public. Both :path and :url allow the same set of interpolated
103variables.
104
105==Post Processing
106
107Paperclip supports an extendible selection of post-processors. When you define
108a set of styles for an attachment, by default it is expected that those
109"styles" are actually "thumbnails". However, you can do more than just
110thumbnail images. By defining a subclass of Paperclip::Processor, you can
111perform any processing you want on the files that are attached. Any file in
112your Rails app's lib/paperclip_processors directory is automatically loaded by
113paperclip, allowing you to easily define custom processors. You can specify a
114processor with the :processors option to has_attached_file:
115
116 has_attached_file :scan, :styles => { :text => { :quality => :better } },
117 :processors => [:ocr]
118
119This would load the hypothetical class Paperclip::Ocr, which would have the
120hash "{ :quality => :better }" passed to it along with the uploaded file. For
121more information about defining processors, see Paperclip::Processor.
122
123The default processor is Paperclip::Thumbnail. For backwards compatability
124reasons, you can pass a single geometry string or an array containing a
125geometry and a format, which the file will be converted to, like so:
126
127 has_attached_file :avatar, :styles => { :thumb => ["32x32#", :png] }
128
129This will convert the "thumb" style to a 32x32 square in png format, regardless
130of what was uploaded. If the format is not specified, it is kept the same (i.e.
131jpgs will remain jpgs).
132
133Multiple processors can be specified, and they will be invoked in the order
134they are defined in the :processors array. Each successive processor will
135be given the result of the previous processor's execution. All processors will
136receive the same parameters, which are what you define in the :styles hash.
137For example, assuming we had this definition:
138
139 has_attached_file :scan, :styles => { :text => { :quality => :better } },
140 :processors => [:rotator, :ocr]
141
142then both the :rotator processor and the :ocr processor would receive the
143options "{ :quality => :better }". This parameter may not mean anything to one
144or more or the processors, and they are free to ignore it.
145
146==Events
147
148Before and after the Post Processing step, Paperclip calls back to the model
149with a few callbacks, allowing the model to change or cancel the processing
150step. The callbacks are "before_post_process" and "after_post_process" (which
151are called before and after the processing of each attachment), and the
152attachment-specific "before_<attachment>_post_process" and
153"after_<attachment>_post_process". The callbacks are intended to be as close to
154normal ActiveRecord callbacks as possible, so if you return false (specifically
155- returning nil is not the same) in a before_ filter, the post processing step
156will halt. Returning false in an after_ filter will not halt anything, but you
157can access the model and the attachment if necessary.
158
159NOTE: Post processing will not even *start* if the attachment is not valid
160according to the validations. Your callbacks (and processors) will only be
161called with valid attachments.
162
163==Contributing
164
165If you'd like to contribute a feature or bugfix: Thanks! To make sure your
166fix/feature has a high chance of being included, please read the following
167guidelines:
168
1691. Ask on the mailing list, or post a ticket in Lighthouse.
1702. Make sure there are tests! We will not accept any patch that is not tested.
171 It's a rare time when explicit tests aren't needed. If you have questions
172 about writing tests for paperclip, please ask the mailing list.
diff --git a/vendor/plugins/paperclip/Rakefile b/vendor/plugins/paperclip/Rakefile
deleted file mode 100644
index 91f1687..0000000
--- a/vendor/plugins/paperclip/Rakefile
+++ /dev/null
@@ -1,77 +0,0 @@
1require 'rake'
2require 'rake/testtask'
3require 'rake/rdoctask'
4
5$LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
6require 'paperclip'
7
8desc 'Default: run unit tests.'
9task :default => [:clean, :test]
10
11desc 'Test the paperclip plugin.'
12Rake::TestTask.new(:test) do |t|
13 t.libs << 'lib' << 'profile'
14 t.pattern = 'test/**/*_test.rb'
15 t.verbose = true
16end
17
18desc 'Start an IRB session with all necessary files required.'
19task :shell do |t|
20 chdir File.dirname(__FILE__)
21 exec 'irb -I lib/ -I lib/paperclip -r rubygems -r active_record -r tempfile -r init'
22end
23
24desc 'Generate documentation for the paperclip plugin.'
25Rake::RDocTask.new(:rdoc) do |rdoc|
26 rdoc.rdoc_dir = 'doc'
27 rdoc.title = 'Paperclip'
28 rdoc.options << '--line-numbers' << '--inline-source'
29 rdoc.rdoc_files.include('README*')
30 rdoc.rdoc_files.include('lib/**/*.rb')
31end
32
33desc 'Update documentation on website'
34task :sync_docs => 'rdoc' do
35 `rsync -ave ssh doc/ dev@dev.thoughtbot.com:/home/dev/www/dev.thoughtbot.com/paperclip`
36end
37
38desc 'Clean up files.'
39task :clean do |t|
40 FileUtils.rm_rf "doc"
41 FileUtils.rm_rf "tmp"
42 FileUtils.rm_rf "pkg"
43 FileUtils.rm "test/debug.log" rescue nil
44 FileUtils.rm "test/paperclip.db" rescue nil
45end
46
47spec = Gem::Specification.new do |s|
48 s.name = "paperclip"
49 s.version = Paperclip::VERSION
50 s.author = "Jon Yurek"
51 s.email = "jyurek@thoughtbot.com"
52 s.homepage = "http://www.thoughtbot.com/projects/paperclip"
53 s.platform = Gem::Platform::RUBY
54 s.summary = "File attachments as attributes for ActiveRecord"
55 s.files = FileList["README*",
56 "LICENSE",
57 "Rakefile",
58 "init.rb",
59 "{generators,lib,tasks,test,shoulda_macros}/**/*"].to_a
60 s.require_path = "lib"
61 s.test_files = FileList["test/**/test_*.rb"].to_a
62 s.rubyforge_project = "paperclip"
63 s.has_rdoc = true
64 s.extra_rdoc_files = FileList["README*"].to_a
65 s.rdoc_options << '--line-numbers' << '--inline-source'
66 s.requirements << "ImageMagick"
67 s.add_runtime_dependency 'right_aws'
68 s.add_development_dependency 'thoughtbot-shoulda'
69 s.add_development_dependency 'mocha'
70end
71
72desc "Generate a gemspec file for GitHub"
73task :gemspec do
74 File.open("#{spec.name}.gemspec", 'w') do |f|
75 f.write spec.to_ruby
76 end
77end
diff --git a/vendor/plugins/paperclip/generators/paperclip/USAGE b/vendor/plugins/paperclip/generators/paperclip/USAGE
deleted file mode 100644
index 2d611d7..0000000
--- a/vendor/plugins/paperclip/generators/paperclip/USAGE
+++ /dev/null
@@ -1,5 +0,0 @@
1Usage:
2
3 script/generate paperclip Class attachment1 (attachment2 ...)
4
5This will create a migration that will add the proper columns to your class's table. \ No newline at end of file
diff --git a/vendor/plugins/paperclip/generators/paperclip/paperclip_generator.rb b/vendor/plugins/paperclip/generators/paperclip/paperclip_generator.rb
deleted file mode 100644
index 77ee5a2..0000000
--- a/vendor/plugins/paperclip/generators/paperclip/paperclip_generator.rb
+++ /dev/null
@@ -1,27 +0,0 @@
1class PaperclipGenerator < Rails::Generator::NamedBase
2 attr_accessor :attachments, :migration_name
3
4 def initialize(args, options = {})
5 super
6 @class_name, @attachments = args[0], args[1..-1]
7 end
8
9 def manifest
10 file_name = generate_file_name
11 @migration_name = file_name.camelize
12 record do |m|
13 m.migration_template "paperclip_migration.rb.erb",
14 File.join('db', 'migrate'),
15 :migration_file_name => file_name
16 end
17 end
18
19 private
20
21 def generate_file_name
22 names = attachments.map{|a| a.underscore }
23 names = names[0..-2] + ["and", names[-1]] if names.length > 1
24 "add_attachments_#{names.join("_")}_to_#{@class_name.underscore}"
25 end
26
27end
diff --git a/vendor/plugins/paperclip/generators/paperclip/templates/paperclip_migration.rb.erb b/vendor/plugins/paperclip/generators/paperclip/templates/paperclip_migration.rb.erb
deleted file mode 100644
index eebb0e5..0000000
--- a/vendor/plugins/paperclip/generators/paperclip/templates/paperclip_migration.rb.erb
+++ /dev/null
@@ -1,19 +0,0 @@
1class <%= migration_name %> < ActiveRecord::Migration
2 def self.up
3<% attachments.each do |attachment| -%>
4 add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_name, :string
5 add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_content_type, :string
6 add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_size, :integer
7 add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at, :datetime
8<% end -%>
9 end
10
11 def self.down
12<% attachments.each do |attachment| -%>
13 remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_name
14 remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_content_type
15 remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_size
16 remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at
17<% end -%>
18 end
19end
diff --git a/vendor/plugins/paperclip/init.rb b/vendor/plugins/paperclip/init.rb
deleted file mode 100644
index 5a07dda..0000000
--- a/vendor/plugins/paperclip/init.rb
+++ /dev/null
@@ -1 +0,0 @@
1require File.join(File.dirname(__FILE__), "lib", "paperclip")
diff --git a/vendor/plugins/paperclip/lib/paperclip.rb b/vendor/plugins/paperclip/lib/paperclip.rb
deleted file mode 100644
index 74c3d79..0000000
--- a/vendor/plugins/paperclip/lib/paperclip.rb
+++ /dev/null
@@ -1,318 +0,0 @@
1# Paperclip allows file attachments that are stored in the filesystem. All graphical
2# transformations are done using the Graphics/ImageMagick command line utilities and
3# are stored in Tempfiles until the record is saved. Paperclip does not require a
4# separate model for storing the attachment's information, instead adding a few simple
5# columns to your table.
6#
7# Author:: Jon Yurek
8# Copyright:: Copyright (c) 2008 thoughtbot, inc.
9# License:: MIT License (http://www.opensource.org/licenses/mit-license.php)
10#
11# Paperclip defines an attachment as any file, though it makes special considerations
12# for image files. You can declare that a model has an attached file with the
13# +has_attached_file+ method:
14#
15# class User < ActiveRecord::Base
16# has_attached_file :avatar, :styles => { :thumb => "100x100" }
17# end
18#
19# user = User.new
20# user.avatar = params[:user][:avatar]
21# user.avatar.url
22# # => "/users/avatars/4/original_me.jpg"
23# user.avatar.url(:thumb)
24# # => "/users/avatars/4/thumb_me.jpg"
25#
26# See the +has_attached_file+ documentation for more details.
27
28require 'tempfile'
29require 'paperclip/upfile'
30require 'paperclip/iostream'
31require 'paperclip/geometry'
32require 'paperclip/processor'
33require 'paperclip/thumbnail'
34require 'paperclip/storage'
35require 'paperclip/attachment'
36if defined? RAILS_ROOT
37 Dir.glob(File.join(File.expand_path(RAILS_ROOT), "lib", "paperclip_processors", "*.rb")).each do |processor|
38 require processor
39 end
40end
41
42# The base module that gets included in ActiveRecord::Base. See the
43# documentation for Paperclip::ClassMethods for more useful information.
44module Paperclip
45
46 VERSION = "2.2.8"
47
48 class << self
49 # Provides configurability to Paperclip. There are a number of options available, such as:
50 # * whiny_thumbnails: Will raise an error if Paperclip cannot process thumbnails of
51 # an uploaded image. Defaults to true.
52 # * log: Logs progress to the Rails log. Uses ActiveRecord's logger, so honors
53 # log levels, etc. Defaults to true.
54 # * command_path: Defines the path at which to find the command line
55 # programs if they are not visible to Rails the system's search path. Defaults to
56 # nil, which uses the first executable found in the user's search path.
57 # * image_magick_path: Deprecated alias of command_path.
58 def options
59 @options ||= {
60 :whiny_thumbnails => true,
61 :image_magick_path => nil,
62 :command_path => nil,
63 :log => true,
64 :swallow_stderr => true
65 }
66 end
67
68 def path_for_command command #:nodoc:
69 if options[:image_magick_path]
70 warn("[DEPRECATION] :image_magick_path is deprecated and will be removed. Use :command_path instead")
71 end
72 path = [options[:command_path] || options[:image_magick_path], command].compact
73 File.join(*path)
74 end
75
76 def interpolates key, &block
77 Paperclip::Attachment.interpolations[key] = block
78 end
79
80 # The run method takes a command to execute and a string of parameters
81 # that get passed to it. The command is prefixed with the :command_path
82 # option from Paperclip.options. If you have many commands to run and
83 # they are in different paths, the suggested course of action is to
84 # symlink them so they are all in the same directory.
85 #
86 # If the command returns with a result code that is not one of the
87 # expected_outcodes, a PaperclipCommandLineError will be raised. Generally
88 # a code of 0 is expected, but a list of codes may be passed if necessary.
89 def run cmd, params = "", expected_outcodes = 0
90 command = %Q<#{%Q[#{path_for_command(cmd)} #{params}].gsub(/\s+/, " ")}>
91 command = "#{command} 2>#{bit_bucket}" if Paperclip.options[:swallow_stderr]
92 output = `#{command}`
93 unless [expected_outcodes].flatten.include?($?.exitstatus)
94 raise PaperclipCommandLineError, "Error while running #{cmd}"
95 end
96 output
97 end
98
99 def bit_bucket #:nodoc:
100 File.exists?("/dev/null") ? "/dev/null" : "NUL"
101 end
102
103 def included base #:nodoc:
104 base.extend ClassMethods
105 unless base.respond_to?(:define_callbacks)
106 base.send(:include, Paperclip::CallbackCompatability)
107 end
108 end
109
110 def processor name #:nodoc:
111 name = name.to_s.camelize
112 processor = Paperclip.const_get(name)
113 unless processor.ancestors.include?(Paperclip::Processor)
114 raise PaperclipError.new("Processor #{name} was not found")
115 end
116 processor
117 end
118 end
119
120 class PaperclipError < StandardError #:nodoc:
121 end
122
123 class PaperclipCommandLineError < StandardError #:nodoc:
124 end
125
126 class NotIdentifiedByImageMagickError < PaperclipError #:nodoc:
127 end
128
129 module ClassMethods
130 # +has_attached_file+ gives the class it is called on an attribute that maps to a file. This
131 # is typically a file stored somewhere on the filesystem and has been uploaded by a user.
132 # The attribute returns a Paperclip::Attachment object which handles the management of
133 # that file. The intent is to make the attachment as much like a normal attribute. The
134 # thumbnails will be created when the new file is assigned, but they will *not* be saved
135 # until +save+ is called on the record. Likewise, if the attribute is set to +nil+ is
136 # called on it, the attachment will *not* be deleted until +save+ is called. See the
137 # Paperclip::Attachment documentation for more specifics. There are a number of options
138 # you can set to change the behavior of a Paperclip attachment:
139 # * +url+: The full URL of where the attachment is publically accessible. This can just
140 # as easily point to a directory served directly through Apache as it can to an action
141 # that can control permissions. You can specify the full domain and path, but usually
142 # just an absolute path is sufficient. The leading slash *must* be included manually for
143 # absolute paths. The default value is
144 # "/system/:attachment/:id/:style/:basename.:extension". See
145 # Paperclip::Attachment#interpolate for more information on variable interpolaton.
146 # :url => "/:class/:attachment/:id/:style_:basename.:extension"
147 # :url => "http://some.other.host/stuff/:class/:id_:extension"
148 # * +default_url+: The URL that will be returned if there is no attachment assigned.
149 # This field is interpolated just as the url is. The default value is
150 # "/:attachment/:style/missing.png"
151 # has_attached_file :avatar, :default_url => "/images/default_:style_avatar.png"
152 # User.new.avatar_url(:small) # => "/images/default_small_avatar.png"
153 # * +styles+: A hash of thumbnail styles and their geometries. You can find more about
154 # geometry strings at the ImageMagick website
155 # (http://www.imagemagick.org/script/command-line-options.php#resize). Paperclip
156 # also adds the "#" option (e.g. "50x50#"), which will resize the image to fit maximally
157 # inside the dimensions and then crop the rest off (weighted at the center). The
158 # default value is to generate no thumbnails.
159 # * +default_style+: The thumbnail style that will be used by default URLs.
160 # Defaults to +original+.
161 # has_attached_file :avatar, :styles => { :normal => "100x100#" },
162 # :default_style => :normal
163 # user.avatar.url # => "/avatars/23/normal_me.png"
164 # * +whiny_thumbnails+: Will raise an error if Paperclip cannot post_process an uploaded file due
165 # to a command line error. This will override the global setting for this attachment.
166 # Defaults to true.
167 # * +convert_options+: When creating thumbnails, use this free-form options
168 # field to pass in various convert command options. Typical options are "-strip" to
169 # remove all Exif data from the image (save space for thumbnails and avatars) or
170 # "-depth 8" to specify the bit depth of the resulting conversion. See ImageMagick
171 # convert documentation for more options: (http://www.imagemagick.org/script/convert.php)
172 # Note that this option takes a hash of options, each of which correspond to the style
173 # of thumbnail being generated. You can also specify :all as a key, which will apply
174 # to all of the thumbnails being generated. If you specify options for the :original,
175 # it would be best if you did not specify destructive options, as the intent of keeping
176 # the original around is to regenerate all the thumbnails when requirements change.
177 # has_attached_file :avatar, :styles => { :large => "300x300", :negative => "100x100" }
178 # :convert_options => {
179 # :all => "-strip",
180 # :negative => "-negate"
181 # }
182 # * +storage+: Chooses the storage backend where the files will be stored. The current
183 # choices are :filesystem and :s3. The default is :filesystem. Make sure you read the
184 # documentation for Paperclip::Storage::Filesystem and Paperclip::Storage::S3
185 # for backend-specific options.
186 def has_attached_file name, options = {}
187 include InstanceMethods
188
189 write_inheritable_attribute(:attachment_definitions, {}) if attachment_definitions.nil?
190 attachment_definitions[name] = {:validations => {}}.merge(options)
191
192 after_save :save_attached_files
193 before_destroy :destroy_attached_files
194
195 define_callbacks :before_post_process, :after_post_process
196 define_callbacks :"before_#{name}_post_process", :"after_#{name}_post_process"
197
198 define_method name do |*args|
199 a = attachment_for(name)
200 (args.length > 0) ? a.to_s(args.first) : a
201 end
202
203 define_method "#{name}=" do |file|
204 attachment_for(name).assign(file)
205 end
206
207 define_method "#{name}?" do
208 attachment_for(name).file?
209 end
210
211 validates_each(name) do |record, attr, value|
212 attachment = record.attachment_for(name)
213 attachment.send(:flush_errors) unless attachment.valid?
214 end
215 end
216
217 # Places ActiveRecord-style validations on the size of the file assigned. The
218 # possible options are:
219 # * +in+: a Range of bytes (i.e. +1..1.megabyte+),
220 # * +less_than+: equivalent to :in => 0..options[:less_than]
221 # * +greater_than+: equivalent to :in => options[:greater_than]..Infinity
222 # * +message+: error message to display, use :min and :max as replacements
223 def validates_attachment_size name, options = {}
224 min = options[:greater_than] || (options[:in] && options[:in].first) || 0
225 max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
226 range = (min..max)
227 message = options[:message] || "file size must be between :min and :max bytes."
228
229 attachment_definitions[name][:validations][:size] = lambda do |attachment, instance|
230 if attachment.file? && !range.include?(attachment.size.to_i)
231 message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
232 end
233 end
234 end
235
236 # Adds errors if thumbnail creation fails. The same as specifying :whiny_thumbnails => true.
237 def validates_attachment_thumbnails name, options = {}
238 attachment_definitions[name][:whiny_thumbnails] = true
239 end
240
241 # Places ActiveRecord-style validations on the presence of a file.
242 def validates_attachment_presence name, options = {}
243 message = options[:message] || "must be set."
244 attachment_definitions[name][:validations][:presence] = lambda do |attachment, instance|
245 message unless attachment.file?
246 end
247 end
248
249 # Places ActiveRecord-style validations on the content type of the file
250 # assigned. The possible options are:
251 # * +content_type+: Allowed content types. Can be a single content type
252 # or an array. Each type can be a String or a Regexp. It should be
253 # noted that Internet Explorer upload files with content_types that you
254 # may not expect. For example, JPEG images are given image/pjpeg and
255 # PNGs are image/x-png, so keep that in mind when determining how you
256 # match. Allows all by default.
257 # * +message+: The message to display when the uploaded file has an invalid
258 # content type.
259 # NOTE: If you do not specify an [attachment]_content_type field on your
260 # model, content_type validation will work _ONLY upon assignment_ and
261 # re-validation after the instance has been reloaded will always succeed.
262 def validates_attachment_content_type name, options = {}
263 attachment_definitions[name][:validations][:content_type] = lambda do |attachment, instance|
264 valid_types = [options[:content_type]].flatten
265
266 unless attachment.original_filename.blank?
267 unless valid_types.blank?
268 content_type = attachment.instance_read(:content_type)
269 unless valid_types.any?{|t| content_type.nil? || t === content_type }
270 options[:message] || "is not one of the allowed file types."
271 end
272 end
273 end
274 end
275 end
276
277 # Returns the attachment definitions defined by each call to
278 # has_attached_file.
279 def attachment_definitions
280 read_inheritable_attribute(:attachment_definitions)
281 end
282 end
283
284 module InstanceMethods #:nodoc:
285 def attachment_for name
286 @_paperclip_attachments ||= {}
287 @_paperclip_attachments[name] ||= Attachment.new(name, self, self.class.attachment_definitions[name])
288 end
289
290 def each_attachment
291 self.class.attachment_definitions.each do |name, definition|
292 yield(name, attachment_for(name))
293 end
294 end
295
296 def save_attached_files
297 logger.info("[paperclip] Saving attachments.")
298 each_attachment do |name, attachment|
299 attachment.send(:save)
300 end
301 end
302
303 def destroy_attached_files
304 logger.info("[paperclip] Deleting attachments.")
305 each_attachment do |name, attachment|
306 attachment.send(:queue_existing_for_delete)
307 attachment.send(:flush_deletes)
308 end
309 end
310 end
311
312end
313
314# Set it all up.
315if Object.const_defined?("ActiveRecord")
316 ActiveRecord::Base.send(:include, Paperclip)
317 File.send(:include, Paperclip::Upfile)
318end
diff --git a/vendor/plugins/paperclip/lib/paperclip/attachment.rb b/vendor/plugins/paperclip/lib/paperclip/attachment.rb
deleted file mode 100644
index 897c67e..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/attachment.rb
+++ /dev/null
@@ -1,403 +0,0 @@
1module Paperclip
2 # The Attachment class manages the files for a given attachment. It saves
3 # when the model saves, deletes when the model is destroyed, and processes
4 # the file upon assignment.
5 class Attachment
6
7 def self.default_options
8 @default_options ||= {
9 :url => "/system/:attachment/:id/:style/:basename.:extension",
10 :path => ":rails_root/public/system/:attachment/:id/:style/:basename.:extension",
11 :styles => {},
12 :default_url => "/:attachment/:style/missing.png",
13 :default_style => :original,
14 :validations => {},
15 :storage => :filesystem
16 }
17 end
18
19 attr_reader :name, :instance, :styles, :default_style, :convert_options, :queued_for_write
20
21 # Creates an Attachment object. +name+ is the name of the attachment,
22 # +instance+ is the ActiveRecord object instance it's attached to, and
23 # +options+ is the same as the hash passed to +has_attached_file+.
24 def initialize name, instance, options = {}
25 @name = name
26 @instance = instance
27
28 options = self.class.default_options.merge(options)
29
30 @url = options[:url]
31 @url = @url.call(self) if @url.is_a?(Proc)
32 @path = options[:path]
33 @path = @path.call(self) if @path.is_a?(Proc)
34 @styles = options[:styles]
35 @styles = @styles.call(self) if @styles.is_a?(Proc)
36 @default_url = options[:default_url]
37 @validations = options[:validations]
38 @default_style = options[:default_style]
39 @storage = options[:storage]
40 @whiny = options[:whiny_thumbnails]
41 @convert_options = options[:convert_options] || {}
42 @processors = options[:processors] || [:thumbnail]
43 @options = options
44 @queued_for_delete = []
45 @queued_for_write = {}
46 @errors = {}
47 @validation_errors = nil
48 @dirty = false
49
50 normalize_style_definition
51 initialize_storage
52 end
53
54 # What gets called when you call instance.attachment = File. It clears
55 # errors, assigns attributes, processes the file, and runs validations. It
56 # also queues up the previous file for deletion, to be flushed away on
57 # #save of its host. In addition to form uploads, you can also assign
58 # another Paperclip attachment:
59 # new_user.avatar = old_user.avatar
60 # If the file that is assigned is not valid, the processing (i.e.
61 # thumbnailing, etc) will NOT be run.
62 def assign uploaded_file
63 %w(file_name).each do |field|
64 unless @instance.class.column_names.include?("#{name}_#{field}")
65 raise PaperclipError.new("#{@instance.class} model does not have required column '#{name}_#{field}'")
66 end
67 end
68
69 if uploaded_file.is_a?(Paperclip::Attachment)
70 uploaded_file = uploaded_file.to_file(:original)
71 close_uploaded_file = uploaded_file.respond_to?(:close)
72 end
73
74 return nil unless valid_assignment?(uploaded_file)
75
76 uploaded_file.binmode if uploaded_file.respond_to? :binmode
77 self.clear
78
79 return nil if uploaded_file.nil?
80
81 @queued_for_write[:original] = uploaded_file.to_tempfile
82 instance_write(:file_name, uploaded_file.original_filename.strip.gsub(/[^\w\d\.\-]+/, '_'))
83 instance_write(:content_type, uploaded_file.content_type.to_s.strip)
84 instance_write(:file_size, uploaded_file.size.to_i)
85 instance_write(:updated_at, Time.now)
86
87 @dirty = true
88
89 post_process if valid?
90
91 # Reset the file size if the original file was reprocessed.
92 instance_write(:file_size, @queued_for_write[:original].size.to_i)
93 ensure
94 uploaded_file.close if close_uploaded_file
95 validate
96 end
97
98 # Returns the public URL of the attachment, with a given style. Note that
99 # this does not necessarily need to point to a file that your web server
100 # can access and can point to an action in your app, if you need fine
101 # grained security. This is not recommended if you don't need the
102 # security, however, for performance reasons. set
103 # include_updated_timestamp to false if you want to stop the attachment
104 # update time appended to the url
105 def url style = default_style, include_updated_timestamp = true
106 url = original_filename.nil? ? interpolate(@default_url, style) : interpolate(@url, style)
107 include_updated_timestamp && updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url
108 end
109
110 # Returns the path of the attachment as defined by the :path option. If the
111 # file is stored in the filesystem the path refers to the path of the file
112 # on disk. If the file is stored in S3, the path is the "key" part of the
113 # URL, and the :bucket option refers to the S3 bucket.
114 def path style = nil #:nodoc:
115 original_filename.nil? ? nil : interpolate(@path, style)
116 end
117
118 # Alias to +url+
119 def to_s style = nil
120 url(style)
121 end
122
123 # Returns true if there are no errors on this attachment.
124 def valid?
125 validate
126 errors.empty?
127 end
128
129 # Returns an array containing the errors on this attachment.
130 def errors
131 @errors
132 end
133
134 # Returns true if there are changes that need to be saved.
135 def dirty?
136 @dirty
137 end
138
139 # Saves the file, if there are no errors. If there are, it flushes them to
140 # the instance's errors and returns false, cancelling the save.
141 def save
142 if valid?
143 flush_deletes
144 flush_writes
145 @dirty = false
146 true
147 else
148 flush_errors
149 false
150 end
151 end
152
153 # Clears out the attachment. Has the same effect as previously assigning
154 # nil to the attachment. Does NOT save. If you wish to clear AND save,
155 # use #destroy.
156 def clear
157 queue_existing_for_delete
158 @errors = {}
159 @validation_errors = nil
160 end
161
162 # Destroys the attachment. Has the same effect as previously assigning
163 # nil to the attachment *and saving*. This is permanent. If you wish to
164 # wipe out the existing attachment but not save, use #clear.
165 def destroy
166 clear
167 save
168 end
169
170 # Returns the name of the file as originally assigned, and lives in the
171 # <attachment>_file_name attribute of the model.
172 def original_filename
173 instance_read(:file_name)
174 end
175
176 # Returns the size of the file as originally assigned, and lives in the
177 # <attachment>_file_size attribute of the model.
178 def size
179 instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size)
180 end
181
182 # Returns the content_type of the file as originally assigned, and lives
183 # in the <attachment>_content_type attribute of the model.
184 def content_type
185 instance_read(:content_type)
186 end
187
188 # Returns the last modified time of the file as originally assigned, and
189 # lives in the <attachment>_updated_at attribute of the model.
190 def updated_at
191 time = instance_read(:updated_at)
192 time && time.to_i
193 end
194
195 # A hash of procs that are run during the interpolation of a path or url.
196 # A variable of the format :name will be replaced with the return value of
197 # the proc named ":name". Each lambda takes the attachment and the current
198 # style as arguments. This hash can be added to with your own proc if
199 # necessary.
200 def self.interpolations
201 @interpolations ||= {
202 :rails_root => lambda{|attachment,style| RAILS_ROOT },
203 :rails_env => lambda{|attachment,style| RAILS_ENV },
204 :class => lambda do |attachment,style|
205 attachment.instance.class.name.underscore.pluralize
206 end,
207 :basename => lambda do |attachment,style|
208 attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, "")
209 end,
210 :extension => lambda do |attachment,style|
211 ((style = attachment.styles[style]) && style[:format]) ||
212 File.extname(attachment.original_filename).gsub(/^\.+/, "")
213 end,
214 :id => lambda{|attachment,style| attachment.instance.id },
215 :id_partition => lambda do |attachment, style|
216 ("%09d" % attachment.instance.id).scan(/\d{3}/).join("/")
217 end,
218 :attachment => lambda{|attachment,style| attachment.name.to_s.downcase.pluralize },
219 :style => lambda{|attachment,style| style || attachment.default_style },
220 }
221 end
222
223 # This method really shouldn't be called that often. It's expected use is
224 # in the paperclip:refresh rake task and that's it. It will regenerate all
225 # thumbnails forcefully, by reobtaining the original file and going through
226 # the post-process again.
227 def reprocess!
228 new_original = Tempfile.new("paperclip-reprocess")
229 new_original.binmode
230 if old_original = to_file(:original)
231 new_original.write( old_original.read )
232 new_original.rewind
233
234 @queued_for_write = { :original => new_original }
235 post_process
236
237 old_original.close if old_original.respond_to?(:close)
238
239 save
240 else
241 true
242 end
243 end
244
245 # Returns true if a file has been assigned.
246 def file?
247 !original_filename.blank?
248 end
249
250 # Writes the attachment-specific attribute on the instance. For example,
251 # instance_write(:file_name, "me.jpg") will write "me.jpg" to the instance's
252 # "avatar_file_name" field (assuming the attachment is called avatar).
253 def instance_write(attr, value)
254 setter = :"#{name}_#{attr}="
255 responds = instance.respond_to?(setter)
256 self.instance_variable_set("@_#{setter.to_s.chop}", value)
257 instance.send(setter, value) if responds || attr.to_s == "file_name"
258 end
259
260 # Reads the attachment-specific attribute on the instance. See instance_write
261 # for more details.
262 def instance_read(attr)
263 getter = :"#{name}_#{attr}"
264 responds = instance.respond_to?(getter)
265 cached = self.instance_variable_get("@_#{getter}")
266 return cached if cached
267 instance.send(getter) if responds || attr.to_s == "file_name"
268 end
269
270 private
271
272 def logger #:nodoc:
273 instance.logger
274 end
275
276 def log message #:nodoc:
277 logger.info("[paperclip] #{message}") if logging?
278 end
279
280 def logging? #:nodoc:
281 Paperclip.options[:log]
282 end
283
284 def valid_assignment? file #:nodoc:
285 file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
286 end
287
288 def validate #:nodoc:
289 unless @validation_errors
290 @validation_errors = @validations.inject({}) do |errors, validation|
291 name, block = validation
292 errors[name] = block.call(self, instance) if block
293 errors
294 end
295 @validation_errors.reject!{|k,v| v == nil }
296 @errors.merge!(@validation_errors)
297 end
298 @validation_errors
299 end
300
301 def normalize_style_definition #:nodoc:
302 @styles.each do |name, args|
303 unless args.is_a? Hash
304 dimensions, format = *args
305 @styles[name] = {
306 :processors => @processors,
307 :geometry => dimensions,
308 :format => format,
309 :whiny => @whiny,
310 :convert_options => extra_options_for(name)
311 }
312 else
313 @styles[name] = {
314 :processors => @processors,
315 :whiny => @whiny,
316 :convert_options => extra_options_for(name)
317 }.merge(@styles[name])
318 end
319 end
320 end
321
322 def solidify_style_definitions #:nodoc:
323 @styles.each do |name, args|
324 @styles[name][:geometry] = @styles[name][:geometry].call(instance) if @styles[name][:geometry].respond_to?(:call)
325 @styles[name][:processors] = @styles[name][:processors].call(instance) if @styles[name][:processors].respond_to?(:call)
326 end
327 end
328
329 def initialize_storage #:nodoc:
330 @storage_module = Paperclip::Storage.const_get(@storage.to_s.capitalize)
331 self.extend(@storage_module)
332 end
333
334 def extra_options_for(style) #:nodoc:
335 all_options = convert_options[:all]
336 all_options = all_options.call(instance) if all_options.respond_to?(:call)
337 style_options = convert_options[style]
338 style_options = style_options.call(instance) if style_options.respond_to?(:call)
339
340 [ style_options, all_options ].compact.join(" ")
341 end
342
343 def post_process #:nodoc:
344 return if @queued_for_write[:original].nil?
345 solidify_style_definitions
346 return if fire_events(:before)
347 post_process_styles
348 return if fire_events(:after)
349 end
350
351 def fire_events(which)
352 return true if callback(:"#{which}_post_process") == false
353 return true if callback(:"#{which}_#{name}_post_process") == false
354 end
355
356 def callback which #:nodoc:
357 instance.run_callbacks(which, @queued_for_write){|result, obj| result == false }
358 end
359
360 def post_process_styles
361 @styles.each do |name, args|
362 begin
363 raise RuntimeError.new("Style #{name} has no processors defined.") if args[:processors].blank?
364 @queued_for_write[name] = args[:processors].inject(@queued_for_write[:original]) do |file, processor|
365 Paperclip.processor(processor).make(file, args, self)
366 end
367 rescue PaperclipError => e
368 log("An error was received while processing: #{e.inspect}")
369 (@errors[:processing] ||= []) << e.message if @whiny
370 end
371 end
372 end
373
374 def interpolate pattern, style = default_style #:nodoc:
375 interpolations = self.class.interpolations.sort{|a,b| a.first.to_s <=> b.first.to_s }
376 interpolations.reverse.inject( pattern.dup ) do |result, interpolation|
377 tag, blk = interpolation
378 result.gsub(/:#{tag}/) do |match|
379 blk.call( self, style )
380 end
381 end
382 end
383
384 def queue_existing_for_delete #:nodoc:
385 return unless file?
386 @queued_for_delete += [:original, *@styles.keys].uniq.map do |style|
387 path(style) if exists?(style)
388 end.compact
389 instance_write(:file_name, nil)
390 instance_write(:content_type, nil)
391 instance_write(:file_size, nil)
392 instance_write(:updated_at, nil)
393 end
394
395 def flush_errors #:nodoc:
396 @errors.each do |error, message|
397 [message].flatten.each {|m| instance.errors.add(name, m) }
398 end
399 end
400
401 end
402end
403
diff --git a/vendor/plugins/paperclip/lib/paperclip/callback_compatability.rb b/vendor/plugins/paperclip/lib/paperclip/callback_compatability.rb
deleted file mode 100644
index 10b08fc..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/callback_compatability.rb
+++ /dev/null
@@ -1,33 +0,0 @@
1module Paperclip
2 # This module is intended as a compatability shim for the differences in
3 # callbacks between Rails 2.0 and Rails 2.1.
4 module CallbackCompatability
5 def self.included(base)
6 base.extend(ClassMethods)
7 base.send(:include, InstanceMethods)
8 end
9
10 module ClassMethods
11 # The implementation of this method is taken from the Rails 1.2.6 source,
12 # from rails/activerecord/lib/active_record/callbacks.rb, line 192.
13 def define_callbacks(*args)
14 args.each do |method|
15 self.class_eval <<-"end_eval"
16 def self.#{method}(*callbacks, &block)
17 callbacks << block if block_given?
18 write_inheritable_array(#{method.to_sym.inspect}, callbacks)
19 end
20 end_eval
21 end
22 end
23 end
24
25 module InstanceMethods
26 # The callbacks in < 2.1 don't worry about the extra options or the
27 # block, so just run what we have available.
28 def run_callbacks(meth, opts = nil, &blk)
29 callback(meth)
30 end
31 end
32 end
33end
diff --git a/vendor/plugins/paperclip/lib/paperclip/geometry.rb b/vendor/plugins/paperclip/lib/paperclip/geometry.rb
deleted file mode 100644
index 7fbe038..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/geometry.rb
+++ /dev/null
@@ -1,115 +0,0 @@
1module Paperclip
2
3 # Defines the geometry of an image.
4 class Geometry
5 attr_accessor :height, :width, :modifier
6
7 # Gives a Geometry representing the given height and width
8 def initialize width = nil, height = nil, modifier = nil
9 @height = height.to_f
10 @width = width.to_f
11 @modifier = modifier
12 end
13
14 # Uses ImageMagick to determing the dimensions of a file, passed in as either a
15 # File or path.
16 def self.from_file file
17 file = file.path if file.respond_to? "path"
18 geometry = begin
19 Paperclip.run("identify", %Q[-format "%wx%h" "#{file}"[0]])
20 rescue PaperclipCommandLineError
21 ""
22 end
23 parse(geometry) ||
24 raise(NotIdentifiedByImageMagickError.new("#{file} is not recognized by the 'identify' command."))
25 end
26
27 # Parses a "WxH" formatted string, where W is the width and H is the height.
28 def self.parse string
29 if match = (string && string.match(/\b(\d*)x?(\d*)\b([\>\<\#\@\%^!])?/))
30 Geometry.new(*match[1,3])
31 end
32 end
33
34 # True if the dimensions represent a square
35 def square?
36 height == width
37 end
38
39 # True if the dimensions represent a horizontal rectangle
40 def horizontal?
41 height < width
42 end
43
44 # True if the dimensions represent a vertical rectangle
45 def vertical?
46 height > width
47 end
48
49 # The aspect ratio of the dimensions.
50 def aspect
51 width / height
52 end
53
54 # Returns the larger of the two dimensions
55 def larger
56 [height, width].max
57 end
58
59 # Returns the smaller of the two dimensions
60 def smaller
61 [height, width].min
62 end
63
64 # Returns the width and height in a format suitable to be passed to Geometry.parse
65 def to_s
66 s = ""
67 s << width.to_i.to_s if width > 0
68 s << "x#{height.to_i}" if height > 0
69 s << modifier.to_s
70 s
71 end
72
73 # Same as to_s
74 def inspect
75 to_s
76 end
77
78 # Returns the scaling and cropping geometries (in string-based ImageMagick format)
79 # neccessary to transform this Geometry into the Geometry given. If crop is true,
80 # then it is assumed the destination Geometry will be the exact final resolution.
81 # In this case, the source Geometry is scaled so that an image containing the
82 # destination Geometry would be completely filled by the source image, and any
83 # overhanging image would be cropped. Useful for square thumbnail images. The cropping
84 # is weighted at the center of the Geometry.
85 def transformation_to dst, crop = false
86 if crop
87 ratio = Geometry.new( dst.width / self.width, dst.height / self.height )
88 scale_geometry, scale = scaling(dst, ratio)
89 crop_geometry = cropping(dst, ratio, scale)
90 else
91 scale_geometry = dst.to_s
92 end
93
94 [ scale_geometry, crop_geometry ]
95 end
96
97 private
98
99 def scaling dst, ratio
100 if ratio.horizontal? || ratio.square?
101 [ "%dx" % dst.width, ratio.width ]
102 else
103 [ "x%d" % dst.height, ratio.height ]
104 end
105 end
106
107 def cropping dst, ratio, scale
108 if ratio.horizontal? || ratio.square?
109 "%dx%d+%d+%d" % [ dst.width, dst.height, 0, (self.height * scale - dst.height) / 2 ]
110 else
111 "%dx%d+%d+%d" % [ dst.width, dst.height, (self.width * scale - dst.width) / 2, 0 ]
112 end
113 end
114 end
115end
diff --git a/vendor/plugins/paperclip/lib/paperclip/iostream.rb b/vendor/plugins/paperclip/lib/paperclip/iostream.rb
deleted file mode 100644
index 74e2014..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/iostream.rb
+++ /dev/null
@@ -1,58 +0,0 @@
1# Provides method that can be included on File-type objects (IO, StringIO, Tempfile, etc) to allow stream copying
2# and Tempfile conversion.
3module IOStream
4
5 # Returns a Tempfile containing the contents of the readable object.
6 def to_tempfile
7 tempfile = Tempfile.new("stream")
8 tempfile.binmode
9 self.stream_to(tempfile)
10 end
11
12 # Copies one read-able object from one place to another in blocks, obviating the need to load
13 # the whole thing into memory. Defaults to 8k blocks. If this module is included in both
14 # StringIO and Tempfile, then either can have its data copied anywhere else without typing
15 # worries or memory overhead worries. Returns a File if a String is passed in as the destination
16 # and returns the IO or Tempfile as passed in if one is sent as the destination.
17 def stream_to path_or_file, in_blocks_of = 8192
18 dstio = case path_or_file
19 when String then File.new(path_or_file, "wb+")
20 when IO then path_or_file
21 when Tempfile then path_or_file
22 end
23 buffer = ""
24 self.rewind
25 while self.read(in_blocks_of, buffer) do
26 dstio.write(buffer)
27 end
28 dstio.rewind
29 dstio
30 end
31end
32
33class IO #:nodoc:
34 include IOStream
35end
36
37%w( Tempfile StringIO ).each do |klass|
38 if Object.const_defined? klass
39 Object.const_get(klass).class_eval do
40 include IOStream
41 end
42 end
43end
44
45# Corrects a bug in Windows when asking for Tempfile size.
46if defined? Tempfile
47 class Tempfile
48 def size
49 if @tmpfile
50 @tmpfile.fsync
51 @tmpfile.flush
52 @tmpfile.stat.size
53 else
54 0
55 end
56 end
57 end
58end
diff --git a/vendor/plugins/paperclip/lib/paperclip/matchers.rb b/vendor/plugins/paperclip/lib/paperclip/matchers.rb
deleted file mode 100644
index ca24b5e..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/matchers.rb
+++ /dev/null
@@ -1,4 +0,0 @@
1require 'paperclip/matchers/have_attached_file_matcher'
2require 'paperclip/matchers/validate_attachment_presence_matcher'
3require 'paperclip/matchers/validate_attachment_content_type_matcher'
4require 'paperclip/matchers/validate_attachment_size_matcher'
diff --git a/vendor/plugins/paperclip/lib/paperclip/matchers/have_attached_file_matcher.rb b/vendor/plugins/paperclip/lib/paperclip/matchers/have_attached_file_matcher.rb
deleted file mode 100644
index da0dd8b..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/matchers/have_attached_file_matcher.rb
+++ /dev/null
@@ -1,49 +0,0 @@
1module Paperclip
2 module Shoulda
3 module Matchers
4 def have_attached_file name
5 HaveAttachedFileMatcher.new(name)
6 end
7
8 class HaveAttachedFileMatcher
9 def initialize attachment_name
10 @attachment_name = attachment_name
11 end
12
13 def matches? subject
14 @subject = subject
15 responds? && has_column? && included?
16 end
17
18 def failure_message
19 "Should have an attachment named #{@attachment_name}"
20 end
21
22 def negative_failure_message
23 "Should not have an attachment named #{@attachment_name}"
24 end
25
26 def description
27 "have an attachment named #{@attachment_name}"
28 end
29
30 protected
31
32 def responds?
33 methods = @subject.instance_methods
34 methods.include?("#{@attachment_name}") &&
35 methods.include?("#{@attachment_name}=") &&
36 methods.include?("#{@attachment_name}?")
37 end
38
39 def has_column?
40 @subject.column_names.include?("#{@attachment_name}_file_name")
41 end
42
43 def included?
44 @subject.ancestors.include?(Paperclip::InstanceMethods)
45 end
46 end
47 end
48 end
49end
diff --git a/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb b/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb
deleted file mode 100644
index b4e97fd..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb
+++ /dev/null
@@ -1,66 +0,0 @@
1module Paperclip
2 module Shoulda
3 module Matchers
4 def validate_attachment_content_type name
5 ValidateAttachmentContentTypeMatcher.new(name)
6 end
7
8 class ValidateAttachmentContentTypeMatcher
9 def initialize attachment_name
10 @attachment_name = attachment_name
11 end
12
13 def allowing *types
14 @allowed_types = types.flatten
15 self
16 end
17
18 def rejecting *types
19 @rejected_types = types.flatten
20 self
21 end
22
23 def matches? subject
24 @subject = subject
25 @allowed_types && @rejected_types &&
26 allowed_types_allowed? && rejected_types_rejected?
27 end
28
29 def failure_message
30 "Content types #{@allowed_types.join(", ")} should be accepted" +
31 " and #{@rejected_types.join(", ")} rejected by #{@attachment_name}"
32 end
33
34 def negative_failure_message
35 "Content types #{@allowed_types.join(", ")} should be rejected" +
36 " and #{@rejected_types.join(", ")} accepted by #{@attachment_name}"
37 end
38
39 def description
40 "validate the content types allowed on attachment #{@attachment_name}"
41 end
42
43 protected
44
45 def allow_types?(types)
46 types.all? do |type|
47 file = StringIO.new(".")
48 file.content_type = type
49 attachment = @subject.new.attachment_for(@attachment_name)
50 attachment.assign(file)
51 attachment.errors[:content_type].nil?
52 end
53 end
54
55 def allowed_types_allowed?
56 allow_types?(@allowed_types)
57 end
58
59 def rejected_types_rejected?
60 not allow_types?(@rejected_types)
61 end
62 end
63 end
64 end
65end
66
diff --git a/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_presence_matcher.rb b/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_presence_matcher.rb
deleted file mode 100644
index 61dc0ea..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_presence_matcher.rb
+++ /dev/null
@@ -1,48 +0,0 @@
1module Paperclip
2 module Shoulda
3 module Matchers
4 def validate_attachment_presence name
5 ValidateAttachmentPresenceMatcher.new(name)
6 end
7
8 class ValidateAttachmentPresenceMatcher
9 def initialize attachment_name
10 @attachment_name = attachment_name
11 end
12
13 def matches? subject
14 @subject = subject
15 error_when_not_valid? && no_error_when_valid?
16 end
17
18 def failure_message
19 "Attachment #{@attachment_name} should be required"
20 end
21
22 def negative_failure_message
23 "Attachment #{@attachment_name} should not be required"
24 end
25
26 def description
27 "require presence of attachment #{@attachment_name}"
28 end
29
30 protected
31
32 def error_when_not_valid?
33 @attachment = @subject.new.send(@attachment_name)
34 @attachment.assign(nil)
35 not @attachment.errors[:presence].nil?
36 end
37
38 def no_error_when_valid?
39 @file = StringIO.new(".")
40 @attachment = @subject.new.send(@attachment_name)
41 @attachment.assign(@file)
42 @attachment.errors[:presence].nil?
43 end
44 end
45 end
46 end
47end
48
diff --git a/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_size_matcher.rb b/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_size_matcher.rb
deleted file mode 100644
index f84c479..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/matchers/validate_attachment_size_matcher.rb
+++ /dev/null
@@ -1,83 +0,0 @@
1module Paperclip
2 module Shoulda
3 module Matchers
4 def validate_attachment_size name
5 ValidateAttachmentSizeMatcher.new(name)
6 end
7
8 class ValidateAttachmentSizeMatcher
9 def initialize attachment_name
10 @attachment_name = attachment_name
11 @low, @high = 0, (1.0/0)
12 end
13
14 def less_than size
15 @high = size
16 self
17 end
18
19 def greater_than size
20 @low = size
21 self
22 end
23
24 def in range
25 @low, @high = range.first, range.last
26 self
27 end
28
29 def matches? subject
30 @subject = subject
31 lower_than_low? && higher_than_low? && lower_than_high? && higher_than_high?
32 end
33
34 def failure_message
35 "Attachment #{@attachment_name} must be between #{@low} and #{@high} bytes"
36 end
37
38 def negative_failure_message
39 "Attachment #{@attachment_name} cannot be between #{@low} and #{@high} bytes"
40 end
41
42 def description
43 "validate the size of attachment #{@attachment_name}"
44 end
45
46 protected
47
48 def override_method object, method, &replacement
49 (class << object; self; end).class_eval do
50 define_method(method, &replacement)
51 end
52 end
53
54 def passes_validation_with_size(new_size)
55 file = StringIO.new(".")
56 override_method(file, :size){ new_size }
57 attachment = @subject.new.attachment_for(@attachment_name)
58 attachment.assign(file)
59 attachment.errors[:size].nil?
60 end
61
62 def lower_than_low?
63 not passes_validation_with_size(@low - 1)
64 end
65
66 def higher_than_low?
67 passes_validation_with_size(@low + 1)
68 end
69
70 def lower_than_high?
71 return true if @high == (1.0/0)
72 passes_validation_with_size(@high - 1)
73 end
74
75 def higher_than_high?
76 return true if @high == (1.0/0)
77 not passes_validation_with_size(@high + 1)
78 end
79 end
80 end
81 end
82end
83
diff --git a/vendor/plugins/paperclip/lib/paperclip/processor.rb b/vendor/plugins/paperclip/lib/paperclip/processor.rb
deleted file mode 100644
index 9082bfd..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/processor.rb
+++ /dev/null
@@ -1,48 +0,0 @@
1module Paperclip
2 # Paperclip processors allow you to modify attached files when they are
3 # attached in any way you are able. Paperclip itself uses command-line
4 # programs for its included Thumbnail processor, but custom processors
5 # are not required to follow suit.
6 #
7 # Processors are required to be defined inside the Paperclip module and
8 # are also required to be a subclass of Paperclip::Processor. There are
9 # only two methods you must implement to properly be a subclass:
10 # #initialize and #make. Initialize's arguments are the file that will
11 # be operated on (which is an instance of File), and a hash of options
12 # that were defined in has_attached_file's style hash.
13 #
14 # All #make needs to do is return an instance of File (Tempfile is
15 # acceptable) which contains the results of the processing.
16 #
17 # See Paperclip.run for more information about using command-line
18 # utilities from within Processors.
19 class Processor
20 attr_accessor :file, :options, :attachment
21
22 def initialize file, options = {}, attachment = nil
23 @file = file
24 @options = options
25 @attachment = attachment
26 end
27
28 def make
29 end
30
31 def self.make file, options = {}, attachment = nil
32 new(file, options, attachment).make
33 end
34 end
35
36 # Due to how ImageMagick handles its image format conversion and how Tempfile
37 # handles its naming scheme, it is necessary to override how Tempfile makes
38 # its names so as to allow for file extensions. Idea taken from the comments
39 # on this blog post:
40 # http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
41 class Tempfile < ::Tempfile
42 # Replaces Tempfile's +make_tmpname+ with one that honors file extensions.
43 def make_tmpname(basename, n)
44 extension = File.extname(basename)
45 sprintf("%s,%d,%d%s", File.basename(basename, extension), $$, n, extension)
46 end
47 end
48end
diff --git a/vendor/plugins/paperclip/lib/paperclip/storage.rb b/vendor/plugins/paperclip/lib/paperclip/storage.rb
deleted file mode 100644
index 58913ad..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/storage.rb
+++ /dev/null
@@ -1,236 +0,0 @@
1module Paperclip
2 module Storage
3
4 # The default place to store attachments is in the filesystem. Files on the local
5 # filesystem can be very easily served by Apache without requiring a hit to your app.
6 # They also can be processed more easily after they've been saved, as they're just
7 # normal files. There is one Filesystem-specific option for has_attached_file.
8 # * +path+: The location of the repository of attachments on disk. This can (and, in
9 # almost all cases, should) be coordinated with the value of the +url+ option to
10 # allow files to be saved into a place where Apache can serve them without
11 # hitting your app. Defaults to
12 # ":rails_root/public/:attachment/:id/:style/:basename.:extension"
13 # By default this places the files in the app's public directory which can be served
14 # directly. If you are using capistrano for deployment, a good idea would be to
15 # make a symlink to the capistrano-created system directory from inside your app's
16 # public directory.
17 # See Paperclip::Attachment#interpolate for more information on variable interpolaton.
18 # :path => "/var/app/attachments/:class/:id/:style/:basename.:extension"
19 module Filesystem
20 def self.extended base
21 end
22
23 def exists?(style = default_style)
24 if original_filename
25 File.exist?(path(style))
26 else
27 false
28 end
29 end
30
31 # Returns representation of the data of the file assigned to the given
32 # style, in the format most representative of the current storage.
33 def to_file style = default_style
34 @queued_for_write[style] || (File.new(path(style), 'rb') if exists?(style))
35 end
36 alias_method :to_io, :to_file
37
38 def flush_writes #:nodoc:
39 @queued_for_write.each do |style, file|
40 file.close
41 FileUtils.mkdir_p(File.dirname(path(style)))
42 logger.info("[paperclip] saving #{path(style)}")
43 FileUtils.mv(file.path, path(style))
44 FileUtils.chmod(0644, path(style))
45 end
46 @queued_for_write = {}
47 end
48
49 def flush_deletes #:nodoc:
50 @queued_for_delete.each do |path|
51 begin
52 logger.info("[paperclip] deleting #{path}")
53 FileUtils.rm(path) if File.exist?(path)
54 rescue Errno::ENOENT => e
55 # ignore file-not-found, let everything else pass
56 end
57 begin
58 while(true)
59 path = File.dirname(path)
60 FileUtils.rmdir(path)
61 end
62 rescue Errno::EEXIST, Errno::ENOTEMPTY, Errno::ENOENT, Errno::EINVAL, Errno::ENOTDIR
63 # Stop trying to remove parent directories
64 rescue SystemCallError => e
65 logger.info("[paperclip] There was an unexpected error while deleting directories: #{e.class}")
66 # Ignore it
67 end
68 end
69 @queued_for_delete = []
70 end
71 end
72
73 # Amazon's S3 file hosting service is a scalable, easy place to store files for
74 # distribution. You can find out more about it at http://aws.amazon.com/s3
75 # There are a few S3-specific options for has_attached_file:
76 # * +s3_credentials+: Takes a path, a File, or a Hash. The path (or File) must point
77 # to a YAML file containing the +access_key_id+ and +secret_access_key+ that Amazon
78 # gives you. You can 'environment-space' this just like you do to your
79 # database.yml file, so different environments can use different accounts:
80 # development:
81 # access_key_id: 123...
82 # secret_access_key: 123...
83 # test:
84 # access_key_id: abc...
85 # secret_access_key: abc...
86 # production:
87 # access_key_id: 456...
88 # secret_access_key: 456...
89 # This is not required, however, and the file may simply look like this:
90 # access_key_id: 456...
91 # secret_access_key: 456...
92 # In which case, those access keys will be used in all environments. You can also
93 # put your bucket name in this file, instead of adding it to the code directly.
94 # This is useful when you want the same account but a different bucket for
95 # development versus production.
96 # * +s3_permissions+: This is a String that should be one of the "canned" access
97 # policies that S3 provides (more information can be found here:
98 # http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html#RESTCannedAccessPolicies)
99 # The default for Paperclip is "public-read".
100 # * +s3_protocol+: The protocol for the URLs generated to your S3 assets. Can be either
101 # 'http' or 'https'. Defaults to 'http' when your :s3_permissions are 'public-read' (the
102 # default), and 'https' when your :s3_permissions are anything else.
103 # * +s3_headers+: A hash of headers such as {'Expires' => 1.year.from_now.httpdate}
104 # * +bucket+: This is the name of the S3 bucket that will store your files. Remember
105 # that the bucket must be unique across all of Amazon S3. If the bucket does not exist
106 # Paperclip will attempt to create it. The bucket name will not be interpolated.
107 # You can define the bucket as a Proc if you want to determine it's name at runtime.
108 # Paperclip will call that Proc with attachment as the only argument.
109 # * +s3_host_alias+: The fully-qualified domain name (FQDN) that is the alias to the
110 # S3 domain of your bucket. Used with the :s3_alias_url url interpolation. See the
111 # link in the +url+ entry for more information about S3 domains and buckets.
112 # * +url+: There are three options for the S3 url. You can choose to have the bucket's name
113 # placed domain-style (bucket.s3.amazonaws.com) or path-style (s3.amazonaws.com/bucket).
114 # Lastly, you can specify a CNAME (which requires the CNAME to be specified as
115 # :s3_alias_url. You can read more about CNAMEs and S3 at
116 # http://docs.amazonwebservices.com/AmazonS3/latest/index.html?VirtualHosting.html
117 # Normally, this won't matter in the slightest and you can leave the default (which is
118 # path-style, or :s3_path_url). But in some cases paths don't work and you need to use
119 # the domain-style (:s3_domain_url). Anything else here will be treated like path-style.
120 # NOTE: If you use a CNAME for use with CloudFront, you can NOT specify https as your
121 # :s3_protocol; This is *not supported* by S3/CloudFront. Finally, when using the host
122 # alias, the :bucket parameter is ignored, as the hostname is used as the bucket name
123 # by S3.
124 # * +path+: This is the key under the bucket in which the file will be stored. The
125 # URL will be constructed from the bucket and the path. This is what you will want
126 # to interpolate. Keys should be unique, like filenames, and despite the fact that
127 # S3 (strictly speaking) does not support directories, you can still use a / to
128 # separate parts of your file name.
129 module S3
130 def self.extended base
131 require 'right_aws'
132 base.instance_eval do
133 @s3_credentials = parse_credentials(@options[:s3_credentials])
134 @bucket = @options[:bucket] || @s3_credentials[:bucket]
135 @bucket = @bucket.call(self) if @bucket.is_a?(Proc)
136 @s3_options = @options[:s3_options] || {}
137 @s3_permissions = @options[:s3_permissions] || 'public-read'
138 @s3_protocol = @options[:s3_protocol] || (@s3_permissions == 'public-read' ? 'http' : 'https')
139 @s3_headers = @options[:s3_headers] || {}
140 @s3_host_alias = @options[:s3_host_alias]
141 @url = ":s3_path_url" unless @url.to_s.match(/^:s3.*url$/)
142 end
143 base.class.interpolations[:s3_alias_url] = lambda do |attachment, style|
144 "#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, "")}"
145 end
146 base.class.interpolations[:s3_path_url] = lambda do |attachment, style|
147 "#{attachment.s3_protocol}://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
148 end
149 base.class.interpolations[:s3_domain_url] = lambda do |attachment, style|
150 "#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, "")}"
151 end
152 end
153
154 def s3
155 @s3 ||= RightAws::S3.new(@s3_credentials[:access_key_id],
156 @s3_credentials[:secret_access_key],
157 @s3_options)
158 end
159
160 def s3_bucket
161 @s3_bucket ||= s3.bucket(@bucket, true, @s3_permissions)
162 end
163
164 def bucket_name
165 @bucket
166 end
167
168 def s3_host_alias
169 @s3_host_alias
170 end
171
172 def parse_credentials creds
173 creds = find_credentials(creds).stringify_keys
174 (creds[ENV['RAILS_ENV']] || creds).symbolize_keys
175 end
176
177 def exists?(style = default_style)
178 s3_bucket.key(path(style)) ? true : false
179 end
180
181 def s3_protocol
182 @s3_protocol
183 end
184
185 # Returns representation of the data of the file assigned to the given
186 # style, in the format most representative of the current storage.
187 def to_file style = default_style
188 @queued_for_write[style] || s3_bucket.key(path(style))
189 end
190 alias_method :to_io, :to_file
191
192 def flush_writes #:nodoc:
193 @queued_for_write.each do |style, file|
194 begin
195 logger.info("[paperclip] saving #{path(style)}")
196 key = s3_bucket.key(path(style))
197 key.data = file
198 key.put(nil, @s3_permissions, {'Content-type' => instance_read(:content_type)}.merge(@s3_headers))
199 rescue RightAws::AwsError => e
200 raise
201 end
202 end
203 @queued_for_write = {}
204 end
205
206 def flush_deletes #:nodoc:
207 @queued_for_delete.each do |path|
208 begin
209 logger.info("[paperclip] deleting #{path}")
210 if file = s3_bucket.key(path)
211 file.delete
212 end
213 rescue RightAws::AwsError
214 # Ignore this.
215 end
216 end
217 @queued_for_delete = []
218 end
219
220 def find_credentials creds
221 case creds
222 when File
223 YAML.load_file(creds.path)
224 when String
225 YAML.load_file(creds)
226 when Hash
227 creds
228 else
229 raise ArgumentError, "Credentials are not a path, file, or hash."
230 end
231 end
232 private :find_credentials
233
234 end
235 end
236end
diff --git a/vendor/plugins/paperclip/lib/paperclip/thumbnail.rb b/vendor/plugins/paperclip/lib/paperclip/thumbnail.rb
deleted file mode 100644
index 2178a9c..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/thumbnail.rb
+++ /dev/null
@@ -1,70 +0,0 @@
1module Paperclip
2 # Handles thumbnailing images that are uploaded.
3 class Thumbnail < Processor
4
5 attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options
6
7 # Creates a Thumbnail object set to work on the +file+ given. It
8 # will attempt to transform the image into one defined by +target_geometry+
9 # which is a "WxH"-style string. +format+ will be inferred from the +file+
10 # unless specified. Thumbnail creation will raise no errors unless
11 # +whiny+ is true (which it is, by default. If +convert_options+ is
12 # set, the options will be appended to the convert command upon image conversion
13 def initialize file, options = {}, attachment = nil
14 super
15 geometry = options[:geometry]
16 @file = file
17 @crop = geometry[-1,1] == '#'
18 @target_geometry = Geometry.parse geometry
19 @current_geometry = Geometry.from_file @file
20 @convert_options = options[:convert_options]
21 @whiny = options[:whiny].nil? ? true : options[:whiny]
22 @format = options[:format]
23
24 @current_format = File.extname(@file.path)
25 @basename = File.basename(@file.path, @current_format)
26 end
27
28 # Returns true if the +target_geometry+ is meant to crop.
29 def crop?
30 @crop
31 end
32
33 # Returns true if the image is meant to make use of additional convert options.
34 def convert_options?
35 not @convert_options.blank?
36 end
37
38 # Performs the conversion of the +file+ into a thumbnail. Returns the Tempfile
39 # that contains the new image.
40 def make
41 src = @file
42 dst = Tempfile.new([@basename, @format].compact.join("."))
43 dst.binmode
44
45 command = <<-end_command
46 "#{ File.expand_path(src.path) }[0]"
47 #{ transformation_command }
48 "#{ File.expand_path(dst.path) }"
49 end_command
50
51 begin
52 success = Paperclip.run("convert", command.gsub(/\s+/, " "))
53 rescue PaperclipCommandLineError
54 raise PaperclipError, "There was an error processing the thumbnail for #{@basename}" if @whiny
55 end
56
57 dst
58 end
59
60 # Returns the command ImageMagick's +convert+ needs to transform the image
61 # into the thumbnail.
62 def transformation_command
63 scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
64 trans = "-resize \"#{scale}\""
65 trans << " -crop \"#{crop}\" +repage" if crop
66 trans << " #{convert_options}" if convert_options?
67 trans
68 end
69 end
70end
diff --git a/vendor/plugins/paperclip/lib/paperclip/upfile.rb b/vendor/plugins/paperclip/lib/paperclip/upfile.rb
deleted file mode 100644
index f0c8a44..0000000
--- a/vendor/plugins/paperclip/lib/paperclip/upfile.rb
+++ /dev/null
@@ -1,48 +0,0 @@
1module Paperclip
2 # The Upfile module is a convenience module for adding uploaded-file-type methods
3 # to the +File+ class. Useful for testing.
4 # user.avatar = File.new("test/test_avatar.jpg")
5 module Upfile
6
7 # Infer the MIME-type of the file from the extension.
8 def content_type
9 type = (self.path.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
10 case type
11 when %r"jpe?g" then "image/jpeg"
12 when %r"tiff?" then "image/tiff"
13 when %r"png", "gif", "bmp" then "image/#{type}"
14 when "txt" then "text/plain"
15 when %r"html?" then "text/html"
16 when "csv", "xml", "css", "js" then "text/#{type}"
17 else "application/x-#{type}"
18 end
19 end
20
21 # Returns the file's normal name.
22 def original_filename
23 File.basename(self.path)
24 end
25
26 # Returns the size of the file.
27 def size
28 File.size(self)
29 end
30 end
31end
32
33if defined? StringIO
34 class StringIO
35 attr_accessor :original_filename, :content_type
36 def original_filename
37 @original_filename ||= "stringio.txt"
38 end
39 def content_type
40 @content_type ||= "text/plain"
41 end
42 end
43end
44
45class File #:nodoc:
46 include Paperclip::Upfile
47end
48
diff --git a/vendor/plugins/paperclip/paperclip.gemspec b/vendor/plugins/paperclip/paperclip.gemspec
deleted file mode 100644
index 12ce4c2..0000000
--- a/vendor/plugins/paperclip/paperclip.gemspec
+++ /dev/null
@@ -1,40 +0,0 @@
1# -*- encoding: utf-8 -*-
2
3Gem::Specification.new do |s|
4 s.name = %q{paperclip}
5 s.version = "2.2.8"
6
7 s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8 s.authors = ["Jon Yurek"]
9 s.date = %q{2009-04-02}
10 s.email = %q{jyurek@thoughtbot.com}
11 s.extra_rdoc_files = ["README.rdoc"]
12 s.files = ["README.rdoc", "LICENSE", "Rakefile", "init.rb", "generators/paperclip", "generators/paperclip/paperclip_generator.rb", "generators/paperclip/templates", "generators/paperclip/templates/paperclip_migration.rb.erb", "generators/paperclip/USAGE", "lib/paperclip", "lib/paperclip/attachment.rb", "lib/paperclip/callback_compatability.rb", "lib/paperclip/geometry.rb", "lib/paperclip/iostream.rb", "lib/paperclip/matchers", "lib/paperclip/matchers/have_attached_file_matcher.rb", "lib/paperclip/matchers/validate_attachment_content_type_matcher.rb", "lib/paperclip/matchers/validate_attachment_presence_matcher.rb", "lib/paperclip/matchers/validate_attachment_size_matcher.rb", "lib/paperclip/matchers.rb", "lib/paperclip/processor.rb", "lib/paperclip/storage.rb", "lib/paperclip/thumbnail.rb", "lib/paperclip/upfile.rb", "lib/paperclip.rb", "tasks/paperclip_tasks.rake", "test/attachment_test.rb", "test/database.yml", "test/debug.log", "test/fixtures", "test/fixtures/12k.png", "test/fixtures/50x50.png", "test/fixtures/5k.png", "test/fixtures/bad.png", "test/fixtures/s3.yml", "test/fixtures/text.txt", "test/fixtures/twopage.pdf", "test/geometry_test.rb", "test/helper.rb", "test/integration_test.rb", "test/iostream_test.rb", "test/matchers", "test/matchers/have_attached_file_matcher_test.rb", "test/matchers/validate_attachment_content_type_matcher_test.rb", "test/matchers/validate_attachment_presence_matcher_test.rb", "test/matchers/validate_attachment_size_matcher_test.rb", "test/paperclip_test.rb", "test/processor_test.rb", "test/s3.yml", "test/storage_test.rb", "test/thumbnail_test.rb", "test/tmp", "test/tmp/storage.txt", "shoulda_macros/paperclip.rb"]
13 s.has_rdoc = true
14 s.homepage = %q{http://www.thoughtbot.com/projects/paperclip}
15 s.rdoc_options = ["--line-numbers", "--inline-source"]
16 s.require_paths = ["lib"]
17 s.requirements = ["ImageMagick"]
18 s.rubyforge_project = %q{paperclip}
19 s.rubygems_version = %q{1.3.1}
20 s.summary = %q{File attachments as attributes for ActiveRecord}
21
22 if s.respond_to? :specification_version then
23 current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24 s.specification_version = 2
25
26 if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27 s.add_runtime_dependency(%q<right_aws>, [">= 0"])
28 s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
29 s.add_development_dependency(%q<mocha>, [">= 0"])
30 else
31 s.add_dependency(%q<right_aws>, [">= 0"])
32 s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
33 s.add_dependency(%q<mocha>, [">= 0"])
34 end
35 else
36 s.add_dependency(%q<right_aws>, [">= 0"])
37 s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
38 s.add_dependency(%q<mocha>, [">= 0"])
39 end
40end
diff --git a/vendor/plugins/paperclip/shoulda_macros/paperclip.rb b/vendor/plugins/paperclip/shoulda_macros/paperclip.rb
deleted file mode 100644
index d690357..0000000
--- a/vendor/plugins/paperclip/shoulda_macros/paperclip.rb
+++ /dev/null
@@ -1,68 +0,0 @@
1require 'paperclip/matchers'
2
3module Paperclip
4 # =Paperclip Shoulda Macros
5 #
6 # These macros are intended for use with shoulda, and will be included into
7 # your tests automatically. All of the macros use the standard shoulda
8 # assumption that the name of the test is based on the name of the model
9 # you're testing (that is, UserTest is the test for the User model), and
10 # will load that class for testing purposes.
11 module Shoulda
12 include Matchers
13 # This will test whether you have defined your attachment correctly by
14 # checking for all the required fields exist after the definition of the
15 # attachment.
16 def should_have_attached_file name
17 klass = self.name.gsub(/Test$/, '').constantize
18 matcher = have_attached_file name
19 should matcher.description do
20 assert_accepts(matcher, klass)
21 end
22 end
23
24 # Tests for validations on the presence of the attachment.
25 def should_validate_attachment_presence name
26 klass = self.name.gsub(/Test$/, '').constantize
27 matcher = validate_attachment_presence name
28 should matcher.description do
29 assert_accepts(matcher, klass)
30 end
31 end
32
33 # Tests that you have content_type validations specified. There are two
34 # options, :valid and :invalid. Both accept an array of strings. The
35 # strings should be a list of content types which will pass and fail
36 # validation, respectively.
37 def should_validate_attachment_content_type name, options = {}
38 klass = self.name.gsub(/Test$/, '').constantize
39 valid = [options[:valid]].flatten
40 invalid = [options[:invalid]].flatten
41 matcher = validate_attachment_content_type(name).allowing(valid).rejecting(invalid)
42 should matcher.description do
43 assert_accepts(matcher, klass)
44 end
45 end
46
47 # Tests to ensure that you have file size validations turned on. You
48 # can pass the same options to this that you can to
49 # validate_attachment_file_size - :less_than, :greater_than, and :in.
50 # :less_than checks that a file is less than a certain size, :greater_than
51 # checks that a file is more than a certain size, and :in takes a Range or
52 # Array which specifies the lower and upper limits of the file size.
53 def should_validate_attachment_size name, options = {}
54 klass = self.name.gsub(/Test$/, '').constantize
55 min = options[:greater_than] || (options[:in] && options[:in].first) || 0
56 max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
57 range = (min..max)
58 matcher = validate_attachment_size(name).in(range)
59 should matcher.description do
60 assert_accepts(matcher, klass)
61 end
62 end
63 end
64end
65
66class Test::Unit::TestCase #:nodoc:
67 extend Paperclip::Shoulda
68end
diff --git a/vendor/plugins/paperclip/tasks/paperclip_tasks.rake b/vendor/plugins/paperclip/tasks/paperclip_tasks.rake
deleted file mode 100644
index 23e4c11..0000000
--- a/vendor/plugins/paperclip/tasks/paperclip_tasks.rake
+++ /dev/null
@@ -1,79 +0,0 @@
1def obtain_class
2 class_name = ENV['CLASS'] || ENV['class']
3 raise "Must specify CLASS" unless class_name
4 @klass = Object.const_get(class_name)
5end
6
7def obtain_attachments
8 name = ENV['ATTACHMENT'] || ENV['attachment']
9 raise "Class #{@klass.name} has no attachments specified" unless @klass.respond_to?(:attachment_definitions)
10 if !name.blank? && @klass.attachment_definitions.keys.include?(name)
11 [ name ]
12 else
13 @klass.attachment_definitions.keys
14 end
15end
16
17def for_all_attachments
18 klass = obtain_class
19 names = obtain_attachments
20 ids = klass.connection.select_values(klass.send(:construct_finder_sql, :select => 'id'))
21
22 ids.each do |id|
23 instance = klass.find(id)
24 names.each do |name|
25 result = if instance.send("#{ name }?")
26 yield(instance, name)
27 else
28 true
29 end
30 print result ? "." : "x"; $stdout.flush
31 end
32 end
33 puts " Done."
34end
35
36namespace :paperclip do
37 desc "Refreshes both metadata and thumbnails."
38 task :refresh => ["paperclip:refresh:metadata", "paperclip:refresh:thumbnails"]
39
40 namespace :refresh do
41 desc "Regenerates thumbnails for a given CLASS (and optional ATTACHMENT)."
42 task :thumbnails => :environment do
43 errors = []
44 for_all_attachments do |instance, name|
45 result = instance.send(name).reprocess!
46 errors << [instance.id, instance.errors] unless instance.errors.blank?
47 result
48 end
49 errors.each{|e| puts "#{e.first}: #{e.last.full_messages.inspect}" }
50 end
51
52 desc "Regenerates content_type/size metadata for a given CLASS (and optional ATTACHMENT)."
53 task :metadata => :environment do
54 for_all_attachments do |instance, name|
55 if file = instance.send(name).to_file
56 instance.send("#{name}_file_name=", instance.send("#{name}_file_name").strip)
57 instance.send("#{name}_content_type=", file.content_type.strip)
58 instance.send("#{name}_file_size=", file.size) if instance.respond_to?("#{name}_file_size")
59 instance.save(false)
60 else
61 true
62 end
63 end
64 end
65 end
66
67 desc "Cleans out invalid attachments. Useful after you've added new validations."
68 task :clean => :environment do
69 for_all_attachments do |instance, name|
70 instance.send(name).send(:validate)
71 if instance.send(name).valid?
72 true
73 else
74 instance.send("#{name}=", nil)
75 instance.save
76 end
77 end
78 end
79end
diff --git a/vendor/plugins/paperclip/test/.gitignore b/vendor/plugins/paperclip/test/.gitignore
deleted file mode 100644
index b14c548..0000000
--- a/vendor/plugins/paperclip/test/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
1debug.log
diff --git a/vendor/plugins/paperclip/test/attachment_test.rb b/vendor/plugins/paperclip/test/attachment_test.rb
deleted file mode 100644
index 61ab2a2..0000000
--- a/vendor/plugins/paperclip/test/attachment_test.rb
+++ /dev/null
@@ -1,742 +0,0 @@
1require 'test/helper'
2
3class Dummy
4 # This is a dummy class
5end
6
7class AttachmentTest < Test::Unit::TestCase
8 context "Attachment default_options" do
9 setup do
10 rebuild_model
11 @old_default_options = Paperclip::Attachment.default_options.dup
12 @new_default_options = @old_default_options.merge({
13 :path => "argle/bargle",
14 :url => "fooferon",
15 :default_url => "not here.png"
16 })
17 end
18
19 teardown do
20 Paperclip::Attachment.default_options.merge! @old_default_options
21 end
22
23 should "be overrideable" do
24 Paperclip::Attachment.default_options.merge!(@new_default_options)
25 @new_default_options.keys.each do |key|
26 assert_equal @new_default_options[key],
27 Paperclip::Attachment.default_options[key]
28 end
29 end
30
31 context "without an Attachment" do
32 setup do
33 @dummy = Dummy.new
34 end
35
36 should "return false when asked exists?" do
37 assert !@dummy.avatar.exists?
38 end
39 end
40
41 context "on an Attachment" do
42 setup do
43 @dummy = Dummy.new
44 @attachment = @dummy.avatar
45 end
46
47 Paperclip::Attachment.default_options.keys.each do |key|
48 should "be the default_options for #{key}" do
49 assert_equal @old_default_options[key],
50 @attachment.instance_variable_get("@#{key}"),
51 key
52 end
53 end
54
55 context "when redefined" do
56 setup do
57 Paperclip::Attachment.default_options.merge!(@new_default_options)
58 @dummy = Dummy.new
59 @attachment = @dummy.avatar
60 end
61
62 Paperclip::Attachment.default_options.keys.each do |key|
63 should "be the new default_options for #{key}" do
64 assert_equal @new_default_options[key],
65 @attachment.instance_variable_get("@#{key}"),
66 key
67 end
68 end
69 end
70 end
71 end
72
73 context "An attachment with similarly named interpolations" do
74 setup do
75 rebuild_model :path => ":id.omg/:id-bbq/:idwhat/:id_partition.wtf"
76 @dummy = Dummy.new
77 @dummy.stubs(:id).returns(1024)
78 @file = File.new(File.join(File.dirname(__FILE__),
79 "fixtures",
80 "5k.png"), 'rb')
81 @dummy.avatar = @file
82 end
83
84 teardown { @file.close }
85
86 should "make sure that they are interpolated correctly" do
87 assert_equal "1024.omg/1024-bbq/1024what/000/001/024.wtf", @dummy.avatar.path
88 end
89 end
90
91 context "An attachment with a :rails_env interpolation" do
92 setup do
93 @rails_env = "blah"
94 @id = 1024
95 rebuild_model :path => ":rails_env/:id.png"
96 @dummy = Dummy.new
97 @dummy.stubs(:id).returns(@id)
98 @file = StringIO.new(".")
99 @dummy.avatar = @file
100 end
101
102 should "return the proper path" do
103 temporary_rails_env(@rails_env) {
104 assert_equal "#{@rails_env}/#{@id}.png", @dummy.avatar.path
105 }
106 end
107 end
108
109 context "An attachment with :convert_options" do
110 setup do
111 rebuild_model :styles => {
112 :thumb => "100x100",
113 :large => "400x400"
114 },
115 :convert_options => {
116 :all => "-do_stuff",
117 :thumb => "-thumbnailize"
118 }
119 @dummy = Dummy.new
120 @dummy.avatar
121 end
122
123 should "report the correct options when sent #extra_options_for(:thumb)" do
124 assert_equal "-thumbnailize -do_stuff", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
125 end
126
127 should "report the correct options when sent #extra_options_for(:large)" do
128 assert_equal "-do_stuff", @dummy.avatar.send(:extra_options_for, :large)
129 end
130
131 before_should "call extra_options_for(:thumb/:large)" do
132 Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
133 Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
134 end
135 end
136
137 context "An attachment with :convert_options that is a proc" do
138 setup do
139 rebuild_model :styles => {
140 :thumb => "100x100",
141 :large => "400x400"
142 },
143 :convert_options => {
144 :all => lambda{|i| i.all },
145 :thumb => lambda{|i| i.thumb }
146 }
147 Dummy.class_eval do
148 def all; "-all"; end
149 def thumb; "-thumb"; end
150 end
151 @dummy = Dummy.new
152 @dummy.avatar
153 end
154
155 should "report the correct options when sent #extra_options_for(:thumb)" do
156 assert_equal "-thumb -all", @dummy.avatar.send(:extra_options_for, :thumb), @dummy.avatar.convert_options.inspect
157 end
158
159 should "report the correct options when sent #extra_options_for(:large)" do
160 assert_equal "-all", @dummy.avatar.send(:extra_options_for, :large)
161 end
162
163 before_should "call extra_options_for(:thumb/:large)" do
164 Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:thumb)
165 Paperclip::Attachment.any_instance.expects(:extra_options_for).with(:large)
166 end
167 end
168
169 context "An attachment with :path that is a proc" do
170 setup do
171 rebuild_model :path => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
172
173 @file = File.new(File.join(File.dirname(__FILE__),
174 "fixtures",
175 "5k.png"), 'rb')
176 @dummyA = Dummy.new(:other => 'a')
177 @dummyA.avatar = @file
178 @dummyB = Dummy.new(:other => 'b')
179 @dummyB.avatar = @file
180 end
181
182 teardown { @file.close }
183
184 should "return correct path" do
185 assert_equal "path/a.png", @dummyA.avatar.path
186 assert_equal "path/b.png", @dummyB.avatar.path
187 end
188 end
189
190 context "An attachment with :styles that is a proc" do
191 setup do
192 rebuild_model :styles => lambda{ |attachment| {:thumb => "50x50#", :large => "400x400"} }
193
194 @attachment = Dummy.new.avatar
195 end
196
197 should "have the correct geometry" do
198 assert_equal "50x50#", @attachment.styles[:thumb][:geometry]
199 end
200 end
201
202 context "An attachment with :url that is a proc" do
203 setup do
204 rebuild_model :url => lambda{ |attachment| "path/#{attachment.instance.other}.:extension" }
205
206 @file = File.new(File.join(File.dirname(__FILE__),
207 "fixtures",
208 "5k.png"), 'rb')
209 @dummyA = Dummy.new(:other => 'a')
210 @dummyA.avatar = @file
211 @dummyB = Dummy.new(:other => 'b')
212 @dummyB.avatar = @file
213 end
214
215 teardown { @file.close }
216
217 should "return correct url" do
218 assert_equal "path/a.png", @dummyA.avatar.url(:original, false)
219 assert_equal "path/b.png", @dummyB.avatar.url(:original, false)
220 end
221 end
222
223 geometry_specs = [
224 [ lambda{|z| "50x50#" }, :png ],
225 lambda{|z| "50x50#" },
226 { :geometry => lambda{|z| "50x50#" } }
227 ]
228 geometry_specs.each do |geometry_spec|
229 context "An attachment geometry like #{geometry_spec}" do
230 setup do
231 rebuild_model :styles => { :normal => geometry_spec }
232 @attachment = Dummy.new.avatar
233 end
234
235 should "not run the procs immediately" do
236 assert_kind_of Proc, @attachment.styles[:normal][:geometry]
237 end
238
239 context "when assigned" do
240 setup do
241 @file = StringIO.new(".")
242 @attachment.assign(@file)
243 end
244
245 should "have the correct geometry" do
246 assert_equal "50x50#", @attachment.styles[:normal][:geometry]
247 end
248 end
249 end
250 end
251
252 context "An attachment with both 'normal' and hash-style styles" do
253 setup do
254 rebuild_model :styles => {
255 :normal => ["50x50#", :png],
256 :hash => { :geometry => "50x50#", :format => :png }
257 }
258 @dummy = Dummy.new
259 @attachment = @dummy.avatar
260 end
261
262 [:processors, :whiny, :convert_options, :geometry, :format].each do |field|
263 should "have the same #{field} field" do
264 assert_equal @attachment.styles[:normal][field], @attachment.styles[:hash][field]
265 end
266 end
267 end
268
269 context "An attachment with :processors that is a proc" do
270 setup do
271 rebuild_model :styles => { :normal => '' }, :processors => lambda { |a| [ :test ] }
272 @attachment = Dummy.new.avatar
273 end
274
275 should "not run the proc immediately" do
276 assert_kind_of Proc, @attachment.styles[:normal][:processors]
277 end
278
279 context "when assigned" do
280 setup do
281 @attachment.assign(StringIO.new("."))
282 end
283
284 should "have the correct processors" do
285 assert_equal [ :test ], @attachment.styles[:normal][:processors]
286 end
287 end
288 end
289
290 context "An attachment with erroring processor" do
291 setup do
292 rebuild_model :processor => [:thumbnail], :styles => { :small => '' }, :whiny_thumbnails => true
293 @dummy = Dummy.new
294 Paperclip::Thumbnail.expects(:make).raises(Paperclip::PaperclipError, "cannot be processed.")
295 @file = StringIO.new("...")
296 @file.stubs(:to_tempfile).returns(@file)
297 @dummy.avatar = @file
298 end
299
300 should "correctly forward processing error message to the instance" do
301 @dummy.valid?
302 assert_contains @dummy.errors.full_messages, "Avatar cannot be processed."
303 end
304 end
305
306 context "An attachment with multiple processors" do
307 setup do
308 class Paperclip::Test < Paperclip::Processor; end
309 @style_params = { :once => {:one => 1, :two => 2} }
310 rebuild_model :processors => [:thumbnail, :test], :styles => @style_params
311 @dummy = Dummy.new
312 @file = StringIO.new("...")
313 @file.stubs(:to_tempfile).returns(@file)
314 Paperclip::Test.stubs(:make).returns(@file)
315 Paperclip::Thumbnail.stubs(:make).returns(@file)
316 end
317
318 context "when assigned" do
319 setup { @dummy.avatar = @file }
320
321 before_should "call #make on all specified processors" do
322 expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => nil, :convert_options => ""})
323 Paperclip::Thumbnail.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
324 Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
325 end
326
327 before_should "call #make with attachment passed as third argument" do
328 expected_params = @style_params[:once].merge({:processors => [:thumbnail, :test], :whiny => nil, :convert_options => ""})
329 Paperclip::Test.expects(:make).with(@file, expected_params, @dummy.avatar).returns(@file)
330 end
331 end
332 end
333
334 context "An attachment with no processors defined" do
335 setup do
336 rebuild_model :processors => [], :styles => {:something => 1}
337 @dummy = Dummy.new
338 @file = StringIO.new("...")
339 end
340 should "raise when assigned to" do
341 assert_raises(RuntimeError){ @dummy.avatar = @file }
342 end
343 end
344
345 context "Assigning an attachment with post_process hooks" do
346 setup do
347 rebuild_model :styles => { :something => "100x100#" }
348 Dummy.class_eval do
349 before_avatar_post_process :do_before_avatar
350 after_avatar_post_process :do_after_avatar
351 before_post_process :do_before_all
352 after_post_process :do_after_all
353 def do_before_avatar; end
354 def do_after_avatar; end
355 def do_before_all; end
356 def do_after_all; end
357 end
358 @file = StringIO.new(".")
359 @file.stubs(:to_tempfile).returns(@file)
360 @dummy = Dummy.new
361 Paperclip::Thumbnail.stubs(:make).returns(@file)
362 @attachment = @dummy.avatar
363 end
364
365 should "call the defined callbacks when assigned" do
366 @dummy.expects(:do_before_avatar).with()
367 @dummy.expects(:do_after_avatar).with()
368 @dummy.expects(:do_before_all).with()
369 @dummy.expects(:do_after_all).with()
370 Paperclip::Thumbnail.expects(:make).returns(@file)
371 @dummy.avatar = @file
372 end
373
374 should "not cancel the processing if a before_post_process returns nil" do
375 @dummy.expects(:do_before_avatar).with().returns(nil)
376 @dummy.expects(:do_after_avatar).with()
377 @dummy.expects(:do_before_all).with().returns(nil)
378 @dummy.expects(:do_after_all).with()
379 Paperclip::Thumbnail.expects(:make).returns(@file)
380 @dummy.avatar = @file
381 end
382
383 should "cancel the processing if a before_post_process returns false" do
384 @dummy.expects(:do_before_avatar).never
385 @dummy.expects(:do_after_avatar).never
386 @dummy.expects(:do_before_all).with().returns(false)
387 @dummy.expects(:do_after_all).never
388 Paperclip::Thumbnail.expects(:make).never
389 @dummy.avatar = @file
390 end
391
392 should "cancel the processing if a before_avatar_post_process returns false" do
393 @dummy.expects(:do_before_avatar).with().returns(false)
394 @dummy.expects(:do_after_avatar).never
395 @dummy.expects(:do_before_all).with().returns(true)
396 @dummy.expects(:do_after_all).never
397 Paperclip::Thumbnail.expects(:make).never
398 @dummy.avatar = @file
399 end
400 end
401
402 context "Assigning an attachment" do
403 setup do
404 rebuild_model :styles => { :something => "100x100#" }
405 @file = StringIO.new(".")
406 @file.expects(:original_filename).returns("5k.png\n\n")
407 @file.expects(:content_type).returns("image/png\n\n")
408 @file.stubs(:to_tempfile).returns(@file)
409 @dummy = Dummy.new
410 Paperclip::Thumbnail.expects(:make).returns(@file)
411 @dummy.expects(:run_callbacks).with(:before_avatar_post_process, {:original => @file})
412 @dummy.expects(:run_callbacks).with(:before_post_process, {:original => @file})
413 @dummy.expects(:run_callbacks).with(:after_avatar_post_process, {:original => @file, :something => @file})
414 @dummy.expects(:run_callbacks).with(:after_post_process, {:original => @file, :something => @file})
415 @attachment = @dummy.avatar
416 @dummy.avatar = @file
417 end
418
419 should "strip whitespace from original_filename field" do
420 assert_equal "5k.png", @dummy.avatar.original_filename
421 end
422
423 should "strip whitespace from content_type field" do
424 assert_equal "image/png", @dummy.avatar.instance.avatar_content_type
425 end
426 end
427
428 context "Attachment with strange letters" do
429 setup do
430 rebuild_model
431
432 @not_file = mock
433 @tempfile = mock
434 @not_file.stubs(:nil?).returns(false)
435 @not_file.expects(:size).returns(10)
436 @tempfile.expects(:size).returns(10)
437 @not_file.expects(:to_tempfile).returns(@tempfile)
438 @not_file.expects(:original_filename).returns("sheep_say_bæ.png\r\n")
439 @not_file.expects(:content_type).returns("image/png\r\n")
440
441 @dummy = Dummy.new
442 @attachment = @dummy.avatar
443 @attachment.expects(:valid_assignment?).with(@not_file).returns(true)
444 @attachment.expects(:queue_existing_for_delete)
445 @attachment.expects(:post_process)
446 @attachment.expects(:valid?).returns(true)
447 @attachment.expects(:validate)
448 @dummy.avatar = @not_file
449 end
450
451 should "remove strange letters and replace with underscore (_)" do
452 assert_equal "sheep_say_b_.png", @dummy.avatar.original_filename
453 end
454
455 end
456
457 context "An attachment" do
458 setup do
459 Paperclip::Attachment.default_options.merge!({
460 :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
461 })
462 FileUtils.rm_rf("tmp")
463 rebuild_model
464 @instance = Dummy.new
465 @attachment = Paperclip::Attachment.new(:avatar, @instance)
466 @file = File.new(File.join(File.dirname(__FILE__),
467 "fixtures",
468 "5k.png"), 'rb')
469 end
470
471 teardown { @file.close }
472
473 should "raise if there are not the correct columns when you try to assign" do
474 @other_attachment = Paperclip::Attachment.new(:not_here, @instance)
475 assert_raises(Paperclip::PaperclipError) do
476 @other_attachment.assign(@file)
477 end
478 end
479
480 should "return its default_url when no file assigned" do
481 assert @attachment.to_file.nil?
482 assert_equal "/avatars/original/missing.png", @attachment.url
483 assert_equal "/avatars/blah/missing.png", @attachment.url(:blah)
484 end
485
486 should "return nil as path when no file assigned" do
487 assert @attachment.to_file.nil?
488 assert_equal nil, @attachment.path
489 assert_equal nil, @attachment.path(:blah)
490 end
491
492 context "with a file assigned in the database" do
493 setup do
494 @attachment.stubs(:instance_read).with(:file_name).returns("5k.png")
495 @attachment.stubs(:instance_read).with(:content_type).returns("image/png")
496 @attachment.stubs(:instance_read).with(:file_size).returns(12345)
497 now = Time.now
498 Time.stubs(:now).returns(now)
499 @attachment.stubs(:instance_read).with(:updated_at).returns(Time.now)
500 end
501
502 should "return a correct url even if the file does not exist" do
503 assert_nil @attachment.to_file
504 assert_match %r{^/system/avatars/#{@instance.id}/blah/5k\.png}, @attachment.url(:blah)
505 end
506
507 should "make sure the updated_at mtime is in the url if it is defined" do
508 assert_match %r{#{Time.now.to_i}$}, @attachment.url(:blah)
509 end
510
511 should "make sure the updated_at mtime is NOT in the url if false is passed to the url method" do
512 assert_no_match %r{#{Time.now.to_i}$}, @attachment.url(:blah, false)
513 end
514
515 context "with the updated_at field removed" do
516 setup do
517 @attachment.stubs(:instance_read).with(:updated_at).returns(nil)
518 end
519
520 should "only return the url without the updated_at when sent #url" do
521 assert_match "/avatars/#{@instance.id}/blah/5k.png", @attachment.url(:blah)
522 end
523 end
524
525 should "return the proper path when filename has a single .'s" do
526 assert_equal "./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.png", @attachment.path
527 end
528
529 should "return the proper path when filename has multiple .'s" do
530 @attachment.stubs(:instance_read).with(:file_name).returns("5k.old.png")
531 assert_equal "./test/../tmp/avatars/dummies/original/#{@instance.id}/5k.old.png", @attachment.path
532 end
533
534 context "when expecting three styles" do
535 setup do
536 styles = {:styles => { :large => ["400x400", :png],
537 :medium => ["100x100", :gif],
538 :small => ["32x32#", :jpg]}}
539 @attachment = Paperclip::Attachment.new(:avatar,
540 @instance,
541 styles)
542 end
543
544 context "and assigned a file" do
545 setup do
546 now = Time.now
547 Time.stubs(:now).returns(now)
548 @attachment.assign(@file)
549 end
550
551 should "be dirty" do
552 assert @attachment.dirty?
553 end
554
555 context "and saved" do
556 setup do
557 @attachment.save
558 end
559
560 should "return the real url" do
561 file = @attachment.to_file
562 assert file
563 assert_match %r{^/system/avatars/#{@instance.id}/original/5k\.png}, @attachment.url
564 assert_match %r{^/system/avatars/#{@instance.id}/small/5k\.jpg}, @attachment.url(:small)
565 file.close
566 end
567
568 should "commit the files to disk" do
569 [:large, :medium, :small].each do |style|
570 io = @attachment.to_io(style)
571 assert File.exists?(io)
572 assert ! io.is_a?(::Tempfile)
573 io.close
574 end
575 end
576
577 should "save the files as the right formats and sizes" do
578 [[:large, 400, 61, "PNG"],
579 [:medium, 100, 15, "GIF"],
580 [:small, 32, 32, "JPEG"]].each do |style|
581 cmd = %Q[identify -format "%w %h %b %m" "#{@attachment.path(style.first)}"]
582 out = `#{cmd}`
583 width, height, size, format = out.split(" ")
584 assert_equal style[1].to_s, width.to_s
585 assert_equal style[2].to_s, height.to_s
586 assert_equal style[3].to_s, format.to_s
587 end
588 end
589
590 should "still have its #file attribute not be nil" do
591 assert ! (file = @attachment.to_file).nil?
592 file.close
593 end
594
595 context "and trying to delete" do
596 setup do
597 @existing_names = @attachment.styles.keys.collect do |style|
598 @attachment.path(style)
599 end
600 end
601
602 should "delete the files after assigning nil" do
603 @attachment.expects(:instance_write).with(:file_name, nil)
604 @attachment.expects(:instance_write).with(:content_type, nil)
605 @attachment.expects(:instance_write).with(:file_size, nil)
606 @attachment.expects(:instance_write).with(:updated_at, nil)
607 @attachment.assign nil
608 @attachment.save
609 @existing_names.each{|f| assert ! File.exists?(f) }
610 end
611
612 should "delete the files when you call #clear and #save" do
613 @attachment.expects(:instance_write).with(:file_name, nil)
614 @attachment.expects(:instance_write).with(:content_type, nil)
615 @attachment.expects(:instance_write).with(:file_size, nil)
616 @attachment.expects(:instance_write).with(:updated_at, nil)
617 @attachment.clear
618 @attachment.save
619 @existing_names.each{|f| assert ! File.exists?(f) }
620 end
621
622 should "delete the files when you call #delete" do
623 @attachment.expects(:instance_write).with(:file_name, nil)
624 @attachment.expects(:instance_write).with(:content_type, nil)
625 @attachment.expects(:instance_write).with(:file_size, nil)
626 @attachment.expects(:instance_write).with(:updated_at, nil)
627 @attachment.destroy
628 @existing_names.each{|f| assert ! File.exists?(f) }
629 end
630 end
631 end
632 end
633 end
634
635 end
636
637 context "when trying a nonexistant storage type" do
638 setup do
639 rebuild_model :storage => :not_here
640 end
641
642 should "not be able to find the module" do
643 assert_raise(NameError){ Dummy.new.avatar }
644 end
645 end
646 end
647
648 context "An attachment with only a avatar_file_name column" do
649 setup do
650 ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
651 table.column :avatar_file_name, :string
652 end
653 rebuild_class
654 @dummy = Dummy.new
655 @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
656 end
657
658 teardown { @file.close }
659
660 should "not error when assigned an attachment" do
661 assert_nothing_raised { @dummy.avatar = @file }
662 end
663
664 should "return the time when sent #avatar_updated_at" do
665 now = Time.now
666 Time.stubs(:now).returns(now)
667 @dummy.avatar = @file
668 assert now, @dummy.avatar.updated_at
669 end
670
671 should "return nil when reloaded and sent #avatar_updated_at" do
672 @dummy.save
673 @dummy.reload
674 assert_nil @dummy.avatar.updated_at
675 end
676
677 should "return the right value when sent #avatar_file_size" do
678 @dummy.avatar = @file
679 assert_equal @file.size, @dummy.avatar.size
680 end
681
682 context "and avatar_updated_at column" do
683 setup do
684 ActiveRecord::Base.connection.add_column :dummies, :avatar_updated_at, :timestamp
685 rebuild_class
686 @dummy = Dummy.new
687 end
688
689 should "not error when assigned an attachment" do
690 assert_nothing_raised { @dummy.avatar = @file }
691 end
692
693 should "return the right value when sent #avatar_updated_at" do
694 now = Time.now
695 Time.stubs(:now).returns(now)
696 @dummy.avatar = @file
697 assert_equal now.to_i, @dummy.avatar.updated_at
698 end
699 end
700
701 context "and avatar_content_type column" do
702 setup do
703 ActiveRecord::Base.connection.add_column :dummies, :avatar_content_type, :string
704 rebuild_class
705 @dummy = Dummy.new
706 end
707
708 should "not error when assigned an attachment" do
709 assert_nothing_raised { @dummy.avatar = @file }
710 end
711
712 should "return the right value when sent #avatar_content_type" do
713 @dummy.avatar = @file
714 assert_equal "image/png", @dummy.avatar.content_type
715 end
716 end
717
718 context "and avatar_file_size column" do
719 setup do
720 ActiveRecord::Base.connection.add_column :dummies, :avatar_file_size, :integer
721 rebuild_class
722 @dummy = Dummy.new
723 end
724
725 should "not error when assigned an attachment" do
726 assert_nothing_raised { @dummy.avatar = @file }
727 end
728
729 should "return the right value when sent #avatar_file_size" do
730 @dummy.avatar = @file
731 assert_equal @file.size, @dummy.avatar.size
732 end
733
734 should "return the right value when saved, reloaded, and sent #avatar_file_size" do
735 @dummy.avatar = @file
736 @dummy.save
737 @dummy = Dummy.find(@dummy.id)
738 assert_equal @file.size, @dummy.avatar.size
739 end
740 end
741 end
742end
diff --git a/vendor/plugins/paperclip/test/fixtures/12k.png b/vendor/plugins/paperclip/test/fixtures/12k.png
deleted file mode 100644
index f819d45..0000000
--- a/vendor/plugins/paperclip/test/fixtures/12k.png
+++ /dev/null
Binary files differ
diff --git a/vendor/plugins/paperclip/test/fixtures/50x50.png b/vendor/plugins/paperclip/test/fixtures/50x50.png
deleted file mode 100644
index 63f5646..0000000
--- a/vendor/plugins/paperclip/test/fixtures/50x50.png
+++ /dev/null
Binary files differ
diff --git a/vendor/plugins/paperclip/test/fixtures/5k.png b/vendor/plugins/paperclip/test/fixtures/5k.png
deleted file mode 100644
index 75d9f04..0000000
--- a/vendor/plugins/paperclip/test/fixtures/5k.png
+++ /dev/null
Binary files differ
diff --git a/vendor/plugins/paperclip/test/fixtures/bad.png b/vendor/plugins/paperclip/test/fixtures/bad.png
deleted file mode 100644
index 7ba4f07..0000000
--- a/vendor/plugins/paperclip/test/fixtures/bad.png
+++ /dev/null
@@ -1 +0,0 @@
1This is not an image.
diff --git a/vendor/plugins/paperclip/test/fixtures/text.txt b/vendor/plugins/paperclip/test/fixtures/text.txt
deleted file mode 100644
index e69de29..0000000
--- a/vendor/plugins/paperclip/test/fixtures/text.txt
+++ /dev/null
diff --git a/vendor/plugins/paperclip/test/fixtures/twopage.pdf b/vendor/plugins/paperclip/test/fixtures/twopage.pdf
deleted file mode 100644
index 0c34a51..0000000
--- a/vendor/plugins/paperclip/test/fixtures/twopage.pdf
+++ /dev/null
Binary files differ
diff --git a/vendor/plugins/paperclip/test/geometry_test.rb b/vendor/plugins/paperclip/test/geometry_test.rb
deleted file mode 100644
index 134372d..0000000
--- a/vendor/plugins/paperclip/test/geometry_test.rb
+++ /dev/null
@@ -1,168 +0,0 @@
1require 'test/helper'
2
3class GeometryTest < Test::Unit::TestCase
4 context "Paperclip::Geometry" do
5 should "correctly report its given dimensions" do
6 assert @geo = Paperclip::Geometry.new(1024, 768)
7 assert_equal 1024, @geo.width
8 assert_equal 768, @geo.height
9 end
10
11 should "set height to 0 if height dimension is missing" do
12 assert @geo = Paperclip::Geometry.new(1024)
13 assert_equal 1024, @geo.width
14 assert_equal 0, @geo.height
15 end
16
17 should "set width to 0 if width dimension is missing" do
18 assert @geo = Paperclip::Geometry.new(nil, 768)
19 assert_equal 0, @geo.width
20 assert_equal 768, @geo.height
21 end
22
23 should "be generated from a WxH-formatted string" do
24 assert @geo = Paperclip::Geometry.parse("800x600")
25 assert_equal 800, @geo.width
26 assert_equal 600, @geo.height
27 end
28
29 should "be generated from a xH-formatted string" do
30 assert @geo = Paperclip::Geometry.parse("x600")
31 assert_equal 0, @geo.width
32 assert_equal 600, @geo.height
33 end
34
35 should "be generated from a Wx-formatted string" do
36 assert @geo = Paperclip::Geometry.parse("800x")
37 assert_equal 800, @geo.width
38 assert_equal 0, @geo.height
39 end
40
41 should "be generated from a W-formatted string" do
42 assert @geo = Paperclip::Geometry.parse("800")
43 assert_equal 800, @geo.width
44 assert_equal 0, @geo.height
45 end
46
47 should "ensure the modifier is nil if not present" do
48 assert @geo = Paperclip::Geometry.parse("123x456")
49 assert_nil @geo.modifier
50 end
51
52 ['>', '<', '#', '@', '%', '^', '!', nil].each do |mod|
53 should "ensure the modifier #{mod.inspect} is preserved" do
54 assert @geo = Paperclip::Geometry.parse("123x456#{mod}")
55 assert_equal mod, @geo.modifier
56 assert_equal "123x456#{mod}", @geo.to_s
57 end
58 end
59
60 ['>', '<', '#', '@', '%', '^', '!', nil].each do |mod|
61 should "ensure the modifier #{mod.inspect} is preserved with no height" do
62 assert @geo = Paperclip::Geometry.parse("123x#{mod}")
63 assert_equal mod, @geo.modifier
64 assert_equal "123#{mod}", @geo.to_s
65 end
66 end
67
68 should "make sure the modifier gets passed during transformation_to" do
69 assert @src = Paperclip::Geometry.parse("123x456")
70 assert @dst = Paperclip::Geometry.parse("123x456>")
71 assert_equal "123x456>", @src.transformation_to(@dst).to_s
72 end
73
74 should "generate correct ImageMagick formatting string for W-formatted string" do
75 assert @geo = Paperclip::Geometry.parse("800")
76 assert_equal "800", @geo.to_s
77 end
78
79 should "generate correct ImageMagick formatting string for Wx-formatted string" do
80 assert @geo = Paperclip::Geometry.parse("800x")
81 assert_equal "800", @geo.to_s
82 end
83
84 should "generate correct ImageMagick formatting string for xH-formatted string" do
85 assert @geo = Paperclip::Geometry.parse("x600")
86 assert_equal "x600", @geo.to_s
87 end
88
89 should "generate correct ImageMagick formatting string for WxH-formatted string" do
90 assert @geo = Paperclip::Geometry.parse("800x600")
91 assert_equal "800x600", @geo.to_s
92 end
93
94 should "be generated from a file" do
95 file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
96 file = File.new(file, 'rb')
97 assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) }
98 assert @geo.height > 0
99 assert @geo.width > 0
100 end
101
102 should "be generated from a file path" do
103 file = File.join(File.dirname(__FILE__), "fixtures", "5k.png")
104 assert_nothing_raised{ @geo = Paperclip::Geometry.from_file(file) }
105 assert @geo.height > 0
106 assert @geo.width > 0
107 end
108
109 should "not generate from a bad file" do
110 file = "/home/This File Does Not Exist.omg"
111 assert_raise(Paperclip::NotIdentifiedByImageMagickError){ @geo = Paperclip::Geometry.from_file(file) }
112 end
113
114 [['vertical', 900, 1440, true, false, false, 1440, 900, 0.625],
115 ['horizontal', 1024, 768, false, true, false, 1024, 768, 1.3333],
116 ['square', 100, 100, false, false, true, 100, 100, 1]].each do |args|
117 context "performing calculations on a #{args[0]} viewport" do
118 setup do
119 @geo = Paperclip::Geometry.new(args[1], args[2])
120 end
121
122 should "#{args[3] ? "" : "not"} be vertical" do
123 assert_equal args[3], @geo.vertical?
124 end
125
126 should "#{args[4] ? "" : "not"} be horizontal" do
127 assert_equal args[4], @geo.horizontal?
128 end
129
130 should "#{args[5] ? "" : "not"} be square" do
131 assert_equal args[5], @geo.square?
132 end
133
134 should "report that #{args[6]} is the larger dimension" do
135 assert_equal args[6], @geo.larger
136 end
137
138 should "report that #{args[7]} is the smaller dimension" do
139 assert_equal args[7], @geo.smaller
140 end
141
142 should "have an aspect ratio of #{args[8]}" do
143 assert_in_delta args[8], @geo.aspect, 0.0001
144 end
145 end
146 end
147
148 [[ [1000, 100], [64, 64], "x64", "64x64+288+0" ],
149 [ [100, 1000], [50, 950], "x950", "50x950+22+0" ],
150 [ [100, 1000], [50, 25], "50x", "50x25+0+237" ]]. each do |args|
151 context "of #{args[0].inspect} and given a Geometry #{args[1].inspect} and sent transform_to" do
152 setup do
153 @geo = Paperclip::Geometry.new(*args[0])
154 @dst = Paperclip::Geometry.new(*args[1])
155 @scale, @crop = @geo.transformation_to @dst, true
156 end
157
158 should "be able to return the correct scaling transformation geometry #{args[2]}" do
159 assert_equal args[2], @scale
160 end
161
162 should "be able to return the correct crop transformation geometry #{args[3]}" do
163 assert_equal args[3], @crop
164 end
165 end
166 end
167 end
168end
diff --git a/vendor/plugins/paperclip/test/helper.rb b/vendor/plugins/paperclip/test/helper.rb
deleted file mode 100644
index 3e7e6a1..0000000
--- a/vendor/plugins/paperclip/test/helper.rb
+++ /dev/null
@@ -1,82 +0,0 @@
1require 'rubygems'
2require 'test/unit'
3gem 'thoughtbot-shoulda', ">= 2.9.0"
4require 'shoulda'
5require 'mocha'
6require 'tempfile'
7
8gem 'sqlite3-ruby'
9
10require 'active_record'
11require 'active_support'
12begin
13 require 'ruby-debug'
14rescue LoadError
15 puts "ruby-debug not loaded"
16end
17
18ROOT = File.join(File.dirname(__FILE__), '..')
19RAILS_ROOT = ROOT
20
21$LOAD_PATH << File.join(ROOT, 'lib')
22$LOAD_PATH << File.join(ROOT, 'lib', 'paperclip')
23
24require File.join(ROOT, 'lib', 'paperclip.rb')
25
26require 'shoulda_macros/paperclip'
27
28ENV['RAILS_ENV'] ||= 'test'
29
30FIXTURES_DIR = File.join(File.dirname(__FILE__), "fixtures")
31config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
32ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
33ActiveRecord::Base.establish_connection(config['test'])
34
35def reset_class class_name
36 ActiveRecord::Base.send(:include, Paperclip)
37 Object.send(:remove_const, class_name) rescue nil
38 klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
39 klass.class_eval{ include Paperclip }
40 klass
41end
42
43def reset_table table_name, &block
44 block ||= lambda{ true }
45 ActiveRecord::Base.connection.create_table :dummies, {:force => true}, &block
46end
47
48def modify_table table_name, &block
49 ActiveRecord::Base.connection.change_table :dummies, &block
50end
51
52def rebuild_model options = {}
53 ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
54 table.column :other, :string
55 table.column :avatar_file_name, :string
56 table.column :avatar_content_type, :string
57 table.column :avatar_file_size, :integer
58 table.column :avatar_updated_at, :datetime
59 end
60 rebuild_class options
61end
62
63def rebuild_class options = {}
64 ActiveRecord::Base.send(:include, Paperclip)
65 Object.send(:remove_const, "Dummy") rescue nil
66 Object.const_set("Dummy", Class.new(ActiveRecord::Base))
67 Dummy.class_eval do
68 include Paperclip
69 has_attached_file :avatar, options
70 end
71end
72
73def temporary_rails_env(new_env)
74 old_env = defined?(RAILS_ENV) ? RAILS_ENV : nil
75 silence_warnings do
76 Object.const_set("RAILS_ENV", new_env)
77 end
78 yield
79 silence_warnings do
80 Object.const_set("RAILS_ENV", old_env)
81 end
82end
diff --git a/vendor/plugins/paperclip/test/integration_test.rb b/vendor/plugins/paperclip/test/integration_test.rb
deleted file mode 100644
index f7014ac..0000000
--- a/vendor/plugins/paperclip/test/integration_test.rb
+++ /dev/null
@@ -1,481 +0,0 @@
1require 'test/helper'
2
3class IntegrationTest < Test::Unit::TestCase
4 context "Many models at once" do
5 setup do
6 rebuild_model
7 @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
8 300.times do |i|
9 Dummy.create! :avatar => @file
10 end
11 end
12
13 should "not exceed the open file limit" do
14 assert_nothing_raised do
15 dummies = Dummy.find(:all)
16 dummies.each { |dummy| dummy.avatar }
17 end
18 end
19 end
20
21 context "An attachment" do
22 setup do
23 rebuild_model :styles => { :thumb => "50x50#" }
24 @dummy = Dummy.new
25 @file = File.new(File.join(File.dirname(__FILE__),
26 "fixtures",
27 "5k.png"), 'rb')
28 @dummy.avatar = @file
29 assert @dummy.save
30 end
31
32 teardown { @file.close }
33
34 should "create its thumbnails properly" do
35 assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
36 end
37
38 context "redefining its attachment styles" do
39 setup do
40 Dummy.class_eval do
41 has_attached_file :avatar, :styles => { :thumb => "150x25#" }
42 has_attached_file :avatar, :styles => { :thumb => "150x25#", :dynamic => lambda { |a| '50x50#' } }
43 end
44 @d2 = Dummy.find(@dummy.id)
45 @d2.avatar.reprocess!
46 @d2.save
47 end
48
49 should "create its thumbnails properly" do
50 assert_match /\b150x25\b/, `identify "#{@dummy.avatar.path(:thumb)}"`
51 assert_match /\b50x50\b/, `identify "#{@dummy.avatar.path(:dynamic)}"`
52 end
53 end
54 end
55
56 context "A model that modifies its original" do
57 setup do
58 rebuild_model :styles => { :original => "2x2#" }
59 @dummy = Dummy.new
60 @file = File.new(File.join(File.dirname(__FILE__),
61 "fixtures",
62 "5k.png"), 'rb')
63 @dummy.avatar = @file
64 end
65
66 should "report the file size of the processed file and not the original" do
67 assert_not_equal @file.size, @dummy.avatar.size
68 end
69
70 teardown { @file.close }
71 end
72
73 context "A model with attachments scoped under an id" do
74 setup do
75 rebuild_model :styles => { :large => "100x100",
76 :medium => "50x50" },
77 :path => ":rails_root/tmp/:id/:attachments/:style.:extension"
78 @dummy = Dummy.new
79 @file = File.new(File.join(File.dirname(__FILE__),
80 "fixtures",
81 "5k.png"), 'rb')
82 @dummy.avatar = @file
83 end
84
85 teardown { @file.close }
86
87 context "when saved" do
88 setup do
89 @dummy.save
90 @saved_path = @dummy.avatar.path(:large)
91 end
92
93 should "have a large file in the right place" do
94 assert File.exists?(@dummy.avatar.path(:large))
95 end
96
97 context "and deleted" do
98 setup do
99 @dummy.avatar.clear
100 @dummy.save
101 end
102
103 should "not have a large file in the right place anymore" do
104 assert ! File.exists?(@saved_path)
105 end
106
107 should "not have its next two parent directories" do
108 assert ! File.exists?(File.dirname(@saved_path))
109 assert ! File.exists?(File.dirname(File.dirname(@saved_path)))
110 end
111
112 before_should "not die if an unexpected SystemCallError happens" do
113 FileUtils.stubs(:rmdir).raises(Errno::EPIPE)
114 end
115 end
116 end
117 end
118
119 context "A model with no attachment validation" do
120 setup do
121 rebuild_model :styles => { :large => "300x300>",
122 :medium => "100x100",
123 :thumb => ["32x32#", :gif] },
124 :default_style => :medium,
125 :url => "/:attachment/:class/:style/:id/:basename.:extension",
126 :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
127 @dummy = Dummy.new
128 end
129
130 should "have its definition return false when asked about whiny_thumbnails" do
131 assert ! Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
132 end
133
134 context "when validates_attachment_thumbnails is called" do
135 setup do
136 Dummy.validates_attachment_thumbnails :avatar
137 end
138
139 should "have its definition return true when asked about whiny_thumbnails" do
140 assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
141 end
142 end
143
144 context "redefined to have attachment validations" do
145 setup do
146 rebuild_model :styles => { :large => "300x300>",
147 :medium => "100x100",
148 :thumb => ["32x32#", :gif] },
149 :whiny_thumbnails => true,
150 :default_style => :medium,
151 :url => "/:attachment/:class/:style/:id/:basename.:extension",
152 :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
153 end
154
155 should "have its definition return true when asked about whiny_thumbnails" do
156 assert_equal true, Dummy.attachment_definitions[:avatar][:whiny_thumbnails]
157 end
158 end
159 end
160
161 context "A model with no convert_options setting" do
162 setup do
163 rebuild_model :styles => { :large => "300x300>",
164 :medium => "100x100",
165 :thumb => ["32x32#", :gif] },
166 :default_style => :medium,
167 :url => "/:attachment/:class/:style/:id/:basename.:extension",
168 :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
169 @dummy = Dummy.new
170 end
171
172 should "have its definition return nil when asked about convert_options" do
173 assert ! Dummy.attachment_definitions[:avatar][:convert_options]
174 end
175
176 context "redefined to have convert_options setting" do
177 setup do
178 rebuild_model :styles => { :large => "300x300>",
179 :medium => "100x100",
180 :thumb => ["32x32#", :gif] },
181 :convert_options => "-strip -depth 8",
182 :default_style => :medium,
183 :url => "/:attachment/:class/:style/:id/:basename.:extension",
184 :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
185 end
186
187 should "have its definition return convert_options value when asked about convert_options" do
188 assert_equal "-strip -depth 8", Dummy.attachment_definitions[:avatar][:convert_options]
189 end
190 end
191 end
192
193 context "A model with a filesystem attachment" do
194 setup do
195 rebuild_model :styles => { :large => "300x300>",
196 :medium => "100x100",
197 :thumb => ["32x32#", :gif] },
198 :whiny_thumbnails => true,
199 :default_style => :medium,
200 :url => "/:attachment/:class/:style/:id/:basename.:extension",
201 :path => ":rails_root/tmp/:attachment/:class/:style/:id/:basename.:extension"
202 @dummy = Dummy.new
203 @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
204 @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
205
206 assert @dummy.avatar = @file
207 assert @dummy.valid?
208 assert @dummy.save
209 end
210
211 should "write and delete its files" do
212 [["434x66", :original],
213 ["300x46", :large],
214 ["100x15", :medium],
215 ["32x32", :thumb]].each do |geo, style|
216 cmd = %Q[identify -format "%wx%h" "#{@dummy.avatar.path(style)}"]
217 assert_equal geo, `#{cmd}`.chomp, cmd
218 end
219
220 saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
221
222 @d2 = Dummy.find(@dummy.id)
223 assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path}"`.chomp
224 assert_equal "434x66", `identify -format "%wx%h" "#{@d2.avatar.path(:original)}"`.chomp
225 assert_equal "300x46", `identify -format "%wx%h" "#{@d2.avatar.path(:large)}"`.chomp
226 assert_equal "100x15", `identify -format "%wx%h" "#{@d2.avatar.path(:medium)}"`.chomp
227 assert_equal "32x32", `identify -format "%wx%h" "#{@d2.avatar.path(:thumb)}"`.chomp
228
229 @dummy.avatar = "not a valid file but not nil"
230 assert_equal File.basename(@file.path), @dummy.avatar_file_name
231 assert @dummy.valid?
232 assert @dummy.save
233
234 saved_paths.each do |p|
235 assert File.exists?(p)
236 end
237
238 @dummy.avatar.clear
239 assert_nil @dummy.avatar_file_name
240 assert @dummy.valid?
241 assert @dummy.save
242
243 saved_paths.each do |p|
244 assert ! File.exists?(p)
245 end
246
247 @d2 = Dummy.find(@dummy.id)
248 assert_nil @d2.avatar_file_name
249 end
250
251 should "work exactly the same when new as when reloaded" do
252 @d2 = Dummy.find(@dummy.id)
253
254 assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
255 [:thumb, :medium, :large, :original].each do |style|
256 assert_equal @dummy.avatar.path(style), @d2.avatar.path(style)
257 end
258
259 saved_paths = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.path(s) }
260
261 @d2.avatar.clear
262 assert @d2.save
263
264 saved_paths.each do |p|
265 assert ! File.exists?(p)
266 end
267 end
268
269 should "know the difference between good files, bad files, and not files" do
270 expected = @dummy.avatar.to_file
271 @dummy.avatar = "not a file"
272 assert @dummy.valid?
273 assert_equal expected.path, @dummy.avatar.path
274 expected.close
275
276 @dummy.avatar = @bad_file
277 assert ! @dummy.valid?
278 end
279
280 should "know the difference between good files, bad files, and not files when validating" do
281 Dummy.validates_attachment_presence :avatar
282 @d2 = Dummy.find(@dummy.id)
283 @d2.avatar = @file
284 assert @d2.valid?, @d2.errors.full_messages.inspect
285 @d2.avatar = @bad_file
286 assert ! @d2.valid?
287 end
288
289 should "be able to reload without saving and not have the file disappear" do
290 @dummy.avatar = @file
291 assert @dummy.save
292 @dummy.avatar.clear
293 assert_nil @dummy.avatar_file_name
294 @dummy.reload
295 assert_equal "5k.png", @dummy.avatar_file_name
296 end
297
298 context "that is assigned its file from another Paperclip attachment" do
299 setup do
300 @dummy2 = Dummy.new
301 @file2 = File.new(File.join(FIXTURES_DIR, "12k.png"), 'rb')
302 assert @dummy2.avatar = @file2
303 @dummy2.save
304 end
305
306 should "work when assigned a file" do
307 assert_not_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
308 `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
309
310 assert @dummy.avatar = @dummy2.avatar
311 @dummy.save
312 assert_equal `identify -format "%wx%h" "#{@dummy.avatar.path(:original)}"`,
313 `identify -format "%wx%h" "#{@dummy2.avatar.path(:original)}"`
314 end
315 end
316
317 end
318
319 context "A model with an attachments association and a Paperclip attachment" do
320 setup do
321 Dummy.class_eval do
322 has_many :attachments, :class_name => 'Dummy'
323 end
324
325 @dummy = Dummy.new
326 @dummy.avatar = File.new(File.join(File.dirname(__FILE__),
327 "fixtures",
328 "5k.png"), 'rb')
329 end
330
331 should "should not error when saving" do
332 assert_nothing_raised do
333 @dummy.save!
334 end
335 end
336 end
337
338 if ENV['S3_TEST_BUCKET']
339 def s3_files_for attachment
340 [:thumb, :medium, :large, :original].inject({}) do |files, style|
341 data = `curl "#{attachment.url(style)}" 2>/dev/null`.chomp
342 t = Tempfile.new("paperclip-test")
343 t.binmode
344 t.write(data)
345 t.rewind
346 files[style] = t
347 files
348 end
349 end
350
351 def s3_headers_for attachment, style
352 `curl --head "#{attachment.url(style)}" 2>/dev/null`.split("\n").inject({}) do |h,head|
353 split_head = head.chomp.split(/\s*:\s*/, 2)
354 h[split_head.first.downcase] = split_head.last unless split_head.empty?
355 h
356 end
357 end
358
359 context "A model with an S3 attachment" do
360 setup do
361 rebuild_model :styles => { :large => "300x300>",
362 :medium => "100x100",
363 :thumb => ["32x32#", :gif] },
364 :storage => :s3,
365 :whiny_thumbnails => true,
366 # :s3_options => {:logger => Logger.new(StringIO.new)},
367 :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml")),
368 :default_style => :medium,
369 :bucket => ENV['S3_TEST_BUCKET'],
370 :path => ":class/:attachment/:id/:style/:basename.:extension"
371 @dummy = Dummy.new
372 @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
373 @bad_file = File.new(File.join(FIXTURES_DIR, "bad.png"), 'rb')
374
375 assert @dummy.avatar = @file
376 assert @dummy.valid?
377 assert @dummy.save
378
379 @files_on_s3 = s3_files_for @dummy.avatar
380 end
381
382 should "write and delete its files" do
383 [["434x66", :original],
384 ["300x46", :large],
385 ["100x15", :medium],
386 ["32x32", :thumb]].each do |geo, style|
387 cmd = %Q[identify -format "%wx%h" "#{@files_on_s3[style].path}"]
388 assert_equal geo, `#{cmd}`.chomp, cmd
389 end
390
391 @d2 = Dummy.find(@dummy.id)
392 @d2_files = s3_files_for @d2.avatar
393 [["434x66", :original],
394 ["300x46", :large],
395 ["100x15", :medium],
396 ["32x32", :thumb]].each do |geo, style|
397 cmd = %Q[identify -format "%wx%h" "#{@d2_files[style].path}"]
398 assert_equal geo, `#{cmd}`.chomp, cmd
399 end
400
401 @dummy.avatar = "not a valid file but not nil"
402 assert_equal File.basename(@file.path), @dummy.avatar_file_name
403 assert @dummy.valid?
404 assert @dummy.save
405
406 saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) }
407
408 saved_keys.each do |key|
409 assert key.exists?
410 end
411
412 @dummy.avatar.clear
413 assert_nil @dummy.avatar_file_name
414 assert @dummy.valid?
415 assert @dummy.save
416
417 saved_keys.each do |key|
418 assert ! key.exists?
419 end
420
421 @d2 = Dummy.find(@dummy.id)
422 assert_nil @d2.avatar_file_name
423 end
424
425 should "work exactly the same when new as when reloaded" do
426 @d2 = Dummy.find(@dummy.id)
427
428 assert_equal @dummy.avatar_file_name, @d2.avatar_file_name
429 [:thumb, :medium, :large, :original].each do |style|
430 assert_equal @dummy.avatar.to_file(style).to_s, @d2.avatar.to_file(style).to_s
431 end
432
433 saved_keys = [:thumb, :medium, :large, :original].collect{|s| @dummy.avatar.to_file(s) }
434
435 @d2.avatar.clear
436 assert @d2.save
437
438 saved_keys.each do |key|
439 assert ! key.exists?
440 end
441 end
442
443 should "know the difference between good files, bad files, not files, and nil" do
444 expected = @dummy.avatar.to_file
445 @dummy.avatar = "not a file"
446 assert @dummy.valid?
447 assert_equal expected.full_name, @dummy.avatar.to_file.full_name
448
449 @dummy.avatar = @bad_file
450 assert ! @dummy.valid?
451 @dummy.avatar = nil
452 assert @dummy.valid?
453
454 Dummy.validates_attachment_presence :avatar
455 @d2 = Dummy.find(@dummy.id)
456 @d2.avatar = @file
457 assert @d2.valid?
458 @d2.avatar = @bad_file
459 assert ! @d2.valid?
460 @d2.avatar = nil
461 assert ! @d2.valid?
462 end
463
464 should "be able to reload without saving and not have the file disappear" do
465 @dummy.avatar = @file
466 assert @dummy.save
467 @dummy.avatar = nil
468 assert_nil @dummy.avatar_file_name
469 @dummy.reload
470 assert_equal "5k.png", @dummy.avatar_file_name
471 end
472
473 should "have the right content type" do
474 headers = s3_headers_for(@dummy.avatar, :original)
475 p headers
476 assert_equal 'image/png', headers['content-type']
477 end
478 end
479 end
480end
481
diff --git a/vendor/plugins/paperclip/test/iostream_test.rb b/vendor/plugins/paperclip/test/iostream_test.rb
deleted file mode 100644
index 97030b5..0000000
--- a/vendor/plugins/paperclip/test/iostream_test.rb
+++ /dev/null
@@ -1,71 +0,0 @@
1require 'test/helper'
2
3class IOStreamTest < Test::Unit::TestCase
4 context "IOStream" do
5 should "be included in IO, File, Tempfile, and StringIO" do
6 [IO, File, Tempfile, StringIO].each do |klass|
7 assert klass.included_modules.include?(IOStream), "Not in #{klass}"
8 end
9 end
10 end
11
12 context "A file" do
13 setup do
14 @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
15 end
16
17 teardown { @file.close }
18
19 context "that is sent #stream_to" do
20
21 context "and given a String" do
22 setup do
23 FileUtils.mkdir_p(File.join(ROOT, 'tmp'))
24 assert @result = @file.stream_to(File.join(ROOT, 'tmp', 'iostream.string.test'))
25 end
26
27 should "return a File" do
28 assert @result.is_a?(File)
29 end
30
31 should "contain the same data as the original file" do
32 @file.rewind; @result.rewind
33 assert_equal @file.read, @result.read
34 end
35 end
36
37 context "and given a Tempfile" do
38 setup do
39 tempfile = Tempfile.new('iostream.test')
40 tempfile.binmode
41 assert @result = @file.stream_to(tempfile)
42 end
43
44 should "return a Tempfile" do
45 assert @result.is_a?(Tempfile)
46 end
47
48 should "contain the same data as the original file" do
49 @file.rewind; @result.rewind
50 assert_equal @file.read, @result.read
51 end
52 end
53
54 end
55
56 context "that is sent #to_tempfile" do
57 setup do
58 assert @tempfile = @file.to_tempfile
59 end
60
61 should "convert it to a Tempfile" do
62 assert @tempfile.is_a?(Tempfile)
63 end
64
65 should "have the Tempfile contain the same data as the file" do
66 @file.rewind; @tempfile.rewind
67 assert_equal @file.read, @tempfile.read
68 end
69 end
70 end
71end
diff --git a/vendor/plugins/paperclip/test/matchers/have_attached_file_matcher_test.rb b/vendor/plugins/paperclip/test/matchers/have_attached_file_matcher_test.rb
deleted file mode 100644
index b29ec37..0000000
--- a/vendor/plugins/paperclip/test/matchers/have_attached_file_matcher_test.rb
+++ /dev/null
@@ -1,21 +0,0 @@
1require 'test/helper'
2
3class HaveAttachedFileMatcherTest < Test::Unit::TestCase
4 context "have_attached_file" do
5 setup do
6 @dummy_class = reset_class "Dummy"
7 reset_table "dummies"
8 @matcher = self.class.have_attached_file(:avatar)
9 end
10
11 should "reject a class with no attachment" do
12 assert_rejects @matcher, @dummy_class
13 end
14
15 should "accept a class with an attachment" do
16 modify_table("dummies"){|d| d.string :avatar_file_name }
17 @dummy_class.has_attached_file :avatar
18 assert_accepts @matcher, @dummy_class
19 end
20 end
21end
diff --git a/vendor/plugins/paperclip/test/matchers/validate_attachment_content_type_matcher_test.rb b/vendor/plugins/paperclip/test/matchers/validate_attachment_content_type_matcher_test.rb
deleted file mode 100644
index 241eb5f..0000000
--- a/vendor/plugins/paperclip/test/matchers/validate_attachment_content_type_matcher_test.rb
+++ /dev/null
@@ -1,30 +0,0 @@
1require 'test/helper'
2
3class ValidateAttachmentContentTypeMatcherTest < Test::Unit::TestCase
4 context "validate_attachment_content_type" do
5 setup do
6 reset_table("dummies") do |d|
7 d.string :avatar_file_name
8 end
9 @dummy_class = reset_class "Dummy"
10 @dummy_class.has_attached_file :avatar
11 @matcher = self.class.validate_attachment_content_type(:avatar).
12 allowing(%w(image/png image/jpeg)).
13 rejecting(%w(audio/mp3 application/octet-stream))
14 end
15
16 should "reject a class with no validation" do
17 assert_rejects @matcher, @dummy_class
18 end
19
20 should "reject a class with a validation that doesn't match" do
21 @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{audio/.*}
22 assert_rejects @matcher, @dummy_class
23 end
24
25 should "accept a class with a validation" do
26 @dummy_class.validates_attachment_content_type :avatar, :content_type => %r{image/.*}
27 assert_accepts @matcher, @dummy_class
28 end
29 end
30end
diff --git a/vendor/plugins/paperclip/test/matchers/validate_attachment_presence_matcher_test.rb b/vendor/plugins/paperclip/test/matchers/validate_attachment_presence_matcher_test.rb
deleted file mode 100644
index 860a760..0000000
--- a/vendor/plugins/paperclip/test/matchers/validate_attachment_presence_matcher_test.rb
+++ /dev/null
@@ -1,21 +0,0 @@
1require 'test/helper'
2
3class ValidateAttachmentPresenceMatcherTest < Test::Unit::TestCase
4 context "validate_attachment_presence" do
5 setup do
6 reset_table("dummies"){|d| d.string :avatar_file_name }
7 @dummy_class = reset_class "Dummy"
8 @dummy_class.has_attached_file :avatar
9 @matcher = self.class.validate_attachment_presence(:avatar)
10 end
11
12 should "reject a class with no validation" do
13 assert_rejects @matcher, @dummy_class
14 end
15
16 should "accept a class with a validation" do
17 @dummy_class.validates_attachment_presence :avatar
18 assert_accepts @matcher, @dummy_class
19 end
20 end
21end
diff --git a/vendor/plugins/paperclip/test/matchers/validate_attachment_size_matcher_test.rb b/vendor/plugins/paperclip/test/matchers/validate_attachment_size_matcher_test.rb
deleted file mode 100644
index 7e4c9b4..0000000
--- a/vendor/plugins/paperclip/test/matchers/validate_attachment_size_matcher_test.rb
+++ /dev/null
@@ -1,50 +0,0 @@
1require 'test/helper'
2
3class ValidateAttachmentSizeMatcherTest < Test::Unit::TestCase
4 context "validate_attachment_size" do
5 setup do
6 reset_table("dummies") do |d|
7 d.string :avatar_file_name
8 end
9 @dummy_class = reset_class "Dummy"
10 @dummy_class.has_attached_file :avatar
11 end
12
13 context "of limited size" do
14 setup{ @matcher = self.class.validate_attachment_size(:avatar).in(256..1024) }
15
16 should "reject a class with no validation" do
17 assert_rejects @matcher, @dummy_class
18 end
19
20 should "reject a class with a validation that's too high" do
21 @dummy_class.validates_attachment_size :avatar, :in => 256..2048
22 assert_rejects @matcher, @dummy_class
23 end
24
25 should "reject a class with a validation that's too low" do
26 @dummy_class.validates_attachment_size :avatar, :in => 0..1024
27 assert_rejects @matcher, @dummy_class
28 end
29
30 should "accept a class with a validation that matches" do
31 @dummy_class.validates_attachment_size :avatar, :in => 256..1024
32 assert_accepts @matcher, @dummy_class
33 end
34 end
35
36 context "validates_attachment_size with infinite range" do
37 setup{ @matcher = self.class.validate_attachment_size(:avatar) }
38
39 should "accept a class with an upper limit" do
40 @dummy_class.validates_attachment_size :avatar, :less_than => 1
41 assert_accepts @matcher, @dummy_class
42 end
43
44 should "accept a class with no upper limit" do
45 @dummy_class.validates_attachment_size :avatar, :greater_than => 1
46 assert_accepts @matcher, @dummy_class
47 end
48 end
49 end
50end
diff --git a/vendor/plugins/paperclip/test/paperclip_test.rb b/vendor/plugins/paperclip/test/paperclip_test.rb
deleted file mode 100644
index 8365649..0000000
--- a/vendor/plugins/paperclip/test/paperclip_test.rb
+++ /dev/null
@@ -1,233 +0,0 @@
1require 'test/helper'
2
3class PaperclipTest < Test::Unit::TestCase
4 [:image_magick_path, :convert_path].each do |path|
5 context "Calling Paperclip.run with an #{path} specified" do
6 setup do
7 Paperclip.options[:image_magick_path] = nil
8 Paperclip.options[:convert_path] = nil
9 Paperclip.options[path] = "/usr/bin"
10 end
11
12 should "execute the right command" do
13 Paperclip.expects(:path_for_command).with("convert").returns("/usr/bin/convert")
14 Paperclip.expects(:bit_bucket).returns("/dev/null")
15 Paperclip.expects(:"`").with("/usr/bin/convert one.jpg two.jpg 2>/dev/null")
16 Paperclip.run("convert", "one.jpg two.jpg")
17 end
18 end
19 end
20
21 context "Calling Paperclip.run with no path specified" do
22 setup do
23 Paperclip.options[:image_magick_path] = nil
24 Paperclip.options[:convert_path] = nil
25 end
26
27 should "execute the right command" do
28 Paperclip.expects(:path_for_command).with("convert").returns("convert")
29 Paperclip.expects(:bit_bucket).returns("/dev/null")
30 Paperclip.expects(:"`").with("convert one.jpg two.jpg 2>/dev/null")
31 Paperclip.run("convert", "one.jpg two.jpg")
32 end
33 end
34
35 should "raise when sent #processor and the name of a class that exists but isn't a subclass of Processor" do
36 assert_raises(Paperclip::PaperclipError){ Paperclip.processor(:attachment) }
37 end
38
39 should "raise when sent #processor and the name of a class that doesn't exist" do
40 assert_raises(NameError){ Paperclip.processor(:boogey_man) }
41 end
42
43 should "return a class when sent #processor and the name of a class under Paperclip" do
44 assert_equal ::Paperclip::Thumbnail, Paperclip.processor(:thumbnail)
45 end
46
47 context "Paperclip.bit_bucket" do
48 context "on systems without /dev/null" do
49 setup do
50 File.expects(:exists?).with("/dev/null").returns(false)
51 end
52
53 should "return 'NUL'" do
54 assert_equal "NUL", Paperclip.bit_bucket
55 end
56 end
57
58 context "on systems with /dev/null" do
59 setup do
60 File.expects(:exists?).with("/dev/null").returns(true)
61 end
62
63 should "return '/dev/null'" do
64 assert_equal "/dev/null", Paperclip.bit_bucket
65 end
66 end
67 end
68
69 context "An ActiveRecord model with an 'avatar' attachment" do
70 setup do
71 rebuild_model :path => "tmp/:class/omg/:style.:extension"
72 @file = File.new(File.join(FIXTURES_DIR, "5k.png"), 'rb')
73 end
74
75 teardown { @file.close }
76
77 should "not error when trying to also create a 'blah' attachment" do
78 assert_nothing_raised do
79 Dummy.class_eval do
80 has_attached_file :blah
81 end
82 end
83 end
84
85 context "that is attr_protected" do
86 setup do
87 Dummy.class_eval do
88 attr_protected :avatar
89 end
90 @dummy = Dummy.new
91 end
92
93 should "not assign the avatar on mass-set" do
94 @dummy.attributes = { :other => "I'm set!",
95 :avatar => @file }
96
97 assert_equal "I'm set!", @dummy.other
98 assert ! @dummy.avatar?
99 end
100
101 should "still allow assigment on normal set" do
102 @dummy.other = "I'm set!"
103 @dummy.avatar = @file
104
105 assert_equal "I'm set!", @dummy.other
106 assert @dummy.avatar?
107 end
108 end
109
110 context "with a subclass" do
111 setup do
112 class ::SubDummy < Dummy; end
113 end
114
115 should "be able to use the attachment from the subclass" do
116 assert_nothing_raised do
117 @subdummy = SubDummy.create(:avatar => @file)
118 end
119 end
120
121 should "be able to see the attachment definition from the subclass's class" do
122 assert_equal "tmp/:class/omg/:style.:extension", SubDummy.attachment_definitions[:avatar][:path]
123 end
124
125 teardown do
126 Object.send(:remove_const, "SubDummy") rescue nil
127 end
128 end
129
130 should "have an #avatar method" do
131 assert Dummy.new.respond_to?(:avatar)
132 end
133
134 should "have an #avatar= method" do
135 assert Dummy.new.respond_to?(:avatar=)
136 end
137
138 context "that is valid" do
139 setup do
140 @dummy = Dummy.new
141 @dummy.avatar = @file
142 end
143
144 should "be valid" do
145 assert @dummy.valid?
146 end
147
148 context "then has a validation added that makes it invalid" do
149 setup do
150 assert @dummy.save
151 Dummy.class_eval do
152 validates_attachment_content_type :avatar, :content_type => ["text/plain"]
153 end
154 @dummy2 = Dummy.find(@dummy.id)
155 end
156
157 should "be invalid when reloaded" do
158 assert ! @dummy2.valid?, @dummy2.errors.inspect
159 end
160
161 should "be able to call #valid? twice without having duplicate errors" do
162 @dummy2.avatar.valid?
163 first_errors = @dummy2.avatar.errors
164 @dummy2.avatar.valid?
165 assert_equal first_errors, @dummy2.avatar.errors
166 end
167 end
168 end
169
170 def self.should_validate validation, options, valid_file, invalid_file
171 context "with #{validation} validation and #{options.inspect} options" do
172 setup do
173 Dummy.send(:"validates_attachment_#{validation}", :avatar, options)
174 @dummy = Dummy.new
175 end
176 context "and assigning nil" do
177 setup do
178 @dummy.avatar = nil
179 @dummy.valid?
180 end
181 if validation == :presence
182 should "have an error on the attachment" do
183 assert @dummy.errors.on(:avatar)
184 end
185 else
186 should "not have an error on the attachment" do
187 assert_nil @dummy.errors.on(:avatar)
188 end
189 end
190 end
191 context "and assigned a valid file" do
192 setup do
193 @dummy.avatar = valid_file
194 @dummy.valid?
195 end
196 should "not have an error when assigned a valid file" do
197 assert ! @dummy.avatar.errors.key?(validation)
198 end
199 should "not have an error on the attachment" do
200 assert_nil @dummy.errors.on(:avatar)
201 end
202 end
203 context "and assigned an invalid file" do
204 setup do
205 @dummy.avatar = invalid_file
206 @dummy.valid?
207 end
208 should "have an error when assigned a valid file" do
209 assert_not_nil @dummy.avatar.errors[validation]
210 end
211 should "have an error on the attachment" do
212 assert @dummy.errors.on(:avatar)
213 end
214 end
215 end
216 end
217
218 [[:presence, {}, "5k.png", nil],
219 [:size, {:in => 1..10240}, nil, "12k.png"],
220 [:size, {:less_than => 10240}, "5k.png", "12k.png"],
221 [:size, {:greater_than => 8096}, "12k.png", "5k.png"],
222 [:content_type, {:content_type => "image/png"}, "5k.png", "text.txt"],
223 [:content_type, {:content_type => "text/plain"}, "text.txt", "5k.png"],
224 [:content_type, {:content_type => %r{image/.*}}, "5k.png", "text.txt"]].each do |args|
225 validation, options, valid_file, invalid_file = args
226 valid_file &&= File.open(File.join(FIXTURES_DIR, valid_file), "rb")
227 invalid_file &&= File.open(File.join(FIXTURES_DIR, invalid_file), "rb")
228
229 should_validate validation, options, valid_file, invalid_file
230 end
231
232 end
233end
diff --git a/vendor/plugins/paperclip/test/processor_test.rb b/vendor/plugins/paperclip/test/processor_test.rb
deleted file mode 100644
index a05f0a9..0000000
--- a/vendor/plugins/paperclip/test/processor_test.rb
+++ /dev/null
@@ -1,10 +0,0 @@
1require 'test/helper'
2
3class ProcessorTest < Test::Unit::TestCase
4 should "instantiate and call #make when sent #make to the class" do
5 processor = mock
6 processor.expects(:make).with()
7 Paperclip::Processor.expects(:new).with(:one, :two, :three).returns(processor)
8 Paperclip::Processor.make(:one, :two, :three)
9 end
10end
diff --git a/vendor/plugins/paperclip/test/storage_test.rb b/vendor/plugins/paperclip/test/storage_test.rb
deleted file mode 100644
index e2ed8a4..0000000
--- a/vendor/plugins/paperclip/test/storage_test.rb
+++ /dev/null
@@ -1,277 +0,0 @@
1require 'test/helper'
2
3class StorageTest < Test::Unit::TestCase
4 context "Parsing S3 credentials" do
5 setup do
6 rebuild_model :storage => :s3,
7 :bucket => "testing",
8 :s3_credentials => {:not => :important}
9
10 @dummy = Dummy.new
11 @avatar = @dummy.avatar
12
13 @current_env = ENV['RAILS_ENV']
14 end
15
16 teardown do
17 ENV['RAILS_ENV'] = @current_env
18 end
19
20 should "get the correct credentials when RAILS_ENV is production" do
21 ENV['RAILS_ENV'] = 'production'
22 assert_equal({:key => "12345"},
23 @avatar.parse_credentials('production' => {:key => '12345'},
24 :development => {:key => "54321"}))
25 end
26
27 should "get the correct credentials when RAILS_ENV is development" do
28 ENV['RAILS_ENV'] = 'development'
29 assert_equal({:key => "54321"},
30 @avatar.parse_credentials('production' => {:key => '12345'},
31 :development => {:key => "54321"}))
32 end
33
34 should "return the argument if the key does not exist" do
35 ENV['RAILS_ENV'] = "not really an env"
36 assert_equal({:test => "12345"}, @avatar.parse_credentials(:test => "12345"))
37 end
38 end
39
40 context "" do
41 setup do
42 rebuild_model :storage => :s3,
43 :s3_credentials => {},
44 :bucket => "bucket",
45 :path => ":attachment/:basename.:extension",
46 :url => ":s3_path_url"
47 @dummy = Dummy.new
48 @dummy.avatar = StringIO.new(".")
49 end
50
51 should "return a url based on an S3 path" do
52 assert_match %r{^http://s3.amazonaws.com/bucket/avatars/stringio.txt}, @dummy.avatar.url
53 end
54 end
55 context "" do
56 setup do
57 rebuild_model :storage => :s3,
58 :s3_credentials => {},
59 :bucket => "bucket",
60 :path => ":attachment/:basename.:extension",
61 :url => ":s3_domain_url"
62 @dummy = Dummy.new
63 @dummy.avatar = StringIO.new(".")
64 end
65
66 should "return a url based on an S3 subdomain" do
67 assert_match %r{^http://bucket.s3.amazonaws.com/avatars/stringio.txt}, @dummy.avatar.url
68 end
69 end
70 context "" do
71 setup do
72 rebuild_model :storage => :s3,
73 :s3_credentials => {
74 :production => { :bucket => "prod_bucket" },
75 :development => { :bucket => "dev_bucket" }
76 },
77 :s3_host_alias => "something.something.com",
78 :path => ":attachment/:basename.:extension",
79 :url => ":s3_alias_url"
80 @dummy = Dummy.new
81 @dummy.avatar = StringIO.new(".")
82 end
83
84 should "return a url based on the host_alias" do
85 assert_match %r{^http://something.something.com/avatars/stringio.txt}, @dummy.avatar.url
86 end
87 end
88
89 context "Parsing S3 credentials with a bucket in them" do
90 setup do
91 rebuild_model :storage => :s3,
92 :s3_credentials => {
93 :production => { :bucket => "prod_bucket" },
94 :development => { :bucket => "dev_bucket" }
95 }
96 @dummy = Dummy.new
97 end
98
99 should "get the right bucket in production", :before => lambda{ ENV.expects(:[]).returns('production') } do
100 assert_equal "prod_bucket", @dummy.avatar.bucket_name
101 end
102
103 should "get the right bucket in development", :before => lambda{ ENV.expects(:[]).returns('development') } do
104 assert_equal "dev_bucket", @dummy.avatar.bucket_name
105 end
106 end
107
108 context "An attachment with S3 storage" do
109 setup do
110 rebuild_model :storage => :s3,
111 :bucket => "testing",
112 :path => ":attachment/:style/:basename.:extension",
113 :s3_credentials => {
114 'access_key_id' => "12345",
115 'secret_access_key' => "54321"
116 }
117 end
118
119 should "be extended by the S3 module" do
120 assert Dummy.new.avatar.is_a?(Paperclip::Storage::S3)
121 end
122
123 should "not be extended by the Filesystem module" do
124 assert ! Dummy.new.avatar.is_a?(Paperclip::Storage::Filesystem)
125 end
126
127 context "when assigned" do
128 setup do
129 @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
130 @dummy = Dummy.new
131 @dummy.avatar = @file
132 end
133
134 teardown { @file.close }
135
136 should "not get a bucket to get a URL" do
137 @dummy.avatar.expects(:s3).never
138 @dummy.avatar.expects(:s3_bucket).never
139 assert_match %r{^http://s3\.amazonaws\.com/testing/avatars/original/5k\.png}, @dummy.avatar.url
140 end
141
142 context "and saved" do
143 setup do
144 @s3_mock = stub
145 @bucket_mock = stub
146 RightAws::S3.expects(:new).with("12345", "54321", {}).returns(@s3_mock)
147 @s3_mock.expects(:bucket).with("testing", true, "public-read").returns(@bucket_mock)
148 @key_mock = stub
149 @bucket_mock.expects(:key).returns(@key_mock)
150 @key_mock.expects(:data=)
151 @key_mock.expects(:put).with(nil, 'public-read', 'Content-type' => 'image/png')
152 @dummy.save
153 end
154
155 should "succeed" do
156 assert true
157 end
158 end
159
160 context "and remove" do
161 setup do
162 @s3_mock = stub
163 @bucket_mock = stub
164 RightAws::S3.expects(:new).with("12345", "54321", {}).returns(@s3_mock)
165 @s3_mock.expects(:bucket).with("testing", true, "public-read").returns(@bucket_mock)
166 @key_mock = stub
167 @bucket_mock.expects(:key).at_least(2).returns(@key_mock)
168 @key_mock.expects(:delete)
169 @dummy.destroy_attached_files
170 end
171
172 should "succeed" do
173 assert true
174 end
175 end
176 end
177 end
178
179 context "An attachment with S3 storage and bucket defined as a Proc" do
180 setup do
181 rebuild_model :storage => :s3,
182 :bucket => lambda { |attachment| "bucket_#{attachment.instance.other}" },
183 :s3_credentials => {:not => :important}
184 end
185
186 should "get the right bucket name" do
187 assert "bucket_a", Dummy.new(:other => 'a').avatar.bucket_name
188 assert "bucket_b", Dummy.new(:other => 'b').avatar.bucket_name
189 end
190 end
191
192 context "An attachment with S3 storage and specific s3 headers set" do
193 setup do
194 rebuild_model :storage => :s3,
195 :bucket => "testing",
196 :path => ":attachment/:style/:basename.:extension",
197 :s3_credentials => {
198 'access_key_id' => "12345",
199 'secret_access_key' => "54321"
200 },
201 :s3_headers => {'Cache-Control' => 'max-age=31557600'}
202 end
203
204 context "when assigned" do
205 setup do
206 @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
207 @dummy = Dummy.new
208 @dummy.avatar = @file
209 end
210
211 teardown { @file.close }
212
213 context "and saved" do
214 setup do
215 @s3_mock = stub
216 @bucket_mock = stub
217 RightAws::S3.expects(:new).with("12345", "54321", {}).returns(@s3_mock)
218 @s3_mock.expects(:bucket).with("testing", true, "public-read").returns(@bucket_mock)
219 @key_mock = stub
220 @bucket_mock.expects(:key).returns(@key_mock)
221 @key_mock.expects(:data=)
222 @key_mock.expects(:put).with(nil,
223 'public-read',
224 'Content-type' => 'image/png',
225 'Cache-Control' => 'max-age=31557600')
226 @dummy.save
227 end
228
229 should "succeed" do
230 assert true
231 end
232 end
233 end
234 end
235
236 unless ENV["S3_TEST_BUCKET"].blank?
237 context "Using S3 for real, an attachment with S3 storage" do
238 setup do
239 rebuild_model :styles => { :thumb => "100x100", :square => "32x32#" },
240 :storage => :s3,
241 :bucket => ENV["S3_TEST_BUCKET"],
242 :path => ":class/:attachment/:id/:style.:extension",
243 :s3_credentials => File.new(File.join(File.dirname(__FILE__), "s3.yml"))
244
245 Dummy.delete_all
246 @dummy = Dummy.new
247 end
248
249 should "be extended by the S3 module" do
250 assert Dummy.new.avatar.is_a?(Paperclip::Storage::S3)
251 end
252
253 context "when assigned" do
254 setup do
255 @file = File.new(File.join(File.dirname(__FILE__), 'fixtures', '5k.png'), 'rb')
256 @dummy.avatar = @file
257 end
258
259 teardown { @file.close }
260
261 should "still return a Tempfile when sent #to_io" do
262 assert_equal Tempfile, @dummy.avatar.to_io.class
263 end
264
265 context "and saved" do
266 setup do
267 @dummy.save
268 end
269
270 should "be on S3" do
271 assert true
272 end
273 end
274 end
275 end
276 end
277end
diff --git a/vendor/plugins/paperclip/test/thumbnail_test.rb b/vendor/plugins/paperclip/test/thumbnail_test.rb
deleted file mode 100644
index 624e7fa..0000000
--- a/vendor/plugins/paperclip/test/thumbnail_test.rb
+++ /dev/null
@@ -1,177 +0,0 @@
1require 'test/helper'
2
3class ThumbnailTest < Test::Unit::TestCase
4
5 context "A Paperclip Tempfile" do
6 setup do
7 @tempfile = Paperclip::Tempfile.new("file.jpg")
8 end
9
10 should "have its path contain a real extension" do
11 assert_equal ".jpg", File.extname(@tempfile.path)
12 end
13
14 should "be a real Tempfile" do
15 assert @tempfile.is_a?(::Tempfile)
16 end
17 end
18
19 context "Another Paperclip Tempfile" do
20 setup do
21 @tempfile = Paperclip::Tempfile.new("file")
22 end
23
24 should "not have an extension if not given one" do
25 assert_equal "", File.extname(@tempfile.path)
26 end
27
28 should "still be a real Tempfile" do
29 assert @tempfile.is_a?(::Tempfile)
30 end
31 end
32
33 context "An image" do
34 setup do
35 @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "5k.png"), 'rb')
36 end
37
38 teardown { @file.close }
39
40 [["600x600>", "434x66"],
41 ["400x400>", "400x61"],
42 ["32x32<", "434x66"]
43 ].each do |args|
44 context "being thumbnailed with a geometry of #{args[0]}" do
45 setup do
46 @thumb = Paperclip::Thumbnail.new(@file, :geometry => args[0])
47 end
48
49 should "start with dimensions of 434x66" do
50 cmd = %Q[identify -format "%wx%h" "#{@file.path}"]
51 assert_equal "434x66", `#{cmd}`.chomp
52 end
53
54 should "report the correct target geometry" do
55 assert_equal args[0], @thumb.target_geometry.to_s
56 end
57
58 context "when made" do
59 setup do
60 @thumb_result = @thumb.make
61 end
62
63 should "be the size we expect it to be" do
64 cmd = %Q[identify -format "%wx%h" "#{@thumb_result.path}"]
65 assert_equal args[1], `#{cmd}`.chomp
66 end
67 end
68 end
69 end
70
71 context "being thumbnailed at 100x50 with cropping" do
72 setup do
73 @thumb = Paperclip::Thumbnail.new(@file, :geometry => "100x50#")
74 end
75
76 should "report its correct current and target geometries" do
77 assert_equal "100x50#", @thumb.target_geometry.to_s
78 assert_equal "434x66", @thumb.current_geometry.to_s
79 end
80
81 should "report its correct format" do
82 assert_nil @thumb.format
83 end
84
85 should "have whiny turned on by default" do
86 assert @thumb.whiny
87 end
88
89 should "have convert_options set to nil by default" do
90 assert_equal nil, @thumb.convert_options
91 end
92
93 should "send the right command to convert when sent #make" do
94 Paperclip.expects(:"`").with do |arg|
95 arg.match %r{convert\s+"#{File.expand_path(@thumb.file.path)}\[0\]"\s+-resize\s+\"x50\"\s+-crop\s+\"100x50\+114\+0\"\s+\+repage\s+".*?"}
96 end
97 @thumb.make
98 end
99
100 should "create the thumbnail when sent #make" do
101 dst = @thumb.make
102 assert_match /100x50/, `identify "#{dst.path}"`
103 end
104 end
105
106 context "being thumbnailed with convert options set" do
107 setup do
108 @thumb = Paperclip::Thumbnail.new(@file,
109 :geometry => "100x50#",
110 :convert_options => "-strip -depth 8")
111 end
112
113 should "have convert_options value set" do
114 assert_equal "-strip -depth 8", @thumb.convert_options
115 end
116
117 should "send the right command to convert when sent #make" do
118 Paperclip.expects(:"`").with do |arg|
119 arg.match %r{convert\s+"#{File.expand_path(@thumb.file.path)}\[0\]"\s+-resize\s+"x50"\s+-crop\s+"100x50\+114\+0"\s+\+repage\s+-strip\s+-depth\s+8\s+".*?"}
120 end
121 @thumb.make
122 end
123
124 should "create the thumbnail when sent #make" do
125 dst = @thumb.make
126 assert_match /100x50/, `identify "#{dst.path}"`
127 end
128
129 context "redefined to have bad convert_options setting" do
130 setup do
131 @thumb = Paperclip::Thumbnail.new(@file,
132 :geometry => "100x50#",
133 :convert_options => "-this-aint-no-option")
134 end
135
136 should "error when trying to create the thumbnail" do
137 assert_raises(Paperclip::PaperclipError) do
138 @thumb.make
139 end
140 end
141 end
142 end
143 end
144
145 context "A multipage PDF" do
146 setup do
147 @file = File.new(File.join(File.dirname(__FILE__), "fixtures", "twopage.pdf"), 'rb')
148 end
149
150 teardown { @file.close }
151
152 should "start with two pages with dimensions 612x792" do
153 cmd = %Q[identify -format "%wx%h" "#{@file.path}"]
154 assert_equal "612x792"*2, `#{cmd}`.chomp
155 end
156
157 context "being thumbnailed at 100x100 with cropping" do
158 setup do
159 @thumb = Paperclip::Thumbnail.new(@file, :geometry => "100x100#", :format => :png)
160 end
161
162 should "report its correct current and target geometries" do
163 assert_equal "100x100#", @thumb.target_geometry.to_s
164 assert_equal "612x792", @thumb.current_geometry.to_s
165 end
166
167 should "report its correct format" do
168 assert_equal :png, @thumb.format
169 end
170
171 should "create the thumbnail when sent #make" do
172 dst = @thumb.make
173 assert_match /100x100/, `identify "#{dst.path}"`
174 end
175 end
176 end
177end
diff --git a/vendor/plugins/routing-filter/.gitignore b/vendor/plugins/routing-filter/.gitignore
deleted file mode 100644
index 5657f6e..0000000
--- a/vendor/plugins/routing-filter/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
1vendor \ No newline at end of file
diff --git a/vendor/plugins/routing-filter/MIT-LICENSE b/vendor/plugins/routing-filter/MIT-LICENSE
deleted file mode 100644
index ac93a58..0000000
--- a/vendor/plugins/routing-filter/MIT-LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
1Copyright (c) 2008 Sven Fuchs
2
3Permission is hereby granted, free of charge, to any person obtaining
4a copy of this software and associated documentation files (the
5"Software"), to deal in the Software without restriction, including
6without limitation the rights to use, copy, modify, merge, publish,
7distribute, sublicense, and/or sell copies of the Software, and to
8permit persons to whom the Software is furnished to do so, subject to
9the following conditions:
10
11The above copyright notice and this permission notice shall be
12included in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/vendor/plugins/routing-filter/README.markdown b/vendor/plugins/routing-filter/README.markdown
deleted file mode 100644
index 77ee55d..0000000
--- a/vendor/plugins/routing-filter/README.markdown
+++ /dev/null
@@ -1,123 +0,0 @@
1## Routing Filter
2
3This plugin is a wild hack that wraps around the complex beast that the Rails
4routing system is to allow for unseen flexibility and power in Rails URL
5recognition and generation.
6
7As powerful and awesome the Rails' routes are, when you need to design your
8URLs in a manner that only slightly leaves the paved cowpaths of Rails
9conventions, you're usually unable to use all the goodness of helpers and
10convenience that Rails ships with.
11
12## Usage
13
14This plugin comes with a locale routing filter that demonstrates the
15implementation of a custom filter.
16
17The following would be a sceleton of an empty filter:
18
19 module RoutingFilter
20 class Awesomeness < Base
21 def around_recognize(route, path, env)
22 # Alter the path here before it gets recognized.
23 # Make sure to yield (calls the next around filter if present and
24 # eventually `recognize_path` on the routeset):
25 returning yield do |params|
26 # You can additionally modify the params here before they get passed
27 # to the controller.
28 end
29 end
30
31 def around_generate(controller, *args, &block)
32 # Alter arguments here before they are passed to `url_for`.
33 # Make sure to yield (calls the next around filter if present and
34 # eventually `url_for` on the controller):
35 returning yield do |result|
36 # You can change the generated url_or_path here. Make sure to use
37 # one of the "in-place" modifying String methods though (like sub!
38 # and friends).
39 end
40 end
41 end
42 end
43
44You then have to specify the filter explicitely in your routes.rb:
45
46 ActionController::Routing::Routes.draw do |map|
47 map.filter 'awesomeness'
48 end
49
50(I am not sure if it makes sense to provide more technical information than
51this because the usage of this plugin definitely requires some advanced
52knowledge about Rails internals and especially its routing system. So, I
53figure, anyone who could use this should also be able to read the code and
54figure out what it's doing much better then from any lengthy documentation.
55
56If I'm mistaken on this please drop me an email with your suggestions.)
57
58
59## Rationale: Two example usecases
60
61An early usecase from which this originated was the need to define a locale
62at the beginning of an URL in a way so that
63
64* the locale can be omitted when it is the default locale
65* all the url\_helpers that are generated by named routes continue to work in
66a concise manner (i.e. without specifying all parameters again and again)
67* ideally also plays nicely with default route helpers in tests/specs
68
69You can read about this struggle and two possible, yet unsatisfying solutions
70[here](http://www.artweb-design.de/2007/5/13/concise-localized-rails-url-helpers-solved-twice).
71The conclusion so far is that Rails itself does not provide the tools to solve
72this problem in a clean and dry way.
73
74Another usecase that eventually spawned the manifestation of this plugin was
75the need to map an arbitrary count of path segments to a certain model
76instance. In an application that I've been working on recently I needed to
77map URL paths to a nested tree of models like so:
78
79 root
80 + docs
81 + api
82 + wiki
83
84E.g. the docs section should map to the path `/docs`, the api section to
85the path `/docs/api` and so on. Furthermore, after these paths there need to be
86more things to be specified. E.g. the wiki needs to define a whole Rails
87resource with URLs like `/docs/wiki/pages/1/edit`.
88
89The only way to solve this problem with Rails' routing toolkit is to map
90a big, bold `/*everything` catch-all ("globbing") route and process the whole
91path in a custom dispatcher.
92
93This, of course, is a really unsatisfying solution because one has to
94reimplement everything that Rails routes are here to help with: regarding both
95URL recognition (like parameter mappings, resources, ...) and generation
96(url\_helpers).
97
98## Solution
99
100This plugin offers a solution that takes exactly the opposite route.
101
102Instead of trying to change things *between* the URL recognition and
103generation stages to achieve the desired result it *wraps around* the whole
104routing system and allows to pre- and post-filter both what goes into it
105(URL recognition) and what comes out of it (URL generation).
106
107This way we can leave *everything* else completely untouched.
108
109* We can tinker with the URLs that we receive from the server and feed URLs to
110Rails that perfectly match the best breed of Rails' conventions.
111* Inside of the application we can use all the nice helper goodness and
112conveniences that rely on these conventions being followed.
113* Finally we can accept URLs that have been generated by the url\_helpers and,
114again, mutate them in the way that matches our requirements.
115
116So, even though the plugin itself is a blatant monkey-patch to one of the
117most complex area of Rails internals, this solution seems to be effectively
118less intrusive and pricey than others are.
119
120## Etc
121
122Authors: [Sven Fuchs](http://www.artweb-design.de) <svenfuchs at artweb-design dot de>
123License: MIT \ No newline at end of file
diff --git a/vendor/plugins/routing-filter/init.rb b/vendor/plugins/routing-filter/init.rb
deleted file mode 100644
index 1189921..0000000
--- a/vendor/plugins/routing-filter/init.rb
+++ /dev/null
@@ -1 +0,0 @@
1require 'routing_filter' \ No newline at end of file
diff --git a/vendor/plugins/routing-filter/lib/routing_filter.rb b/vendor/plugins/routing-filter/lib/routing_filter.rb
deleted file mode 100644
index d67be14..0000000
--- a/vendor/plugins/routing-filter/lib/routing_filter.rb
+++ /dev/null
@@ -1,69 +0,0 @@
1module RoutingFilter
2 mattr_accessor :active
3 @@active = true
4
5 class Chain < Array
6 def << (filter)
7 filter.successor = last
8 super
9 end
10
11 def run(method, *args, &final)
12 RoutingFilter.active ? last.run(method, *args, &final) : final.call
13 end
14 end
15end
16
17# allows to install a filter to the route set by calling: map.filter 'locale'
18ActionController::Routing::RouteSet::Mapper.class_eval do
19 def filter(name, options = {})
20 require "routing_filter/#{name}"
21 klass = RoutingFilter.const_get name.to_s.camelize
22 @set.filters << klass.new(options)
23 end
24end
25
26# same here for the optimized url generation in named routes
27ActionController::Routing::RouteSet::NamedRouteCollection.class_eval do
28 # gosh. monkey engineering optimization code
29 def generate_optimisation_block_with_filtering(*args)
30 code = generate_optimisation_block_without_filtering *args
31 if match = code.match(%r(^return (.*) if (.*)))
32 # returned string must not contain newlines, or we'll spill out of inline code comments in ActionController::Routing::RouteSet::NamedRouteCollection#define_url_helper (as of http://github.com/rails/rails/commit/a2270ef2594b97891994848138614657363f2806)
33 "returning(#{match[1]}) { |result| ActionController::Routing::Routes.filters.run :around_generate, *args, &lambda{ result } } if #{match[2]}"
34 end
35 end
36 alias_method_chain :generate_optimisation_block, :filtering
37end
38
39ActionController::Routing::RouteSet.class_eval do
40 def filters
41 @filters ||= RoutingFilter::Chain.new
42 end
43
44 def recognize_path_with_filtering(path, env)
45 path = path.dup # string is frozen due to memoize
46 filters.run :around_recognize, path, env, &lambda{ recognize_path_without_filtering(path, env) }
47 end
48 alias_method_chain :recognize_path, :filtering
49
50 def generate_with_filtering(*args)
51 filters.run :around_generate, args.first, &lambda{ generate_without_filtering(*args) }
52 end
53 alias_method_chain :generate, :filtering
54
55 # add some useful information to the request environment
56 # right, this is from jamis buck's excellent article about routes internals
57 # http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2
58 # TODO move this ... where?
59 alias_method :extract_request_environment_without_host, :extract_request_environment unless method_defined? :extract_request_environment_without_host
60 def extract_request_environment(request)
61 returning extract_request_environment_without_host(request) do |env|
62 env.merge! :host => request.host,
63 :port => request.port,
64 :host_with_port => request.host_with_port,
65 :domain => request.domain,
66 :subdomain => request.subdomains.first
67 end
68 end
69end \ No newline at end of file
diff --git a/vendor/plugins/routing-filter/lib/routing_filter/base.rb b/vendor/plugins/routing-filter/lib/routing_filter/base.rb
deleted file mode 100644
index bc60ba5..0000000
--- a/vendor/plugins/routing-filter/lib/routing_filter/base.rb
+++ /dev/null
@@ -1,15 +0,0 @@
1module RoutingFilter
2 class Base
3 attr_accessor :successor, :options
4
5 def initialize(options)
6 @options = options
7 options.each{|name, value| instance_variable_set :"@#{name}", value }
8 end
9
10 def run(method, *args, &block)
11 successor = @successor ? lambda { @successor.run(method, *args, &block) } : block
12 send method, *args, &successor
13 end
14 end
15end \ No newline at end of file
diff --git a/vendor/plugins/routing-filter/lib/routing_filter/locale.rb b/vendor/plugins/routing-filter/lib/routing_filter/locale.rb
deleted file mode 100644
index 6fc3e11..0000000
--- a/vendor/plugins/routing-filter/lib/routing_filter/locale.rb
+++ /dev/null
@@ -1,35 +0,0 @@
1require 'i18n'
2require 'routing_filter/base'
3
4module RoutingFilter
5 class Locale < Base
6 @@default_locale = :he
7 cattr_reader :default_locale
8
9 class << self
10 def default_locale=(locale)
11 @@default_locale = locale.to_sym
12 end
13 end
14
15 # remove the locale from the beginning of the path, pass the path
16 # to the given block and set it to the resulting params hash
17 def around_recognize(path, env, &block)
18 locale = nil
19 path.sub! %r(^/([a-zA-Z]{2})(?=/|$)) do locale = $1; '' end
20 returning yield do |params|
21 params[:locale] = locale if locale
22 end
23 end
24
25 def around_generate(*args, &block)
26 locale = args.extract_options!.delete(:locale) || I18n.locale
27 returning yield do |result|
28 if locale.to_sym != @@default_locale
29 target = result.is_a?(Array) ? result.first : result
30 target.sub!(%r(^(http.?://[^/]*)?(.*))){ "#{$1}/#{locale}#{$2}" }
31 end
32 end
33 end
34 end
35end \ No newline at end of file
diff --git a/vendor/plugins/routing-filter/lib/routing_filter/pagination.rb b/vendor/plugins/routing-filter/lib/routing_filter/pagination.rb
deleted file mode 100644
index a5bd7aa..0000000
--- a/vendor/plugins/routing-filter/lib/routing_filter/pagination.rb
+++ /dev/null
@@ -1,19 +0,0 @@
1require 'routing_filter/base'
2
3module RoutingFilter
4 class Pagination < Base
5 def around_recognize(path, env, &block)
6 path.gsub! %r(/pages/([\d]+)/?$), ''
7 returning yield(path, env) do |params|
8 params[:page] = $1.to_i if $1
9 end
10 end
11
12 def around_generate(*args, &block)
13 page = args.extract_options!.delete(:page)
14 returning yield do |result|
15 result.replace "#{result}/pages/#{page}" if page && page != 1
16 end
17 end
18 end
19end \ No newline at end of file
diff --git a/vendor/plugins/routing-filter/spec/generation_spec.rb b/vendor/plugins/routing-filter/spec/generation_spec.rb
deleted file mode 100644
index 224cd4e..0000000
--- a/vendor/plugins/routing-filter/spec/generation_spec.rb
+++ /dev/null
@@ -1,283 +0,0 @@
1require File.dirname(__FILE__) + '/spec_helper.rb'
2
3describe 'RoutingFilter', 'url generation' do
4 include RoutingFilterHelpers
5
6 before :each do
7 RoutingFilter::Locale.default_locale = :en
8 I18n.default_locale = :en
9 I18n.locale = :en
10
11 @controller = instantiate_controller :locale => 'de', :id => 1
12 @set = draw_routes do |map|
13 map.section 'sections/:id', :controller => 'sections', :action => "show"
14 map.section_article 'sections/:section_id/articles/:id', :controller => 'articles', :action => "show"
15
16 map.filter 'locale'
17 map.filter 'pagination'
18 end
19
20 @site = Site.new
21 @section = Section.new
22 @article = Article.new
23
24 @params = {:controller => 'sections', :action => "show", :id => "1"}
25 @article_params = {:controller => 'articles', :action => 'show', :section_id => "1", :id => "1"}
26 @locale_filter = @set.filters.first
27
28 Section.stub!(:types).and_return ['Section']
29 Section.stub!(:find).and_return @section
30 end
31
32 def should_recognize_path(path, params)
33 @set.recognize_path(path, {}).should == params
34 end
35
36 def section_path(*args)
37 @controller.send :section_path, *args
38 end
39
40 def section_article_path(*args)
41 @controller.send :section_article_path, *args
42 end
43
44 def url_for(*args)
45 @controller.send :url_for, *args
46 end
47
48 describe "named route url_helpers" do
49 describe "a not nested resource" do
50 it 'does not change the section_path when the current locale is the default locale and no page option given' do
51 section_path(:id => 1).should == '/sections/1'
52 end
53
54 it 'does not change the section_path when given page option equals 1' do
55 section_path(:id => 1, :page => 1).should == '/sections/1'
56 end
57
58 it 'appends the pages segments to section_path when given page option does not equal 1' do
59 section_path(:id => 1, :page => 2).should == '/sections/1/pages/2'
60 end
61
62 it 'prepends the current locale to section_path when it is not the default locale' do
63 I18n.locale = :de
64 section_path(:id => 1).should == '/de/sections/1'
65 end
66
67 it 'prepends a given locale param to section_path when it is not the default locale' do
68 I18n.locale = :de
69 section_path(:id => 1, :locale => :fi).should == '/fi/sections/1'
70 end
71
72 it 'works on section_path with both a locale and page option' do
73 section_path(:id => 1, :locale => :fi, :page => 2).should == '/fi/sections/1/pages/2'
74 end
75 end
76
77 describe "a nested resource" do
78 it 'does not change the section_article_path when the current locale is the default locale and no page option given' do
79 section_article_path(:section_id => 1, :id => 1).should == '/sections/1/articles/1'
80 end
81
82 it 'does not change the section_article_path when given page option equals 1' do
83 section_article_path(:section_id => 1, :id => 1, :page => 1).should == '/sections/1/articles/1'
84 end
85
86 it 'appends the pages segments to section_article_path when given page option does not equal 1' do
87 section_article_path(:section_id => 1, :id => 1, :page => 2).should == '/sections/1/articles/1/pages/2'
88 end
89
90 it 'prepends the current locale to section_article_path when it is not the default locale' do
91 I18n.locale = :de
92 section_article_path(:section_id => 1, :id => 1).should == '/de/sections/1/articles/1'
93 end
94
95 it 'prepends a given locale param to section_article_path when it is not the default locale' do
96 I18n.locale = :de
97 section_article_path(:section_id => 1, :id => 1, :locale => :fi).should == '/fi/sections/1/articles/1'
98 end
99
100 it 'works on section_article_path with both a locale and page option' do
101 section_article_path(:section_id => 1, :id => 1, :locale => :fi, :page => 2).should == '/fi/sections/1/articles/1/pages/2'
102 end
103 end
104 end
105
106 describe 'when used with named route url_helper with "optimized" generation blocks' do
107 describe "a not nested resource" do
108 # uses optimization
109 it 'does not change the section_path when the current locale is the default locale and no page option given' do
110 section_path(1).should == '/sections/1'
111 end
112
113 # uses optimization
114 it 'prepends the current locale to section_path when it is not the default locale' do
115 I18n.locale = :de
116 section_path(1).should == '/de/sections/1'
117 end
118
119 it 'prepends a given locale param to section_path when it is not the default locale' do
120 I18n.locale = :de
121 section_path(1, :locale => :fi).should == '/fi/sections/1'
122 end
123
124 it 'does not change the section_path when given page option equals 1' do
125 section_path(1, :page => 1).should == '/sections/1'
126 end
127
128 it 'appends the pages segments to section_path when given page option does not equal 1' do
129 section_path(1, :page => 2).should == '/sections/1/pages/2'
130 end
131
132 it 'works for section_path with both a locale and page option' do
133 section_path(1, :locale => :fi, :page => 2).should == '/fi/sections/1/pages/2'
134 end
135 end
136
137 describe "a nested resource" do
138 # uses optimization
139 it 'does not change the section_article_path when the current locale is the default locale and no page option given' do
140 section_article_path(1, 1).should == '/sections/1/articles/1'
141 end
142
143 # uses optimization
144 it 'prepends the current locale to section_article_path when it is not the default locale' do
145 I18n.locale = :de
146 section_article_path(1, 1).should == '/de/sections/1/articles/1'
147 end
148
149 it 'prepends a given locale param when it is not the default locale' do
150 I18n.locale = :de
151 section_article_path(1, 1, :locale => :fi).should == '/fi/sections/1/articles/1'
152 end
153
154 it 'does not change the section_article_path when given page option equals 1' do
155 section_article_path(1, 1, :page => 1).should == '/sections/1/articles/1'
156 end
157
158 it 'appends the pages segments to section_article_path when given page option does not equal 1' do
159 section_article_path(1, 1, :page => 2).should == '/sections/1/articles/1/pages/2'
160 end
161
162 it 'works for section_article_path with both a locale and page option' do
163 section_article_path(1, 1, :locale => :fi, :page => 2).should == '/fi/sections/1/articles/1/pages/2'
164 end
165 end
166 end
167
168 describe 'when used with a polymorphic_path' do
169 describe "a not nested resource" do
170 # uses optimization
171 it 'does not change the section_path when the current locale is the default locale and no page option given' do
172 section_path(@section).should == '/sections/1'
173 end
174
175 # uses optimization
176 it 'prepends the current locale to section_path when it is not the default locale' do
177 I18n.locale = :de
178 section_path(@section).should == '/de/sections/1'
179 end
180
181 it 'prepends a given locale param to section_path when it is not the default locale' do
182 I18n.locale = :de
183 section_path(@section, :locale => :fi).should == '/fi/sections/1'
184 end
185
186 it 'does not change the section_path when given page option equals 1' do
187 section_path(@section, :page => 1).should == '/sections/1'
188 end
189
190 it 'appends the pages segments to section_path when given page option does not equal 1' do
191 section_path(@section, :page => 2).should == '/sections/1/pages/2'
192 end
193
194 it 'works for section_path with both a locale and page option' do
195 section_path(@section, :locale => :fi, :page => 2).should == '/fi/sections/1/pages/2'
196 end
197 end
198
199 describe "a nested resource" do
200 # uses optimization
201 it 'does not change the section_article_path when the current locale is the default locale and no page option given' do
202 section_article_path(@section, @article).should == '/sections/1/articles/1'
203 end
204
205 # uses optimization
206 it 'prepends the current locale to section_article_path when it is not the default locale' do
207 I18n.locale = :de
208 section_article_path(@section, @article).should == '/de/sections/1/articles/1'
209 end
210
211 it 'prepends a given locale param to section_article_path when it is not the default locale' do
212 I18n.locale = :de
213 section_article_path(@section, @article, :locale => :fi).should == '/fi/sections/1/articles/1'
214 end
215
216 it 'does not change the section_article_path when given page option equals 1' do
217 section_article_path(@section, @article, :page => 1).should == '/sections/1/articles/1'
218 end
219
220 it 'appends the pages segments to section_article_path when given page option does not equal 1' do
221 section_article_path(@section, @article, :page => 2).should == '/sections/1/articles/1/pages/2'
222 end
223
224 it 'works for section_article_path with both a locale and page option' do
225 section_article_path(@section, @article, :locale => :fi, :page => 2).should == '/fi/sections/1/articles/1/pages/2'
226 end
227 end
228 end
229
230 describe 'when used with url_for and an ActivRecord instance' do
231 describe "a not nested resource" do
232 it 'prepends the current locale to section_path when it is not the default locale' do
233 I18n.locale = :de
234 url_for(@section).should == 'http://test.host/de/sections/1'
235 end
236
237 it 'does not change the section_path when no page option given' do
238 url_for(@section).should == 'http://test.host/sections/1'
239 end
240
241 it 'does not change the section_path when given page option equals 1' do
242 params = @params.update :id => @section, :page => 1
243 url_for(params).should == 'http://test.host/sections/1'
244 end
245
246 it 'appends the pages segments to section_path when given page option does not equal 1' do
247 params = @params.update :id => @section, :page => 2
248 url_for(params).should == 'http://test.host/sections/1/pages/2'
249 end
250
251 it 'works for section_path with both a locale and page option' do
252 params = @params.update :id => @section, :locale => :fi, :page => 2
253 url_for(params).should == 'http://test.host/fi/sections/1/pages/2'
254 end
255 end
256
257 describe "a nested resource" do
258 it 'prepends the current locale to section_article_path when it is not the default locale' do
259 I18n.locale = :de
260 url_for([@section, @article]).should == 'http://test.host/de/sections/1/articles/1'
261 end
262
263 it 'does not change the section_article_path when no page option given' do
264 url_for([@section, @article]).should == 'http://test.host/sections/1/articles/1'
265 end
266
267 it 'does not change the section_article_path when given page option equals 1' do
268 params = @article_params.update :section_id => @section, :id => @article, :page => 1
269 url_for(params).should == 'http://test.host/sections/1/articles/1'
270 end
271
272 it 'appends the pages segments to section_article_path when given page option does not equal 1' do
273 params = @article_params.update :section_id => @section, :id => @article, :page => 2
274 url_for(params).should == 'http://test.host/sections/1/articles/1/pages/2'
275 end
276
277 it 'works for section_article_path with both a locale and page option' do
278 params = @article_params.update :section_id => @section, :id => @article, :locale => :fi, :page => 2
279 url_for(params).should == 'http://test.host/fi/sections/1/articles/1/pages/2'
280 end
281 end
282 end
283end \ No newline at end of file
diff --git a/vendor/plugins/routing-filter/spec/recognition_spec.rb b/vendor/plugins/routing-filter/spec/recognition_spec.rb
deleted file mode 100644
index 11f0892..0000000
--- a/vendor/plugins/routing-filter/spec/recognition_spec.rb
+++ /dev/null
@@ -1,76 +0,0 @@
1require File.dirname(__FILE__) + '/spec_helper.rb'
2
3describe 'RoutingFilter', 'url recognition' do
4 include RoutingFilterHelpers
5
6 before :each do
7 RoutingFilter::Locale.default_locale = :en
8 I18n.default_locale = :en
9 I18n.locale = :en
10
11 @controller = instantiate_controller :locale => 'de', :id => 1
12 @set = draw_routes do |map|
13 map.filter 'locale'
14 map.filter 'pagination'
15
16 map.section 'sections/:id', :controller => 'sections', :action => "show"
17 map.article 'sections/:section_id/articles/:id', :controller => 'articles', :action => "show"
18 end
19
20 @section_params = {:controller => 'sections', :action => "show", :id => "1"}
21 @article_params = {:controller => 'articles', :action => "show", :section_id => "1", :id => "1"}
22 @locale_filter = @set.filters.first
23 end
24
25 def should_recognize_path(path, params)
26 @set.recognize_path(path, {}).should == params
27 end
28
29 def section_path(*args)
30 @controller.send :section_path, *args
31 end
32
33 def url_for(*args)
34 @controller.send :url_for, *args
35 end
36
37 it 'recognizes the path /de/sections/1 and sets the :locale param' do
38 should_recognize_path '/de/sections/1', @section_params.update(:locale => 'de')
39 end
40
41 it 'recognizes the path /sections/1/pages/1 and sets the :page param' do
42 should_recognize_path '/sections/1/pages/1', @section_params.update(:page => 1)
43 end
44
45 it 'recognizes the path /de/sections/1/pages/1 and sets the :locale param' do
46 should_recognize_path '/de/sections/1/pages/1', @section_params.update(:locale => 'de', :page => 1)
47 end
48
49 it 'recognizes the path /sections/1/articles/1 and sets the :locale param' do
50 should_recognize_path '/sections/1/articles/1', @article_params
51 end
52
53 it 'recognizes the path /de/sections/1/articles/1 and sets the :locale param' do
54 should_recognize_path '/de/sections/1/articles/1', @article_params.update(:locale => 'de')
55 end
56
57 it 'recognizes the path /de/sections/1/articles/1/pages/1 and sets the :locale param' do
58 should_recognize_path '/de/sections/1/articles/1/pages/1', @article_params.update(:locale => 'de', :page => 1)
59 end
60
61 it 'recognizes the path /sections/1 and does not set a :locale param' do
62 should_recognize_path '/sections/1', @section_params
63 end
64
65 it 'recognizes the path /sections/1 and does not set a :page param' do
66 should_recognize_path '/sections/1', @section_params
67 end
68
69 it 'recognizes the path /sections/1/articles/1 and does not set a :locale param' do
70 should_recognize_path '/sections/1/articles/1', @article_params
71 end
72
73 it 'recognizes the path /sections/1/articles/1 and does not set a :page param' do
74 should_recognize_path '/sections/1/articles/1', @article_params
75 end
76end \ No newline at end of file
diff --git a/vendor/plugins/routing-filter/spec/routing_filter_spec.rb b/vendor/plugins/routing-filter/spec/routing_filter_spec.rb
deleted file mode 100644
index f919c29..0000000
--- a/vendor/plugins/routing-filter/spec/routing_filter_spec.rb
+++ /dev/null
@@ -1,70 +0,0 @@
1require File.dirname(__FILE__) + '/spec_helper.rb'
2
3describe 'RoutingFilter' do
4 include RoutingFilterHelpers
5
6 before :each do
7 @controller = instantiate_controller :locale => 'de', :section_id => 1
8 @set = draw_routes do |map|
9 map.section 'sections/:section_id', :controller => 'sections', :action => "show"
10 map.filter 'locale'
11 map.filter 'pagination'
12 end
13 @locale_filter = @set.filters.first
14 @pagination_filter = @set.filters.last
15 end
16
17 def recognize_path(path = '/de/sections/1', options = {})
18 @set.recognize_path path, options
19 end
20
21 def url_for(options)
22 @controller.send :url_for, options
23 end
24
25 def section_path(*args)
26 @controller.send :section_path, *args
27 end
28
29 it 'installs filters to the route set' do
30 @locale_filter.should be_instance_of(RoutingFilter::Locale)
31 @pagination_filter.should be_instance_of(RoutingFilter::Pagination)
32 end
33
34 it 'calls the first filter for route recognition' do
35 @locale_filter.should_receive(:around_recognize).and_return :foo => :bar
36 recognize_path.should == {:foo => :bar}
37 end
38
39 it 'calls the second filter for route recognition' do
40 @pagination_filter.should_receive(:around_recognize).and_return :foo => :bar
41 recognize_path.should == {:foo => :bar}
42 end
43
44 it 'calls the first filter for url generation' do
45 @locale_filter.should_receive(:around_generate).and_return '/sections/1'
46 url_for :controller => 'sections', :action => 'show', :section_id => 1
47 end
48
49 it 'calls the second filter for url generation' do
50 @pagination_filter.should_receive(:around_generate).and_return '/sections/1'
51 url_for :controller => 'sections', :action => 'show', :section_id => 1
52 end
53
54 it 'calls the first filter for named route url_helper' do
55 @locale_filter.should_receive(:around_generate).and_return '/sections/1'
56 section_path :section_id => 1
57 end
58
59 it 'calls the filter for named route url_helper with "optimized" generation blocks' do
60 # at_least(1) since the inline code comments in ActionController::Routing::RouteSet::NamedRouteCollection#define_url_helper also call us (as of http://github.com/rails/rails/commit/a2270ef2594b97891994848138614657363f2806)
61 @locale_filter.should_receive(:around_generate).at_least(1).and_return '/sections/1'
62 section_path 1
63 end
64
65 it 'calls the filter for named route polymorphic_path' do
66 # at_least(1) since the inline code comments in ActionController::Routing::RouteSet::NamedRouteCollection#define_url_helper also call us (as of http://github.com/rails/rails/commit/a2270ef2594b97891994848138614657363f2806)
67 @locale_filter.should_receive(:around_generate).at_least(1).and_return '/sections/1'
68 section_path Section.new
69 end
70end \ No newline at end of file
diff --git a/vendor/plugins/routing-filter/spec/spec.opts b/vendor/plugins/routing-filter/spec/spec.opts
deleted file mode 100644
index 391705b..0000000
--- a/vendor/plugins/routing-filter/spec/spec.opts
+++ /dev/null
@@ -1,4 +0,0 @@
1--colour
2--format progress
3--loadby mtime
4--reverse
diff --git a/vendor/plugins/routing-filter/spec/spec_helper.rb b/vendor/plugins/routing-filter/spec/spec_helper.rb
deleted file mode 100644
index a92a3d9..0000000
--- a/vendor/plugins/routing-filter/spec/spec_helper.rb
+++ /dev/null
@@ -1,49 +0,0 @@
1$: << File.dirname(__FILE__)
2$: << File.dirname(__FILE__) + '/../lib/'
3$: << File.dirname(__FILE__) + '/../vendor/rails/actionpack/lib'
4$: << File.dirname(__FILE__) + '/../vendor/rails/activesupport/lib'
5
6require 'action_controller'
7require 'action_controller/test_process'
8require 'active_support/vendor'
9
10require 'routing_filter'
11require 'routing_filter/locale'
12require 'routing_filter/pagination'
13
14class Site
15end
16
17class Section
18 def id; 1 end
19 alias :to_param :id
20
21 def type; 'Section' end
22
23 def path; 'section' end
24end
25
26class Article
27 def to_param; 1 end
28end
29
30module RoutingFilterHelpers
31 def draw_routes(&block)
32 set = returning ActionController::Routing::RouteSet.new do |set|
33 class << set; def clear!; end; end
34 set.draw &block
35 silence_warnings{ ActionController::Routing.const_set 'Routes', set }
36 end
37 set
38 end
39
40 def instantiate_controller(params)
41 returning ActionController::Base.new do |controller|
42 request = ActionController::TestRequest.new
43 url = ActionController::UrlRewriter.new(request, params)
44 controller.stub!(:request).and_return request
45 controller.instance_variable_set :@url, url
46 controller
47 end
48 end
49end \ No newline at end of file
diff --git a/vendor/plugins/will_paginate/.gitignore b/vendor/plugins/will_paginate/.gitignore
deleted file mode 100644
index 2b437b9..0000000
--- a/vendor/plugins/will_paginate/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
1/doc
2/rails
3*.gem
4/coverage
diff --git a/vendor/plugins/will_paginate/.manifest b/vendor/plugins/will_paginate/.manifest
deleted file mode 100644
index cda4af44..0000000
--- a/vendor/plugins/will_paginate/.manifest
+++ /dev/null
@@ -1,49 +0,0 @@
1CHANGELOG.rdoc
2LICENSE
3README.rdoc
4Rakefile
5examples
6examples/apple-circle.gif
7examples/index.haml
8examples/index.html
9examples/pagination.css
10examples/pagination.sass
11init.rb
12lib
13lib/will_paginate
14lib/will_paginate.rb
15lib/will_paginate/array.rb
16lib/will_paginate/collection.rb
17lib/will_paginate/core_ext.rb
18lib/will_paginate/finder.rb
19lib/will_paginate/named_scope.rb
20lib/will_paginate/named_scope_patch.rb
21lib/will_paginate/version.rb
22lib/will_paginate/view_helpers.rb
23test
24test/boot.rb
25test/collection_test.rb
26test/console
27test/database.yml
28test/finder_test.rb
29test/fixtures
30test/fixtures/admin.rb
31test/fixtures/developer.rb
32test/fixtures/developers_projects.yml
33test/fixtures/project.rb
34test/fixtures/projects.yml
35test/fixtures/replies.yml
36test/fixtures/reply.rb
37test/fixtures/schema.rb
38test/fixtures/topic.rb
39test/fixtures/topics.yml
40test/fixtures/user.rb
41test/fixtures/users.yml
42test/helper.rb
43test/lib
44test/lib/activerecord_test_case.rb
45test/lib/activerecord_test_connector.rb
46test/lib/load_fixtures.rb
47test/lib/view_test_process.rb
48test/tasks.rake
49test/view_test.rb \ No newline at end of file
diff --git a/vendor/plugins/will_paginate/CHANGELOG.rdoc b/vendor/plugins/will_paginate/CHANGELOG.rdoc
deleted file mode 100644
index 01aea3a..0000000
--- a/vendor/plugins/will_paginate/CHANGELOG.rdoc
+++ /dev/null
@@ -1,110 +0,0 @@
1== 2.3.6, released 2008-10-26
2
3* Rails 2.2 fix: stop using `extract_attribute_names_from_match` inernal AR method, it no longer exists
4
5== 2.3.5, released 2008-10-07
6
7* update the backported named_scope implementation for Rails versions older than 2.1
8* break out of scope of paginated_each() yielded block when used on named scopes
9* fix paginate(:from)
10
11== 2.3.4, released 2008-09-16
12
13* Removed gem dependency to Active Support (causes trouble with vendored rails).
14* Rails 2.1: fix a failing test and a deprecation warning.
15* Cope with scoped :select when counting.
16
17== 2.3.3, released 2008-08-29
18
19* Ensure that paginate_by_sql doesn't change the original SQL query.
20* RDoc love (now live at http://mislav.caboo.se/static/will_paginate/doc/)
21* Rename :prev_label to :previous_label for consistency. old name still functions but is deprecated
22* ActiveRecord 2.1: Remove :include option from count_all query when it's possible.
23
24== 2.3.2, released 2008-05-16
25
26* Fixed LinkRenderer#stringified_merge by removing "return" from iterator block
27* Ensure that 'href' values in pagination links are escaped URLs
28
29== 2.3.1, released 2008-05-04
30
31* Fixed page numbers not showing with custom routes and implicit first page
32* Try to use Hanna for documentation (falls back to default RDoc template if not)
33
34== 2.3.0, released 2008-04-29
35
36* Changed LinkRenderer to receive collection, options and reference to view template NOT in
37 constructor, but with the #prepare method. This is a step towards supporting passing of
38 LinkRenderer (or subclass) instances that may be preconfigured in some way
39* LinkRenderer now has #page_link and #page_span methods for easier customization of output in
40 subclasses
41* Changed page_entries_info() method to adjust its output according to humanized class name of
42 collection items. Override this with :entry_name parameter (singular).
43
44 page_entries_info(@posts)
45 #-> "Displaying all 12 posts"
46 page_entries_info(@posts, :entry_name => 'item')
47 #-> "Displaying all 12 items"
48
49== 2.2.3, released 2008-04-26
50
51* will_paginate gem is no longer published on RubyForge, but on
52 gems.github.com:
53
54 gem sources -a http://gems.github.com/ (you only need to do this once)
55 gem install mislav-will_paginate
56
57* extract reusable pagination testing stuff into WillPaginate::View
58* rethink the page URL construction mechanizm to be more bulletproof when
59 combined with custom routing for page parameter
60* test that anchor parameter can be used in pagination links
61
62== 2.2.2, released 2008-04-21
63
64* Add support for page parameter in custom routes like "/foo/page/2"
65* Change output of "page_entries_info" on single-page collection and erraneous
66 output with empty collection as reported by Tim Chater
67
68== 2.2.1, released 2008-04-08
69
70* take less risky path when monkeypatching named_scope; fix that it no longer
71 requires ActiveRecord::VERSION
72* use strings in "respond_to?" calls to work around a bug in acts_as_ferret
73 stable (ugh)
74* add rake release task
75
76
77== 2.2.0, released 2008-04-07
78
79=== API changes
80* Rename WillPaginate::Collection#page_count to "total_pages" for consistency.
81 If you implemented this interface, change your implementation accordingly.
82* Remove old, deprecated style of calling Array#paginate as "paginate(page,
83 per_page)". If you want to specify :page, :per_page or :total_entries, use a
84 parameter hash.
85* Rename LinkRenderer#url_options to "url_for" and drastically optimize it
86
87=== View changes
88* Added "prev_page" and "next_page" CSS classes on previous/next page buttons
89* Add examples of pagination links styling in "examples/index.html"
90* Change gap in pagination links from "..." to
91 "<span class="gap">&hellip;</span>".
92* Add "paginated_section", a block helper that renders pagination both above and
93 below content in the block
94* Add rel="prev|next|start" to page links
95
96=== Other
97
98* Add ability to opt-in for Rails 2.1 feature "named_scope" by calling
99 WillPaginate.enable_named_scope (tested in Rails 1.2.6 and 2.0.2)
100* Support complex page parameters like "developers[page]"
101* Move Array#paginate definition to will_paginate/array.rb. You can now easily
102 use pagination on arrays outside of Rails:
103
104 gem 'will_paginate'
105 require 'will_paginate/array'
106
107* Add "paginated_each" method for iterating through every record by loading only
108 one page of records at the time
109* Rails 2: Rescue from WillPaginate::InvalidPage error with 404 Not Found by
110 default
diff --git a/vendor/plugins/will_paginate/LICENSE b/vendor/plugins/will_paginate/LICENSE
deleted file mode 100644
index 96a48cb..0000000
--- a/vendor/plugins/will_paginate/LICENSE
+++ /dev/null
@@ -1,18 +0,0 @@
1Copyright (c) 2007 PJ Hyett and Mislav Marohnić
2
3Permission is hereby granted, free of charge, to any person obtaining a copy of
4this software and associated documentation files (the "Software"), to deal in
5the Software without restriction, including without limitation the rights to
6use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7the Software, and to permit persons to whom the Software is furnished to do so,
8subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in all
11copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/plugins/will_paginate/README.rdoc b/vendor/plugins/will_paginate/README.rdoc
deleted file mode 100644
index 2a412f5..0000000
--- a/vendor/plugins/will_paginate/README.rdoc
+++ /dev/null
@@ -1,107 +0,0 @@
1= WillPaginate
2
3Pagination is just limiting the number of records displayed. Why should you let
4it get in your way while developing, then? This plugin makes magic happen. Did
5you ever want to be able to do just this on a model:
6
7 Post.paginate :page => 1, :order => 'created_at DESC'
8
9... and then render the page links with a single view helper? Well, now you
10can.
11
12Some resources to get you started:
13
14* {Installation instructions}[http://github.com/mislav/will_paginate/wikis/installation]
15 on {the wiki}[http://github.com/mislav/will_paginate/wikis]
16* Your mind reels with questions? Join our
17 {Google group}[http://groups.google.com/group/will_paginate].
18* {How to report bugs}[http://github.com/mislav/will_paginate/wikis/report-bugs]
19
20
21== Example usage
22
23Use a paginate finder in the controller:
24
25 @posts = Post.paginate_by_board_id @board.id, :page => params[:page], :order => 'updated_at DESC'
26
27Yeah, +paginate+ works just like +find+ -- it just doesn't fetch all the
28records. Don't forget to tell it which page you want, or it will complain!
29Read more on WillPaginate::Finder::ClassMethods.
30
31Render the posts in your view like you would normally do. When you need to render
32pagination, just stick this in:
33
34 <%= will_paginate @posts %>
35
36You're done. (You can find the option list at WillPaginate::ViewHelpers.)
37
38How does it know how much items to fetch per page? It asks your model by calling
39its <tt>per_page</tt> class method. You can define it like this:
40
41 class Post < ActiveRecord::Base
42 cattr_reader :per_page
43 @@per_page = 50
44 end
45
46... or like this:
47
48 class Post < ActiveRecord::Base
49 def self.per_page
50 50
51 end
52 end
53
54... or don't worry about it at all. WillPaginate defines it to be <b>30</b> by default.
55But you can always specify the count explicitly when calling +paginate+:
56
57 @posts = Post.paginate :page => params[:page], :per_page => 50
58
59The +paginate+ finder wraps the original finder and returns your resultset that now has
60some new properties. You can use the collection as you would with any ActiveRecord
61resultset. WillPaginate view helpers also need that object to be able to render pagination:
62
63 <ol>
64 <% for post in @posts -%>
65 <li>Render `post` in some nice way.</li>
66 <% end -%>
67 </ol>
68
69 <p>Now let's render us some pagination!</p>
70 <%= will_paginate @posts %>
71
72More detailed documentation:
73
74* WillPaginate::Finder::ClassMethods for pagination on your models;
75* WillPaginate::ViewHelpers for your views.
76
77
78== Authors and credits
79
80Authors:: Mislav Marohnić, PJ Hyett
81Original announcement:: http://errtheblog.com/post/929
82Original PHP source:: http://www.strangerstudios.com/sandbox/pagination/diggstyle.php
83
84All these people helped making will_paginate what it is now with their code
85contributions or just simply awesome ideas:
86
87Chris Wanstrath, Dr. Nic Williams, K. Adam Christensen, Mike Garey, Bence
88Golda, Matt Aimonetti, Charles Brian Quinn, Desi McAdam, James Coglan, Matijs
89van Zuijlen, Maria, Brendan Ribera, Todd Willey, Bryan Helmkamp, Jan Berkel,
90Lourens Naudé, Rick Olson, Russell Norris, Piotr Usewicz, Chris Eppstein,
91Denis Barushev, Ben Pickles.
92
93
94== Usable pagination in the UI
95
96There are some CSS styles to get you started in the "examples/" directory. They
97are {showcased online here}[http://mislav.caboo.se/static/will_paginate/].
98
99More reading about pagination as design pattern:
100
101* {Pagination 101}[http://kurafire.net/log/archive/2007/06/22/pagination-101]
102* {Pagination gallery}[http://www.smashingmagazine.com/2007/11/16/pagination-gallery-examples-and-good-practices/]
103* {Pagination on Yahoo Design Pattern Library}[http://developer.yahoo.com/ypatterns/parent.php?pattern=pagination]
104
105Want to discuss, request features, ask questions? Join the
106{Google group}[http://groups.google.com/group/will_paginate].
107
diff --git a/vendor/plugins/will_paginate/Rakefile b/vendor/plugins/will_paginate/Rakefile
deleted file mode 100644
index 253efd0..0000000
--- a/vendor/plugins/will_paginate/Rakefile
+++ /dev/null
@@ -1,62 +0,0 @@
1require 'rubygems'
2begin
3 hanna_dir = '/Users/mislav/Projects/Hanna/lib'
4 $:.unshift hanna_dir if File.exists? hanna_dir
5 require 'hanna/rdoctask'
6rescue LoadError
7 require 'rake'
8 require 'rake/rdoctask'
9end
10load 'test/tasks.rake'
11
12desc 'Default: run unit tests.'
13task :default => :test
14
15desc 'Generate RDoc documentation for the will_paginate plugin.'
16Rake::RDocTask.new(:rdoc) do |rdoc|
17 rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'CHANGELOG.rdoc').
18 include('lib/**/*.rb').
19 exclude('lib/will_paginate/named_scope*').
20 exclude('lib/will_paginate/array.rb').
21 exclude('lib/will_paginate/version.rb')
22
23 rdoc.main = "README.rdoc" # page to start on
24 rdoc.title = "will_paginate documentation"
25
26 rdoc.rdoc_dir = 'doc' # rdoc output folder
27 rdoc.options << '--inline-source' << '--charset=UTF-8'
28 rdoc.options << '--webcvs=http://github.com/mislav/will_paginate/tree/master/'
29end
30
31desc %{Update ".manifest" with the latest list of project filenames. Respect\
32.gitignore by excluding everything that git ignores. Update `files` and\
33`test_files` arrays in "*.gemspec" file if it's present.}
34task :manifest do
35 list = Dir['**/*'].sort
36 spec_file = Dir['*.gemspec'].first
37 list -= [spec_file] if spec_file
38
39 File.read('.gitignore').each_line do |glob|
40 glob = glob.chomp.sub(/^\//, '')
41 list -= Dir[glob]
42 list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
43 puts "excluding #{glob}"
44 end
45
46 if spec_file
47 spec = File.read spec_file
48 spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
49 assignment = $1
50 bunch = $2 ? list.grep(/^test\//) : list
51 '%s%%w(%s)' % [assignment, bunch.join(' ')]
52 end
53
54 File.open(spec_file, 'w') {|f| f << spec }
55 end
56 File.open('.manifest', 'w') {|f| f << list.join("\n") }
57end
58
59task :examples do
60 %x(haml examples/index.haml examples/index.html)
61 %x(sass examples/pagination.sass examples/pagination.css)
62end
diff --git a/vendor/plugins/will_paginate/examples/apple-circle.gif b/vendor/plugins/will_paginate/examples/apple-circle.gif
deleted file mode 100644
index df8cbf7..0000000
--- a/vendor/plugins/will_paginate/examples/apple-circle.gif
+++ /dev/null
Binary files differ
diff --git a/vendor/plugins/will_paginate/examples/index.haml b/vendor/plugins/will_paginate/examples/index.haml
deleted file mode 100644
index fb41ac8..0000000
--- a/vendor/plugins/will_paginate/examples/index.haml
+++ /dev/null
@@ -1,69 +0,0 @@
1!!!
2%html
3%head
4 %title Samples of pagination styling for will_paginate
5 %link{ :rel => 'stylesheet', :type => 'text/css', :href => 'pagination.css' }
6 %style{ :type => 'text/css' }
7 :sass
8 html
9 :margin 0
10 :padding 0
11 :background #999
12 :font normal 76% "Lucida Grande", Verdana, Helvetica, sans-serif
13 body
14 :margin 2em
15 :padding 2em
16 :border 2px solid gray
17 :background white
18 :color #222
19 h1
20 :font-size 2em
21 :font-weight normal
22 :margin 0 0 1em 0
23 h2
24 :font-size 1.4em
25 :margin 1em 0 .5em 0
26 pre
27 :font-size 13px
28 :font-family Monaco, "DejaVu Sans Mono", "Bitstream Vera Mono", "Courier New", monospace
29
30- pagination = '<span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>'
31- pagination_no_page_links = '<span class="disabled prev_page">&laquo; Previous</span> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>'
32
33%body
34 %h1 Samples of pagination styling for will_paginate
35 %p
36 Find these styles in <b>"examples/pagination.css"</b> of <i>will_paginate</i> library.
37 There is a Sass version of it for all you sassy people.
38 %p
39 Read about good rules for pagination:
40 %a{ :href => 'http://kurafire.net/log/archive/2007/06/22/pagination-101' } Pagination 101
41 %p
42 %em Warning:
43 page links below don't lead anywhere (so don't click on them).
44
45 %h2 Unstyled pagination <span style="font-weight:normal">(<i>ewww!</i>)</span>
46 %div= pagination
47
48 %h2 Digg.com
49 .digg_pagination= pagination
50
51 %h2 Digg-style, no page links
52 .digg_pagination= pagination_no_page_links
53 %p Code that renders this:
54 %pre= '<code>%s</code>' % %[<%= will_paginate @posts, :page_links => false %>].gsub('<', '&lt;').gsub('>', '&gt;')
55
56 %h2 Digg-style, extra content
57 .digg_pagination
58 .page_info Displaying entries <b>1&nbsp;-&nbsp;6</b> of <b>180</b> in total
59 = pagination
60 %p Code that renders this:
61 %pre= '<code>%s</code>' % %[<div class="digg_pagination">\n <div clas="page_info">\n <%= page_entries_info @posts %>\n </div>\n <%= will_paginate @posts, :container => false %>\n</div>].gsub('<', '&lt;').gsub('>', '&gt;')
62
63 %h2 Apple.com store
64 .apple_pagination= pagination
65
66 %h2 Flickr.com
67 .flickr_pagination
68 = pagination
69 .page_info (118 photos)
diff --git a/vendor/plugins/will_paginate/examples/index.html b/vendor/plugins/will_paginate/examples/index.html
deleted file mode 100644
index 858f7c6..0000000
--- a/vendor/plugins/will_paginate/examples/index.html
+++ /dev/null
@@ -1,92 +0,0 @@
1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2<html>
3</html>
4<head>
5 <title>Samples of pagination styling for will_paginate</title>
6 <link href='pagination.css' rel='stylesheet' type='text/css' />
7 <style type='text/css'>
8 html {
9 margin: 0;
10 padding: 0;
11 background: #999;
12 font: normal 76% "Lucida Grande", Verdana, Helvetica, sans-serif; }
13
14 body {
15 margin: 2em;
16 padding: 2em;
17 border: 2px solid gray;
18 background: white;
19 color: #222; }
20
21 h1 {
22 font-size: 2em;
23 font-weight: normal;
24 margin: 0 0 1em 0; }
25
26 h2 {
27 font-size: 1.4em;
28 margin: 1em 0 .5em 0; }
29
30 pre {
31 font-size: 13px;
32 font-family: Monaco, "DejaVu Sans Mono", "Bitstream Vera Mono", "Courier New", monospace; }
33 </style>
34</head>
35<body>
36 <h1>Samples of pagination styling for will_paginate</h1>
37 <p>
38 Find these styles in <b>"examples/pagination.css"</b> of <i>will_paginate</i> library.
39 There is a Sass version of it for all you sassy people.
40 </p>
41 <p>
42 Read about good rules for pagination:
43 <a href='http://kurafire.net/log/archive/2007/06/22/pagination-101'>Pagination 101</a>
44 </p>
45 <p>
46 <em>Warning:</em>
47 page links below don't lead anywhere (so don't click on them).
48 </p>
49 <h2>
50 Unstyled pagination <span style="font-weight:normal">(<i>ewww!</i>)</span>
51 </h2>
52 <div>
53 <span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
54 </div>
55 <h2>Digg.com</h2>
56 <div class='digg_pagination'>
57 <span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
58 </div>
59 <h2>Digg-style, no page links</h2>
60 <div class='digg_pagination'>
61 <span class="disabled prev_page">&laquo; Previous</span> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
62 </div>
63 <p>Code that renders this:</p>
64 <pre>
65 <code>&lt;%= will_paginate @posts, :page_links =&gt; false %&gt;</code>
66 </pre>
67 <h2>Digg-style, extra content</h2>
68 <div class='digg_pagination'>
69 <div class='page_info'>
70 Displaying entries <b>1&nbsp;-&nbsp;6</b> of <b>180</b> in total
71 </div>
72 <span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
73 </div>
74 <p>Code that renders this:</p>
75 <pre>
76 <code>&lt;div class="digg_pagination"&gt;
77 &lt;div clas="page_info"&gt;
78 &lt;%= page_entries_info @posts %&gt;
79 &lt;/div&gt;
80 &lt;%= will_paginate @posts, :container =&gt; false %&gt;
81 &lt;/div&gt;</code>
82 </pre>
83 <h2>Apple.com store</h2>
84 <div class='apple_pagination'>
85 <span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
86 </div>
87 <h2>Flickr.com</h2>
88 <div class='flickr_pagination'>
89 <span class="disabled prev_page">&laquo; Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
90 <div class='page_info'>(118 photos)</div>
91 </div>
92</body>
diff --git a/vendor/plugins/will_paginate/examples/pagination.css b/vendor/plugins/will_paginate/examples/pagination.css
deleted file mode 100644
index b55e977..0000000
--- a/vendor/plugins/will_paginate/examples/pagination.css
+++ /dev/null
@@ -1,90 +0,0 @@
1.digg_pagination {
2 background: white;
3 /* self-clearing method: */ }
4 .digg_pagination a, .digg_pagination span {
5 padding: .2em .5em;
6 display: block;
7 float: left;
8 margin-right: 1px; }
9 .digg_pagination span.disabled {
10 color: #999;
11 border: 1px solid #DDD; }
12 .digg_pagination span.current {
13 font-weight: bold;
14 background: #2E6AB1;
15 color: white;
16 border: 1px solid #2E6AB1; }
17 .digg_pagination a {
18 text-decoration: none;
19 color: #105CB6;
20 border: 1px solid #9AAFE5; }
21 .digg_pagination a:hover, .digg_pagination a:focus {
22 color: #003;
23 border-color: #003; }
24 .digg_pagination .page_info {
25 background: #2E6AB1;
26 color: white;
27 padding: .4em .6em;
28 width: 22em;
29 margin-bottom: .3em;
30 text-align: center; }
31 .digg_pagination .page_info b {
32 color: #003;
33 background: #6aa6ed;
34 padding: .1em .25em; }
35 .digg_pagination:after {
36 content: ".";
37 display: block;
38 height: 0;
39 clear: both;
40 visibility: hidden; }
41 * html .digg_pagination {
42 height: 1%; }
43 *:first-child+html .digg_pagination {
44 overflow: hidden; }
45
46.apple_pagination {
47 background: #F1F1F1;
48 border: 1px solid #E5E5E5;
49 text-align: center;
50 padding: 1em; }
51 .apple_pagination a, .apple_pagination span {
52 padding: .2em .3em; }
53 .apple_pagination span.disabled {
54 color: #AAA; }
55 .apple_pagination span.current {
56 font-weight: bold;
57 background: transparent url(apple-circle.gif) no-repeat 50% 50%; }
58 .apple_pagination a {
59 text-decoration: none;
60 color: black; }
61 .apple_pagination a:hover, .apple_pagination a:focus {
62 text-decoration: underline; }
63
64.flickr_pagination {
65 text-align: center;
66 padding: .3em; }
67 .flickr_pagination a, .flickr_pagination span {
68 padding: .2em .5em; }
69 .flickr_pagination span.disabled {
70 color: #AAA; }
71 .flickr_pagination span.current {
72 font-weight: bold;
73 color: #FF0084; }
74 .flickr_pagination a {
75 border: 1px solid #DDDDDD;
76 color: #0063DC;
77 text-decoration: none; }
78 .flickr_pagination a:hover, .flickr_pagination a:focus {
79 border-color: #003366;
80 background: #0063DC;
81 color: white; }
82 .flickr_pagination .page_info {
83 color: #aaa;
84 padding-top: .8em; }
85 .flickr_pagination .prev_page, .flickr_pagination .next_page {
86 border-width: 2px; }
87 .flickr_pagination .prev_page {
88 margin-right: 1em; }
89 .flickr_pagination .next_page {
90 margin-left: 1em; }
diff --git a/vendor/plugins/will_paginate/examples/pagination.sass b/vendor/plugins/will_paginate/examples/pagination.sass
deleted file mode 100644
index 737a97b..0000000
--- a/vendor/plugins/will_paginate/examples/pagination.sass
+++ /dev/null
@@ -1,91 +0,0 @@
1.digg_pagination
2 :background white
3 a, span
4 :padding .2em .5em
5 :display block
6 :float left
7 :margin-right 1px
8 span.disabled
9 :color #999
10 :border 1px solid #DDD
11 span.current
12 :font-weight bold
13 :background #2E6AB1
14 :color white
15 :border 1px solid #2E6AB1
16 a
17 :text-decoration none
18 :color #105CB6
19 :border 1px solid #9AAFE5
20 &:hover, &:focus
21 :color #003
22 :border-color #003
23 .page_info
24 :background #2E6AB1
25 :color white
26 :padding .4em .6em
27 :width 22em
28 :margin-bottom .3em
29 :text-align center
30 b
31 :color #003
32 :background = #2E6AB1 + 60
33 :padding .1em .25em
34
35 /* self-clearing method:
36 &:after
37 :content "."
38 :display block
39 :height 0
40 :clear both
41 :visibility hidden
42 * html &
43 :height 1%
44 *:first-child+html &
45 :overflow hidden
46
47.apple_pagination
48 :background #F1F1F1
49 :border 1px solid #E5E5E5
50 :text-align center
51 :padding 1em
52 a, span
53 :padding .2em .3em
54 span.disabled
55 :color #AAA
56 span.current
57 :font-weight bold
58 :background transparent url(apple-circle.gif) no-repeat 50% 50%
59 a
60 :text-decoration none
61 :color black
62 &:hover, &:focus
63 :text-decoration underline
64
65.flickr_pagination
66 :text-align center
67 :padding .3em
68 a, span
69 :padding .2em .5em
70 span.disabled
71 :color #AAA
72 span.current
73 :font-weight bold
74 :color #FF0084
75 a
76 :border 1px solid #DDDDDD
77 :color #0063DC
78 :text-decoration none
79 &:hover, &:focus
80 :border-color #003366
81 :background #0063DC
82 :color white
83 .page_info
84 :color #aaa
85 :padding-top .8em
86 .prev_page, .next_page
87 :border-width 2px
88 .prev_page
89 :margin-right 1em
90 .next_page
91 :margin-left 1em
diff --git a/vendor/plugins/will_paginate/init.rb b/vendor/plugins/will_paginate/init.rb
deleted file mode 100644
index 838d30e..0000000
--- a/vendor/plugins/will_paginate/init.rb
+++ /dev/null
@@ -1 +0,0 @@
1require 'will_paginate'
diff --git a/vendor/plugins/will_paginate/lib/will_paginate.rb b/vendor/plugins/will_paginate/lib/will_paginate.rb
deleted file mode 100644
index e072412..0000000
--- a/vendor/plugins/will_paginate/lib/will_paginate.rb
+++ /dev/null
@@ -1,78 +0,0 @@
1require 'active_support'
2
3# = You *will* paginate!
4#
5# First read about WillPaginate::Finder::ClassMethods, then see
6# WillPaginate::ViewHelpers. The magical array you're handling in-between is
7# WillPaginate::Collection.
8#
9# Happy paginating!
10module WillPaginate
11 class << self
12 # shortcut for <tt>enable_actionpack</tt> and <tt>enable_activerecord</tt> combined
13 def enable
14 enable_actionpack
15 enable_activerecord
16 end
17
18 # hooks WillPaginate::ViewHelpers into ActionView::Base
19 def enable_actionpack
20 return if ActionView::Base.instance_methods.include? 'will_paginate'
21 require 'will_paginate/view_helpers'
22 ActionView::Base.send :include, ViewHelpers
23
24 if defined?(ActionController::Base) and ActionController::Base.respond_to? :rescue_responses
25 ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] = :not_found
26 end
27 end
28
29 # hooks WillPaginate::Finder into ActiveRecord::Base and classes that deal
30 # with associations
31 def enable_activerecord
32 return if ActiveRecord::Base.respond_to? :paginate
33 require 'will_paginate/finder'
34 ActiveRecord::Base.send :include, Finder
35
36 # support pagination on associations
37 a = ActiveRecord::Associations
38 returning([ a::AssociationCollection ]) { |classes|
39 # detect http://dev.rubyonrails.org/changeset/9230
40 unless a::HasManyThroughAssociation.superclass == a::HasManyAssociation
41 classes << a::HasManyThroughAssociation
42 end
43 }.each do |klass|
44 klass.send :include, Finder::ClassMethods
45 klass.class_eval { alias_method_chain :method_missing, :paginate }
46 end
47 end
48
49 # Enable named_scope, a feature of Rails 2.1, even if you have older Rails
50 # (tested on Rails 2.0.2 and 1.2.6).
51 #
52 # You can pass +false+ for +patch+ parameter to skip monkeypatching
53 # *associations*. Use this if you feel that <tt>named_scope</tt> broke
54 # has_many, has_many :through or has_and_belongs_to_many associations in
55 # your app. By passing +false+, you can still use <tt>named_scope</tt> in
56 # your models, but not through associations.
57 def enable_named_scope(patch = true)
58 return if defined? ActiveRecord::NamedScope
59 require 'will_paginate/named_scope'
60 require 'will_paginate/named_scope_patch' if patch
61
62 ActiveRecord::Base.send :include, WillPaginate::NamedScope
63 end
64 end
65
66 module Deprecation # :nodoc:
67 extend ActiveSupport::Deprecation
68
69 def self.warn(message, callstack = caller)
70 message = 'WillPaginate: ' + message.strip.gsub(/\s+/, ' ')
71 ActiveSupport::Deprecation.warn(message, callstack)
72 end
73 end
74end
75
76if defined?(Rails) and defined?(ActiveRecord) and defined?(ActionController)
77 WillPaginate.enable
78end
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/array.rb b/vendor/plugins/will_paginate/lib/will_paginate/array.rb
deleted file mode 100644
index d061d2b..0000000
--- a/vendor/plugins/will_paginate/lib/will_paginate/array.rb
+++ /dev/null
@@ -1,16 +0,0 @@
1require 'will_paginate/collection'
2
3# http://www.desimcadam.com/archives/8
4Array.class_eval do
5 def paginate(options = {})
6 raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options
7
8 WillPaginate::Collection.create(
9 options[:page] || 1,
10 options[:per_page] || 30,
11 options[:total_entries] || self.length
12 ) { |pager|
13 pager.replace self[pager.offset, pager.per_page].to_a
14 }
15 end
16end
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/collection.rb b/vendor/plugins/will_paginate/lib/will_paginate/collection.rb
deleted file mode 100644
index e253570..0000000
--- a/vendor/plugins/will_paginate/lib/will_paginate/collection.rb
+++ /dev/null
@@ -1,146 +0,0 @@
1module WillPaginate
2 # = Invalid page number error
3 # This is an ArgumentError raised in case a page was requested that is either
4 # zero or negative number. You should decide how do deal with such errors in
5 # the controller.
6 #
7 # If you're using Rails 2, then this error will automatically get handled like
8 # 404 Not Found. The hook is in "will_paginate.rb":
9 #
10 # ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] = :not_found
11 #
12 # If you don't like this, use your preffered method of rescuing exceptions in
13 # public from your controllers to handle this differently. The +rescue_from+
14 # method is a nice addition to Rails 2.
15 #
16 # This error is *not* raised when a page further than the last page is
17 # requested. Use <tt>WillPaginate::Collection#out_of_bounds?</tt> method to
18 # check for those cases and manually deal with them as you see fit.
19 class InvalidPage < ArgumentError
20 def initialize(page, page_num)
21 super "#{page.inspect} given as value, which translates to '#{page_num}' as page number"
22 end
23 end
24
25 # = The key to pagination
26 # Arrays returned from paginating finds are, in fact, instances of this little
27 # class. You may think of WillPaginate::Collection as an ordinary array with
28 # some extra properties. Those properties are used by view helpers to generate
29 # correct page links.
30 #
31 # WillPaginate::Collection also assists in rolling out your own pagination
32 # solutions: see +create+.
33 #
34 # If you are writing a library that provides a collection which you would like
35 # to conform to this API, you don't have to copy these methods over; simply
36 # make your plugin/gem dependant on the "mislav-will_paginate" gem:
37 #
38 # gem 'mislav-will_paginate'
39 # require 'will_paginate/collection'
40 #
41 # # WillPaginate::Collection is now available for use
42 class Collection < Array
43 attr_reader :current_page, :per_page, :total_entries, :total_pages
44
45 # Arguments to the constructor are the current page number, per-page limit
46 # and the total number of entries. The last argument is optional because it
47 # is best to do lazy counting; in other words, count *conditionally* after
48 # populating the collection using the +replace+ method.
49 def initialize(page, per_page, total = nil)
50 @current_page = page.to_i
51 raise InvalidPage.new(page, @current_page) if @current_page < 1
52 @per_page = per_page.to_i
53 raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
54
55 self.total_entries = total if total
56 end
57
58 # Just like +new+, but yields the object after instantiation and returns it
59 # afterwards. This is very useful for manual pagination:
60 #
61 # @entries = WillPaginate::Collection.create(1, 10) do |pager|
62 # result = Post.find(:all, :limit => pager.per_page, :offset => pager.offset)
63 # # inject the result array into the paginated collection:
64 # pager.replace(result)
65 #
66 # unless pager.total_entries
67 # # the pager didn't manage to guess the total count, do it manually
68 # pager.total_entries = Post.count
69 # end
70 # end
71 #
72 # The possibilities with this are endless. For another example, here is how
73 # WillPaginate used to define pagination for Array instances:
74 #
75 # Array.class_eval do
76 # def paginate(page = 1, per_page = 15)
77 # WillPaginate::Collection.create(page, per_page, size) do |pager|
78 # pager.replace self[pager.offset, pager.per_page].to_a
79 # end
80 # end
81 # end
82 #
83 # The Array#paginate API has since then changed, but this still serves as a
84 # fine example of WillPaginate::Collection usage.
85 def self.create(page, per_page, total = nil)
86 pager = new(page, per_page, total)
87 yield pager
88 pager
89 end
90
91 # Helper method that is true when someone tries to fetch a page with a
92 # larger number than the last page. Can be used in combination with flashes
93 # and redirecting.
94 def out_of_bounds?
95 current_page > total_pages
96 end
97
98 # Current offset of the paginated collection. If we're on the first page,
99 # it is always 0. If we're on the 2nd page and there are 30 entries per page,
100 # the offset is 30. This property is useful if you want to render ordinals
101 # side by side with records in the view: simply start with offset + 1.
102 def offset
103 (current_page - 1) * per_page
104 end
105
106 # current_page - 1 or nil if there is no previous page
107 def previous_page
108 current_page > 1 ? (current_page - 1) : nil
109 end
110
111 # current_page + 1 or nil if there is no next page
112 def next_page
113 current_page < total_pages ? (current_page + 1) : nil
114 end
115
116 # sets the <tt>total_entries</tt> property and calculates <tt>total_pages</tt>
117 def total_entries=(number)
118 @total_entries = number.to_i
119 @total_pages = (@total_entries / per_page.to_f).ceil
120 end
121
122 # This is a magic wrapper for the original Array#replace method. It serves
123 # for populating the paginated collection after initialization.
124 #
125 # Why magic? Because it tries to guess the total number of entries judging
126 # by the size of given array. If it is shorter than +per_page+ limit, then we
127 # know we're on the last page. This trick is very useful for avoiding
128 # unnecessary hits to the database to do the counting after we fetched the
129 # data for the current page.
130 #
131 # However, after using +replace+ you should always test the value of
132 # +total_entries+ and set it to a proper value if it's +nil+. See the example
133 # in +create+.
134 def replace(array)
135 result = super
136
137 # The collection is shorter then page limit? Rejoice, because
138 # then we know that we are on the last page!
139 if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
140 self.total_entries = offset + length
141 end
142
143 result
144 end
145 end
146end
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb b/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb
deleted file mode 100644
index 32f10f5..0000000
--- a/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb
+++ /dev/null
@@ -1,32 +0,0 @@
1require 'set'
2require 'will_paginate/array'
3
4unless Hash.instance_methods.include? 'except'
5 Hash.class_eval do
6 # Returns a new hash without the given keys.
7 def except(*keys)
8 rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
9 reject { |key,| rejected.include?(key) }
10 end
11
12 # Replaces the hash without only the given keys.
13 def except!(*keys)
14 replace(except(*keys))
15 end
16 end
17end
18
19unless Hash.instance_methods.include? 'slice'
20 Hash.class_eval do
21 # Returns a new hash with only the given keys.
22 def slice(*keys)
23 allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
24 reject { |key,| !allowed.include?(key) }
25 end
26
27 # Replaces the hash with only the given keys.
28 def slice!(*keys)
29 replace(slice(*keys))
30 end
31 end
32end
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/finder.rb b/vendor/plugins/will_paginate/lib/will_paginate/finder.rb
deleted file mode 100644
index d0e5668..0000000
--- a/vendor/plugins/will_paginate/lib/will_paginate/finder.rb
+++ /dev/null
@@ -1,264 +0,0 @@
1require 'will_paginate/core_ext'
2
3module WillPaginate
4 # A mixin for ActiveRecord::Base. Provides +per_page+ class method
5 # and hooks things up to provide paginating finders.
6 #
7 # Find out more in WillPaginate::Finder::ClassMethods
8 #
9 module Finder
10 def self.included(base)
11 base.extend ClassMethods
12 class << base
13 alias_method_chain :method_missing, :paginate
14 # alias_method_chain :find_every, :paginate
15 define_method(:per_page) { 30 } unless respond_to?(:per_page)
16 end
17 end
18
19 # = Paginating finders for ActiveRecord models
20 #
21 # WillPaginate adds +paginate+, +per_page+ and other methods to
22 # ActiveRecord::Base class methods and associations. It also hooks into
23 # +method_missing+ to intercept pagination calls to dynamic finders such as
24 # +paginate_by_user_id+ and translate them to ordinary finders
25 # (+find_all_by_user_id+ in this case).
26 #
27 # In short, paginating finders are equivalent to ActiveRecord finders; the
28 # only difference is that we start with "paginate" instead of "find" and
29 # that <tt>:page</tt> is required parameter:
30 #
31 # @posts = Post.paginate :all, :page => params[:page], :order => 'created_at DESC'
32 #
33 # In paginating finders, "all" is implicit. There is no sense in paginating
34 # a single record, right? So, you can drop the <tt>:all</tt> argument:
35 #
36 # Post.paginate(...) => Post.find :all
37 # Post.paginate_all_by_something => Post.find_all_by_something
38 # Post.paginate_by_something => Post.find_all_by_something
39 #
40 # == The importance of the <tt>:order</tt> parameter
41 #
42 # In ActiveRecord finders, <tt>:order</tt> parameter specifies columns for
43 # the <tt>ORDER BY</tt> clause in SQL. It is important to have it, since
44 # pagination only makes sense with ordered sets. Without the <tt>ORDER
45 # BY</tt> clause, databases aren't required to do consistent ordering when
46 # performing <tt>SELECT</tt> queries; this is especially true for
47 # PostgreSQL.
48 #
49 # Therefore, make sure you are doing ordering on a column that makes the
50 # most sense in the current context. Make that obvious to the user, also.
51 # For perfomance reasons you will also want to add an index to that column.
52 module ClassMethods
53 # This is the main paginating finder.
54 #
55 # == Special parameters for paginating finders
56 # * <tt>:page</tt> -- REQUIRED, but defaults to 1 if false or nil
57 # * <tt>:per_page</tt> -- defaults to <tt>CurrentModel.per_page</tt> (which is 30 if not overridden)
58 # * <tt>:total_entries</tt> -- use only if you manually count total entries
59 # * <tt>:count</tt> -- additional options that are passed on to +count+
60 # * <tt>:finder</tt> -- name of the ActiveRecord finder used (default: "find")
61 #
62 # All other options (+conditions+, +order+, ...) are forwarded to +find+
63 # and +count+ calls.
64 def paginate(*args)
65 options = args.pop
66 page, per_page, total_entries = wp_parse_options(options)
67 finder = (options[:finder] || 'find').to_s
68
69 if finder == 'find'
70 # an array of IDs may have been given:
71 total_entries ||= (Array === args.first and args.first.size)
72 # :all is implicit
73 args.unshift(:all) if args.empty?
74 end
75
76 WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
77 count_options = options.except :page, :per_page, :total_entries, :finder
78 find_options = count_options.except(:count).update(:offset => pager.offset, :limit => pager.per_page)
79
80 args << find_options
81 # @options_from_last_find = nil
82 pager.replace(send(finder, *args) { |*a| yield(*a) if block_given? })
83
84 # magic counting for user convenience:
85 pager.total_entries = wp_count(count_options, args, finder) unless pager.total_entries
86 end
87 end
88
89 # Iterates through all records by loading one page at a time. This is useful
90 # for migrations or any other use case where you don't want to load all the
91 # records in memory at once.
92 #
93 # It uses +paginate+ internally; therefore it accepts all of its options.
94 # You can specify a starting page with <tt>:page</tt> (default is 1). Default
95 # <tt>:order</tt> is <tt>"id"</tt>, override if necessary.
96 #
97 # See {Faking Cursors in ActiveRecord}[http://weblog.jamisbuck.org/2007/4/6/faking-cursors-in-activerecord]
98 # where Jamis Buck describes this and a more efficient way for MySQL.
99 def paginated_each(options = {})
100 options = { :order => 'id', :page => 1 }.merge options
101 options[:page] = options[:page].to_i
102 options[:total_entries] = 0 # skip the individual count queries
103 total = 0
104
105 begin
106 collection = paginate(options)
107 with_exclusive_scope(:find => {}) do
108 # using exclusive scope so that the block is yielded in scope-free context
109 total += collection.each { |item| yield item }.size
110 end
111 options[:page] += 1
112 end until collection.size < collection.per_page
113
114 total
115 end
116
117 # Wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string
118 # based on the params otherwise used by paginating finds: +page+ and
119 # +per_page+.
120 #
121 # Example:
122 #
123 # @developers = Developer.paginate_by_sql ['select * from developers where salary > ?', 80000],
124 # :page => params[:page], :per_page => 3
125 #
126 # A query for counting rows will automatically be generated if you don't
127 # supply <tt>:total_entries</tt>. If you experience problems with this
128 # generated SQL, you might want to perform the count manually in your
129 # application.
130 #
131 def paginate_by_sql(sql, options)
132 WillPaginate::Collection.create(*wp_parse_options(options)) do |pager|
133 query = sanitize_sql(sql.dup)
134 original_query = query.dup
135 # add limit, offset
136 add_limit! query, :offset => pager.offset, :limit => pager.per_page
137 # perfom the find
138 pager.replace find_by_sql(query)
139
140 unless pager.total_entries
141 count_query = original_query.sub /\bORDER\s+BY\s+[\w`,\s]+$/mi, ''
142 count_query = "SELECT COUNT(*) FROM (#{count_query})"
143
144 unless ['oracle', 'oci'].include?(self.connection.adapter_name.downcase)
145 count_query << ' AS count_table'
146 end
147 # perform the count query
148 pager.total_entries = count_by_sql(count_query)
149 end
150 end
151 end
152
153 def respond_to?(method, include_priv = false) #:nodoc:
154 case method.to_sym
155 when :paginate, :paginate_by_sql
156 true
157 else
158 super(method.to_s.sub(/^paginate/, 'find'), include_priv)
159 end
160 end
161
162 protected
163
164 def method_missing_with_paginate(method, *args) #:nodoc:
165 # did somebody tried to paginate? if not, let them be
166 unless method.to_s.index('paginate') == 0
167 if block_given?
168 return method_missing_without_paginate(method, *args) { |*a| yield(*a) }
169 else
170 return method_missing_without_paginate(method, *args)
171 end
172 end
173
174 # paginate finders are really just find_* with limit and offset
175 finder = method.to_s.sub('paginate', 'find')
176 finder.sub!('find', 'find_all') if finder.index('find_by_') == 0
177
178 options = args.pop
179 raise ArgumentError, 'parameter hash expected' unless options.respond_to? :symbolize_keys
180 options = options.dup
181 options[:finder] = finder
182 args << options
183
184 paginate(*args) { |*a| yield(*a) if block_given? }
185 end
186
187 # Does the not-so-trivial job of finding out the total number of entries
188 # in the database. It relies on the ActiveRecord +count+ method.
189 def wp_count(options, args, finder)
190 excludees = [:count, :order, :limit, :offset, :readonly]
191 excludees << :from unless ActiveRecord::Calculations::CALCULATIONS_OPTIONS.include?(:from)
192
193 # we may be in a model or an association proxy
194 klass = (@owner and @reflection) ? @reflection.klass : self
195
196 # Use :select from scope if it isn't already present.
197 options[:select] = scope(:find, :select) unless options[:select]
198
199 if options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
200 # Remove quoting and check for table_name.*-like statement.
201 if options[:select].gsub('`', '') =~ /\w+\.\*/
202 options[:select] = "DISTINCT #{klass.table_name}.#{klass.primary_key}"
203 end
204 else
205 excludees << :select # only exclude the select param if it doesn't begin with DISTINCT
206 end
207
208 # count expects (almost) the same options as find
209 count_options = options.except *excludees
210
211 # merge the hash found in :count
212 # this allows you to specify :select, :order, or anything else just for the count query
213 count_options.update options[:count] if options[:count]
214
215 # forget about includes if they are irrelevant (Rails 2.1)
216 if count_options[:include] and
217 klass.private_methods.include?('references_eager_loaded_tables?') and
218 !klass.send(:references_eager_loaded_tables?, count_options)
219 count_options.delete :include
220 end
221
222 # we may have to scope ...
223 counter = Proc.new { count(count_options) }
224
225 count = if finder.index('find_') == 0 and klass.respond_to?(scoper = finder.sub('find', 'with'))
226 # scope_out adds a 'with_finder' method which acts like with_scope, if it's present
227 # then execute the count with the scoping provided by the with_finder
228 send(scoper, &counter)
229 elsif finder =~ /^find_(all_by|by)_([_a-zA-Z]\w*)$/
230 # extract conditions from calls like "paginate_by_foo_and_bar"
231 attribute_names = $2.split('_and_')
232 conditions = construct_attributes_from_arguments(attribute_names, args)
233 with_scope(:find => { :conditions => conditions }, &counter)
234 else
235 counter.call
236 end
237
238 count.respond_to?(:length) ? count.length : count
239 end
240
241 def wp_parse_options(options) #:nodoc:
242 raise ArgumentError, 'parameter hash expected' unless options.respond_to? :symbolize_keys
243 options = options.symbolize_keys
244 raise ArgumentError, ':page parameter required' unless options.key? :page
245
246 if options[:count] and options[:total_entries]
247 raise ArgumentError, ':count and :total_entries are mutually exclusive'
248 end
249
250 page = options[:page] || 1
251 per_page = options[:per_page] || self.per_page
252 total = options[:total_entries]
253 [page, per_page, total]
254 end
255
256 private
257
258 # def find_every_with_paginate(options)
259 # @options_from_last_find = options
260 # find_every_without_paginate(options)
261 # end
262 end
263 end
264end
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/named_scope.rb b/vendor/plugins/will_paginate/lib/will_paginate/named_scope.rb
deleted file mode 100644
index 5a743d7..0000000
--- a/vendor/plugins/will_paginate/lib/will_paginate/named_scope.rb
+++ /dev/null
@@ -1,170 +0,0 @@
1module WillPaginate
2 # This is a feature backported from Rails 2.1 because of its usefullness not only with will_paginate,
3 # but in other aspects when managing complex conditions that you want to be reusable.
4 module NamedScope
5 # All subclasses of ActiveRecord::Base have two named_scopes:
6 # * <tt>all</tt>, which is similar to a <tt>find(:all)</tt> query, and
7 # * <tt>scoped</tt>, which allows for the creation of anonymous scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
8 #
9 # These anonymous scopes tend to be useful when procedurally generating complex queries, where passing
10 # intermediate values (scopes) around as first-class objects is convenient.
11 def self.included(base)
12 base.class_eval do
13 extend ClassMethods
14 named_scope :scoped, lambda { |scope| scope }
15 end
16 end
17
18 module ClassMethods
19 def scopes
20 read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
21 end
22
23 # Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
24 # such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
25 #
26 # class Shirt < ActiveRecord::Base
27 # named_scope :red, :conditions => {:color => 'red'}
28 # named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
29 # end
30 #
31 # The above calls to <tt>named_scope</tt> define class methods <tt>Shirt.red</tt> and <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>,
32 # in effect, represents the query <tt>Shirt.find(:all, :conditions => {:color => 'red'})</tt>.
33 #
34 # Unlike Shirt.find(...), however, the object returned by <tt>Shirt.red</tt> is not an Array; it resembles the association object
35 # constructed by a <tt>has_many</tt> declaration. For instance, you can invoke <tt>Shirt.red.find(:first)</tt>, <tt>Shirt.red.count</tt>,
36 # <tt>Shirt.red.find(:all, :conditions => {:size => 'small'})</tt>. Also, just
37 # as with the association objects, name scopes acts like an Array, implementing Enumerable; <tt>Shirt.red.each(&block)</tt>,
38 # <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if Shirt.red really were an Array.
39 #
40 # These named scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are both red and dry clean only.
41 # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
42 # for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
43 #
44 # All scopes are available as class methods on the ActiveRecord::Base descendent upon which the scopes were defined. But they are also available to
45 # <tt>has_many</tt> associations. If,
46 #
47 # class Person < ActiveRecord::Base
48 # has_many :shirts
49 # end
50 #
51 # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
52 # only shirts.
53 #
54 # Named scopes can also be procedural.
55 #
56 # class Shirt < ActiveRecord::Base
57 # named_scope :colored, lambda { |color|
58 # { :conditions => { :color => color } }
59 # }
60 # end
61 #
62 # In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
63 #
64 # Named scopes can also have extensions, just as with <tt>has_many</tt> declarations:
65 #
66 # class Shirt < ActiveRecord::Base
67 # named_scope :red, :conditions => {:color => 'red'} do
68 # def dom_id
69 # 'red_shirts'
70 # end
71 # end
72 # end
73 #
74 #
75 # For testing complex named scopes, you can examine the scoping options using the
76 # <tt>proxy_options</tt> method on the proxy itself.
77 #
78 # class Shirt < ActiveRecord::Base
79 # named_scope :colored, lambda { |color|
80 # { :conditions => { :color => color } }
81 # }
82 # end
83 #
84 # expected_options = { :conditions => { :colored => 'red' } }
85 # assert_equal expected_options, Shirt.colored('red').proxy_options
86 def named_scope(name, options = {})
87 name = name.to_sym
88 scopes[name] = lambda do |parent_scope, *args|
89 Scope.new(parent_scope, case options
90 when Hash
91 options
92 when Proc
93 options.call(*args)
94 end) { |*a| yield(*a) if block_given? }
95 end
96 (class << self; self end).instance_eval do
97 define_method name do |*args|
98 scopes[name].call(self, *args)
99 end
100 end
101 end
102 end
103
104 class Scope
105 attr_reader :proxy_scope, :proxy_options
106
107 [].methods.each do |m|
108 unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|^find$|count|sum|average|maximum|minimum|paginate|first|last|empty\?|respond_to\?)/
109 delegate m, :to => :proxy_found
110 end
111 end
112
113 delegate :scopes, :with_scope, :to => :proxy_scope
114
115 def initialize(proxy_scope, options)
116 [options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
117 extend Module.new { |*args| yield(*args) } if block_given?
118 @proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
119 end
120
121 def reload
122 load_found; self
123 end
124
125 def first(*args)
126 if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
127 proxy_found.first(*args)
128 else
129 find(:first, *args)
130 end
131 end
132
133 def last(*args)
134 if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
135 proxy_found.last(*args)
136 else
137 find(:last, *args)
138 end
139 end
140
141 def empty?
142 @found ? @found.empty? : count.zero?
143 end
144
145 def respond_to?(method, include_private = false)
146 super || @proxy_scope.respond_to?(method, include_private)
147 end
148
149 protected
150 def proxy_found
151 @found || load_found
152 end
153
154 private
155 def method_missing(method, *args)
156 if scopes.include?(method)
157 scopes[method].call(self, *args)
158 else
159 with_scope :find => proxy_options do
160 proxy_scope.send(method, *args) { |*a| yield(*a) if block_given? }
161 end
162 end
163 end
164
165 def load_found
166 @found = find(:all)
167 end
168 end
169 end
170end
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/named_scope_patch.rb b/vendor/plugins/will_paginate/lib/will_paginate/named_scope_patch.rb
deleted file mode 100644
index 7daff59..0000000
--- a/vendor/plugins/will_paginate/lib/will_paginate/named_scope_patch.rb
+++ /dev/null
@@ -1,37 +0,0 @@
1ActiveRecord::Associations::AssociationProxy.class_eval do
2 protected
3 def with_scope(*args)
4 @reflection.klass.send(:with_scope, *args) { |*a| yield(*a) if block_given? }
5 end
6end
7
8[ ActiveRecord::Associations::AssociationCollection,
9 ActiveRecord::Associations::HasManyThroughAssociation ].each do |klass|
10 klass.class_eval do
11 protected
12 alias :method_missing_without_scopes :method_missing_without_paginate
13 def method_missing_without_paginate(method, *args)
14 if @reflection.klass.scopes.include?(method)
15 @reflection.klass.scopes[method].call(self, *args) { |*a| yield(*a) if block_given? }
16 else
17 method_missing_without_scopes(method, *args) { |*a| yield(*a) if block_given? }
18 end
19 end
20 end
21end
22
23# Rails 1.2.6
24ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do
25 protected
26 def method_missing(method, *args)
27 if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
28 super
29 elsif @reflection.klass.scopes.include?(method)
30 @reflection.klass.scopes[method].call(self, *args)
31 else
32 @reflection.klass.with_scope(:find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false }) do
33 @reflection.klass.send(method, *args) { |*a| yield(*a) if block_given? }
34 end
35 end
36 end
37end if ActiveRecord::Base.respond_to? :find_first
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/version.rb b/vendor/plugins/will_paginate/lib/will_paginate/version.rb
deleted file mode 100644
index 99f3238..0000000
--- a/vendor/plugins/will_paginate/lib/will_paginate/version.rb
+++ /dev/null
@@ -1,9 +0,0 @@
1module WillPaginate
2 module VERSION
3 MAJOR = 2
4 MINOR = 3
5 TINY = 5
6
7 STRING = [MAJOR, MINOR, TINY].join('.')
8 end
9end
diff --git a/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb
deleted file mode 100644
index 5f7ae42..0000000
--- a/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb
+++ /dev/null
@@ -1,389 +0,0 @@
1require 'will_paginate/core_ext'
2
3module WillPaginate
4 # = Will Paginate view helpers
5 #
6 # The main view helper, #will_paginate, renders
7 # pagination links for the given collection. The helper itself is lightweight
8 # and serves only as a wrapper around LinkRenderer instantiation; the
9 # renderer then does all the hard work of generating the HTML.
10 #
11 # == Global options for helpers
12 #
13 # Options for pagination helpers are optional and get their default values from the
14 # <tt>WillPaginate::ViewHelpers.pagination_options</tt> hash. You can write to this hash to
15 # override default options on the global level:
16 #
17 # WillPaginate::ViewHelpers.pagination_options[:previous_label] = 'Previous page'
18 #
19 # By putting this into "config/initializers/will_paginate.rb" (or simply environment.rb in
20 # older versions of Rails) you can easily translate link texts to previous
21 # and next pages, as well as override some other defaults to your liking.
22 module ViewHelpers
23 # default options that can be overridden on the global level
24 @@pagination_options = {
25 :class => 'pagination',
26 :previous_label => '&laquo; Previous',
27 :next_label => 'Next &raquo;',
28 :inner_window => 4, # links around the current page
29 :outer_window => 1, # links around beginning and end
30 :separator => ' ', # single space is friendly to spiders and non-graphic browsers
31 :param_name => :page,
32 :params => nil,
33 :renderer => 'WillPaginate::LinkRenderer',
34 :page_links => true,
35 :container => true
36 }
37 mattr_reader :pagination_options
38
39 # Renders Digg/Flickr-style pagination for a WillPaginate::Collection
40 # object. Nil is returned if there is only one page in total; no point in
41 # rendering the pagination in that case...
42 #
43 # ==== Options
44 # Display options:
45 # * <tt>:previous_label</tt> -- default: "« Previous" (this parameter is called <tt>:prev_label</tt> in versions <b>2.3.2</b> and older!)
46 # * <tt>:next_label</tt> -- default: "Next »"
47 # * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
48 # * <tt>:inner_window</tt> -- how many links are shown around the current page (default: 4)
49 # * <tt>:outer_window</tt> -- how many links are around the first and the last page (default: 1)
50 # * <tt>:separator</tt> -- string separator for page HTML elements (default: single space)
51 #
52 # HTML options:
53 # * <tt>:class</tt> -- CSS class name for the generated DIV (default: "pagination")
54 # * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
55 # false only when you are rendering your own pagination markup (default: true)
56 # * <tt>:id</tt> -- HTML ID for the container (default: nil). Pass +true+ to have the ID
57 # automatically generated from the class name of objects in collection: for example, paginating
58 # ArticleComment models would yield an ID of "article_comments_pagination".
59 #
60 # Advanced options:
61 # * <tt>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
62 # * <tt>:params</tt> -- additional parameters when generating pagination links
63 # (eg. <tt>:controller => "foo", :action => nil</tt>)
64 # * <tt>:renderer</tt> -- class name, class or instance of a link renderer (default:
65 # <tt>WillPaginate::LinkRenderer</tt>)
66 #
67 # All options not recognized by will_paginate will become HTML attributes on the container
68 # element for pagination links (the DIV). For example:
69 #
70 # <%= will_paginate @posts, :style => 'font-size: small' %>
71 #
72 # ... will result in:
73 #
74 # <div class="pagination" style="font-size: small"> ... </div>
75 #
76 # ==== Using the helper without arguments
77 # If the helper is called without passing in the collection object, it will
78 # try to read from the instance variable inferred by the controller name.
79 # For example, calling +will_paginate+ while the current controller is
80 # PostsController will result in trying to read from the <tt>@posts</tt>
81 # variable. Example:
82 #
83 # <%= will_paginate :id => true %>
84 #
85 # ... will result in <tt>@post</tt> collection getting paginated:
86 #
87 # <div class="pagination" id="posts_pagination"> ... </div>
88 #
89 def will_paginate(collection = nil, options = {})
90 options, collection = collection, nil if collection.is_a? Hash
91 unless collection or !controller
92 collection_name = "@#{controller.controller_name}"
93 collection = instance_variable_get(collection_name)
94 raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " +
95 "forget to pass the collection object for will_paginate?" unless collection
96 end
97 # early exit if there is nothing to render
98 return nil unless WillPaginate::ViewHelpers.total_pages_for_collection(collection) > 1
99
100 options = options.symbolize_keys.reverse_merge WillPaginate::ViewHelpers.pagination_options
101 if options[:prev_label]
102 WillPaginate::Deprecation::warn(":prev_label view parameter is now :previous_label; the old name has been deprecated", caller)
103 options[:previous_label] = options.delete(:prev_label)
104 end
105
106 # get the renderer instance
107 renderer = case options[:renderer]
108 when String
109 options[:renderer].to_s.constantize.new
110 when Class
111 options[:renderer].new
112 else
113 options[:renderer]
114 end
115 # render HTML for pagination
116 renderer.prepare collection, options, self
117 renderer.to_html
118 end
119
120 # Wrapper for rendering pagination links at both top and bottom of a block
121 # of content.
122 #
123 # <% paginated_section @posts do %>
124 # <ol id="posts">
125 # <% for post in @posts %>
126 # <li> ... </li>
127 # <% end %>
128 # </ol>
129 # <% end %>
130 #
131 # will result in:
132 #
133 # <div class="pagination"> ... </div>
134 # <ol id="posts">
135 # ...
136 # </ol>
137 # <div class="pagination"> ... </div>
138 #
139 # Arguments are passed to a <tt>will_paginate</tt> call, so the same options
140 # apply. Don't use the <tt>:id</tt> option; otherwise you'll finish with two
141 # blocks of pagination links sharing the same ID (which is invalid HTML).
142 def paginated_section(*args, &block)
143 pagination = will_paginate(*args).to_s
144
145 unless ActionView::Base.respond_to? :erb_variable
146 concat pagination
147 yield
148 concat pagination
149 else
150 content = pagination + capture(&block) + pagination
151 concat(content, block.binding)
152 end
153 end
154
155 # Renders a helpful message with numbers of displayed vs. total entries.
156 # You can use this as a blueprint for your own, similar helpers.
157 #
158 # <%= page_entries_info @posts %>
159 # #-> Displaying posts 6 - 10 of 26 in total
160 #
161 # By default, the message will use the humanized class name of objects
162 # in collection: for instance, "project types" for ProjectType models.
163 # Override this with the <tt>:entry_name</tt> parameter:
164 #
165 # <%= page_entries_info @posts, :entry_name => 'item' %>
166 # #-> Displaying items 6 - 10 of 26 in total
167 def page_entries_info(collection, options = {})
168 entry_name = options[:entry_name] ||
169 (collection.empty?? 'entry' : collection.first.class.name.underscore.sub('_', ' '))
170
171 if collection.total_pages < 2
172 case collection.size
173 when 0; "No #{entry_name.pluralize} found"
174 when 1; "Displaying <b>1</b> #{entry_name}"
175 else; "Displaying <b>all #{collection.size}</b> #{entry_name.pluralize}"
176 end
177 else
178 %{Displaying #{entry_name.pluralize} <b>%d&nbsp;-&nbsp;%d</b> of <b>%d</b> in total} % [
179 collection.offset + 1,
180 collection.offset + collection.length,
181 collection.total_entries
182 ]
183 end
184 end
185
186 def self.total_pages_for_collection(collection) #:nodoc:
187 if collection.respond_to?('page_count') and !collection.respond_to?('total_pages')
188 WillPaginate::Deprecation.warn %{
189 You are using a paginated collection of class #{collection.class.name}
190 which conforms to the old API of WillPaginate::Collection by using
191 `page_count`, while the current method name is `total_pages`. Please
192 upgrade yours or 3rd-party code that provides the paginated collection}, caller
193 class << collection
194 def total_pages; page_count; end
195 end
196 end
197 collection.total_pages
198 end
199 end
200
201 # This class does the heavy lifting of actually building the pagination
202 # links. It is used by the <tt>will_paginate</tt> helper internally.
203 class LinkRenderer
204
205 # The gap in page links is represented by:
206 #
207 # <span class="gap">&hellip;</span>
208 attr_accessor :gap_marker
209
210 def initialize
211 @gap_marker = '<span class="gap">&hellip;</span>'
212 end
213
214 # * +collection+ is a WillPaginate::Collection instance or any other object
215 # that conforms to that API
216 # * +options+ are forwarded from +will_paginate+ view helper
217 # * +template+ is the reference to the template being rendered
218 def prepare(collection, options, template)
219 @collection = collection
220 @options = options
221 @template = template
222
223 # reset values in case we're re-using this instance
224 @total_pages = @param_name = @url_string = nil
225 end
226
227 # Process it! This method returns the complete HTML string which contains
228 # pagination links. Feel free to subclass LinkRenderer and change this
229 # method as you see fit.
230 def to_html
231 links = @options[:page_links] ? windowed_links : []
232 # previous/next buttons
233 links.unshift page_link_or_span(@collection.previous_page, 'disabled prev_page', @options[:previous_label])
234 links.push page_link_or_span(@collection.next_page, 'disabled next_page', @options[:next_label])
235
236 html = links.join(@options[:separator])
237 @options[:container] ? @template.content_tag(:div, html, html_attributes) : html
238 end
239
240 # Returns the subset of +options+ this instance was initialized with that
241 # represent HTML attributes for the container element of pagination links.
242 def html_attributes
243 return @html_attributes if @html_attributes
244 @html_attributes = @options.except *(WillPaginate::ViewHelpers.pagination_options.keys - [:class])
245 # pagination of Post models will have the ID of "posts_pagination"
246 if @options[:container] and @options[:id] === true
247 @html_attributes[:id] = @collection.first.class.name.underscore.pluralize + '_pagination'
248 end
249 @html_attributes
250 end
251
252 protected
253
254 # Collects link items for visible page numbers.
255 def windowed_links
256 prev = nil
257
258 visible_page_numbers.inject [] do |links, n|
259 # detect gaps:
260 links << gap_marker if prev and n > prev + 1
261 links << page_link_or_span(n, 'current')
262 prev = n
263 links
264 end
265 end
266
267 # Calculates visible page numbers using the <tt>:inner_window</tt> and
268 # <tt>:outer_window</tt> options.
269 def visible_page_numbers
270 inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i
271 window_from = current_page - inner_window
272 window_to = current_page + inner_window
273
274 # adjust lower or upper limit if other is out of bounds
275 if window_to > total_pages
276 window_from -= window_to - total_pages
277 window_to = total_pages
278 end
279 if window_from < 1
280 window_to += 1 - window_from
281 window_from = 1
282 window_to = total_pages if window_to > total_pages
283 end
284
285 visible = (1..total_pages).to_a
286 left_gap = (2 + outer_window)...window_from
287 right_gap = (window_to + 1)...(total_pages - outer_window)
288 visible -= left_gap.to_a if left_gap.last - left_gap.first > 1
289 visible -= right_gap.to_a if right_gap.last - right_gap.first > 1
290
291 visible
292 end
293
294 def page_link_or_span(page, span_class, text = nil)
295 text ||= page.to_s
296
297 if page and page != current_page
298 classnames = span_class && span_class.index(' ') && span_class.split(' ', 2).last
299 page_link page, text, :rel => rel_value(page), :class => classnames
300 else
301 page_span page, text, :class => span_class
302 end
303 end
304
305 def page_link(page, text, attributes = {})
306 @template.link_to text, url_for(page), attributes
307 end
308
309 def page_span(page, text, attributes = {})
310 @template.content_tag :span, text, attributes
311 end
312
313 # Returns URL params for +page_link_or_span+, taking the current GET params
314 # and <tt>:params</tt> option into account.
315 def url_for(page)
316 page_one = page == 1
317 unless @url_string and !page_one
318 @url_params = {}
319 # page links should preserve GET parameters
320 stringified_merge @url_params, @template.params if @template.request.get?
321 stringified_merge @url_params, @options[:params] if @options[:params]
322
323 if complex = param_name.index(/[^\w-]/)
324 page_param = (defined?(CGIMethods) ? CGIMethods : ActionController::AbstractRequest).
325 parse_query_parameters("#{param_name}=#{page}")
326
327 stringified_merge @url_params, page_param
328 else
329 @url_params[param_name] = page_one ? 1 : 2
330 end
331
332 url = @template.url_for(@url_params)
333 return url if page_one
334
335 if complex
336 @url_string = url.sub(%r!((?:\?|&amp;)#{CGI.escape param_name}=)#{page}!, '\1@')
337 return url
338 else
339 @url_string = url
340 @url_params[param_name] = 3
341 @template.url_for(@url_params).split(//).each_with_index do |char, i|
342 if char == '3' and url[i, 1] == '2'
343 @url_string[i] = '@'
344 break
345 end
346 end
347 end
348 end
349 # finally!
350 @url_string.sub '@', page.to_s
351 end
352
353 private
354
355 def rel_value(page)
356 case page
357 when @collection.previous_page; 'prev' + (page == 1 ? ' start' : '')
358 when @collection.next_page; 'next'
359 when 1; 'start'
360 end
361 end
362
363 def current_page
364 @collection.current_page
365 end
366
367 def total_pages
368 @total_pages ||= WillPaginate::ViewHelpers.total_pages_for_collection(@collection)
369 end
370
371 def param_name
372 @param_name ||= @options[:param_name].to_s
373 end
374
375 # Recursively merge into target hash by using stringified keys from the other one
376 def stringified_merge(target, other)
377 other.each do |key, value|
378 key = key.to_s # this line is what it's all about!
379 existing = target[key]
380
381 if value.is_a?(Hash) and (existing.is_a?(Hash) or existing.nil?)
382 stringified_merge(existing || (target[key] = {}), value)
383 else
384 target[key] = value
385 end
386 end
387 end
388 end
389end
diff --git a/vendor/plugins/will_paginate/test/boot.rb b/vendor/plugins/will_paginate/test/boot.rb
deleted file mode 100644
index 622fc93..0000000
--- a/vendor/plugins/will_paginate/test/boot.rb
+++ /dev/null
@@ -1,21 +0,0 @@
1plugin_root = File.join(File.dirname(__FILE__), '..')
2version = ENV['RAILS_VERSION']
3version = nil if version and version == ""
4
5# first look for a symlink to a copy of the framework
6if !version and framework_root = ["#{plugin_root}/rails", "#{plugin_root}/../../rails"].find { |p| File.directory? p }
7 puts "found framework root: #{framework_root}"
8 # this allows for a plugin to be tested outside of an app and without Rails gems
9 $:.unshift "#{framework_root}/activesupport/lib", "#{framework_root}/activerecord/lib", "#{framework_root}/actionpack/lib"
10else
11 # simply use installed gems if available
12 puts "using Rails#{version ? ' ' + version : nil} gems"
13 require 'rubygems'
14
15 if version
16 gem 'rails', version
17 else
18 gem 'actionpack'
19 gem 'activerecord'
20 end
21end
diff --git a/vendor/plugins/will_paginate/test/collection_test.rb b/vendor/plugins/will_paginate/test/collection_test.rb
deleted file mode 100644
index a9336bb..0000000
--- a/vendor/plugins/will_paginate/test/collection_test.rb
+++ /dev/null
@@ -1,143 +0,0 @@
1require 'helper'
2require 'will_paginate/array'
3
4class ArrayPaginationTest < Test::Unit::TestCase
5
6 def setup ; end
7
8 def test_simple
9 collection = ('a'..'e').to_a
10
11 [{ :page => 1, :per_page => 3, :expected => %w( a b c ) },
12 { :page => 2, :per_page => 3, :expected => %w( d e ) },
13 { :page => 1, :per_page => 5, :expected => %w( a b c d e ) },
14 { :page => 3, :per_page => 5, :expected => [] },
15 ].
16 each do |conditions|
17 expected = conditions.delete :expected
18 assert_equal expected, collection.paginate(conditions)
19 end
20 end
21
22 def test_defaults
23 result = (1..50).to_a.paginate
24 assert_equal 1, result.current_page
25 assert_equal 30, result.size
26 end
27
28 def test_deprecated_api
29 assert_raise(ArgumentError) { [].paginate(2) }
30 assert_raise(ArgumentError) { [].paginate(2, 10) }
31 end
32
33 def test_total_entries_has_precedence
34 result = %w(a b c).paginate :total_entries => 5
35 assert_equal 5, result.total_entries
36 end
37
38 def test_argument_error_with_params_and_another_argument
39 assert_raise ArgumentError do
40 [].paginate({}, 5)
41 end
42 end
43
44 def test_paginated_collection
45 entries = %w(a b c)
46 collection = create(2, 3, 10) do |pager|
47 assert_equal entries, pager.replace(entries)
48 end
49
50 assert_equal entries, collection
51 assert_respond_to_all collection, %w(total_pages each offset size current_page per_page total_entries)
52 assert_kind_of Array, collection
53 assert_instance_of Array, collection.entries
54 assert_equal 3, collection.offset
55 assert_equal 4, collection.total_pages
56 assert !collection.out_of_bounds?
57 end
58
59 def test_previous_next_pages
60 collection = create(1, 1, 3)
61 assert_nil collection.previous_page
62 assert_equal 2, collection.next_page
63
64 collection = create(2, 1, 3)
65 assert_equal 1, collection.previous_page
66 assert_equal 3, collection.next_page
67
68 collection = create(3, 1, 3)
69 assert_equal 2, collection.previous_page
70 assert_nil collection.next_page
71 end
72
73 def test_out_of_bounds
74 entries = create(2, 3, 2){}
75 assert entries.out_of_bounds?
76
77 entries = create(1, 3, 2){}
78 assert !entries.out_of_bounds?
79 end
80
81 def test_guessing_total_count
82 entries = create do |pager|
83 # collection is shorter than limit
84 pager.replace array
85 end
86 assert_equal 8, entries.total_entries
87
88 entries = create(2, 5, 10) do |pager|
89 # collection is shorter than limit, but we have an explicit count
90 pager.replace array
91 end
92 assert_equal 10, entries.total_entries
93
94 entries = create do |pager|
95 # collection is the same as limit; we can't guess
96 pager.replace array(5)
97 end
98 assert_equal nil, entries.total_entries
99
100 entries = create do |pager|
101 # collection is empty; we can't guess
102 pager.replace array(0)
103 end
104 assert_equal nil, entries.total_entries
105
106 entries = create(1) do |pager|
107 # collection is empty and we're on page 1,
108 # so the whole thing must be empty, too
109 pager.replace array(0)
110 end
111 assert_equal 0, entries.total_entries
112 end
113
114 def test_invalid_page
115 bad_inputs = [0, -1, nil, '', 'Schnitzel']
116
117 bad_inputs.each do |bad|
118 assert_raise(WillPaginate::InvalidPage) { create bad }
119 end
120 end
121
122 def test_invalid_per_page_setting
123 assert_raise(ArgumentError) { create(1, -1) }
124 end
125
126 def test_page_count_was_removed
127 assert_raise(NoMethodError) { create.page_count }
128 # It's `total_pages` now.
129 end
130
131 private
132 def create(page = 2, limit = 5, total = nil, &block)
133 if block_given?
134 WillPaginate::Collection.create(page, limit, total, &block)
135 else
136 WillPaginate::Collection.new(page, limit, total)
137 end
138 end
139
140 def array(size = 3)
141 Array.new(size)
142 end
143end
diff --git a/vendor/plugins/will_paginate/test/console b/vendor/plugins/will_paginate/test/console
deleted file mode 100755
index 3f282f1..0000000
--- a/vendor/plugins/will_paginate/test/console
+++ /dev/null
@@ -1,8 +0,0 @@
1#!/usr/bin/env ruby
2irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
3libs = []
4
5libs << 'irb/completion'
6libs << File.join('lib', 'load_fixtures')
7
8exec "#{irb} -Ilib:test#{libs.map{ |l| " -r #{l}" }.join} --simple-prompt"
diff --git a/vendor/plugins/will_paginate/test/finder_test.rb b/vendor/plugins/will_paginate/test/finder_test.rb
deleted file mode 100644
index 38ef565..0000000
--- a/vendor/plugins/will_paginate/test/finder_test.rb
+++ /dev/null
@@ -1,476 +0,0 @@
1require 'helper'
2require 'lib/activerecord_test_case'
3
4require 'will_paginate'
5WillPaginate.enable_activerecord
6WillPaginate.enable_named_scope
7
8class FinderTest < ActiveRecordTestCase
9 fixtures :topics, :replies, :users, :projects, :developers_projects
10
11 def test_new_methods_presence
12 assert_respond_to_all Topic, %w(per_page paginate paginate_by_sql)
13 end
14
15 def test_simple_paginate
16 assert_queries(1) do
17 entries = Topic.paginate :page => nil
18 assert_equal 1, entries.current_page
19 assert_equal 1, entries.total_pages
20 assert_equal 4, entries.size
21 end
22
23 assert_queries(2) do
24 entries = Topic.paginate :page => 2
25 assert_equal 1, entries.total_pages
26 assert entries.empty?
27 end
28 end
29
30 def test_parameter_api
31 # :page parameter in options is required!
32 assert_raise(ArgumentError){ Topic.paginate }
33 assert_raise(ArgumentError){ Topic.paginate({}) }
34
35 # explicit :all should not break anything
36 assert_equal Topic.paginate(:page => nil), Topic.paginate(:all, :page => 1)
37
38 # :count could be nil and we should still not cry
39 assert_nothing_raised { Topic.paginate :page => 1, :count => nil }
40 end
41
42 def test_paginate_with_per_page
43 entries = Topic.paginate :page => 1, :per_page => 1
44 assert_equal 1, entries.size
45 assert_equal 4, entries.total_pages
46
47 # Developer class has explicit per_page at 10
48 entries = Developer.paginate :page => 1
49 assert_equal 10, entries.size
50 assert_equal 2, entries.total_pages
51
52 entries = Developer.paginate :page => 1, :per_page => 5
53 assert_equal 11, entries.total_entries
54 assert_equal 5, entries.size
55 assert_equal 3, entries.total_pages
56 end
57
58 def test_paginate_with_order
59 entries = Topic.paginate :page => 1, :order => 'created_at desc'
60 expected = [topics(:futurama), topics(:harvey_birdman), topics(:rails), topics(:ar)].reverse
61 assert_equal expected, entries.to_a
62 assert_equal 1, entries.total_pages
63 end
64
65 def test_paginate_with_conditions
66 entries = Topic.paginate :page => 1, :conditions => ["created_at > ?", 30.minutes.ago]
67 expected = [topics(:rails), topics(:ar)]
68 assert_equal expected, entries.to_a
69 assert_equal 1, entries.total_pages
70 end
71
72 def test_paginate_with_include_and_conditions
73 entries = Topic.paginate \
74 :page => 1,
75 :include => :replies,
76 :conditions => "replies.content LIKE 'Bird%' ",
77 :per_page => 10
78
79 expected = Topic.find :all,
80 :include => 'replies',
81 :conditions => "replies.content LIKE 'Bird%' ",
82 :limit => 10
83
84 assert_equal expected, entries.to_a
85 assert_equal 1, entries.total_entries
86 end
87
88 def test_paginate_with_include_and_order
89 entries = nil
90 assert_queries(2) do
91 entries = Topic.paginate \
92 :page => 1,
93 :include => :replies,
94 :order => 'replies.created_at asc, topics.created_at asc',
95 :per_page => 10
96 end
97
98 expected = Topic.find :all,
99 :include => 'replies',
100 :order => 'replies.created_at asc, topics.created_at asc',
101 :limit => 10
102
103 assert_equal expected, entries.to_a
104 assert_equal 4, entries.total_entries
105 end
106
107 def test_paginate_associations_with_include
108 entries, project = nil, projects(:active_record)
109
110 assert_nothing_raised "THIS IS A BUG in Rails 1.2.3 that was fixed in [7326]. " +
111 "Please upgrade to a newer version of Rails." do
112 entries = project.topics.paginate \
113 :page => 1,
114 :include => :replies,
115 :conditions => "replies.content LIKE 'Nice%' ",
116 :per_page => 10
117 end
118
119 expected = Topic.find :all,
120 :include => 'replies',
121 :conditions => "project_id = #{project.id} AND replies.content LIKE 'Nice%' ",
122 :limit => 10
123
124 assert_equal expected, entries.to_a
125 end
126
127 def test_paginate_associations
128 dhh = users :david
129 expected_name_ordered = [projects(:action_controller), projects(:active_record)]
130 expected_id_ordered = [projects(:active_record), projects(:action_controller)]
131
132 assert_queries(2) do
133 # with association-specified order
134 entries = dhh.projects.paginate(:page => 1)
135 assert_equal expected_name_ordered, entries
136 assert_equal 2, entries.total_entries
137 end
138
139 # with explicit order
140 entries = dhh.projects.paginate(:page => 1, :order => 'projects.id')
141 assert_equal expected_id_ordered, entries
142 assert_equal 2, entries.total_entries
143
144 assert_nothing_raised { dhh.projects.find(:all, :order => 'projects.id', :limit => 4) }
145 entries = dhh.projects.paginate(:page => 1, :order => 'projects.id', :per_page => 4)
146 assert_equal expected_id_ordered, entries
147
148 # has_many with implicit order
149 topic = Topic.find(1)
150 expected = [replies(:spam), replies(:witty_retort)]
151 assert_equal expected.map(&:id).sort, topic.replies.paginate(:page => 1).map(&:id).sort
152 assert_equal expected.reverse, topic.replies.paginate(:page => 1, :order => 'replies.id ASC')
153 end
154
155 def test_paginate_association_extension
156 project = Project.find(:first)
157
158 assert_queries(2) do
159 entries = project.replies.paginate_recent :page => 1
160 assert_equal [replies(:brave)], entries
161 end
162 end
163
164 def test_paginate_with_joins
165 entries = nil
166
167 assert_queries(1) do
168 entries = Developer.paginate :page => 1,
169 :joins => 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id',
170 :conditions => 'project_id = 1'
171 assert_equal 2, entries.size
172 developer_names = entries.map &:name
173 assert developer_names.include?('David')
174 assert developer_names.include?('Jamis')
175 end
176
177 assert_queries(1) do
178 expected = entries.to_a
179 entries = Developer.paginate :page => 1,
180 :joins => 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id',
181 :conditions => 'project_id = 1', :count => { :select => "users.id" }
182 assert_equal expected, entries.to_a
183 assert_equal 2, entries.total_entries
184 end
185 end
186
187 def test_paginate_with_group
188 entries = nil
189 assert_queries(1) do
190 entries = Developer.paginate :page => 1, :per_page => 10,
191 :group => 'salary', :select => 'salary', :order => 'salary'
192 end
193
194 expected = [ users(:david), users(:jamis), users(:dev_10), users(:poor_jamis) ].map(&:salary).sort
195 assert_equal expected, entries.map(&:salary)
196 end
197
198 def test_paginate_with_dynamic_finder
199 expected = [replies(:witty_retort), replies(:spam)]
200 assert_equal expected, Reply.paginate_by_topic_id(1, :page => 1)
201
202 entries = Developer.paginate :conditions => { :salary => 100000 }, :page => 1, :per_page => 5
203 assert_equal 8, entries.total_entries
204 assert_equal entries, Developer.paginate_by_salary(100000, :page => 1, :per_page => 5)
205
206 # dynamic finder + conditions
207 entries = Developer.paginate_by_salary(100000, :page => 1,
208 :conditions => ['id > ?', 6])
209 assert_equal 4, entries.total_entries
210 assert_equal (7..10).to_a, entries.map(&:id)
211
212 assert_raises NoMethodError do
213 Developer.paginate_by_inexistent_attribute 100000, :page => 1
214 end
215 end
216
217 def test_scoped_paginate
218 entries = Developer.with_poor_ones { Developer.paginate :page => 1 }
219
220 assert_equal 2, entries.size
221 assert_equal 2, entries.total_entries
222 end
223
224 ## named_scope ##
225
226 def test_paginate_in_named_scope
227 entries = Developer.poor.paginate :page => 1, :per_page => 1
228
229 assert_equal 1, entries.size
230 assert_equal 2, entries.total_entries
231 end
232
233 def test_paginate_in_named_scope_on_habtm_association
234 project = projects(:active_record)
235 assert_queries(2) do
236 entries = project.developers.poor.paginate :page => 1, :per_page => 1
237
238 assert_equal 1, entries.size, 'one developer should be found'
239 assert_equal 1, entries.total_entries, 'only one developer should be found'
240 end
241 end
242
243 def test_paginate_in_named_scope_on_hmt_association
244 project = projects(:active_record)
245 expected = [replies(:brave)]
246
247 assert_queries(2) do
248 entries = project.replies.recent.paginate :page => 1, :per_page => 1
249 assert_equal expected, entries
250 assert_equal 1, entries.total_entries, 'only one reply should be found'
251 end
252 end
253
254 def test_paginate_in_named_scope_on_has_many_association
255 project = projects(:active_record)
256 expected = [topics(:ar)]
257
258 assert_queries(2) do
259 entries = project.topics.mentions_activerecord.paginate :page => 1, :per_page => 1
260 assert_equal expected, entries
261 assert_equal 1, entries.total_entries, 'only one topic should be found'
262 end
263 end
264
265 def test_named_scope_with_include
266 project = projects(:active_record)
267 entries = project.topics.with_replies_starting_with('AR ').paginate(:page => 1, :per_page => 1)
268 assert_equal 1, entries.size
269 end
270
271 ## misc ##
272
273 def test_count_and_total_entries_options_are_mutually_exclusive
274 e = assert_raise ArgumentError do
275 Developer.paginate :page => 1, :count => {}, :total_entries => 1
276 end
277 assert_match /exclusive/, e.to_s
278 end
279
280 def test_readonly
281 assert_nothing_raised { Developer.paginate :readonly => true, :page => 1 }
282 end
283
284 # this functionality is temporarily removed
285 def xtest_pagination_defines_method
286 pager = "paginate_by_created_at"
287 assert !User.methods.include?(pager), "User methods should not include `#{pager}` method"
288 # paginate!
289 assert 0, User.send(pager, nil, :page => 1).total_entries
290 # the paging finder should now be defined
291 assert User.methods.include?(pager), "`#{pager}` method should be defined on User"
292 end
293
294 # Is this Rails 2.0? Find out by testing find_all which was removed in [6998]
295 unless ActiveRecord::Base.respond_to? :find_all
296 def test_paginate_array_of_ids
297 # AR finders also accept arrays of IDs
298 # (this was broken in Rails before [6912])
299 assert_queries(1) do
300 entries = Developer.paginate((1..8).to_a, :per_page => 3, :page => 2, :order => 'id')
301 assert_equal (4..6).to_a, entries.map(&:id)
302 assert_equal 8, entries.total_entries
303 end
304 end
305 end
306
307 uses_mocha 'internals' do
308 def test_implicit_all_with_dynamic_finders
309 Topic.expects(:find_all_by_foo).returns([])
310 Topic.expects(:count).returns(0)
311 Topic.paginate_by_foo :page => 2
312 end
313
314 def test_guessing_the_total_count
315 Topic.expects(:find).returns(Array.new(2))
316 Topic.expects(:count).never
317
318 entries = Topic.paginate :page => 2, :per_page => 4
319 assert_equal 6, entries.total_entries
320 end
321
322 def test_guessing_that_there_are_no_records
323 Topic.expects(:find).returns([])
324 Topic.expects(:count).never
325
326 entries = Topic.paginate :page => 1, :per_page => 4
327 assert_equal 0, entries.total_entries
328 end
329
330 def test_extra_parameters_stay_untouched
331 Topic.expects(:find).with(:all, {:foo => 'bar', :limit => 4, :offset => 0 }).returns(Array.new(5))
332 Topic.expects(:count).with({:foo => 'bar'}).returns(1)
333
334 Topic.paginate :foo => 'bar', :page => 1, :per_page => 4
335 end
336
337 def test_count_skips_select
338 Developer.stubs(:find).returns([])
339 Developer.expects(:count).with({}).returns(0)
340 Developer.paginate :select => 'salary', :page => 2
341 end
342
343 def test_count_select_when_distinct
344 Developer.stubs(:find).returns([])
345 Developer.expects(:count).with(:select => 'DISTINCT salary').returns(0)
346 Developer.paginate :select => 'DISTINCT salary', :page => 2
347 end
348
349 def test_count_with_scoped_select_when_distinct
350 Developer.stubs(:find).returns([])
351 Developer.expects(:count).with(:select => 'DISTINCT users.id').returns(0)
352 Developer.distinct.paginate :page => 2
353 end
354
355 def test_should_use_scoped_finders_if_present
356 # scope-out compatibility
357 Topic.expects(:find_best).returns(Array.new(5))
358 Topic.expects(:with_best).returns(1)
359
360 Topic.paginate_best :page => 1, :per_page => 4
361 end
362
363 def test_paginate_by_sql
364 assert_respond_to Developer, :paginate_by_sql
365 Developer.expects(:find_by_sql).with(regexp_matches(/sql LIMIT 3(,| OFFSET) 3/)).returns([])
366 Developer.expects(:count_by_sql).with('SELECT COUNT(*) FROM (sql) AS count_table').returns(0)
367
368 entries = Developer.paginate_by_sql 'sql', :page => 2, :per_page => 3
369 end
370
371 def test_paginate_by_sql_respects_total_entries_setting
372 Developer.expects(:find_by_sql).returns([])
373 Developer.expects(:count_by_sql).never
374
375 entries = Developer.paginate_by_sql 'sql', :page => 1, :total_entries => 999
376 assert_equal 999, entries.total_entries
377 end
378
379 def test_paginate_by_sql_strips_order_by_when_counting
380 Developer.expects(:find_by_sql).returns([])
381 Developer.expects(:count_by_sql).with("SELECT COUNT(*) FROM (sql\n ) AS count_table").returns(0)
382
383 Developer.paginate_by_sql "sql\n ORDER\nby foo, bar, `baz` ASC", :page => 2
384 end
385
386 # TODO: counts are still wrong
387 def test_ability_to_use_with_custom_finders
388 # acts_as_taggable defines find_tagged_with(tag, options)
389 Topic.expects(:find_tagged_with).with('will_paginate', :offset => 5, :limit => 5).returns([])
390 Topic.expects(:count).with({}).returns(0)
391
392 Topic.paginate_tagged_with 'will_paginate', :page => 2, :per_page => 5
393 end
394
395 def test_array_argument_doesnt_eliminate_count
396 ids = (1..8).to_a
397 Developer.expects(:find_all_by_id).returns([])
398 Developer.expects(:count).returns(0)
399
400 Developer.paginate_by_id(ids, :per_page => 3, :page => 2, :order => 'id')
401 end
402
403 def test_paginating_finder_doesnt_mangle_options
404 Developer.expects(:find).returns([])
405 options = { :page => 1, :per_page => 2, :foo => 'bar' }
406 options_before = options.dup
407
408 Developer.paginate(options)
409 assert_equal options_before, options
410 end
411
412 def test_paginate_by_sql_doesnt_change_original_query
413 query = 'SQL QUERY'
414 original_query = query.dup
415 Developer.expects(:find_by_sql).returns([])
416
417 Developer.paginate_by_sql query, :page => 1
418 assert_equal original_query, query
419 end
420
421 def test_paginated_each
422 collection = stub('collection', :size => 5, :empty? => false, :per_page => 5)
423 collection.expects(:each).times(2).returns(collection)
424 last_collection = stub('collection', :size => 4, :empty? => false, :per_page => 5)
425 last_collection.expects(:each).returns(last_collection)
426
427 params = { :order => 'id', :total_entries => 0 }
428
429 Developer.expects(:paginate).with(params.merge(:page => 2)).returns(collection)
430 Developer.expects(:paginate).with(params.merge(:page => 3)).returns(collection)
431 Developer.expects(:paginate).with(params.merge(:page => 4)).returns(last_collection)
432
433 assert_equal 14, Developer.paginated_each(:page => '2') { }
434 end
435
436 def test_paginated_each_with_named_scope
437 assert_equal 2, Developer.poor.paginated_each(:per_page => 1) {
438 assert_equal 11, Developer.count
439 }
440 end
441
442 # detect ActiveRecord 2.1
443 if ActiveRecord::Base.private_methods.include?('references_eager_loaded_tables?')
444 def test_removes_irrelevant_includes_in_count
445 Developer.expects(:find).returns([1])
446 Developer.expects(:count).with({}).returns(0)
447
448 Developer.paginate :page => 1, :per_page => 1, :include => :projects
449 end
450
451 def test_doesnt_remove_referenced_includes_in_count
452 Developer.expects(:find).returns([1])
453 Developer.expects(:count).with({ :include => :projects, :conditions => 'projects.id > 2' }).returns(0)
454
455 Developer.paginate :page => 1, :per_page => 1,
456 :include => :projects, :conditions => 'projects.id > 2'
457 end
458 end
459
460 def test_paginate_from
461 result = Developer.paginate(:from => 'users', :page => 1, :per_page => 1)
462 assert_equal 1, result.size
463 end
464
465 def test_hmt_with_include
466 # ticket #220
467 reply = projects(:active_record).replies.find(:first, :order => 'replies.id')
468 assert_equal replies(:decisive), reply
469
470 # ticket #223
471 Project.find(1, :include => :replies)
472
473 # I cannot reproduce any of the failures from those reports :(
474 end
475 end
476end
diff --git a/vendor/plugins/will_paginate/test/fixtures/admin.rb b/vendor/plugins/will_paginate/test/fixtures/admin.rb
deleted file mode 100644
index 1d5e7f3..0000000
--- a/vendor/plugins/will_paginate/test/fixtures/admin.rb
+++ /dev/null
@@ -1,3 +0,0 @@
1class Admin < User
2 has_many :companies, :finder_sql => 'SELECT * FROM companies'
3end
diff --git a/vendor/plugins/will_paginate/test/fixtures/developer.rb b/vendor/plugins/will_paginate/test/fixtures/developer.rb
deleted file mode 100644
index 0224f4b..0000000
--- a/vendor/plugins/will_paginate/test/fixtures/developer.rb
+++ /dev/null
@@ -1,14 +0,0 @@
1class Developer < User
2 has_and_belongs_to_many :projects, :include => :topics, :order => 'projects.name'
3
4 def self.with_poor_ones(&block)
5 with_scope :find => { :conditions => ['salary <= ?', 80000], :order => 'salary' } do
6 yield
7 end
8 end
9
10 named_scope :distinct, :select => 'DISTINCT `users`.*'
11 named_scope :poor, :conditions => ['salary <= ?', 80000], :order => 'salary'
12
13 def self.per_page() 10 end
14end
diff --git a/vendor/plugins/will_paginate/test/fixtures/developers_projects.yml b/vendor/plugins/will_paginate/test/fixtures/developers_projects.yml
deleted file mode 100644
index cee359c..0000000
--- a/vendor/plugins/will_paginate/test/fixtures/developers_projects.yml
+++ /dev/null
@@ -1,13 +0,0 @@
1david_action_controller:
2 developer_id: 1
3 project_id: 2
4 joined_on: 2004-10-10
5
6david_active_record:
7 developer_id: 1
8 project_id: 1
9 joined_on: 2004-10-10
10
11jamis_active_record:
12 developer_id: 2
13 project_id: 1 \ No newline at end of file
diff --git a/vendor/plugins/will_paginate/test/fixtures/project.rb b/vendor/plugins/will_paginate/test/fixtures/project.rb
deleted file mode 100644
index 0f85ef5..0000000
--- a/vendor/plugins/will_paginate/test/fixtures/project.rb
+++ /dev/null
@@ -1,15 +0,0 @@
1class Project < ActiveRecord::Base
2 has_and_belongs_to_many :developers, :uniq => true
3
4 has_many :topics
5 # :finder_sql => 'SELECT * FROM topics WHERE (topics.project_id = #{id})',
6 # :counter_sql => 'SELECT COUNT(*) FROM topics WHERE (topics.project_id = #{id})'
7
8 has_many :replies, :through => :topics do
9 def find_recent(params = {})
10 with_scope :find => { :conditions => ['replies.created_at > ?', 15.minutes.ago] } do
11 find :all, params
12 end
13 end
14 end
15end
diff --git a/vendor/plugins/will_paginate/test/fixtures/projects.yml b/vendor/plugins/will_paginate/test/fixtures/projects.yml
deleted file mode 100644
index 74f3c32..0000000
--- a/vendor/plugins/will_paginate/test/fixtures/projects.yml
+++ /dev/null
@@ -1,6 +0,0 @@
1active_record:
2 id: 1
3 name: Active Record
4action_controller:
5 id: 2
6 name: Active Controller
diff --git a/vendor/plugins/will_paginate/test/fixtures/replies.yml b/vendor/plugins/will_paginate/test/fixtures/replies.yml
deleted file mode 100644
index 9a83c00..0000000
--- a/vendor/plugins/will_paginate/test/fixtures/replies.yml
+++ /dev/null
@@ -1,29 +0,0 @@
1witty_retort:
2 id: 1
3 topic_id: 1
4 content: Birdman is better!
5 created_at: <%= 6.hours.ago.to_s(:db) %>
6
7another:
8 id: 2
9 topic_id: 2
10 content: Nuh uh!
11 created_at: <%= 1.hour.ago.to_s(:db) %>
12
13spam:
14 id: 3
15 topic_id: 1
16 content: Nice site!
17 created_at: <%= 1.hour.ago.to_s(:db) %>
18
19decisive:
20 id: 4
21 topic_id: 4
22 content: "I'm getting to the bottom of this"
23 created_at: <%= 30.minutes.ago.to_s(:db) %>
24
25brave:
26 id: 5
27 topic_id: 4
28 content: "AR doesn't scare me a bit"
29 created_at: <%= 10.minutes.ago.to_s(:db) %>
diff --git a/vendor/plugins/will_paginate/test/fixtures/reply.rb b/vendor/plugins/will_paginate/test/fixtures/reply.rb
deleted file mode 100644
index ecaf3c1..0000000
--- a/vendor/plugins/will_paginate/test/fixtures/reply.rb
+++ /dev/null
@@ -1,7 +0,0 @@
1class Reply < ActiveRecord::Base
2 belongs_to :topic, :include => [:replies]
3
4 named_scope :recent, :conditions => ['replies.created_at > ?', 15.minutes.ago]
5
6 validates_presence_of :content
7end
diff --git a/vendor/plugins/will_paginate/test/fixtures/topic.rb b/vendor/plugins/will_paginate/test/fixtures/topic.rb
deleted file mode 100644
index 2c2ce72..0000000
--- a/vendor/plugins/will_paginate/test/fixtures/topic.rb
+++ /dev/null
@@ -1,10 +0,0 @@
1class Topic < ActiveRecord::Base
2 has_many :replies, :dependent => :destroy, :order => 'replies.created_at DESC'
3 belongs_to :project
4
5 named_scope :mentions_activerecord, :conditions => ['topics.title LIKE ?', '%ActiveRecord%']
6
7 named_scope :with_replies_starting_with, lambda { |text|
8 { :conditions => "replies.content LIKE '#{text}%' ", :include => :replies }
9 }
10end
diff --git a/vendor/plugins/will_paginate/test/fixtures/topics.yml b/vendor/plugins/will_paginate/test/fixtures/topics.yml
deleted file mode 100644
index 0a26904..0000000
--- a/vendor/plugins/will_paginate/test/fixtures/topics.yml
+++ /dev/null
@@ -1,30 +0,0 @@
1futurama:
2 id: 1
3 title: Isnt futurama awesome?
4 subtitle: It really is, isnt it.
5 content: I like futurama
6 created_at: <%= 1.day.ago.to_s(:db) %>
7 updated_at:
8
9harvey_birdman:
10 id: 2
11 title: Harvey Birdman is the king of all men
12 subtitle: yup
13 content: He really is
14 created_at: <%= 2.hours.ago.to_s(:db) %>
15 updated_at:
16
17rails:
18 id: 3
19 project_id: 1
20 title: Rails is nice
21 subtitle: It makes me happy
22 content: except when I have to hack internals to fix pagination. even then really.
23 created_at: <%= 20.minutes.ago.to_s(:db) %>
24
25ar:
26 id: 4
27 project_id: 1
28 title: ActiveRecord sometimes freaks me out
29 content: "I mean, what's the deal with eager loading?"
30 created_at: <%= 15.minutes.ago.to_s(:db) %>
diff --git a/vendor/plugins/will_paginate/test/fixtures/user.rb b/vendor/plugins/will_paginate/test/fixtures/user.rb
deleted file mode 100644
index 4a57cf0..0000000
--- a/vendor/plugins/will_paginate/test/fixtures/user.rb
+++ /dev/null
@@ -1,2 +0,0 @@
1class User < ActiveRecord::Base
2end
diff --git a/vendor/plugins/will_paginate/test/fixtures/users.yml b/vendor/plugins/will_paginate/test/fixtures/users.yml
deleted file mode 100644
index ed2c03a..0000000
--- a/vendor/plugins/will_paginate/test/fixtures/users.yml
+++ /dev/null
@@ -1,35 +0,0 @@
1david:
2 id: 1
3 name: David
4 salary: 80000
5 type: Developer
6
7jamis:
8 id: 2
9 name: Jamis
10 salary: 150000
11 type: Developer
12
13<% for digit in 3..10 %>
14dev_<%= digit %>:
15 id: <%= digit %>
16 name: fixture_<%= digit %>
17 salary: 100000
18 type: Developer
19<% end %>
20
21poor_jamis:
22 id: 11
23 name: Jamis
24 salary: 9000
25 type: Developer
26
27admin:
28 id: 12
29 name: admin
30 type: Admin
31
32goofy:
33 id: 13
34 name: Goofy
35 type: Admin
diff --git a/vendor/plugins/will_paginate/test/helper.rb b/vendor/plugins/will_paginate/test/helper.rb
deleted file mode 100644
index ad52b1b..0000000
--- a/vendor/plugins/will_paginate/test/helper.rb
+++ /dev/null
@@ -1,37 +0,0 @@
1require 'test/unit'
2require 'rubygems'
3
4# gem install redgreen for colored test output
5begin require 'redgreen'; rescue LoadError; end
6
7require 'boot' unless defined?(ActiveRecord)
8
9class Test::Unit::TestCase
10 protected
11 def assert_respond_to_all object, methods
12 methods.each do |method|
13 [method.to_s, method.to_sym].each { |m| assert_respond_to object, m }
14 end
15 end
16
17 def collect_deprecations
18 old_behavior = WillPaginate::Deprecation.behavior
19 deprecations = []
20 WillPaginate::Deprecation.behavior = Proc.new do |message, callstack|
21 deprecations << message
22 end
23 result = yield
24 [result, deprecations]
25 ensure
26 WillPaginate::Deprecation.behavior = old_behavior
27 end
28end
29
30# Wrap tests that use Mocha and skip if unavailable.
31def uses_mocha(test_name)
32 require 'mocha' unless Object.const_defined?(:Mocha)
33rescue LoadError => load_error
34 $stderr.puts "Skipping #{test_name} tests. `gem install mocha` and try again."
35else
36 yield
37end
diff --git a/vendor/plugins/will_paginate/test/lib/activerecord_test_case.rb b/vendor/plugins/will_paginate/test/lib/activerecord_test_case.rb
deleted file mode 100644
index 8f66ebe..0000000
--- a/vendor/plugins/will_paginate/test/lib/activerecord_test_case.rb
+++ /dev/null
@@ -1,36 +0,0 @@
1require 'lib/activerecord_test_connector'
2
3class ActiveRecordTestCase < Test::Unit::TestCase
4 # Set our fixture path
5 if ActiveRecordTestConnector.able_to_connect
6 self.fixture_path = File.join(File.dirname(__FILE__), '..', 'fixtures')
7 self.use_transactional_fixtures = true
8 end
9
10 def self.fixtures(*args)
11 super if ActiveRecordTestConnector.connected
12 end
13
14 def run(*args)
15 super if ActiveRecordTestConnector.connected
16 end
17
18 # Default so Test::Unit::TestCase doesn't complain
19 def test_truth
20 end
21
22 protected
23
24 def assert_queries(num = 1)
25 $query_count = 0
26 yield
27 ensure
28 assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed."
29 end
30
31 def assert_no_queries(&block)
32 assert_queries(0, &block)
33 end
34end
35
36ActiveRecordTestConnector.setup
diff --git a/vendor/plugins/will_paginate/test/lib/activerecord_test_connector.rb b/vendor/plugins/will_paginate/test/lib/activerecord_test_connector.rb
deleted file mode 100644
index 1130cf5..0000000
--- a/vendor/plugins/will_paginate/test/lib/activerecord_test_connector.rb
+++ /dev/null
@@ -1,75 +0,0 @@
1require 'active_record'
2require 'active_record/version'
3require 'active_record/fixtures'
4
5class ActiveRecordTestConnector
6 cattr_accessor :able_to_connect
7 cattr_accessor :connected
8
9 FIXTURES_PATH = File.join(File.dirname(__FILE__), '..', 'fixtures')
10
11 # Set our defaults
12 self.connected = false
13 self.able_to_connect = true
14
15 def self.setup
16 unless self.connected || !self.able_to_connect
17 setup_connection
18 load_schema
19 add_load_path FIXTURES_PATH
20 self.connected = true
21 end
22 rescue Exception => e # errors from ActiveRecord setup
23 $stderr.puts "\nSkipping ActiveRecord tests: #{e}\n\n"
24 self.able_to_connect = false
25 end
26
27 private
28
29 def self.add_load_path(path)
30 dep = defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies : ::Dependencies
31 dep.load_paths.unshift path
32 end
33
34 def self.setup_connection
35 db = ENV['DB'].blank?? 'sqlite3' : ENV['DB']
36
37 configurations = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'database.yml'))
38 raise "no configuration for '#{db}'" unless configurations.key? db
39 configuration = configurations[db]
40
41 ActiveRecord::Base.logger = Logger.new(STDOUT) if $0 == 'irb'
42 puts "using #{configuration['adapter']} adapter" unless ENV['DB'].blank?
43
44 gem 'sqlite3-ruby' if 'sqlite3' == db
45
46 ActiveRecord::Base.establish_connection(configuration)
47 ActiveRecord::Base.configurations = { db => configuration }
48 prepare ActiveRecord::Base.connection
49
50 unless Object.const_defined?(:QUOTED_TYPE)
51 Object.send :const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')
52 end
53 end
54
55 def self.load_schema
56 ActiveRecord::Base.silence do
57 ActiveRecord::Migration.verbose = false
58 load File.join(FIXTURES_PATH, 'schema.rb')
59 end
60 end
61
62 def self.prepare(conn)
63 class << conn
64 IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SHOW FIELDS /]
65
66 def execute_with_counting(sql, name = nil, &block)
67 $query_count ||= 0
68 $query_count += 1 unless IGNORED_SQL.any? { |r| sql =~ r }
69 execute_without_counting(sql, name, &block)
70 end
71
72 alias_method_chain :execute, :counting
73 end
74 end
75end
diff --git a/vendor/plugins/will_paginate/test/lib/load_fixtures.rb b/vendor/plugins/will_paginate/test/lib/load_fixtures.rb
deleted file mode 100644
index 10d6f42..0000000
--- a/vendor/plugins/will_paginate/test/lib/load_fixtures.rb
+++ /dev/null
@@ -1,11 +0,0 @@
1require 'boot'
2require 'lib/activerecord_test_connector'
3
4# setup the connection
5ActiveRecordTestConnector.setup
6
7# load all fixtures
8Fixtures.create_fixtures(ActiveRecordTestConnector::FIXTURES_PATH, ActiveRecord::Base.connection.tables)
9
10require 'will_paginate'
11WillPaginate.enable_activerecord
diff --git a/vendor/plugins/will_paginate/test/lib/view_test_process.rb b/vendor/plugins/will_paginate/test/lib/view_test_process.rb
deleted file mode 100644
index 508411e..0000000
--- a/vendor/plugins/will_paginate/test/lib/view_test_process.rb
+++ /dev/null
@@ -1,171 +0,0 @@
1require 'action_controller'
2require 'action_controller/test_process'
3
4require 'will_paginate'
5WillPaginate.enable_actionpack
6
7ActionController::Routing::Routes.draw do |map|
8 map.connect 'dummy/page/:page', :controller => 'dummy'
9 map.connect 'dummy/dots/page.:page', :controller => 'dummy', :action => 'dots'
10 map.connect 'ibocorp/:page', :controller => 'ibocorp',
11 :requirements => { :page => /\d+/ },
12 :defaults => { :page => 1 }
13
14 map.connect ':controller/:action/:id'
15end
16
17ActionController::Base.perform_caching = false
18
19class WillPaginate::ViewTestCase < Test::Unit::TestCase
20 def setup
21 super
22 @controller = DummyController.new
23 @request = @controller.request
24 @html_result = nil
25 @template = '<%= will_paginate collection, options %>'
26
27 @view = ActionView::Base.new
28 @view.assigns['controller'] = @controller
29 @view.assigns['_request'] = @request
30 @view.assigns['_params'] = @request.params
31 end
32
33 def test_no_complain; end
34
35 protected
36
37 def paginate(collection = {}, options = {}, &block)
38 if collection.instance_of? Hash
39 page_options = { :page => 1, :total_entries => 11, :per_page => 4 }.merge(collection)
40 collection = [1].paginate(page_options)
41 end
42
43 locals = { :collection => collection, :options => options }
44
45 unless @view.respond_to? :render_template
46 # Rails 2.2
47 @html_result = ActionView::InlineTemplate.new(@template).render(@view, locals)
48 else
49 if defined? ActionView::InlineTemplate
50 # Rails 2.1
51 args = [ ActionView::InlineTemplate.new(@view, @template, locals) ]
52 else
53 # older Rails versions
54 args = [nil, @template, nil, locals]
55 end
56
57 @html_result = @view.render_template(*args)
58 end
59
60 @html_document = HTML::Document.new(@html_result, true, false)
61
62 if block_given?
63 classname = options[:class] || WillPaginate::ViewHelpers.pagination_options[:class]
64 assert_select("div.#{classname}", 1, 'no main DIV', &block)
65 end
66 end
67
68 def response_from_page_or_rjs
69 @html_document.root
70 end
71
72 def validate_page_numbers expected, links, param_name = :page
73 param_pattern = /\W#{CGI.escape(param_name.to_s)}=([^&]*)/
74
75 assert_equal(expected, links.map { |e|
76 e['href'] =~ param_pattern
77 $1 ? $1.to_i : $1
78 })
79 end
80
81 def assert_links_match pattern, links = nil, numbers = nil
82 links ||= assert_select 'div.pagination a[href]' do |elements|
83 elements
84 end
85
86 pages = [] if numbers
87
88 links.each do |el|
89 assert_match pattern, el['href']
90 if numbers
91 el['href'] =~ pattern
92 pages << ($1.nil?? nil : $1.to_i)
93 end
94 end
95
96 assert_equal numbers, pages, "page numbers don't match" if numbers
97 end
98
99 def assert_no_links_match pattern
100 assert_select 'div.pagination a[href]' do |elements|
101 elements.each do |el|
102 assert_no_match pattern, el['href']
103 end
104 end
105 end
106end
107
108class DummyRequest
109 attr_accessor :symbolized_path_parameters
110
111 def initialize
112 @get = true
113 @params = {}
114 @symbolized_path_parameters = { :controller => 'foo', :action => 'bar' }
115 end
116
117 def get?
118 @get
119 end
120
121 def post
122 @get = false
123 end
124
125 def relative_url_root
126 ''
127 end
128
129 def params(more = nil)
130 @params.update(more) if more
131 @params
132 end
133end
134
135class DummyController
136 attr_reader :request
137 attr_accessor :controller_name
138
139 def initialize
140 @request = DummyRequest.new
141 @url = ActionController::UrlRewriter.new(@request, @request.params)
142 end
143
144 def params
145 @request.params
146 end
147
148 def url_for(params)
149 @url.rewrite(params)
150 end
151end
152
153module HTML
154 Node.class_eval do
155 def inner_text
156 children.map(&:inner_text).join('')
157 end
158 end
159
160 Text.class_eval do
161 def inner_text
162 self.to_s
163 end
164 end
165
166 Tag.class_eval do
167 def inner_text
168 childless?? '' : super
169 end
170 end
171end
diff --git a/vendor/plugins/will_paginate/test/tasks.rake b/vendor/plugins/will_paginate/test/tasks.rake
deleted file mode 100644
index 59f2f94..0000000
--- a/vendor/plugins/will_paginate/test/tasks.rake
+++ /dev/null
@@ -1,59 +0,0 @@
1require 'rake/testtask'
2
3desc 'Test the will_paginate plugin.'
4Rake::TestTask.new(:test) do |t|
5 t.pattern = 'test/**/*_test.rb'
6 t.verbose = true
7 t.libs << 'test'
8end
9
10# I want to specify environment variables at call time
11class EnvTestTask < Rake::TestTask
12 attr_accessor :env
13
14 def ruby(*args)
15 env.each { |key, value| ENV[key] = value } if env
16 super
17 env.keys.each { |key| ENV.delete key } if env
18 end
19end
20
21for configuration in %w( sqlite3 mysql postgres )
22 EnvTestTask.new("test_#{configuration}") do |t|
23 t.pattern = 'test/finder_test.rb'
24 t.verbose = true
25 t.env = { 'DB' => configuration }
26 t.libs << 'test'
27 end
28end
29
30task :test_databases => %w(test_mysql test_sqlite3 test_postgres)
31
32desc %{Test everything on SQLite3, MySQL and PostgreSQL}
33task :test_full => %w(test test_mysql test_postgres)
34
35desc %{Test everything with Rails 2.1.x, 2.0.x & 1.2.x gems}
36task :test_all do
37 all = Rake::Task['test_full']
38 versions = %w(2.1.0 2.0.4 1.2.6)
39 versions.each do |version|
40 ENV['RAILS_VERSION'] = "~> #{version}"
41 all.invoke
42 reset_invoked unless version == versions.last
43 end
44end
45
46def reset_invoked
47 %w( test_full test test_mysql test_postgres ).each do |name|
48 Rake::Task[name].instance_variable_set '@already_invoked', false
49 end
50end
51
52task :rcov do
53 excludes = %w( lib/will_paginate/named_scope*
54 lib/will_paginate/core_ext.rb
55 lib/will_paginate.rb
56 rails* )
57
58 system %[rcov -Itest:lib test/*.rb -x #{excludes.join(',')}]
59end
diff --git a/vendor/plugins/will_paginate/test/view_test.rb b/vendor/plugins/will_paginate/test/view_test.rb
deleted file mode 100644
index 3ada457..0000000
--- a/vendor/plugins/will_paginate/test/view_test.rb
+++ /dev/null
@@ -1,365 +0,0 @@
1require 'helper'
2require 'lib/view_test_process'
3
4class AdditionalLinkAttributesRenderer < WillPaginate::LinkRenderer
5 def initialize(link_attributes = nil)
6 super()
7 @additional_link_attributes = link_attributes || { :default => 'true' }
8 end
9
10 def page_link(page, text, attributes = {})
11 @template.link_to text, url_for(page), attributes.merge(@additional_link_attributes)
12 end
13end
14
15class ViewTest < WillPaginate::ViewTestCase
16
17 ## basic pagination ##
18
19 def test_will_paginate
20 paginate do |pagination|
21 assert_select 'a[href]', 3 do |elements|
22 validate_page_numbers [2,3,2], elements
23 assert_select elements.last, ':last-child', "Next &raquo;"
24 end
25 assert_select 'span', 2
26 assert_select 'span.disabled:first-child', '&laquo; Previous'
27 assert_select 'span.current', '1'
28 assert_equal '&laquo; Previous 1 2 3 Next &raquo;', pagination.first.inner_text
29 end
30 end
31
32 def test_no_pagination_when_page_count_is_one
33 paginate :per_page => 30
34 assert_equal '', @html_result
35 end
36
37 def test_will_paginate_with_options
38 paginate({ :page => 2 },
39 :class => 'will_paginate', :previous_label => 'Prev', :next_label => 'Next') do
40 assert_select 'a[href]', 4 do |elements|
41 validate_page_numbers [1,1,3,3], elements
42 # test rel attribute values:
43 assert_select elements[1], 'a', '1' do |link|
44 assert_equal 'prev start', link.first['rel']
45 end
46 assert_select elements.first, 'a', "Prev" do |link|
47 assert_equal 'prev start', link.first['rel']
48 end
49 assert_select elements.last, 'a', "Next" do |link|
50 assert_equal 'next', link.first['rel']
51 end
52 end
53 assert_select 'span.current', '2'
54 end
55 end
56
57 def test_will_paginate_using_renderer_class
58 paginate({}, :renderer => AdditionalLinkAttributesRenderer) do
59 assert_select 'a[default=true]', 3
60 end
61 end
62
63 def test_will_paginate_using_renderer_instance
64 renderer = WillPaginate::LinkRenderer.new
65 renderer.gap_marker = '<span class="my-gap">~~</span>'
66
67 paginate({ :per_page => 2 }, :inner_window => 0, :outer_window => 0, :renderer => renderer) do
68 assert_select 'span.my-gap', '~~'
69 end
70
71 renderer = AdditionalLinkAttributesRenderer.new(:title => 'rendered')
72 paginate({}, :renderer => renderer) do
73 assert_select 'a[title=rendered]', 3
74 end
75 end
76
77 def test_prev_next_links_have_classnames
78 paginate do |pagination|
79 assert_select 'span.disabled.prev_page:first-child'
80 assert_select 'a.next_page[href]:last-child'
81 end
82 end
83
84 def test_prev_label_deprecated
85 assert_deprecated ':previous_label' do
86 paginate({ :page => 2 }, :prev_label => 'Deprecated') do
87 assert_select 'a[href]:first-child', 'Deprecated'
88 end
89 end
90 end
91
92 def test_full_output
93 paginate
94 expected = <<-HTML
95 <div class="pagination"><span class="disabled prev_page">&laquo; Previous</span>
96 <span class="current">1</span>
97 <a href="/foo/bar?page=2" rel="next">2</a>
98 <a href="/foo/bar?page=3">3</a>
99 <a href="/foo/bar?page=2" class="next_page" rel="next">Next &raquo;</a></div>
100 HTML
101 expected.strip!.gsub!(/\s{2,}/, ' ')
102
103 assert_dom_equal expected, @html_result
104 end
105
106 def test_escaping_of_urls
107 paginate({:page => 1, :per_page => 1, :total_entries => 2},
108 :page_links => false, :params => { :tag => '<br>' })
109
110 assert_select 'a[href]', 1 do |links|
111 query = links.first['href'].split('?', 2)[1]
112 assert_equal %w(page=2 tag=%3Cbr%3E), query.split('&amp;').sort
113 end
114 end
115
116 ## advanced options for pagination ##
117
118 def test_will_paginate_without_container
119 paginate({}, :container => false)
120 assert_select 'div.pagination', 0, 'main DIV present when it shouldn\'t'
121 assert_select 'a[href]', 3
122 end
123
124 def test_will_paginate_without_page_links
125 paginate({ :page => 2 }, :page_links => false) do
126 assert_select 'a[href]', 2 do |elements|
127 validate_page_numbers [1,3], elements
128 end
129 end
130 end
131
132 def test_will_paginate_windows
133 paginate({ :page => 6, :per_page => 1 }, :inner_window => 1) do |pagination|
134 assert_select 'a[href]', 8 do |elements|
135 validate_page_numbers [5,1,2,5,7,10,11,7], elements
136 assert_select elements.first, 'a', '&laquo; Previous'
137 assert_select elements.last, 'a', 'Next &raquo;'
138 end
139 assert_select 'span.current', '6'
140 assert_equal '&laquo; Previous 1 2 &hellip; 5 6 7 &hellip; 10 11 Next &raquo;', pagination.first.inner_text
141 end
142 end
143
144 def test_will_paginate_eliminates_small_gaps
145 paginate({ :page => 6, :per_page => 1 }, :inner_window => 2) do
146 assert_select 'a[href]', 12 do |elements|
147 validate_page_numbers [5,1,2,3,4,5,7,8,9,10,11,7], elements
148 end
149 end
150 end
151
152 def test_container_id
153 paginate do |div|
154 assert_nil div.first['id']
155 end
156
157 # magic ID
158 paginate({}, :id => true) do |div|
159 assert_equal 'fixnums_pagination', div.first['id']
160 end
161
162 # explicit ID
163 paginate({}, :id => 'custom_id') do |div|
164 assert_equal 'custom_id', div.first['id']
165 end
166 end
167
168 ## other helpers ##
169
170 def test_paginated_section
171 @template = <<-ERB
172 <% paginated_section collection, options do %>
173 <%= content_tag :div, '', :id => "developers" %>
174 <% end %>
175 ERB
176
177 paginate
178 assert_select 'div.pagination', 2
179 assert_select 'div.pagination + div#developers', 1
180 end
181
182 def test_page_entries_info
183 @template = '<%= page_entries_info collection %>'
184 array = ('a'..'z').to_a
185
186 paginate array.paginate(:page => 2, :per_page => 5)
187 assert_equal %{Displaying strings <b>6&nbsp;-&nbsp;10</b> of <b>26</b> in total},
188 @html_result
189
190 paginate array.paginate(:page => 7, :per_page => 4)
191 assert_equal %{Displaying strings <b>25&nbsp;-&nbsp;26</b> of <b>26</b> in total},
192 @html_result
193 end
194
195 uses_mocha 'class name' do
196 def test_page_entries_info_with_longer_class_name
197 @template = '<%= page_entries_info collection %>'
198 collection = ('a'..'z').to_a.paginate
199 collection.first.stubs(:class).returns(mock('class', :name => 'ProjectType'))
200
201 paginate collection
202 assert @html_result.index('project types'), "expected <#{@html_result.inspect}> to mention 'project types'"
203 end
204 end
205
206 def test_page_entries_info_with_single_page_collection
207 @template = '<%= page_entries_info collection %>'
208
209 paginate(('a'..'d').to_a.paginate(:page => 1, :per_page => 5))
210 assert_equal %{Displaying <b>all 4</b> strings}, @html_result
211
212 paginate(['a'].paginate(:page => 1, :per_page => 5))
213 assert_equal %{Displaying <b>1</b> string}, @html_result
214
215 paginate([].paginate(:page => 1, :per_page => 5))
216 assert_equal %{No entries found}, @html_result
217 end
218
219 def test_page_entries_info_with_custom_entry_name
220 @template = '<%= page_entries_info collection, :entry_name => "author" %>'
221
222 entries = (1..20).to_a
223
224 paginate(entries.paginate(:page => 1, :per_page => 5))
225 assert_equal %{Displaying authors <b>1&nbsp;-&nbsp;5</b> of <b>20</b> in total}, @html_result
226
227 paginate(entries.paginate(:page => 1, :per_page => 20))
228 assert_equal %{Displaying <b>all 20</b> authors}, @html_result
229
230 paginate(['a'].paginate(:page => 1, :per_page => 5))
231 assert_equal %{Displaying <b>1</b> author}, @html_result
232
233 paginate([].paginate(:page => 1, :per_page => 5))
234 assert_equal %{No authors found}, @html_result
235 end
236
237 ## parameter handling in page links ##
238
239 def test_will_paginate_preserves_parameters_on_get
240 @request.params :foo => { :bar => 'baz' }
241 paginate
242 assert_links_match /foo%5Bbar%5D=baz/
243 end
244
245 def test_will_paginate_doesnt_preserve_parameters_on_post
246 @request.post
247 @request.params :foo => 'bar'
248 paginate
249 assert_no_links_match /foo=bar/
250 end
251
252 def test_adding_additional_parameters
253 paginate({}, :params => { :foo => 'bar' })
254 assert_links_match /foo=bar/
255 end
256
257 def test_adding_anchor_parameter
258 paginate({}, :params => { :anchor => 'anchor' })
259 assert_links_match /#anchor$/
260 end
261
262 def test_removing_arbitrary_parameters
263 @request.params :foo => 'bar'
264 paginate({}, :params => { :foo => nil })
265 assert_no_links_match /foo=bar/
266 end
267
268 def test_adding_additional_route_parameters
269 paginate({}, :params => { :controller => 'baz', :action => 'list' })
270 assert_links_match %r{\Wbaz/list\W}
271 end
272
273 def test_will_paginate_with_custom_page_param
274 paginate({ :page => 2 }, :param_name => :developers_page) do
275 assert_select 'a[href]', 4 do |elements|
276 validate_page_numbers [1,1,3,3], elements, :developers_page
277 end
278 end
279 end
280
281 def test_complex_custom_page_param
282 @request.params :developers => { :page => 2 }
283
284 paginate({ :page => 2 }, :param_name => 'developers[page]') do
285 assert_select 'a[href]', 4 do |links|
286 assert_links_match /\?developers%5Bpage%5D=\d+$/, links
287 validate_page_numbers [1,1,3,3], links, 'developers[page]'
288 end
289 end
290 end
291
292 def test_custom_routing_page_param
293 @request.symbolized_path_parameters.update :controller => 'dummy', :action => nil
294 paginate :per_page => 2 do
295 assert_select 'a[href]', 6 do |links|
296 assert_links_match %r{/page/(\d+)$}, links, [2, 3, 4, 5, 6, 2]
297 end
298 end
299 end
300
301 def test_custom_routing_page_param_with_dot_separator
302 @request.symbolized_path_parameters.update :controller => 'dummy', :action => 'dots'
303 paginate :per_page => 2 do
304 assert_select 'a[href]', 6 do |links|
305 assert_links_match %r{/page\.(\d+)$}, links, [2, 3, 4, 5, 6, 2]
306 end
307 end
308 end
309
310 def test_custom_routing_with_first_page_hidden
311 @request.symbolized_path_parameters.update :controller => 'ibocorp', :action => nil
312 paginate :page => 2, :per_page => 2 do
313 assert_select 'a[href]', 7 do |links|
314 assert_links_match %r{/ibocorp(?:/(\d+))?$}, links, [nil, nil, 3, 4, 5, 6, 3]
315 end
316 end
317 end
318
319 ## internal hardcore stuff ##
320
321 class LegacyCollection < WillPaginate::Collection
322 alias :page_count :total_pages
323 undef :total_pages
324 end
325
326 def test_deprecation_notices_with_page_count
327 collection = LegacyCollection.new(1, 1, 2)
328
329 assert_deprecated collection.class.name do
330 paginate collection
331 end
332 end
333
334 uses_mocha 'view internals' do
335 def test_collection_name_can_be_guessed
336 collection = mock
337 collection.expects(:total_pages).returns(1)
338
339 @template = '<%= will_paginate options %>'
340 @controller.controller_name = 'developers'
341 @view.assigns['developers'] = collection
342
343 paginate(nil)
344 end
345 end
346
347 def test_inferred_collection_name_raises_error_when_nil
348 @template = '<%= will_paginate options %>'
349 @controller.controller_name = 'developers'
350
351 e = assert_raise ArgumentError do
352 paginate(nil)
353 end
354 assert e.message.include?('@developers')
355 end
356
357 if ActionController::Base.respond_to? :rescue_responses
358 # only on Rails 2
359 def test_rescue_response_hook_presence
360 assert_equal :not_found,
361 ActionController::Base.rescue_responses['WillPaginate::InvalidPage']
362 end
363 end
364
365end
diff --git a/vendor/plugins/will_paginate/will_paginate.gemspec b/vendor/plugins/will_paginate/will_paginate.gemspec
deleted file mode 100644
index 1072c3a..0000000
--- a/vendor/plugins/will_paginate/will_paginate.gemspec
+++ /dev/null
@@ -1,20 +0,0 @@
1Gem::Specification.new do |s|
2 s.name = 'will_paginate'
3 s.version = '2.3.7'
4 s.date = '2009-02-10'
5
6 s.summary = "Most awesome pagination solution for Rails"
7 s.description = "The will_paginate library provides a simple, yet powerful and extensible API for ActiveRecord pagination and rendering of pagination links in ActionView templates."
8
9 s.authors = ['Mislav Marohnić', 'PJ Hyett']
10 s.email = 'mislav.marohnic@gmail.com'
11 s.homepage = 'http://github.com/mislav/will_paginate/wikis'
12
13 s.has_rdoc = true
14 s.rdoc_options = ['--main', 'README.rdoc']
15 s.rdoc_options << '--inline-source' << '--charset=UTF-8'
16 s.extra_rdoc_files = ['README.rdoc', 'LICENSE', 'CHANGELOG.rdoc']
17
18 s.files = %w(CHANGELOG.rdoc LICENSE README.rdoc Rakefile examples examples/apple-circle.gif examples/index.haml examples/index.html examples/pagination.css examples/pagination.sass init.rb lib lib/will_paginate lib/will_paginate.rb lib/will_paginate/array.rb lib/will_paginate/collection.rb lib/will_paginate/core_ext.rb lib/will_paginate/finder.rb lib/will_paginate/named_scope.rb lib/will_paginate/named_scope_patch.rb lib/will_paginate/version.rb lib/will_paginate/view_helpers.rb test test/boot.rb test/collection_test.rb test/console test/database.yml test/finder_test.rb test/fixtures test/fixtures/admin.rb test/fixtures/developer.rb test/fixtures/developers_projects.yml test/fixtures/project.rb test/fixtures/projects.yml test/fixtures/replies.yml test/fixtures/reply.rb test/fixtures/schema.rb test/fixtures/topic.rb test/fixtures/topics.yml test/fixtures/user.rb test/fixtures/users.yml test/helper.rb test/lib test/lib/activerecord_test_case.rb test/lib/activerecord_test_connector.rb test/lib/load_fixtures.rb test/lib/view_test_process.rb test/tasks.rake test/view_test.rb)
19 s.test_files = %w(test/boot.rb test/collection_test.rb test/console test/database.yml test/finder_test.rb test/fixtures test/fixtures/admin.rb test/fixtures/developer.rb test/fixtures/developers_projects.yml test/fixtures/project.rb test/fixtures/projects.yml test/fixtures/replies.yml test/fixtures/reply.rb test/fixtures/schema.rb test/fixtures/topic.rb test/fixtures/topics.yml test/fixtures/user.rb test/fixtures/users.yml test/helper.rb test/lib test/lib/activerecord_test_case.rb test/lib/activerecord_test_connector.rb test/lib/load_fixtures.rb test/lib/view_test_process.rb test/tasks.rake test/view_test.rb)
20end