admin_assistant 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (418) hide show
  1. data/Rakefile +2 -2
  2. data/VERSION +1 -1
  3. data/admin_assistant.gemspec +404 -8
  4. data/rails_2_test/README +256 -0
  5. data/rails_2_test/Rakefile +13 -0
  6. data/rails_2_test/app/controllers/admin/appointments2_controller.rb +15 -0
  7. data/rails_2_test/app/controllers/admin/appointments_controller.rb +11 -0
  8. data/rails_2_test/app/controllers/admin/blog_posts2_controller.rb +138 -0
  9. data/rails_2_test/app/controllers/admin/blog_posts3_controller.rb +76 -0
  10. data/rails_2_test/app/controllers/admin/blog_posts4_controller.rb +21 -0
  11. data/rails_2_test/app/controllers/admin/blog_posts5_controller.rb +27 -0
  12. data/rails_2_test/app/controllers/admin/blog_posts6_controller.rb +10 -0
  13. data/rails_2_test/app/controllers/admin/blog_posts_controller.rb +8 -0
  14. data/rails_2_test/app/controllers/admin/blog_posts_custom_new_and_edit_controller.rb +15 -0
  15. data/rails_2_test/app/controllers/admin/blog_posts_read_only_controller.rb +19 -0
  16. data/rails_2_test/app/controllers/admin/bookmarks_controller.rb +11 -0
  17. data/rails_2_test/app/controllers/admin/comments2_controller.rb +14 -0
  18. data/rails_2_test/app/controllers/admin/comments_controller.rb +12 -0
  19. data/rails_2_test/app/controllers/admin/file_column_images2_controller.rb +10 -0
  20. data/rails_2_test/app/controllers/admin/file_column_images_controller.rb +6 -0
  21. data/rails_2_test/app/controllers/admin/images2_controller.rb +11 -0
  22. data/rails_2_test/app/controllers/admin/images_controller.rb +6 -0
  23. data/rails_2_test/app/controllers/admin/misconfigured1_controller.rb +7 -0
  24. data/rails_2_test/app/controllers/admin/not_migrated_yets_controller.rb +9 -0
  25. data/rails_2_test/app/controllers/admin/product_categories2_controller.rb +7 -0
  26. data/rails_2_test/app/controllers/admin/product_categories_controller.rb +5 -0
  27. data/rails_2_test/app/controllers/admin/products2_controller.rb +15 -0
  28. data/rails_2_test/app/controllers/admin/products_controller.rb +31 -0
  29. data/rails_2_test/app/controllers/admin/television_airings_controller.rb +5 -0
  30. data/rails_2_test/app/controllers/admin/television_time_slots_controller.rb +5 -0
  31. data/rails_2_test/app/controllers/admin/users2_controller.rb +10 -0
  32. data/rails_2_test/app/controllers/admin/users_controller.rb +63 -0
  33. data/rails_2_test/app/controllers/application.rb +16 -0
  34. data/rails_2_test/app/controllers/application_controller.rb +16 -0
  35. data/rails_2_test/app/controllers/blog_posts_controller.rb +5 -0
  36. data/rails_2_test/app/helpers/admin/appointments2_helper.rb +2 -0
  37. data/rails_2_test/app/helpers/admin/appointments_helper.rb +2 -0
  38. data/rails_2_test/app/helpers/admin/blog_posts2_helper.rb +27 -0
  39. data/rails_2_test/app/helpers/admin/blog_posts3_helper.rb +2 -0
  40. data/rails_2_test/app/helpers/admin/blog_posts4_helper.rb +2 -0
  41. data/rails_2_test/app/helpers/admin/blog_posts6_helper.rb +16 -0
  42. data/rails_2_test/app/helpers/admin/blog_posts_custom_new_and_edit_helper.rb +29 -0
  43. data/rails_2_test/app/helpers/admin/blog_posts_helper.rb +2 -0
  44. data/rails_2_test/app/helpers/admin/blog_posts_read_only_helper.rb +2 -0
  45. data/rails_2_test/app/helpers/admin/bookmarks_helper.rb +2 -0
  46. data/rails_2_test/app/helpers/admin/comments2_helper.rb +2 -0
  47. data/rails_2_test/app/helpers/admin/comments_helper.rb +2 -0
  48. data/rails_2_test/app/helpers/admin/file_column_images2_helper.rb +2 -0
  49. data/rails_2_test/app/helpers/admin/file_column_images_helper.rb +2 -0
  50. data/rails_2_test/app/helpers/admin/images2_helper.rb +8 -0
  51. data/rails_2_test/app/helpers/admin/images_helper.rb +2 -0
  52. data/rails_2_test/app/helpers/admin/misconfigured1_helper.rb +2 -0
  53. data/rails_2_test/app/helpers/admin/not_migrated_yets_helper.rb +2 -0
  54. data/rails_2_test/app/helpers/admin/product_categories2_helper.rb +2 -0
  55. data/rails_2_test/app/helpers/admin/product_categories_helper.rb +2 -0
  56. data/rails_2_test/app/helpers/admin/products2_helper.rb +3 -0
  57. data/rails_2_test/app/helpers/admin/products_helper.rb +6 -0
  58. data/rails_2_test/app/helpers/admin/television_airings_helper.rb +2 -0
  59. data/rails_2_test/app/helpers/admin/television_time_slots_helper.rb +2 -0
  60. data/rails_2_test/app/helpers/admin/users2_helper.rb +2 -0
  61. data/rails_2_test/app/helpers/admin/users_helper.rb +25 -0
  62. data/rails_2_test/app/helpers/application_helper.rb +3 -0
  63. data/rails_2_test/app/helpers/blog_posts_helper.rb +2 -0
  64. data/rails_2_test/app/models/appointment.rb +5 -0
  65. data/rails_2_test/app/models/blog_post.rb +14 -0
  66. data/rails_2_test/app/models/blog_post_tag.rb +6 -0
  67. data/rails_2_test/app/models/bookmark.rb +6 -0
  68. data/rails_2_test/app/models/comment.rb +4 -0
  69. data/rails_2_test/app/models/file_column_image.rb +3 -0
  70. data/rails_2_test/app/models/image.rb +3 -0
  71. data/rails_2_test/app/models/not_migrated_yet.rb +2 -0
  72. data/rails_2_test/app/models/product.rb +7 -0
  73. data/rails_2_test/app/models/product_category.rb +9 -0
  74. data/rails_2_test/app/models/tag.rb +4 -0
  75. data/rails_2_test/app/models/television_airing.rb +3 -0
  76. data/rails_2_test/app/models/television_time_slot.rb +11 -0
  77. data/rails_2_test/app/models/user.rb +14 -0
  78. data/rails_2_test/app/views/admin/appointments/_subject_input.html.erb +2 -0
  79. data/rails_2_test/app/views/admin/appointments2/_time_input.html.erb +31 -0
  80. data/rails_2_test/app/views/admin/blog_posts2/_after_form.html.erb +5 -0
  81. data/rails_2_test/app/views/admin/blog_posts2/_after_index.html.erb +1 -0
  82. data/rails_2_test/app/views/admin/blog_posts2/_after_tags_input.html.erb +16 -0
  83. data/rails_2_test/app/views/admin/blog_posts2/_before_index.html.erb +2 -0
  84. data/rails_2_test/app/views/admin/blog_posts5/_after_index_header.html.erb +8 -0
  85. data/rails_2_test/app/views/admin/blog_posts_read_only/_body_for_show.html.erb +1 -0
  86. data/rails_2_test/app/views/admin/products/_name_input.html.erb +2 -0
  87. data/rails_2_test/app/views/admin/products/_percent_off_input.html.erb +2 -0
  88. data/rails_2_test/app/views/admin/products/_price_input.html.erb +10 -0
  89. data/rails_2_test/app/views/blog_posts/show.html.erb +13 -0
  90. data/rails_2_test/app/views/layouts/admin.html.erb +31 -0
  91. data/rails_2_test/config/boot.rb +109 -0
  92. data/rails_2_test/config/database.yml +28 -0
  93. data/rails_2_test/config/environment.rb +78 -0
  94. data/rails_2_test/config/environments/development.rb +17 -0
  95. data/rails_2_test/config/environments/production.rb +24 -0
  96. data/rails_2_test/config/environments/test.rb +24 -0
  97. data/rails_2_test/config/initializers/inflections.rb +10 -0
  98. data/rails_2_test/config/initializers/mime_types.rb +5 -0
  99. data/rails_2_test/config/initializers/new_rails_defaults.rb +17 -0
  100. data/rails_2_test/config/locales/en.yml +5 -0
  101. data/rails_2_test/config/routes.rb +43 -0
  102. data/rails_2_test/db/migrate/20090213215514_create_blog_posts.rb +12 -0
  103. data/rails_2_test/db/migrate/20090217225542_add_body_to_blog_posts.rb +9 -0
  104. data/rails_2_test/db/migrate/20090221220917_create_tags.rb +13 -0
  105. data/rails_2_test/db/migrate/20090221220947_create_blog_post_tags.rb +14 -0
  106. data/rails_2_test/db/migrate/20090222162204_add_textile_to_blog_posts.rb +9 -0
  107. data/rails_2_test/db/migrate/20090222163231_add_published_at_to_blog_posts.rb +9 -0
  108. data/rails_2_test/db/migrate/20090301191722_create_images.rb +16 -0
  109. data/rails_2_test/db/migrate/20090305165345_create_accounts.rb +14 -0
  110. data/rails_2_test/db/migrate/20090307225027_rename_accounts_to_users.rb +9 -0
  111. data/rails_2_test/db/migrate/20090307225750_add_user_id_to_blog_posts.rb +9 -0
  112. data/rails_2_test/db/migrate/20090309185114_change_blog_posts_textile.rb +9 -0
  113. data/rails_2_test/db/migrate/20090309193635_create_products.rb +14 -0
  114. data/rails_2_test/db/migrate/20090309203056_create_comments.rb +15 -0
  115. data/rails_2_test/db/migrate/20090323005947_create_file_column_images.rb +12 -0
  116. data/rails_2_test/db/migrate/20090326160049_add_birthday_to_users.rb +9 -0
  117. data/rails_2_test/db/migrate/20090326223606_add_state_to_users.rb +9 -0
  118. data/rails_2_test/db/migrate/20090503134004_add_file_column_image_to_products.rb +9 -0
  119. data/rails_2_test/db/migrate/20090617173651_create_bookmarks.rb +15 -0
  120. data/rails_2_test/db/migrate/20090624165355_add_tags_string_to_blog_post.rb +9 -0
  121. data/rails_2_test/db/migrate/20090625144313_add_avatar_fields_to_user.rb +11 -0
  122. data/rails_2_test/db/migrate/20090629202956_add_merged_into_to_blog_posts.rb +9 -0
  123. data/rails_2_test/db/migrate/20090701171857_add_force_textile_to_users.rb +9 -0
  124. data/rails_2_test/db/migrate/20090704163647_add_deleted_at_to_products.rb +9 -0
  125. data/rails_2_test/db/migrate/20090704173800_add_sale_fields_to_products.rb +13 -0
  126. data/rails_2_test/db/migrate/20090714024501_create_product_categories.rb +15 -0
  127. data/rails_2_test/db/migrate/20090819162835_add_admin_level_to_users.rb +9 -0
  128. data/rails_2_test/db/migrate/20091115134559_add_first_and_last_names_to_users.rb +11 -0
  129. data/rails_2_test/db/migrate/20091221011256_add_position_to_product_categories.rb +9 -0
  130. data/rails_2_test/db/migrate/20091222160814_create_appointments.rb +14 -0
  131. data/rails_2_test/db/migrate/20091227224547_add_user_id_to_appointments.rb +9 -0
  132. data/rails_2_test/db/migrate/20100214213359_create_television_time_slots.rb +13 -0
  133. data/rails_2_test/db/migrate/20100214213451_create_television_airings.rb +15 -0
  134. data/rails_2_test/doc/README_FOR_APP +5 -0
  135. data/rails_2_test/lib/tasks/rspec.rake +163 -0
  136. data/rails_2_test/public/404.html +30 -0
  137. data/rails_2_test/public/422.html +30 -0
  138. data/rails_2_test/public/500.html +33 -0
  139. data/rails_2_test/public/dispatch.cgi +10 -0
  140. data/rails_2_test/public/dispatch.fcgi +24 -0
  141. data/rails_2_test/public/dispatch.rb +10 -0
  142. data/rails_2_test/public/favicon.ico +0 -0
  143. data/rails_2_test/public/images/rails.png +0 -0
  144. data/rails_2_test/public/index.html +274 -0
  145. data/rails_2_test/public/javascripts/application.js +2 -0
  146. data/rails_2_test/public/javascripts/jquery-1.4.4.min.js +167 -0
  147. data/rails_2_test/public/robots.txt +5 -0
  148. data/rails_2_test/public/stylesheets/admin.css +3 -0
  149. data/rails_2_test/public/stylesheets/scaffold.css +54 -0
  150. data/rails_2_test/script/about +4 -0
  151. data/rails_2_test/script/autospec +5 -0
  152. data/rails_2_test/script/console +3 -0
  153. data/rails_2_test/script/dbconsole +3 -0
  154. data/rails_2_test/script/destroy +3 -0
  155. data/rails_2_test/script/generate +3 -0
  156. data/rails_2_test/script/performance/benchmarker +3 -0
  157. data/rails_2_test/script/performance/profiler +3 -0
  158. data/rails_2_test/script/performance/request +3 -0
  159. data/rails_2_test/script/plugin +3 -0
  160. data/rails_2_test/script/populate_tables.rb +49 -0
  161. data/rails_2_test/script/process/inspector +3 -0
  162. data/rails_2_test/script/process/reaper +3 -0
  163. data/rails_2_test/script/process/spawner +3 -0
  164. data/rails_2_test/script/runner +3 -0
  165. data/rails_2_test/script/server +3 -0
  166. data/rails_2_test/script/spec +5 -0
  167. data/rails_2_test/script/spec_server +125 -0
  168. data/rails_2_test/test/data/ruby_throated.jpg +0 -0
  169. data/rails_2_test/test/data/tweenbot.jpg +0 -0
  170. data/rails_2_test/test/fixtures/file_column_images.yml +7 -0
  171. data/rails_2_test/test/integration/admin/appointments2_integration_test.rb +79 -0
  172. data/rails_2_test/test/integration/admin/appointments_integration_test.rb +367 -0
  173. data/rails_2_test/test/integration/admin/blog_posts2_integration_test.rb +806 -0
  174. data/rails_2_test/test/integration/admin/blog_posts3_integration_test.rb +418 -0
  175. data/rails_2_test/test/integration/admin/blog_posts4_integration_test.rb +189 -0
  176. data/rails_2_test/test/integration/admin/blog_posts5_integration_test.rb +75 -0
  177. data/rails_2_test/test/integration/admin/blog_posts6_integration_test.rb +39 -0
  178. data/rails_2_test/test/integration/admin/blog_posts_custom_new_and_edit_integration_test.rb +89 -0
  179. data/rails_2_test/test/integration/admin/blog_posts_integration_test.rb +788 -0
  180. data/rails_2_test/test/integration/admin/blog_posts_read_only_integration_test.rb +69 -0
  181. data/rails_2_test/test/integration/admin/bookmarks_integration_test.rb +445 -0
  182. data/rails_2_test/test/integration/admin/comments2_integration_test.rb +52 -0
  183. data/rails_2_test/test/integration/admin/comments_integration_test.rb +50 -0
  184. data/rails_2_test/test/integration/admin/file_column_images2_integration_test.rb +19 -0
  185. data/rails_2_test/test/integration/admin/file_column_images_integration_test.rb +69 -0
  186. data/rails_2_test/test/integration/admin/images2_integration_test.rb +33 -0
  187. data/rails_2_test/test/integration/admin/images_integration_test.rb +79 -0
  188. data/rails_2_test/test/integration/admin/misconfigured1_integration_test.rb +17 -0
  189. data/rails_2_test/test/integration/admin/product_categories2_integration_test.rb +179 -0
  190. data/rails_2_test/test/integration/admin/product_categories_integration_test.rb +12 -0
  191. data/rails_2_test/test/integration/admin/products2_integration_test.rb +155 -0
  192. data/rails_2_test/test/integration/admin/products_integration_test.rb +210 -0
  193. data/rails_2_test/test/integration/admin/television_airings_integration_test.rb +25 -0
  194. data/rails_2_test/test/integration/admin/users2_integration_test.rb +48 -0
  195. data/rails_2_test/test/integration/admin/users_integration_test.rb +266 -0
  196. data/rails_2_test/test/integration/blog_posts_integration_test.rb +9 -0
  197. data/rails_2_test/test/test_helper.rb +114 -0
  198. data/rails_2_test/vendor/plugins/file_column/CHANGELOG +69 -0
  199. data/rails_2_test/vendor/plugins/file_column/README +54 -0
  200. data/rails_2_test/vendor/plugins/file_column/Rakefile +36 -0
  201. data/rails_2_test/vendor/plugins/file_column/TODO +6 -0
  202. data/rails_2_test/vendor/plugins/file_column/init.rb +13 -0
  203. data/rails_2_test/vendor/plugins/file_column/lib/file_column.rb +723 -0
  204. data/rails_2_test/vendor/plugins/file_column/lib/file_column_helper.rb +150 -0
  205. data/rails_2_test/vendor/plugins/file_column/lib/file_compat.rb +28 -0
  206. data/rails_2_test/vendor/plugins/file_column/lib/magick_file_column.rb +260 -0
  207. data/rails_2_test/vendor/plugins/file_column/lib/rails_file_column.rb +19 -0
  208. data/rails_2_test/vendor/plugins/file_column/lib/test_case.rb +124 -0
  209. data/rails_2_test/vendor/plugins/file_column/lib/validations.rb +112 -0
  210. data/rails_2_test/vendor/plugins/file_column/test/abstract_unit.rb +63 -0
  211. data/rails_2_test/vendor/plugins/file_column/test/connection.rb +17 -0
  212. data/rails_2_test/vendor/plugins/file_column/test/file_column_helper_test.rb +97 -0
  213. data/rails_2_test/vendor/plugins/file_column/test/file_column_test.rb +650 -0
  214. data/rails_2_test/vendor/plugins/file_column/test/fixtures/entry.rb +32 -0
  215. data/rails_2_test/vendor/plugins/file_column/test/fixtures/invalid-image.jpg +1 -0
  216. data/rails_2_test/vendor/plugins/file_column/test/fixtures/kerb.jpg +0 -0
  217. data/rails_2_test/vendor/plugins/file_column/test/fixtures/mysql.sql +25 -0
  218. data/rails_2_test/vendor/plugins/file_column/test/fixtures/schema.rb +10 -0
  219. data/rails_2_test/vendor/plugins/file_column/test/fixtures/skanthak.png +0 -0
  220. data/rails_2_test/vendor/plugins/file_column/test/magick_test.rb +380 -0
  221. data/rails_2_test/vendor/plugins/file_column/test/magick_view_only_test.rb +21 -0
  222. data/rails_3_test/.gitignore +4 -0
  223. data/rails_3_test/Gemfile +12 -0
  224. data/rails_3_test/Gemfile.lock +100 -0
  225. data/rails_3_test/README +256 -0
  226. data/rails_3_test/Rakefile +7 -0
  227. data/rails_3_test/app/controllers/admin/appointments2_controller.rb +15 -0
  228. data/rails_3_test/app/controllers/admin/appointments_controller.rb +11 -0
  229. data/rails_3_test/app/controllers/admin/blog_posts2_controller.rb +138 -0
  230. data/rails_3_test/app/controllers/admin/blog_posts3_controller.rb +76 -0
  231. data/rails_3_test/app/controllers/admin/blog_posts4_controller.rb +21 -0
  232. data/rails_3_test/app/controllers/admin/blog_posts5_controller.rb +27 -0
  233. data/rails_3_test/app/controllers/admin/blog_posts6_controller.rb +10 -0
  234. data/rails_3_test/app/controllers/admin/blog_posts_controller.rb +9 -0
  235. data/rails_3_test/app/controllers/admin/blog_posts_custom_new_and_edit_controller.rb +15 -0
  236. data/rails_3_test/app/controllers/admin/blog_posts_read_only_controller.rb +19 -0
  237. data/rails_3_test/app/controllers/admin/bookmarks_controller.rb +11 -0
  238. data/rails_3_test/app/controllers/admin/comments2_controller.rb +14 -0
  239. data/rails_3_test/app/controllers/admin/comments_controller.rb +12 -0
  240. data/rails_3_test/app/controllers/admin/images2_controller.rb +11 -0
  241. data/rails_3_test/app/controllers/admin/images_controller.rb +6 -0
  242. data/rails_3_test/app/controllers/admin/misconfigured1_controller.rb +7 -0
  243. data/rails_3_test/app/controllers/admin/not_migrated_yets_controller.rb +9 -0
  244. data/rails_3_test/app/controllers/admin/product_categories2_controller.rb +7 -0
  245. data/rails_3_test/app/controllers/admin/product_categories_controller.rb +5 -0
  246. data/rails_3_test/app/controllers/admin/television_airings_controller.rb +5 -0
  247. data/rails_3_test/app/controllers/admin/television_time_slots_controller.rb +5 -0
  248. data/rails_3_test/app/controllers/admin/users2_controller.rb +10 -0
  249. data/rails_3_test/app/controllers/admin/users_controller.rb +48 -0
  250. data/rails_3_test/app/controllers/application_controller.rb +7 -0
  251. data/rails_3_test/app/controllers/blog_posts_controller.rb +5 -0
  252. data/rails_3_test/app/helpers/admin/appointments_helper.rb +2 -0
  253. data/rails_3_test/app/helpers/admin/blog_posts2_helper.rb +27 -0
  254. data/rails_3_test/app/helpers/admin/blog_posts3_helper.rb +2 -0
  255. data/rails_3_test/app/helpers/admin/blog_posts4_helper.rb +2 -0
  256. data/rails_3_test/app/helpers/admin/blog_posts6_helper.rb +13 -0
  257. data/rails_3_test/app/helpers/admin/blog_posts_custom_new_and_edit_helper.rb +29 -0
  258. data/rails_3_test/app/helpers/admin/blog_posts_helper.rb +2 -0
  259. data/rails_3_test/app/helpers/admin/blog_posts_read_only_helper.rb +2 -0
  260. data/rails_3_test/app/helpers/admin/bookmarks_helper.rb +2 -0
  261. data/rails_3_test/app/helpers/admin/comments2_helper.rb +2 -0
  262. data/rails_3_test/app/helpers/admin/comments_helper.rb +2 -0
  263. data/rails_3_test/app/helpers/admin/images2_helper.rb +8 -0
  264. data/rails_3_test/app/helpers/admin/images_helper.rb +2 -0
  265. data/rails_3_test/app/helpers/admin/misconfigured1_helper.rb +2 -0
  266. data/rails_3_test/app/helpers/admin/not_migrated_yets_helper.rb +2 -0
  267. data/rails_3_test/app/helpers/admin/product_categories2_helper.rb +2 -0
  268. data/rails_3_test/app/helpers/admin/product_categories_helper.rb +2 -0
  269. data/rails_3_test/app/helpers/admin/television_airings_helper.rb +2 -0
  270. data/rails_3_test/app/helpers/admin/television_time_slots_helper.rb +2 -0
  271. data/rails_3_test/app/helpers/admin/users2_helper.rb +2 -0
  272. data/rails_3_test/app/helpers/admin/users_helper.rb +13 -0
  273. data/rails_3_test/app/helpers/application_helper.rb +2 -0
  274. data/rails_3_test/app/models/appointment.rb +5 -0
  275. data/rails_3_test/app/models/blog_post.rb +14 -0
  276. data/rails_3_test/app/models/blog_post_tag.rb +6 -0
  277. data/rails_3_test/app/models/bookmark.rb +6 -0
  278. data/rails_3_test/app/models/comment.rb +4 -0
  279. data/rails_3_test/app/models/image.rb +3 -0
  280. data/rails_3_test/app/models/not_migrated_yet.rb +2 -0
  281. data/rails_3_test/app/models/product.rb +5 -0
  282. data/rails_3_test/app/models/product_category.rb +9 -0
  283. data/rails_3_test/app/models/tag.rb +4 -0
  284. data/rails_3_test/app/models/television_airing.rb +3 -0
  285. data/rails_3_test/app/models/television_time_slot.rb +11 -0
  286. data/rails_3_test/app/models/user.rb +11 -0
  287. data/rails_3_test/app/views/admin/appointments/_subject_input.html.erb +2 -0
  288. data/rails_3_test/app/views/admin/appointments2/_time_input.html.erb +31 -0
  289. data/rails_3_test/app/views/admin/blog_posts2/_after_form.html.erb +5 -0
  290. data/rails_3_test/app/views/admin/blog_posts2/_after_index.html.erb +1 -0
  291. data/rails_3_test/app/views/admin/blog_posts2/_after_tags_input.html.erb +16 -0
  292. data/rails_3_test/app/views/admin/blog_posts2/_before_index.html.erb +2 -0
  293. data/rails_3_test/app/views/admin/blog_posts5/_after_index_header.html.erb +8 -0
  294. data/rails_3_test/app/views/admin/blog_posts_read_only/_body_for_show.html.erb +1 -0
  295. data/rails_3_test/app/views/layouts/admin.html.erb +32 -0
  296. data/rails_3_test/app/views/layouts/application.html.erb +14 -0
  297. data/rails_3_test/config/application.rb +42 -0
  298. data/rails_3_test/config/boot.rb +6 -0
  299. data/rails_3_test/config/database.yml +22 -0
  300. data/rails_3_test/config/environment.rb +5 -0
  301. data/rails_3_test/config/environments/development.rb +26 -0
  302. data/rails_3_test/config/environments/production.rb +49 -0
  303. data/rails_3_test/config/environments/test.rb +35 -0
  304. data/rails_3_test/config/initializers/backtrace_silencers.rb +7 -0
  305. data/rails_3_test/config/initializers/inflections.rb +10 -0
  306. data/rails_3_test/config/initializers/mime_types.rb +5 -0
  307. data/rails_3_test/config/initializers/secret_token.rb +7 -0
  308. data/rails_3_test/config/initializers/session_store.rb +8 -0
  309. data/rails_3_test/config/locales/en.yml +5 -0
  310. data/rails_3_test/config/routes.rb +63 -0
  311. data/rails_3_test/config.ru +4 -0
  312. data/rails_3_test/db/migrate/20090213215514_create_blog_posts.rb +12 -0
  313. data/rails_3_test/db/migrate/20090217225542_add_body_to_blog_posts.rb +9 -0
  314. data/rails_3_test/db/migrate/20090221220917_create_tags.rb +13 -0
  315. data/rails_3_test/db/migrate/20090221220947_create_blog_post_tags.rb +14 -0
  316. data/rails_3_test/db/migrate/20090222162204_add_textile_to_blog_posts.rb +9 -0
  317. data/rails_3_test/db/migrate/20090222163231_add_published_at_to_blog_posts.rb +9 -0
  318. data/rails_3_test/db/migrate/20090301191722_create_images.rb +16 -0
  319. data/rails_3_test/db/migrate/20090305165345_create_accounts.rb +14 -0
  320. data/rails_3_test/db/migrate/20090307225027_rename_accounts_to_users.rb +9 -0
  321. data/rails_3_test/db/migrate/20090307225750_add_user_id_to_blog_posts.rb +9 -0
  322. data/rails_3_test/db/migrate/20090309185114_change_blog_posts_textile.rb +9 -0
  323. data/rails_3_test/db/migrate/20090309193635_create_products.rb +14 -0
  324. data/rails_3_test/db/migrate/20090309203056_create_comments.rb +15 -0
  325. data/rails_3_test/db/migrate/20090323005947_create_file_column_images.rb +12 -0
  326. data/rails_3_test/db/migrate/20090326160049_add_birthday_to_users.rb +9 -0
  327. data/rails_3_test/db/migrate/20090326223606_add_state_to_users.rb +9 -0
  328. data/rails_3_test/db/migrate/20090503134004_add_file_column_image_to_products.rb +9 -0
  329. data/rails_3_test/db/migrate/20090617173651_create_bookmarks.rb +15 -0
  330. data/rails_3_test/db/migrate/20090624165355_add_tags_string_to_blog_post.rb +9 -0
  331. data/rails_3_test/db/migrate/20090625144313_add_avatar_fields_to_user.rb +11 -0
  332. data/rails_3_test/db/migrate/20090629202956_add_merged_into_to_blog_posts.rb +9 -0
  333. data/rails_3_test/db/migrate/20090701171857_add_force_textile_to_users.rb +9 -0
  334. data/rails_3_test/db/migrate/20090704163647_add_deleted_at_to_products.rb +9 -0
  335. data/rails_3_test/db/migrate/20090704173800_add_sale_fields_to_products.rb +13 -0
  336. data/rails_3_test/db/migrate/20090714024501_create_product_categories.rb +15 -0
  337. data/rails_3_test/db/migrate/20090819162835_add_admin_level_to_users.rb +9 -0
  338. data/rails_3_test/db/migrate/20091115134559_add_first_and_last_names_to_users.rb +11 -0
  339. data/rails_3_test/db/migrate/20091221011256_add_position_to_product_categories.rb +9 -0
  340. data/rails_3_test/db/migrate/20091222160814_create_appointments.rb +14 -0
  341. data/rails_3_test/db/migrate/20091227224547_add_user_id_to_appointments.rb +9 -0
  342. data/rails_3_test/db/migrate/20100214213359_create_television_time_slots.rb +13 -0
  343. data/rails_3_test/db/migrate/20100214213451_create_television_airings.rb +15 -0
  344. data/rails_3_test/db/migrate/20110426215702_remove_avatar_fields_from_users.rb +11 -0
  345. data/rails_3_test/db/seeds.rb +7 -0
  346. data/rails_3_test/doc/README_FOR_APP +2 -0
  347. data/rails_3_test/lib/tasks/.gitkeep +0 -0
  348. data/rails_3_test/public/404.html +26 -0
  349. data/rails_3_test/public/422.html +26 -0
  350. data/rails_3_test/public/500.html +26 -0
  351. data/rails_3_test/public/favicon.ico +0 -0
  352. data/rails_3_test/public/images/rails.png +0 -0
  353. data/rails_3_test/public/index.html +239 -0
  354. data/rails_3_test/public/javascripts/application.js +2 -0
  355. data/rails_3_test/public/javascripts/jquery-1.4.4.min.js +167 -0
  356. data/rails_3_test/public/javascripts/rails.js +191 -0
  357. data/rails_3_test/public/robots.txt +5 -0
  358. data/rails_3_test/public/stylesheets/.gitkeep +0 -0
  359. data/rails_3_test/public/stylesheets/admin.css +3 -0
  360. data/rails_3_test/public/stylesheets/scaffold.css +54 -0
  361. data/rails_3_test/script/rails +6 -0
  362. data/rails_3_test/test/data/ruby_throated.jpg +0 -0
  363. data/rails_3_test/test/data/tweenbot.jpg +0 -0
  364. data/rails_3_test/test/fixtures/placeholder.txt +0 -0
  365. data/rails_3_test/test/integration/admin/appointments2_integration_test.rb +79 -0
  366. data/rails_3_test/test/integration/admin/appointments_integration_test.rb +367 -0
  367. data/rails_3_test/test/integration/admin/blog_posts2_integration_test.rb +789 -0
  368. data/rails_3_test/test/integration/admin/blog_posts3_integration_test.rb +418 -0
  369. data/rails_3_test/test/integration/admin/blog_posts4_integration_test.rb +189 -0
  370. data/rails_3_test/test/integration/admin/blog_posts5_integration_test.rb +75 -0
  371. data/rails_3_test/test/integration/admin/blog_posts6_integration_test.rb +39 -0
  372. data/rails_3_test/test/integration/admin/blog_posts_custom_new_and_edit_integration_test.rb +89 -0
  373. data/rails_3_test/test/integration/admin/blog_posts_integration_test.rb +784 -0
  374. data/rails_3_test/test/integration/admin/blog_posts_read_only_integration_test.rb +69 -0
  375. data/rails_3_test/test/integration/admin/bookmarks_integration_test.rb +445 -0
  376. data/rails_3_test/test/integration/admin/comments2_integration_test.rb +52 -0
  377. data/rails_3_test/test/integration/admin/comments_integration_test.rb +50 -0
  378. data/rails_3_test/test/integration/admin/images2_integration_test.rb +33 -0
  379. data/rails_3_test/test/integration/admin/images_integration_test.rb +79 -0
  380. data/rails_3_test/test/integration/admin/misconfigured1_integration_test.rb +21 -0
  381. data/rails_3_test/test/integration/admin/product_categories2_integration_test.rb +179 -0
  382. data/rails_3_test/test/integration/admin/product_categories_integration_test.rb +12 -0
  383. data/rails_3_test/test/integration/admin/television_airings_integration_test.rb +25 -0
  384. data/rails_3_test/test/integration/admin/users2_integration_test.rb +48 -0
  385. data/rails_3_test/test/integration/admin/users_integration_test.rb +182 -0
  386. data/rails_3_test/test/integration/blog_posts_integration_test.rb +10 -0
  387. data/rails_3_test/test/performance/browsing_test.rb +9 -0
  388. data/rails_3_test/test/test_helper.rb +98 -0
  389. data/rails_3_test/vendor/plugins/.gitkeep +0 -0
  390. data/website/_layouts/api.html +20 -17
  391. data/website/_layouts/api1.html +42 -0
  392. data/website/_layouts/default.html +42 -18
  393. data/website/api/core.markdown +6 -3
  394. data/website/api/destroy.markdown +4 -2
  395. data/website/api/form.markdown +6 -5
  396. data/website/api/idx.markdown +8 -7
  397. data/website/api/index.markdown +9 -7
  398. data/website/api/search.markdown +5 -3
  399. data/website/api/show.markdown +4 -2
  400. data/website/community.markdown +3 -2
  401. data/website/css/main.css +83 -17
  402. data/website/design_principles.markdown +3 -8
  403. data/website/index.markdown +13 -9
  404. data/website/js/lightbox.js +2 -2
  405. data/website/quick_start.markdown +10 -10
  406. data/website/screenshots.markdown +11 -11
  407. data/website/tutorial.markdown +23 -14
  408. data/website/v1/api/core.markdown +108 -0
  409. data/website/v1/api/destroy.markdown +27 -0
  410. data/website/v1/api/form.markdown +293 -0
  411. data/website/v1/api/idx.markdown +288 -0
  412. data/website/v1/api/index.markdown +149 -0
  413. data/website/v1/api/search.markdown +110 -0
  414. data/website/v1/api/show.markdown +24 -0
  415. data/website/v1/index.markdown +10 -0
  416. data/website/v1/quick_start.markdown +48 -0
  417. data/website/v1/tutorial.markdown +60 -0
  418. metadata +407 -9
@@ -0,0 +1,723 @@
1
+ require 'fileutils'
2
+ require 'tempfile'
3
+ require 'magick_file_column'
4
+
5
+ module FileColumn # :nodoc:
6
+ def self.append_features(base)
7
+ super
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ def self.create_state(instance,attr)
12
+ filename = instance[attr]
13
+ if filename.nil? or filename.empty?
14
+ NoUploadedFile.new(instance,attr)
15
+ else
16
+ PermanentUploadedFile.new(instance,attr)
17
+ end
18
+ end
19
+
20
+ def self.init_options(defaults, model, attr)
21
+ options = defaults.dup
22
+ options[:store_dir] ||= File.join(options[:root_path], model, attr)
23
+ unless options[:store_dir].is_a?(Symbol)
24
+ options[:tmp_base_dir] ||= File.join(options[:store_dir], "tmp")
25
+ end
26
+ options[:base_url] ||= options[:web_root] + File.join(model, attr)
27
+
28
+ [:store_dir, :tmp_base_dir].each do |dir_sym|
29
+ if options[dir_sym].is_a?(String) and !File.exists?(options[dir_sym])
30
+ FileUtils.mkpath(options[dir_sym])
31
+ end
32
+ end
33
+
34
+ options
35
+ end
36
+
37
+ class BaseUploadedFile # :nodoc:
38
+
39
+ def initialize(instance,attr)
40
+ @instance, @attr = instance, attr
41
+ @options_method = "#{attr}_options".to_sym
42
+ end
43
+
44
+
45
+ def assign(file)
46
+ if file.is_a? File
47
+ # this did not come in via a CGI request. However,
48
+ # assigning files directly may be useful, so we
49
+ # make just this file object similar enough to an uploaded
50
+ # file that we can handle it.
51
+ file.extend FileColumn::FileCompat
52
+ end
53
+
54
+ if file.nil?
55
+ delete
56
+ else
57
+ if file.size == 0
58
+ # user did not submit a file, so we
59
+ # can simply ignore this
60
+ self
61
+ else
62
+ if file.is_a?(String)
63
+ # if file is a non-empty string it is most probably
64
+ # the filename and the user forgot to set the encoding
65
+ # to multipart/form-data. Since we would raise an exception
66
+ # because of the missing "original_filename" method anyways,
67
+ # we raise a more meaningful exception rightaway.
68
+ raise TypeError.new("Do not know how to handle a string with value '#{file}' that was passed to a file_column. Check if the form's encoding has been set to 'multipart/form-data'.")
69
+ end
70
+ upload(file)
71
+ end
72
+ end
73
+ end
74
+
75
+ def just_uploaded?
76
+ @just_uploaded
77
+ end
78
+
79
+ def on_save(&blk)
80
+ @on_save ||= []
81
+ @on_save << Proc.new
82
+ end
83
+
84
+ # the following methods are overriden by sub-classes if needed
85
+
86
+ def temp_path
87
+ nil
88
+ end
89
+
90
+ def absolute_dir
91
+ if absolute_path then File.dirname(absolute_path) else nil end
92
+ end
93
+
94
+ def relative_dir
95
+ if relative_path then File.dirname(relative_path) else nil end
96
+ end
97
+
98
+ def after_save
99
+ @on_save.each { |blk| blk.call } if @on_save
100
+ self
101
+ end
102
+
103
+ def after_destroy
104
+ end
105
+
106
+ def options
107
+ @instance.send(@options_method)
108
+ end
109
+
110
+ private
111
+
112
+ def store_dir
113
+ if options[:store_dir].is_a? Symbol
114
+ raise ArgumentError.new("'#{options[:store_dir]}' is not an instance method of class #{@instance.class.name}") unless @instance.respond_to?(options[:store_dir])
115
+
116
+ dir = File.join(options[:root_path], @instance.send(options[:store_dir]))
117
+ FileUtils.mkpath(dir) unless File.exists?(dir)
118
+ dir
119
+ else
120
+ options[:store_dir]
121
+ end
122
+ end
123
+
124
+ def tmp_base_dir
125
+ if options[:tmp_base_dir]
126
+ options[:tmp_base_dir]
127
+ else
128
+ dir = File.join(store_dir, "tmp")
129
+ FileUtils.mkpath(dir) unless File.exists?(dir)
130
+ dir
131
+ end
132
+ end
133
+
134
+ def clone_as(klass)
135
+ klass.new(@instance, @attr)
136
+ end
137
+
138
+ end
139
+
140
+
141
+ class NoUploadedFile < BaseUploadedFile # :nodoc:
142
+ def delete
143
+ # we do not have a file so deleting is easy
144
+ self
145
+ end
146
+
147
+ def upload(file)
148
+ # replace ourselves with a TempUploadedFile
149
+ temp = clone_as TempUploadedFile
150
+ temp.store_upload(file)
151
+ temp
152
+ end
153
+
154
+ def absolute_path(subdir=nil)
155
+ nil
156
+ end
157
+
158
+
159
+ def relative_path(subdir=nil)
160
+ nil
161
+ end
162
+
163
+ def assign_temp(temp_path)
164
+ return self if temp_path.nil? or temp_path.empty?
165
+ temp = clone_as TempUploadedFile
166
+ temp.parse_temp_path temp_path
167
+ temp
168
+ end
169
+ end
170
+
171
+ class RealUploadedFile < BaseUploadedFile # :nodoc:
172
+ def absolute_path(subdir=nil)
173
+ if subdir
174
+ File.join(@dir, subdir, @filename)
175
+ else
176
+ File.join(@dir, @filename)
177
+ end
178
+ end
179
+
180
+ def relative_path(subdir=nil)
181
+ if subdir
182
+ File.join(relative_path_prefix, subdir, @filename)
183
+ else
184
+ File.join(relative_path_prefix, @filename)
185
+ end
186
+ end
187
+
188
+ private
189
+
190
+ # regular expressions to try for identifying extensions
191
+ EXT_REGEXPS = [
192
+ /^(.+)\.([^.]+\.[^.]+)$/, # matches "something.tar.gz"
193
+ /^(.+)\.([^.]+)$/ # matches "something.jpg"
194
+ ]
195
+
196
+ def split_extension(filename,fallback=nil)
197
+ EXT_REGEXPS.each do |regexp|
198
+ if filename =~ regexp
199
+ base,ext = $1, $2
200
+ return [base, ext] if options[:extensions].include?(ext.downcase)
201
+ end
202
+ end
203
+ if fallback and filename =~ EXT_REGEXPS.last
204
+ return [$1, $2]
205
+ end
206
+ [filename, ""]
207
+ end
208
+
209
+ end
210
+
211
+ class TempUploadedFile < RealUploadedFile # :nodoc:
212
+
213
+ def store_upload(file)
214
+ @tmp_dir = FileColumn.generate_temp_name
215
+ @dir = File.join(tmp_base_dir, @tmp_dir)
216
+ FileUtils.mkdir(@dir)
217
+
218
+ @filename = FileColumn::sanitize_filename(file.original_filename)
219
+ local_file_path = File.join(tmp_base_dir,@tmp_dir,@filename)
220
+
221
+ # stored uploaded file into local_file_path
222
+ # If it was a Tempfile object, the temporary file will be
223
+ # cleaned up automatically, so we do not have to care for this
224
+ if file.respond_to?(:local_path) and file.local_path and File.exists?(file.local_path)
225
+ FileUtils.copy_file(file.local_path, local_file_path)
226
+ elsif file.respond_to?(:read)
227
+ File.open(local_file_path, "wb") { |f| f.write(file.read) }
228
+ else
229
+ raise ArgumentError.new("Do not know how to handle #{file.inspect}")
230
+ end
231
+ File.chmod(options[:permissions], local_file_path)
232
+
233
+ if options[:fix_file_extensions]
234
+ # try to determine correct file extension and fix
235
+ # if necessary
236
+ content_type = get_content_type((file.content_type.chomp if file.content_type))
237
+ if content_type and options[:mime_extensions][content_type]
238
+ @filename = correct_extension(@filename,options[:mime_extensions][content_type])
239
+ end
240
+
241
+ new_local_file_path = File.join(tmp_base_dir,@tmp_dir,@filename)
242
+ File.rename(local_file_path, new_local_file_path) unless new_local_file_path == local_file_path
243
+ local_file_path = new_local_file_path
244
+ end
245
+
246
+ @instance[@attr] = @filename
247
+ @just_uploaded = true
248
+ end
249
+
250
+
251
+ # tries to identify and strip the extension of filename
252
+ # if an regular expresion from EXT_REGEXPS matches and the
253
+ # downcased extension is a known extension (in options[:extensions])
254
+ # we'll strip this extension
255
+ def strip_extension(filename)
256
+ split_extension(filename).first
257
+ end
258
+
259
+ def correct_extension(filename, ext)
260
+ strip_extension(filename) << ".#{ext}"
261
+ end
262
+
263
+ def parse_temp_path(temp_path, instance_options=nil)
264
+ raise ArgumentError.new("invalid format of '#{temp_path}'") unless temp_path =~ %r{^((\d+\.)+\d+)/([^/].+)$}
265
+ @tmp_dir, @filename = $1, FileColumn.sanitize_filename($3)
266
+ @dir = File.join(tmp_base_dir, @tmp_dir)
267
+
268
+ @instance[@attr] = @filename unless instance_options == :ignore_instance
269
+ end
270
+
271
+ def upload(file)
272
+ # store new file
273
+ temp = clone_as TempUploadedFile
274
+ temp.store_upload(file)
275
+
276
+ # delete old copy
277
+ delete_files
278
+
279
+ # and return new TempUploadedFile object
280
+ temp
281
+ end
282
+
283
+ def delete
284
+ delete_files
285
+ @instance[@attr] = ""
286
+ clone_as NoUploadedFile
287
+ end
288
+
289
+ def assign_temp(temp_path)
290
+ return self if temp_path.nil? or temp_path.empty?
291
+ # we can ignore this since we've already received a newly uploaded file
292
+
293
+ # however, we delete the old temporary files
294
+ temp = clone_as TempUploadedFile
295
+ temp.parse_temp_path(temp_path, :ignore_instance)
296
+ temp.delete_files
297
+
298
+ self
299
+ end
300
+
301
+ def temp_path
302
+ File.join(@tmp_dir, @filename)
303
+ end
304
+
305
+ def after_save
306
+ super
307
+
308
+ # we have a newly uploaded image, move it to the correct location
309
+ file = clone_as PermanentUploadedFile
310
+ file.move_from(File.join(tmp_base_dir, @tmp_dir), @just_uploaded)
311
+
312
+ # delete temporary files
313
+ delete_files
314
+
315
+ # replace with the new PermanentUploadedFile object
316
+ file
317
+ end
318
+
319
+ def delete_files
320
+ FileUtils.rm_rf(File.join(tmp_base_dir, @tmp_dir))
321
+ end
322
+
323
+ def get_content_type(fallback=nil)
324
+ if options[:file_exec]
325
+ begin
326
+ content_type = `#{options[:file_exec]} -bi "#{File.join(@dir,@filename)}"`.chomp
327
+ content_type = fallback unless $?.success?
328
+ content_type.gsub!(/;.+$/,"") if content_type
329
+ content_type
330
+ rescue
331
+ fallback
332
+ end
333
+ else
334
+ fallback
335
+ end
336
+ end
337
+
338
+ private
339
+
340
+ def relative_path_prefix
341
+ File.join("tmp", @tmp_dir)
342
+ end
343
+ end
344
+
345
+
346
+ class PermanentUploadedFile < RealUploadedFile # :nodoc:
347
+ def initialize(*args)
348
+ super *args
349
+ @dir = File.join(store_dir, relative_path_prefix)
350
+ @filename = @instance[@attr]
351
+ @filename = nil if @filename.empty?
352
+ end
353
+
354
+ def move_from(local_dir, just_uploaded)
355
+ # remove old permament dir first
356
+ # this creates a short moment, where neither the old nor
357
+ # the new files exist but we can't do much about this as
358
+ # filesystems aren't transactional.
359
+ FileUtils.rm_rf @dir
360
+
361
+ FileUtils.mv local_dir, @dir
362
+
363
+ @just_uploaded = just_uploaded
364
+ end
365
+
366
+ def upload(file)
367
+ temp = clone_as TempUploadedFile
368
+ temp.store_upload(file)
369
+ temp
370
+ end
371
+
372
+ def delete
373
+ file = clone_as NoUploadedFile
374
+ @instance[@attr] = ""
375
+ file.on_save { delete_files }
376
+ file
377
+ end
378
+
379
+ def assign_temp(temp_path)
380
+ return nil if temp_path.nil? or temp_path.empty?
381
+
382
+ temp = clone_as TempUploadedFile
383
+ temp.parse_temp_path(temp_path)
384
+ temp
385
+ end
386
+
387
+ def after_destroy
388
+ delete_files
389
+ end
390
+
391
+ def delete_files
392
+ FileUtils.rm_rf @dir
393
+ end
394
+
395
+ private
396
+
397
+ def relative_path_prefix
398
+ raise RuntimeError.new("Trying to access file_column, but primary key got lost.") if @instance.id.to_s.empty?
399
+ @instance.id.to_s
400
+ end
401
+ end
402
+
403
+ # The FileColumn module allows you to easily handle file uploads. You can designate
404
+ # one or more columns of your model's table as "file columns" like this:
405
+ #
406
+ # class Entry < ActiveRecord::Base
407
+ #
408
+ # file_column :image
409
+ # end
410
+ #
411
+ # Now, by default, an uploaded file "test.png" for an entry object with primary key 42 will
412
+ # be stored in in "public/entry/image/42/test.png". The filename "test.png" will be stored
413
+ # in the record's "image" column. The "entries" table should have a +VARCHAR+ column
414
+ # named "image".
415
+ #
416
+ # The methods of this module are automatically included into <tt>ActiveRecord::Base</tt>
417
+ # as class methods, so that you can use them in your models.
418
+ #
419
+ # == Generated Methods
420
+ #
421
+ # After calling "<tt>file_column :image</tt>" as in the example above, a number of instance methods
422
+ # will automatically be generated, all prefixed by "image":
423
+ #
424
+ # * <tt>Entry#image=(uploaded_file)</tt>: this will handle a newly uploaded file
425
+ # (see below). Note that
426
+ # you can simply call your upload field "entry[image]" in your view (or use the
427
+ # helper).
428
+ # * <tt>Entry#image(subdir=nil)</tt>: This will return an absolute path (as a
429
+ # string) to the currently uploaded file
430
+ # or nil if no file has been uploaded
431
+ # * <tt>Entry#image_relative_path(subdir=nil)</tt>: This will return a path relative to
432
+ # this file column's base directory
433
+ # as a string or nil if no file has been uploaded. This would be "42/test.png" in the example.
434
+ # * <tt>Entry#image_just_uploaded?</tt>: Returns true if a new file has been uploaded to this instance.
435
+ # You can use this in your code to perform certain actions (e. g., validation,
436
+ # custom post-processing) only on newly uploaded files.
437
+ #
438
+ # You can access the raw value of the "image" column (which will contain the filename) via the
439
+ # <tt>ActiveRecord::Base#attributes</tt> or <tt>ActiveRecord::Base#[]</tt> methods like this:
440
+ #
441
+ # entry['image'] # e.g."test.png"
442
+ #
443
+ # == Storage of uploaded files
444
+ #
445
+ # For a model class +Entry+ and a column +image+, all files will be stored under
446
+ # "public/entry/image". A sub-directory named after the primary key of the object will
447
+ # be created, so that files can be stored using their real filename. For example, a file
448
+ # "test.png" stored in an Entry object with id 42 will be stored in
449
+ #
450
+ # public/entry/image/42/test.png
451
+ #
452
+ # Files will be moved to this location in an +after_save+ callback. They will be stored in
453
+ # a temporary location previously as explained in the next section.
454
+ #
455
+ # By default, files will be created with unix permissions of <tt>0644</tt> (i. e., owner has
456
+ # read/write access, group and others only have read access). You can customize
457
+ # this by passing the desired mode as a <tt>:permissions</tt> options. The value
458
+ # you give here is passed directly to <tt>File::chmod</tt>, so on Unix you should
459
+ # give some octal value like 0644, for example.
460
+ #
461
+ # == Handling of form redisplay
462
+ #
463
+ # Suppose you have a form for creating a new object where the user can upload an image. The form may
464
+ # have to be re-displayed because of validation errors. The uploaded file has to be stored somewhere so
465
+ # that the user does not have to upload it again. FileColumn will store these in a temporary directory
466
+ # (called "tmp" and located under the column's base directory by default) so that it can be moved to
467
+ # the final location if the object is successfully created. If the form is never completed, though, you
468
+ # can easily remove all the images in this "tmp" directory once per day or so.
469
+ #
470
+ # So in the example above, the image "test.png" would first be stored in
471
+ # "public/entry/image/tmp/<some_random_key>/test.png" and be moved to
472
+ # "public/entry/image/<primary_key>/test.png".
473
+ #
474
+ # This temporary location of newly uploaded files has another advantage when updating objects. If the
475
+ # update fails for some reasons (e.g. due to validations), the existing image will not be overwritten, so
476
+ # it has a kind of "transactional behaviour".
477
+ #
478
+ # == Additional Files and Directories
479
+ #
480
+ # FileColumn allows you to keep more than one file in a directory and will move/delete
481
+ # all the files and directories it finds in a model object's directory when necessary.
482
+ #
483
+ # As a convenience you can access files stored in sub-directories via the +subdir+
484
+ # parameter if they have the same filename.
485
+ #
486
+ # Suppose your uploaded file is named "vancouver.jpg" and you want to create a
487
+ # thumb-nail and store it in the "thumb" directory. If you call
488
+ # <tt>image("thumb")</tt>, you
489
+ # will receive an absolute path for the file "thumb/vancouver.jpg" in the same
490
+ # directory "vancouver.jpg" is stored. Look at the documentation of FileColumn::Magick
491
+ # for more examples and how to create these thumb-nails automatically.
492
+ #
493
+ # == File Extensions
494
+ #
495
+ # FileColumn will try to fix the file extension of uploaded files, so that
496
+ # the files are served with the correct mime-type by your web-server. Most
497
+ # web-servers are setting the mime-type based on the file's extension. You
498
+ # can disable this behaviour by passing the <tt>:fix_file_extensions</tt> option
499
+ # with a value of +nil+ to +file_column+.
500
+ #
501
+ # In order to set the correct extension, FileColumn tries to determine
502
+ # the files mime-type first. It then uses the +MIME_EXTENSIONS+ hash to
503
+ # choose the corresponding file extension. You can override this hash
504
+ # by passing in a <tt>:mime_extensions</tt> option to +file_column+.
505
+ #
506
+ # The mime-type of the uploaded file is determined with the following steps:
507
+ #
508
+ # 1. Run the external "file" utility. You can specify the full path to
509
+ # the executable in the <tt>:file_exec</tt> option or set this option
510
+ # to +nil+ to disable this step
511
+ #
512
+ # 2. If the file utility couldn't determine the mime-type or the utility was not
513
+ # present, the content-type provided by the user's browser is used
514
+ # as a fallback.
515
+ #
516
+ # == Custom Storage Directories
517
+ #
518
+ # FileColumn's storage location is determined in the following way. All
519
+ # files are saved below the so-called "root_path" directory, which defaults to
520
+ # "RAILS_ROOT/public". For every file_column, you can set a separte "store_dir"
521
+ # option. It defaults to "model_name/attribute_name".
522
+ #
523
+ # Files will always be stored in sub-directories of the store_dir path. The
524
+ # subdirectory is named after the instance's +id+ attribute for a saved model,
525
+ # or "tmp/<randomkey>" for unsaved models.
526
+ #
527
+ # You can specify a custom root_path by setting the <tt>:root_path</tt> option.
528
+ #
529
+ # You can specify a custom storage_dir by setting the <tt>:storage_dir</tt> option.
530
+ #
531
+ # For setting a static storage_dir that doesn't change with respect to a particular
532
+ # instance, you assign <tt>:storage_dir</tt> a String representing a directory
533
+ # as an absolute path.
534
+ #
535
+ # If you need more fine-grained control over the storage directory, you
536
+ # can use the name of a callback-method as a symbol for the
537
+ # <tt>:store_dir</tt> option. This method has to be defined as an
538
+ # instance method in your model. It will be called without any arguments
539
+ # whenever the storage directory for an uploaded file is needed. It should return
540
+ # a String representing a directory relativeo to root_path.
541
+ #
542
+ # Uploaded files for unsaved models objects will be stored in a temporary
543
+ # directory. By default this directory will be a "tmp" directory in
544
+ # your <tt>:store_dir</tt>. You can override this via the
545
+ # <tt>:tmp_base_dir</tt> option.
546
+ module ClassMethods
547
+
548
+ # default mapping of mime-types to file extensions. FileColumn will try to
549
+ # rename a file to the correct extension if it detects a known mime-type
550
+ MIME_EXTENSIONS = {
551
+ "image/gif" => "gif",
552
+ "image/jpeg" => "jpg",
553
+ "image/pjpeg" => "jpg",
554
+ "image/x-png" => "png",
555
+ "image/jpg" => "jpg",
556
+ "image/png" => "png",
557
+ "application/x-shockwave-flash" => "swf",
558
+ "application/pdf" => "pdf",
559
+ "application/pgp-signature" => "sig",
560
+ "application/futuresplash" => "spl",
561
+ "application/msword" => "doc",
562
+ "application/postscript" => "ps",
563
+ "application/x-bittorrent" => "torrent",
564
+ "application/x-dvi" => "dvi",
565
+ "application/x-gzip" => "gz",
566
+ "application/x-ns-proxy-autoconfig" => "pac",
567
+ "application/x-shockwave-flash" => "swf",
568
+ "application/x-tgz" => "tar.gz",
569
+ "application/x-tar" => "tar",
570
+ "application/zip" => "zip",
571
+ "audio/mpeg" => "mp3",
572
+ "audio/x-mpegurl" => "m3u",
573
+ "audio/x-ms-wma" => "wma",
574
+ "audio/x-ms-wax" => "wax",
575
+ "audio/x-wav" => "wav",
576
+ "image/x-xbitmap" => "xbm",
577
+ "image/x-xpixmap" => "xpm",
578
+ "image/x-xwindowdump" => "xwd",
579
+ "text/css" => "css",
580
+ "text/html" => "html",
581
+ "text/javascript" => "js",
582
+ "text/plain" => "txt",
583
+ "text/xml" => "xml",
584
+ "video/mpeg" => "mpeg",
585
+ "video/quicktime" => "mov",
586
+ "video/x-msvideo" => "avi",
587
+ "video/x-ms-asf" => "asf",
588
+ "video/x-ms-wmv" => "wmv"
589
+ }
590
+
591
+ EXTENSIONS = Set.new MIME_EXTENSIONS.values
592
+ EXTENSIONS.merge %w(jpeg)
593
+
594
+ # default options. You can override these with +file_column+'s +options+ parameter
595
+ DEFAULT_OPTIONS = {
596
+ :root_path => File.join(RAILS_ROOT, "public"),
597
+ :web_root => "",
598
+ :mime_extensions => MIME_EXTENSIONS,
599
+ :extensions => EXTENSIONS,
600
+ :fix_file_extensions => true,
601
+ :permissions => 0644,
602
+
603
+ # path to the unix "file" executbale for
604
+ # guessing the content-type of files
605
+ :file_exec => "file"
606
+ }
607
+
608
+ # handle the +attr+ attribute as a "file-upload" column, generating additional methods as explained
609
+ # above. You should pass the attribute's name as a symbol, like this:
610
+ #
611
+ # file_column :image
612
+ #
613
+ # You can pass in an options hash that overrides the options
614
+ # in +DEFAULT_OPTIONS+.
615
+ def file_column(attr, options={})
616
+ options = DEFAULT_OPTIONS.merge(options) if options
617
+
618
+ my_options = FileColumn::init_options(
619
+ options,
620
+ (RAILS_GEM_VERSION=='2.1.0' ? Inflector : ActiveSupport::Inflector).
621
+ underscore(self.name).to_s,
622
+ attr.to_s
623
+ )
624
+
625
+ state_attr = "@#{attr}_state".to_sym
626
+ state_method = "#{attr}_state".to_sym
627
+
628
+ define_method state_method do
629
+ result = instance_variable_get state_attr
630
+ if result.nil?
631
+ result = FileColumn::create_state(self, attr.to_s)
632
+ instance_variable_set state_attr, result
633
+ end
634
+ result
635
+ end
636
+
637
+ private state_method
638
+
639
+ define_method attr do |*args|
640
+ send(state_method).absolute_path *args
641
+ end
642
+
643
+ define_method "#{attr}_relative_path" do |*args|
644
+ send(state_method).relative_path *args
645
+ end
646
+
647
+ define_method "#{attr}_dir" do
648
+ send(state_method).absolute_dir
649
+ end
650
+
651
+ define_method "#{attr}_relative_dir" do
652
+ send(state_method).relative_dir
653
+ end
654
+
655
+ define_method "#{attr}=" do |file|
656
+ state = send(state_method).assign(file)
657
+ instance_variable_set state_attr, state
658
+ if state.options[:after_upload] and state.just_uploaded?
659
+ state.options[:after_upload].each do |sym|
660
+ self.send sym
661
+ end
662
+ end
663
+ end
664
+
665
+ define_method "#{attr}_temp" do
666
+ send(state_method).temp_path
667
+ end
668
+
669
+ define_method "#{attr}_temp=" do |temp_path|
670
+ instance_variable_set state_attr, send(state_method).assign_temp(temp_path)
671
+ end
672
+
673
+ after_save_method = "#{attr}_after_save".to_sym
674
+
675
+ define_method after_save_method do
676
+ instance_variable_set state_attr, send(state_method).after_save
677
+ end
678
+
679
+ after_save after_save_method
680
+
681
+ after_destroy_method = "#{attr}_after_destroy".to_sym
682
+
683
+ define_method after_destroy_method do
684
+ send(state_method).after_destroy
685
+ end
686
+ after_destroy after_destroy_method
687
+
688
+ define_method "#{attr}_just_uploaded?" do
689
+ send(state_method).just_uploaded?
690
+ end
691
+
692
+ # this creates a closure keeping a reference to my_options
693
+ # right now that's the only way we store the options. We
694
+ # might use a class attribute as well
695
+ define_method "#{attr}_options" do
696
+ my_options
697
+ end
698
+
699
+ private after_save_method, after_destroy_method
700
+
701
+ FileColumn::MagickExtension::file_column(self, attr, my_options) if options[:magick]
702
+ end
703
+
704
+ end
705
+
706
+ private
707
+
708
+ def self.generate_temp_name
709
+ now = Time.now
710
+ "#{now.to_i}.#{now.usec}.#{Process.pid}"
711
+ end
712
+
713
+ def self.sanitize_filename(filename)
714
+ filename = File.basename(filename.gsub("\\", "/")) # work-around for IE
715
+ filename.gsub!(/[^a-zA-Z0-9\.\-\+_]/,"_")
716
+ filename = "_#{filename}" if filename =~ /^\.+$/
717
+ filename = "unnamed" if filename.size == 0
718
+ filename
719
+ end
720
+
721
+ end
722
+
723
+