calagator 0.0.1.pre1

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 (384) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +23 -0
  3. data/README.md +75 -0
  4. data/Rakefile +12 -0
  5. data/app/assets/images/confused-alligator-sm.png +0 -0
  6. data/app/assets/images/disk.png +0 -0
  7. data/app/assets/images/edit.png +0 -0
  8. data/app/assets/images/external_sites/epdx.png +0 -0
  9. data/app/assets/images/external_sites/external.gif +0 -0
  10. data/app/assets/images/external_sites/facebook.png +0 -0
  11. data/app/assets/images/external_sites/foursquare.png +0 -0
  12. data/app/assets/images/external_sites/gowalla.png +0 -0
  13. data/app/assets/images/external_sites/lanyrd.png +0 -0
  14. data/app/assets/images/external_sites/meetup.png +0 -0
  15. data/app/assets/images/external_sites/plancast.png +0 -0
  16. data/app/assets/images/external_sites/shizzow.png +0 -0
  17. data/app/assets/images/external_sites/upcoming.png +0 -0
  18. data/app/assets/images/external_sites/yelp.png +0 -0
  19. data/app/assets/images/feed.png +0 -0
  20. data/app/assets/images/heart.png +0 -0
  21. data/app/assets/images/icon_ical.gif +0 -0
  22. data/app/assets/images/information.png +0 -0
  23. data/app/assets/images/nav_marker.gif +0 -0
  24. data/app/assets/images/nav_marker.png +0 -0
  25. data/app/assets/images/plus.png +0 -0
  26. data/app/assets/images/redx.png +0 -0
  27. data/app/assets/images/site-icon.png +0 -0
  28. data/app/assets/images/spinner.gif +0 -0
  29. data/app/assets/images/star.png +0 -0
  30. data/app/assets/images/subnav_marker.gif +0 -0
  31. data/app/assets/images/subnav_marker.png +0 -0
  32. data/app/assets/images/tag_blue.png +0 -0
  33. data/app/assets/images/tag_icons/angular.png +0 -0
  34. data/app/assets/images/tag_icons/beer.png +0 -0
  35. data/app/assets/images/tag_icons/bitcoin.png +0 -0
  36. data/app/assets/images/tag_icons/free.png +0 -0
  37. data/app/assets/images/tag_icons/golang.png +0 -0
  38. data/app/assets/images/tag_icons/html.png +0 -0
  39. data/app/assets/images/tag_icons/javascript.png +0 -0
  40. data/app/assets/images/tag_icons/linux.png +0 -0
  41. data/app/assets/images/tag_icons/php.png +0 -0
  42. data/app/assets/images/tag_icons/pizza.png +0 -0
  43. data/app/assets/images/tag_icons/python.png +0 -0
  44. data/app/assets/images/tag_icons/rails.png +0 -0
  45. data/app/assets/images/tag_icons/ruby.png +0 -0
  46. data/app/assets/images/tag_icons/sass.png +0 -0
  47. data/app/assets/images/tag_icons/swift.png +0 -0
  48. data/app/assets/images/transmit_blue.png +0 -0
  49. data/app/assets/images/weekday_background.gif +0 -0
  50. data/app/assets/javascripts/calagator.js +18 -0
  51. data/app/assets/javascripts/calagator/forms.js +60 -0
  52. data/app/assets/stylesheets/calagator.scss +8 -0
  53. data/app/assets/stylesheets/calagator/common.scss +14 -0
  54. data/app/assets/stylesheets/calagator/datepicker.scss +177 -0
  55. data/app/assets/stylesheets/calagator/errors.css +110 -0
  56. data/app/assets/stylesheets/calagator/forms.scss +4 -0
  57. data/app/assets/stylesheets/calagator/reset.css +40 -0
  58. data/app/assets/stylesheets/calagator/theme.css +0 -0
  59. data/app/controllers/calagator/admin_controller.rb +27 -0
  60. data/app/controllers/calagator/application_controller.rb +82 -0
  61. data/app/controllers/calagator/events_controller.rb +171 -0
  62. data/app/controllers/calagator/site_controller.rb +35 -0
  63. data/app/controllers/calagator/sources_controller.rb +94 -0
  64. data/app/controllers/calagator/venues_controller.rb +133 -0
  65. data/app/controllers/calagator/versions_controller.rb +20 -0
  66. data/app/helpers/calagator/application_helper.rb +128 -0
  67. data/app/helpers/calagator/events_helper.rb +184 -0
  68. data/app/helpers/calagator/google_event_export_helper.rb +67 -0
  69. data/app/helpers/calagator/mapping_helper.rb +103 -0
  70. data/app/helpers/calagator/sources_helper.rb +10 -0
  71. data/app/helpers/calagator/tags_helper.rb +42 -0
  72. data/app/helpers/calagator/time_range_helper.rb +166 -0
  73. data/app/models/calagator/event.rb +246 -0
  74. data/app/models/calagator/event/cloner.rb +39 -0
  75. data/app/models/calagator/event/ical_renderer.rb +155 -0
  76. data/app/models/calagator/event/overview.rb +45 -0
  77. data/app/models/calagator/event/saver.rb +58 -0
  78. data/app/models/calagator/event/search.rb +72 -0
  79. data/app/models/calagator/event/search_engine.rb +28 -0
  80. data/app/models/calagator/event/search_engine/apache_sunspot.rb +106 -0
  81. data/app/models/calagator/event/search_engine/sql.rb +107 -0
  82. data/app/models/calagator/source.rb +115 -0
  83. data/app/models/calagator/source/importer.rb +46 -0
  84. data/app/models/calagator/source/parser.rb +128 -0
  85. data/app/models/calagator/source/parser/facebook.rb +67 -0
  86. data/app/models/calagator/source/parser/hcal.rb +108 -0
  87. data/app/models/calagator/source/parser/http_authentication_required_error.rb +6 -0
  88. data/app/models/calagator/source/parser/ical.rb +186 -0
  89. data/app/models/calagator/source/parser/meetup.rb +72 -0
  90. data/app/models/calagator/source/parser/not_found.rb +9 -0
  91. data/app/models/calagator/source/parser/plancast.rb +59 -0
  92. data/app/models/calagator/venue.rb +161 -0
  93. data/app/models/calagator/venue/geocoder.rb +51 -0
  94. data/app/models/calagator/venue/search.rb +63 -0
  95. data/app/models/calagator/venue/search_engine.rb +24 -0
  96. data/app/models/calagator/venue/search_engine/apache_sunspot.rb +85 -0
  97. data/app/models/calagator/venue/search_engine/sql.rb +68 -0
  98. data/app/models/event/search_engine/base.rb +0 -0
  99. data/app/observers/calagator/cache_observer.rb +37 -0
  100. data/app/views/calagator/admin/events.html.erb +28 -0
  101. data/app/views/calagator/admin/index.html.erb +8 -0
  102. data/app/views/calagator/events/_feed_item.html.erb +62 -0
  103. data/app/views/calagator/events/_form.html.erb +63 -0
  104. data/app/views/calagator/events/_gcal_reminder.html.erb +1 -0
  105. data/app/views/calagator/events/_hcal.html.erb +41 -0
  106. data/app/views/calagator/events/_item.html.erb +120 -0
  107. data/app/views/calagator/events/_list.html.erb +30 -0
  108. data/app/views/calagator/events/_list_item.html.erb +37 -0
  109. data/app/views/calagator/events/_search_section.html.erb +15 -0
  110. data/app/views/calagator/events/_subnav.html.erb +13 -0
  111. data/app/views/calagator/events/_table.html.erb +74 -0
  112. data/app/views/calagator/events/duplicates.html.erb +58 -0
  113. data/app/views/calagator/events/edit.html.erb +3 -0
  114. data/app/views/calagator/events/index.atom.builder +32 -0
  115. data/app/views/calagator/events/index.html.erb +51 -0
  116. data/app/views/calagator/events/index.kml.erb +20 -0
  117. data/app/views/calagator/events/new.html.erb +7 -0
  118. data/app/views/calagator/events/search.html.erb +25 -0
  119. data/app/views/calagator/events/show.html.erb +81 -0
  120. data/app/views/calagator/site/_appropriateness_message.html.erb +15 -0
  121. data/app/views/calagator/site/_description.html.erb +3 -0
  122. data/app/views/calagator/site/about.html.erb +7 -0
  123. data/app/views/calagator/site/defunct.html.erb +15 -0
  124. data/app/views/calagator/site/export.html.erb +4 -0
  125. data/app/views/calagator/site/index.html.erb +40 -0
  126. data/app/views/calagator/site/opensearch.xml.builder +8 -0
  127. data/app/views/calagator/sources/_subnav.html.erb +0 -0
  128. data/app/views/calagator/sources/edit.html.erb +12 -0
  129. data/app/views/calagator/sources/import.html.erb +10 -0
  130. data/app/views/calagator/sources/index.html.erb +22 -0
  131. data/app/views/calagator/sources/new.html.erb +32 -0
  132. data/app/views/calagator/sources/show.html.erb +16 -0
  133. data/app/views/calagator/venues/_form.html.erb +57 -0
  134. data/app/views/calagator/venues/_subnav.html.erb +10 -0
  135. data/app/views/calagator/venues/duplicates.html.erb +65 -0
  136. data/app/views/calagator/venues/edit.html.erb +3 -0
  137. data/app/views/calagator/venues/index.html.erb +93 -0
  138. data/app/views/calagator/venues/index.kml.erb +15 -0
  139. data/app/views/calagator/venues/map.html.erb +4 -0
  140. data/app/views/calagator/venues/new.html.erb +5 -0
  141. data/app/views/calagator/venues/show.html.erb +113 -0
  142. data/app/views/calagator/versions/_chooser.html.erb +28 -0
  143. data/app/views/calagator/versions/_edit_with_chooser.html.erb +16 -0
  144. data/app/views/layouts/calagator/application.html.erb +110 -0
  145. data/config/deploy/local.rb +37 -0
  146. data/config/deploy/lucca.rb +33 -0
  147. data/config/initializers/dates.rb +7 -0
  148. data/config/initializers/formtastic.rb +82 -0
  149. data/config/initializers/geokit.rb +74 -0
  150. data/config/initializers/ics_renderer.rb +6 -0
  151. data/config/initializers/load_tag_model_extensions.rb +3 -0
  152. data/config/initializers/mime_types.rb +9 -0
  153. data/config/initializers/observers.rb +1 -0
  154. data/config/initializers/search_engine.rb +4 -0
  155. data/config/initializers/set_default_url_host.rb +5 -0
  156. data/config/initializers/time_get_zone.rb +8 -0
  157. data/config/locales/en.yml +5 -0
  158. data/config/routes.rb +55 -0
  159. data/config/secrets.yml.blag +75 -0
  160. data/config/sunspot.yml +23 -0
  161. data/db/development.sqlite3 +0 -0
  162. data/db/development.sqlite3.bak +0 -0
  163. data/db/development.sqlite3.old +0 -0
  164. data/db/development~20111112@110950.sqlite3 +0 -0
  165. data/db/migrate/001_create_events.rb +17 -0
  166. data/db/migrate/002_create_venues.rb +17 -0
  167. data/db/migrate/003_create_sources.rb +16 -0
  168. data/db/migrate/004_add_detailed_fields_to_venue.rb +19 -0
  169. data/db/migrate/005_add_end_time_to_events.rb +9 -0
  170. data/db/migrate/006_add_source_id_to_events.rb +9 -0
  171. data/db/migrate/008_add_source_id_to_venues.rb +10 -0
  172. data/db/migrate/009_add_duplicate_of_column_to_venues.rb +9 -0
  173. data/db/migrate/010_add_duplicate_of_column_to_events.rb +9 -0
  174. data/db/migrate/011_change_lat_long_type.rb +12 -0
  175. data/db/migrate/012_add_source_reimport.rb +9 -0
  176. data/db/migrate/013_change_end_time_to_duration.rb +11 -0
  177. data/db/migrate/014_remove_format_type_from_source.rb +9 -0
  178. data/db/migrate/015_create_updates.rb +15 -0
  179. data/db/migrate/016_remove_next_update_from_source.rb +9 -0
  180. data/db/migrate/20080705163959_change_duration_to_end_time.rb +11 -0
  181. data/db/migrate/20080705164959_create_tags_and_taggings.rb +28 -0
  182. data/db/migrate/20081011181519_create_versioned_events.rb +25 -0
  183. data/db/migrate/20081011193124_create_versioned_venues.rb +32 -0
  184. data/db/migrate/20081115190515_add_rrule_to_events.rb +15 -0
  185. data/db/migrate/20090912082129_create_versions.rb +18 -0
  186. data/db/migrate/20110219205156_add_closed_flag_to_venues.rb +9 -0
  187. data/db/migrate/20110220001008_add_wifi_flag_to_venues.rb +9 -0
  188. data/db/migrate/20110220011427_add_access_notes_to_venues.rb +9 -0
  189. data/db/migrate/20110220031117_add_events_count_to_venues.rb +8 -0
  190. data/db/migrate/20110604174521_add_venue_details_to_events.rb +9 -0
  191. data/db/migrate/20110717231316_acts_as_taggable_on_migration.rb +50 -0
  192. data/db/migrate/20120709092821_cleanup.rb +14 -0
  193. data/db/migrate/20120831234448_specify_venues_latitude_and_longitude_precision.rb +11 -0
  194. data/db/migrate/20150206085809_remove_updates.rb +13 -0
  195. data/db/migrate/20150207231355_add_locked_status_to_events.rb +5 -0
  196. data/db/production.sqlite3 +0 -0
  197. data/db/schema.rb +102 -0
  198. data/db/seeds.rb +16 -0
  199. data/db/test.sqlite3 +0 -0
  200. data/db/test2.sqlite3 +0 -0
  201. data/lib/calagator.rb +30 -0
  202. data/lib/calagator/blacklist_validator.rb +69 -0
  203. data/lib/calagator/decode_html_entities_hack.rb +33 -0
  204. data/lib/calagator/duplicate_checking.rb +133 -0
  205. data/lib/calagator/duplicate_checking/controller_actions.rb +38 -0
  206. data/lib/calagator/duplicate_checking/duplicate_finder.rb +83 -0
  207. data/lib/calagator/duplicate_checking/duplicate_squasher.rb +74 -0
  208. data/lib/calagator/engine.rb +30 -0
  209. data/lib/calagator/strip_whitespace.rb +19 -0
  210. data/lib/calagator/tag_model_extensions.rb +104 -0
  211. data/lib/calagator/url_prefixer.rb +9 -0
  212. data/lib/calagator/version.rb +3 -0
  213. data/lib/generators/calagator/install_generator.rb +39 -0
  214. data/lib/generators/calagator/templates/config/calagator.rb +26 -0
  215. data/lib/generators/calagator/templates/config/secrets.yml.sample +83 -0
  216. data/lib/secrets_reader.rb +76 -0
  217. data/lib/tasks/spec_db.rake +53 -0
  218. data/lib/tasks/sunspot_reindex_calagator.rake +6 -0
  219. data/lib/tasks/sunspot_solr_restart_enhancements.rake +19 -0
  220. data/lib/tasks/update_counter_caches.rake +13 -0
  221. data/lib/templates/erb/scaffold/_form.html.erb +11 -0
  222. data/lib/theme_reader.rb +17 -0
  223. data/lib/wait_for_solr.rb +25 -0
  224. data/spec/controllers/calagator/application_controller_spec.rb +47 -0
  225. data/spec/controllers/calagator/events_controller_spec.rb +794 -0
  226. data/spec/controllers/calagator/site_controller_spec.rb +59 -0
  227. data/spec/controllers/calagator/sources_controller_spec.rb +439 -0
  228. data/spec/controllers/calagator/venues_controller_spec.rb +319 -0
  229. data/spec/controllers/calagator/versions_controller_spec.rb +82 -0
  230. data/spec/controllers/squash_many_duplicates_examples.rb +49 -0
  231. data/spec/dummy/Gemfile +39 -0
  232. data/spec/dummy/Gemfile.lock +195 -0
  233. data/spec/dummy/README.rdoc +261 -0
  234. data/spec/dummy/Rakefile +7 -0
  235. data/spec/dummy/app/assets/images/rails.png +0 -0
  236. data/spec/dummy/app/assets/javascripts/application.js +16 -0
  237. data/spec/dummy/app/assets/stylesheets/application.css +14 -0
  238. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  239. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  240. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  241. data/spec/dummy/config.ru +4 -0
  242. data/spec/dummy/config/application.rb +62 -0
  243. data/spec/dummy/config/boot.rb +6 -0
  244. data/spec/dummy/config/database.yml +25 -0
  245. data/spec/dummy/config/environment.rb +5 -0
  246. data/spec/dummy/config/environments/development.rb +37 -0
  247. data/spec/dummy/config/environments/production.rb +67 -0
  248. data/spec/dummy/config/environments/test.rb +37 -0
  249. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  250. data/spec/dummy/config/initializers/calagator.rb +26 -0
  251. data/spec/dummy/config/initializers/inflections.rb +15 -0
  252. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  253. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  254. data/spec/dummy/config/initializers/session_store.rb +8 -0
  255. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  256. data/spec/dummy/config/locales/en.yml +5 -0
  257. data/spec/dummy/config/routes.rb +59 -0
  258. data/spec/dummy/config/secrets.yml +83 -0
  259. data/spec/dummy/db/development.sqlite3 +0 -0
  260. data/spec/dummy/db/migrate/20150309023304_create_events.calagator.rb +18 -0
  261. data/spec/dummy/db/migrate/20150309023305_create_venues.calagator.rb +18 -0
  262. data/spec/dummy/db/migrate/20150309023306_create_sources.calagator.rb +17 -0
  263. data/spec/dummy/db/migrate/20150309023307_add_detailed_fields_to_venue.calagator.rb +20 -0
  264. data/spec/dummy/db/migrate/20150309023308_add_end_time_to_events.calagator.rb +10 -0
  265. data/spec/dummy/db/migrate/20150309023309_add_source_id_to_events.calagator.rb +10 -0
  266. data/spec/dummy/db/migrate/20150309023310_add_source_id_to_venues.calagator.rb +11 -0
  267. data/spec/dummy/db/migrate/20150309023311_add_duplicate_of_column_to_venues.calagator.rb +10 -0
  268. data/spec/dummy/db/migrate/20150309023312_add_duplicate_of_column_to_events.calagator.rb +10 -0
  269. data/spec/dummy/db/migrate/20150309023313_change_lat_long_type.calagator.rb +13 -0
  270. data/spec/dummy/db/migrate/20150309023314_add_source_reimport.calagator.rb +10 -0
  271. data/spec/dummy/db/migrate/20150309023315_change_end_time_to_duration.calagator.rb +12 -0
  272. data/spec/dummy/db/migrate/20150309023316_remove_format_type_from_source.calagator.rb +10 -0
  273. data/spec/dummy/db/migrate/20150309023317_create_updates.calagator.rb +16 -0
  274. data/spec/dummy/db/migrate/20150309023318_remove_next_update_from_source.calagator.rb +10 -0
  275. data/spec/dummy/db/migrate/20150309023319_change_duration_to_end_time.calagator.rb +12 -0
  276. data/spec/dummy/db/migrate/20150309023320_create_tags_and_taggings.calagator.rb +29 -0
  277. data/spec/dummy/db/migrate/20150309023321_create_versioned_events.calagator.rb +26 -0
  278. data/spec/dummy/db/migrate/20150309023322_create_versioned_venues.calagator.rb +33 -0
  279. data/spec/dummy/db/migrate/20150309023323_add_rrule_to_events.calagator.rb +16 -0
  280. data/spec/dummy/db/migrate/20150309023324_create_versions.calagator.rb +19 -0
  281. data/spec/dummy/db/migrate/20150309023325_add_closed_flag_to_venues.calagator.rb +10 -0
  282. data/spec/dummy/db/migrate/20150309023326_add_wifi_flag_to_venues.calagator.rb +10 -0
  283. data/spec/dummy/db/migrate/20150309023327_add_access_notes_to_venues.calagator.rb +10 -0
  284. data/spec/dummy/db/migrate/20150309023328_add_events_count_to_venues.calagator.rb +9 -0
  285. data/spec/dummy/db/migrate/20150309023329_add_venue_details_to_events.calagator.rb +10 -0
  286. data/spec/dummy/db/migrate/20150309023330_acts_as_taggable_on_migration.calagator.rb +51 -0
  287. data/spec/dummy/db/migrate/20150309023331_cleanup.calagator.rb +15 -0
  288. data/spec/dummy/db/migrate/20150309023332_specify_venues_latitude_and_longitude_precision.calagator.rb +12 -0
  289. data/spec/dummy/db/migrate/20150309023333_remove_updates.calagator.rb +14 -0
  290. data/spec/dummy/db/migrate/20150309023334_add_locked_status_to_events.calagator.rb +6 -0
  291. data/spec/dummy/db/schema.rb +95 -0
  292. data/spec/dummy/db/seeds.rb +7 -0
  293. data/spec/dummy/db/test.sqlite3 +0 -0
  294. data/spec/dummy/doc/README_FOR_APP +2 -0
  295. data/spec/dummy/log/development.log +273 -0
  296. data/spec/dummy/public/404.html +26 -0
  297. data/spec/dummy/public/422.html +26 -0
  298. data/spec/dummy/public/500.html +25 -0
  299. data/spec/dummy/public/favicon.ico +0 -0
  300. data/spec/dummy/public/robots.txt +5 -0
  301. data/spec/dummy/script/rails +6 -0
  302. data/spec/dummy/test/performance/browsing_test.rb +12 -0
  303. data/spec/dummy/test/test_helper.rb +13 -0
  304. data/spec/factories.rb +93 -0
  305. data/spec/features/add_event_spec.rb +99 -0
  306. data/spec/features/add_venue_spec.rb +34 -0
  307. data/spec/features/admin_auth_spec.rb +22 -0
  308. data/spec/features/admin_lock_event_spec.rb +41 -0
  309. data/spec/features/import_events_from_feed_spec.rb +43 -0
  310. data/spec/features/managing_event_spec.rb +111 -0
  311. data/spec/features/managing_venue_spec.rb +71 -0
  312. data/spec/features/search_event_spec.rb +27 -0
  313. data/spec/helpers/calagator/application_helper_spec.rb +82 -0
  314. data/spec/helpers/calagator/events_helper_spec.rb +172 -0
  315. data/spec/helpers/calagator/google_event_export_helper_spec.rb +70 -0
  316. data/spec/helpers/calagator/sources_helper_spec.rb +12 -0
  317. data/spec/helpers/calagator/tags_helper_spec.rb +85 -0
  318. data/spec/helpers/calagator/time_range_helper_spec.rb +59 -0
  319. data/spec/lib/calagator/blacklist_validator_spec.rb +65 -0
  320. data/spec/lib/calagator/decode_html_entities_hack_spec.rb +54 -0
  321. data/spec/lib/calagator/settings_spec.rb +20 -0
  322. data/spec/lib/calagator/url_prefixer_spec.rb +33 -0
  323. data/spec/lib/secrets_reader_spec.rb +65 -0
  324. data/spec/models/calagator/event/cloner_spec.rb +43 -0
  325. data/spec/models/calagator/event/overview_spec.rb +79 -0
  326. data/spec/models/calagator/event/search_spec.rb +103 -0
  327. data/spec/models/calagator/event_search_spec.rb +149 -0
  328. data/spec/models/calagator/event_spec.rb +859 -0
  329. data/spec/models/calagator/source/parser_facebook_spec.rb +73 -0
  330. data/spec/models/calagator/source/parser_hcal_spec.rb +69 -0
  331. data/spec/models/calagator/source/parser_ical_non_standard_spec.rb +91 -0
  332. data/spec/models/calagator/source/parser_ical_spec.rb +322 -0
  333. data/spec/models/calagator/source/parser_meetup_spec.rb +69 -0
  334. data/spec/models/calagator/source/parser_plancast_spec.rb +53 -0
  335. data/spec/models/calagator/source/parser_spec.rb +238 -0
  336. data/spec/models/calagator/source_spec.rb +135 -0
  337. data/spec/models/calagator/venue/search_spec.rb +92 -0
  338. data/spec/models/calagator/venue_search_spec.rb +124 -0
  339. data/spec/models/calagator/venue_spec.rb +346 -0
  340. data/spec/models/tag_spec.rb +35 -0
  341. data/spec/rails_helper.rb +39 -0
  342. data/spec/spec_helper.rb +140 -0
  343. data/spec/support/disable_geocoding.rb +1 -0
  344. data/spec/support/http_samples.rb +5 -0
  345. data/spec/support/samples/facebook.json +23 -0
  346. data/spec/support/samples/hcal_basic.xml +6 -0
  347. data/spec/support/samples/hcal_dup_event_dup_venue.xml +13 -0
  348. data/spec/support/samples/hcal_event_duplicates_fixture.xml +13 -0
  349. data/spec/support/samples/hcal_event_wo_lat_and_long.xml +14 -0
  350. data/spec/support/samples/hcal_multiple.xml +16 -0
  351. data/spec/support/samples/hcal_same_event_twice_with_different_venues.xml +12 -0
  352. data/spec/support/samples/hcal_single.xml +8 -0
  353. data/spec/support/samples/hcal_two_identical_events.xml +14 -0
  354. data/spec/support/samples/hcal_upcoming_v1.html +412 -0
  355. data/spec/support/samples/hcal_upcoming_v2.html +749 -0
  356. data/spec/support/samples/hcal_upcoming_v3.html +685 -0
  357. data/spec/support/samples/hcal_upcoming_v4.html +761 -0
  358. data/spec/support/samples/ical_apple.ics +22 -0
  359. data/spec/support/samples/ical_apple_v3.ics +37 -0
  360. data/spec/support/samples/ical_basic.ics +15 -0
  361. data/spec/support/samples/ical_basic_with_duration.ics +16 -0
  362. data/spec/support/samples/ical_event_with_squashed_venue.ics +12 -0
  363. data/spec/support/samples/ical_eventful_many.ics +504 -0
  364. data/spec/support/samples/ical_gmt.ics +12 -0
  365. data/spec/support/samples/ical_google.ics +786 -0
  366. data/spec/support/samples/ical_multiple_calendars.ics +111 -0
  367. data/spec/support/samples/ical_upcoming.ics +36 -0
  368. data/spec/support/samples/ical_upcoming_many.ics +682 -0
  369. data/spec/support/samples/ical_upcoming_v2.ics +43 -0
  370. data/spec/support/samples/ical_z.ics +10 -0
  371. data/spec/support/samples/meetup.ics +16 -0
  372. data/spec/support/samples/meetup.json +31 -0
  373. data/spec/support/samples/plancast.ics +59 -0
  374. data/spec/support/samples/plancast.json +51 -0
  375. data/spec/support/samples/plancast_with_missing_venue.json +39 -0
  376. data/spec/support/samples/upcoming_v1.xml +8 -0
  377. data/spec/support/samples/upcoming_v2_with_invalid_utc_dates.xml +8 -0
  378. data/spec/support/time_convenience_methods.rb +8 -0
  379. data/spec/support/time_zones.rb +12 -0
  380. data/spec/support/url_helpers.rb +5 -0
  381. data/spec/support/wait_for_ajax.rb +15 -0
  382. data/spec/support/webmock.rb +8 -0
  383. data/spec/travis_spec.rb +7 -0
  384. metadata +1194 -0
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ module Calagator
4
+
5
+ describe Venue::Search, :type => :model do
6
+ describe "#venues" do
7
+ before do
8
+ @open_venue = FactoryGirl.create(:venue, title: 'Open Town', description: 'baz', wifi: false, tag_list: %w(foo))
9
+ @closed_venue = FactoryGirl.create(:venue, title: 'Closed Down', closed: true, wifi: false, tag_list: %w(bar))
10
+ @wifi_venue = FactoryGirl.create(:venue, title: "Internetful", wifi: true, tag_list: %w(foo bar))
11
+ end
12
+
13
+ describe "with no parameters" do
14
+ subject { Venue::Search.new }
15
+
16
+ it "should not include closed venues" do
17
+ expect(subject.venues).to eq([@open_venue, @wifi_venue])
18
+ end
19
+
20
+ it "should not declare results" do
21
+ expect(subject.results?).to be_falsey
22
+ end
23
+ end
24
+
25
+ describe "and showing all venues" do
26
+ it "should include closed venues when asked to with the include_closed parameter" do
27
+ subject = Venue::Search.new all: '1', include_closed: '1'
28
+ expect(subject.venues).to eq([@open_venue, @closed_venue, @wifi_venue])
29
+ end
30
+
31
+ it "should include ONLY closed venues when asked to with the closed parameter" do
32
+ subject = Venue::Search.new all: '1', closed: '1'
33
+ expect(subject.venues).to eq([@closed_venue])
34
+ end
35
+
36
+ it "should declare results" do
37
+ subject = Venue::Search.new all: '1'
38
+ expect(subject.results?).to be_truthy
39
+ end
40
+ end
41
+
42
+ describe "when searching" do
43
+ describe "for public wifi (and no keyword)" do
44
+ it "should only include results with public wifi" do
45
+ subject = Venue::Search.new query: '', wifi: '1'
46
+ expect(subject.venues).to eq([@wifi_venue])
47
+ end
48
+
49
+ it "should declare results" do
50
+ subject = Venue::Search.new query: '', wifi: '1'
51
+ expect(subject.results?).to be_truthy
52
+ end
53
+ end
54
+
55
+ describe "when searching by keyword" do
56
+ it "should find venues by title" do
57
+ subject = Venue::Search.new query: 'Open Town'
58
+ expect(subject.venues).to eq([@open_venue])
59
+ end
60
+
61
+ it "should find venues by description" do
62
+ subject = Venue::Search.new query: 'baz'
63
+ expect(subject.venues).to eq([@open_venue])
64
+ end
65
+
66
+ describe "and requiring public wifi" do
67
+ it "should not find venues without public wifi" do
68
+ subject = Venue::Search.new query: 'baz', wifi: '1'
69
+ expect(subject.venues).to be_empty
70
+ end
71
+ end
72
+
73
+ it "should declare results" do
74
+ subject = Venue::Search.new query: 'Open Town'
75
+ expect(subject.results?).to be_truthy
76
+ end
77
+ end
78
+ end
79
+
80
+ it "should be able to return events matching specific tag" do
81
+ subject = Venue::Search.new tag: "foo"
82
+ expect(subject.venues).to match_array([@open_venue, @wifi_venue])
83
+ end
84
+
85
+ it "should declare results" do
86
+ subject = Venue::Search.new tag: "foo"
87
+ expect(subject.results?).to be_truthy
88
+ end
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ module Calagator
4
+
5
+ describe Venue, :type => :model do
6
+ shared_examples_for "#search" do
7
+ it "returns everything when searching by empty string" do
8
+ venue1 = FactoryGirl.create(:venue)
9
+ venue2 = FactoryGirl.create(:venue)
10
+ expect(Venue.search("")).to match_array([venue1, venue2])
11
+ end
12
+
13
+ it "searches venue titles by substring" do
14
+ venue1 = FactoryGirl.create(:venue, title: "wtfbbq")
15
+ venue2 = FactoryGirl.create(:venue, title: "zomg!")
16
+ expect(Venue.search("zomg")).to eq([venue2])
17
+ end
18
+
19
+ it "searches venue descriptions by substring" do
20
+ venue1 = FactoryGirl.create(:venue, description: "wtfbbq")
21
+ venue2 = FactoryGirl.create(:venue, description: "zomg!")
22
+ expect(Venue.search("zomg")).to eq([venue2])
23
+ end
24
+
25
+ it "searches venue tags by exact match" do
26
+ venue1 = FactoryGirl.create(:venue, tag_list: ["wtf", "bbq", "zomg"])
27
+ venue2 = FactoryGirl.create(:venue, tag_list: ["wtf", "bbq", "omg"])
28
+ expect(Venue.search("omg")).to eq([venue2])
29
+ end
30
+
31
+ it "searches case-insensitively" do
32
+ venue1 = FactoryGirl.create(:venue, title: "WTFBBQ")
33
+ venue2 = FactoryGirl.create(:venue, title: "ZOMG!")
34
+ expect(Venue.search("zomg")).to eq([venue2])
35
+ end
36
+
37
+ it "sorts by title" do
38
+ venue2 = FactoryGirl.create(:venue, title: "zomg")
39
+ venue1 = FactoryGirl.create(:venue, title: "omg")
40
+ expect(Venue.search("", order: "name")).to eq([venue1, venue2])
41
+ end
42
+
43
+ it "can limit to venues with wifi" do
44
+ venue1 = FactoryGirl.create(:venue, wifi: false)
45
+ venue2 = FactoryGirl.create(:venue, wifi: true)
46
+ expect(Venue.search("", wifi: true)).to eq([venue2])
47
+ end
48
+
49
+ it "excludes closed venues" do
50
+ venue1 = FactoryGirl.create(:venue, closed: true)
51
+ venue2 = FactoryGirl.create(:venue, closed: false)
52
+ expect(Venue.search("")).to eq([venue2])
53
+ end
54
+
55
+ it "can include closed venues" do
56
+ venue1 = FactoryGirl.create(:venue, closed: true)
57
+ venue2 = FactoryGirl.create(:venue, closed: false)
58
+ expect(Venue.search("", include_closed: true)).to match_array([venue1, venue2])
59
+ end
60
+
61
+ it "can limit number of venues" do
62
+ 2.times { FactoryGirl.create(:venue) }
63
+ expect(Venue.search("", limit: 1).count).to eq(1)
64
+ end
65
+
66
+ it "does not search multiple terms" do
67
+ venue2 = FactoryGirl.create(:venue, title: "zomg")
68
+ venue1 = FactoryGirl.create(:venue, title: "omg")
69
+ expect(Venue.search("zomg omg")).to eq([])
70
+ end
71
+
72
+ it "ANDs terms together to narrow search results" do
73
+ venue2 = FactoryGirl.create(:venue, title: "zomg omg")
74
+ venue1 = FactoryGirl.create(:venue, title: "zomg cats")
75
+ expect(Venue.search("zomg omg")).to eq([venue2])
76
+ end
77
+
78
+ end
79
+
80
+ describe "Sql" do
81
+ around do |example|
82
+ original = Venue::SearchEngine.kind
83
+ Venue::SearchEngine.kind = :sql
84
+ example.run
85
+ Venue::SearchEngine.kind = original
86
+ end
87
+
88
+ it_should_behave_like "#search"
89
+
90
+ it "is using the sql search engine" do
91
+ expect(Venue::SearchEngine.kind).to eq(:sql)
92
+ end
93
+ end
94
+
95
+ describe "Sunspot" do
96
+ around do |example|
97
+ server_running = begin
98
+ # Try opening the configured port. If it works, it's running.
99
+ TCPSocket.new('127.0.0.1', Sunspot::Rails.configuration.port).close
100
+ true
101
+ rescue Errno::ECONNREFUSED
102
+ false
103
+ end
104
+
105
+ if server_running
106
+ Event::SearchEngine.use(:sunspot)
107
+ Venue::SearchEngine.use(:sunspot)
108
+ Event.reindex
109
+ Venue.reindex
110
+ example.run
111
+ else
112
+ pending "Solr not running. Start with `rake sunspot:solr:start RAILS_ENV=test`"
113
+ end
114
+ end
115
+
116
+ it_should_behave_like "#search"
117
+
118
+ it "is using the sunspot search engine" do
119
+ expect(Venue::SearchEngine.kind).to eq(:sunspot)
120
+ end
121
+ end
122
+ end
123
+
124
+ end
@@ -0,0 +1,346 @@
1
+ require 'spec_helper'
2
+
3
+ module Calagator
4
+
5
+ describe Venue, :type => :model do
6
+
7
+ it "should be valid" do
8
+ venue = Venue.new(:title => 'My Event')
9
+ expect(venue).to be_valid
10
+ end
11
+
12
+ it "should add an http prefix to urls missing this before save" do
13
+ venue = Venue.new(:title => 'My Event', :url => 'google.com')
14
+ expect(venue).to be_valid
15
+ end
16
+
17
+ it "validates blacklisted words" do
18
+ BlacklistValidator.any_instance.stub(patterns: [/\bcialis\b/, /\bviagra\b/])
19
+ venue = Venue.new(:title => "Foo bar cialis")
20
+ expect(venue).not_to be_valid
21
+ end
22
+
23
+ describe 'latitude validation' do
24
+ specify do
25
+ venue = Venue.new(:latitude => -91)
26
+ expect(venue).to have(1).error_on(:latitude)
27
+ end
28
+
29
+ specify do
30
+ venue = Venue.new(:latitude => -89)
31
+ expect(venue).to have(0).errors_on(:latitude)
32
+ end
33
+ end
34
+
35
+ describe 'longitude validation' do
36
+ specify do
37
+ venue = Venue.new(:longitude => -181)
38
+ expect(venue).to have(1).error_on(:longitude)
39
+ end
40
+
41
+ specify do
42
+ venue = Venue.new(:longitude => -179)
43
+ expect(venue).to have(0).errors_on(:longitude)
44
+ end
45
+ end
46
+
47
+ describe "when validating" do
48
+ let(:attributes) { {:title => 'My Venue'} }
49
+ let(:bad_data) { ' blargie ' }
50
+ let(:expected_data) { bad_data.strip }
51
+ [:title, :description, :address, :street_address, :locality, :region, :postal_code, :country, :email, :telephone].each do |field|
52
+ it "should strip whitespace from #{field}" do
53
+ venue = Venue.new(attributes.merge(field => bad_data))
54
+ venue.valid?
55
+ expect(venue.send(field)).to eq(expected_data)
56
+ end
57
+ end
58
+
59
+ it "should strip whitespace from url" do
60
+ venue = Venue.new(attributes.merge(:url => bad_data))
61
+ venue.valid?
62
+ expect(venue.url).to eq("http://#{expected_data}")
63
+ end
64
+ end
65
+
66
+ describe "when finding exact duplicates" do
67
+ it "should ignore attributes like created_at" do
68
+ venue1 = Venue.create!(:title => "this", :description => "desc",:created_at => Time.now)
69
+ venue2 = Venue.new( :title => "this", :description => "desc",:created_at => Time.now.yesterday)
70
+
71
+ expect(venue2.find_exact_duplicates).to include(venue1)
72
+ end
73
+
74
+ it "should ignore source_id" do
75
+ venue1 = Venue.create!(:title => "this", :description => "desc",:source_id => "1")
76
+ venue2 = Venue.new( :title => "this", :description => "desc",:source_id => "2")
77
+
78
+ expect(venue2.find_exact_duplicates).to include(venue1)
79
+ end
80
+
81
+ it "should not match non-duplicates" do
82
+ Venue.create!(:title => "this", :description => "desc")
83
+ venue2 = Venue.new(:title => "that", :description => "desc")
84
+
85
+ expect(venue2.find_exact_duplicates).to be_blank
86
+ end
87
+ end
88
+
89
+ describe "when finding duplicates [integration test]" do
90
+ before do
91
+ @existing = FactoryGirl.create(:venue)
92
+ end
93
+
94
+ it "should not match totally different records" do
95
+ FactoryGirl.create(:venue)
96
+ expect(Venue.find_duplicates_by(:title)).to be_empty
97
+ end
98
+
99
+ it "should not match similar records when not searching by duplicated fields" do
100
+ FactoryGirl.create :venue, title: @existing.title
101
+ expect(Venue.find_duplicates_by(:description)).to be_empty
102
+ end
103
+
104
+ it "should match similar records when searching by duplicated fields" do
105
+ FactoryGirl.create :venue, title: @existing.title
106
+ expect(Venue.find_duplicates_by(:title)).to be_present
107
+ end
108
+
109
+ it "should match similar records when searching by :any" do
110
+ FactoryGirl.create :venue, title: @existing.title
111
+ expect(Venue.find_duplicates_by(:any)).to be_present
112
+ end
113
+
114
+ it "should not match similar records when searching by multiple fields where not all are duplicated" do
115
+ FactoryGirl.create :venue, title: @existing.title
116
+ expect(Venue.find_duplicates_by([:title, :description])).to be_empty
117
+ end
118
+
119
+ it "should match similar records when searching by multiple fields where all are duplicated" do
120
+ FactoryGirl.create(:venue, :title => @existing.title, :description => @existing.description)
121
+ expect(Venue.find_duplicates_by([:title, :description])).to be_present
122
+ end
123
+
124
+ it "should not match dissimilar records when searching by :all" do
125
+ FactoryGirl.create(:venue)
126
+ expect(Venue.find_duplicates_by(:all)).to be_empty
127
+ end
128
+
129
+ it "should match similar records when searching by :all" do
130
+ attributes = @existing.attributes.reject{ |k,v| k == 'id'}
131
+ Venue.create!(attributes)
132
+ expect(Venue.find_duplicates_by(:all)).to be_present
133
+ end
134
+
135
+ it "should match non duplicate venues when searching by default" do
136
+ expect(Venue.find_duplicates_by_type).to be_present
137
+ end
138
+ end
139
+
140
+ describe "when checking for squashing" do
141
+ before do
142
+ @master = Venue.create!(:title => "Master")
143
+ @slave_first = Venue.create!(:title => "1st slave", :duplicate_of_id => @master.id)
144
+ @slave_second = Venue.create!(:title => "2nd slave", :duplicate_of_id => @slave_first.id)
145
+ end
146
+
147
+ it "should recognize a master" do
148
+ expect(@master).to be_a_master
149
+ end
150
+
151
+ it "should recognize a slave" do
152
+ expect(@slave_first).to be_a_slave
153
+ end
154
+
155
+ it "should not think that a slave is a master" do
156
+ expect(@slave_second).not_to be_a_master
157
+ end
158
+
159
+ it "should not think that a master is a slave" do
160
+ expect(@master).not_to be_a_slave
161
+ end
162
+
163
+ it "should return the progenitor of a child" do
164
+ expect(@slave_first.progenitor).to eq @master
165
+ end
166
+
167
+ it "should return the progenitor of a grandchild" do
168
+ expect(@slave_second.progenitor).to eq @master
169
+ end
170
+
171
+ it "should return a master as its own progenitor" do
172
+ expect(@master.progenitor).to eq @master
173
+ end
174
+ end
175
+
176
+ describe "when squashing duplicates" do
177
+ before do
178
+ @master_venue = Venue.create!(:title => "Master")
179
+ @submaster_venue = Venue.create!(:title => "Submaster")
180
+ @child_venue = Venue.create!(:title => "Child", :duplicate_of => @submaster_venue)
181
+ @venues = [@master_venue, @submaster_venue, @child_venue]
182
+
183
+ @event_at_child_venue = Event.create!(:title => "Event at child venue", :venue => @child_venue, :start_time => Time.now)
184
+ @event_at_submaster_venue = Event.create!(:title => "Event at submaster venue", :venue => @submaster_venue, :start_time => Time.now)
185
+ @events = [@event_at_child_venue, @event_at_submaster_venue]
186
+ end
187
+
188
+ it "should squash a single duplicate" do
189
+ Venue.squash(@master_venue, @submaster_venue)
190
+
191
+ expect(@submaster_venue.duplicate_of).to eq @master_venue
192
+ expect(@submaster_venue.duplicate?).to be_truthy
193
+ end
194
+
195
+ it "should squash multiple duplicates" do
196
+ Venue.squash(@master_venue, [@submaster_venue, @child_venue])
197
+
198
+ expect(@submaster_venue.duplicate_of).to eq @master_venue
199
+ expect(@child_venue.duplicate_of).to eq @master_venue
200
+ end
201
+
202
+ it "should squash duplicates recursively" do
203
+ Venue.squash(@master_venue, @submaster_venue)
204
+
205
+ expect(@submaster_venue.duplicate_of).to eq @master_venue
206
+ @child_venue.reload # Needed because child was queried through DB, not object graph
207
+ expect(@child_venue.duplicate_of).to eq @master_venue
208
+ end
209
+
210
+ it "should transfer events of duplicates" do
211
+ expect(@venues.map{|venue| venue.events.count}).to eq [0, 1, 1]
212
+
213
+ Venue.squash(@master_venue, @submaster_venue)
214
+
215
+ expect(@venues.map{|venue| venue.events.count}).to eq [2, 0, 0]
216
+
217
+ events = @venues.map(&:events).flatten
218
+ expect(events).to be_present
219
+ for event in events
220
+ expect(event.venue).to eq @master_venue
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ describe "Venue geocoding", :type => :model do
227
+ before do
228
+ @venue = Venue.new(:title => "title", :address => "test")
229
+ @geo_failure = double("geo", :success => false)
230
+ @geo_success = double("geo", :success => true, :lat => 0.0, :lng => 0.0,
231
+ :street_address => "622 SE Grand Ave.", :city => "Portland",
232
+ :state => "OR", :country_code => "US", :zip => "97214")
233
+ @geocodable_address = "#{@geo_success.street_address}, #{@geo_success.city}" \
234
+ "#{@geo_success.state} #{@geo_success.zip}"
235
+ end
236
+
237
+ it "should be valid even if not yet geocoded" do
238
+ expect(@venue.valid?).to be_truthy
239
+ end
240
+
241
+ it "should report its location properly if it has one" do
242
+ expect {
243
+ @venue.latitude = 45.0
244
+ @venue.longitude = -122.0
245
+ }.to change { @venue.location }.from(nil).to([BigDecimal("45.0"), BigDecimal("-122.0")])
246
+ end
247
+
248
+ describe "with geocoding" do
249
+ # Enable geocoding for just these tests
250
+ around do |example|
251
+ original = Venue::Geocoder.perform_geocoding?
252
+ Venue::Geocoder.perform_geocoding = true
253
+ example.run
254
+ Venue::Geocoder.perform_geocoding = original
255
+ end
256
+
257
+ it "should geocode automatically on save" do
258
+ expect(GeoKit::Geocoders::MultiGeocoder).to receive(:geocode).once.and_return(@geo_success)
259
+ @venue.save
260
+ end
261
+
262
+ it "shouldn't geocode automatically unless there's an address" do
263
+ @venue.address = ""
264
+ expect(GeoKit::Geocoders::MultiGeocoder).not_to receive(:geocode)
265
+ @venue.save
266
+ end
267
+
268
+ it "shouldn't geocode automatically if already geocoded" do
269
+ @venue.latitude = @venue.longitude = 0.0
270
+ expect(GeoKit::Geocoders::MultiGeocoder).not_to receive(:geocode)
271
+ @venue.save
272
+ end
273
+
274
+ it "shouldn't fail if the geocoder returns failure" do
275
+ expect(GeoKit::Geocoders::MultiGeocoder).to receive(:geocode).once.and_return(@geo_failure)
276
+ @venue.save
277
+ end
278
+
279
+ it "should fill in empty addressing fields" do
280
+ expect(GeoKit::Geocoders::MultiGeocoder).to receive(:geocode).once.and_return(@geo_success)
281
+ @venue.save
282
+ expect(@venue.street_address).to eq @geo_success.street_address
283
+ expect(@venue.locality).to eq @geo_success.city
284
+ expect(@venue.region).to eq @geo_success.state
285
+ expect(@venue.postal_code).to eq @geo_success.zip
286
+ end
287
+
288
+ it "should leave non-empty addressing fields alone" do
289
+ @venue.locality = "Cleveland"
290
+ expect(GeoKit::Geocoders::MultiGeocoder).to receive(:geocode).once.and_return(@geo_success)
291
+ @venue.save
292
+ expect(@venue.locality).to eq "Cleveland"
293
+ end
294
+ end
295
+ end
296
+
297
+ describe "Venue geocode addressing", :type => :model do
298
+ before do
299
+ @venue = Venue.new(:title => "title")
300
+ end
301
+
302
+ it "should use the street address fields if they're present" do
303
+ @venue.attributes = {
304
+ :street_address => "street_address",
305
+ :locality => "locality",
306
+ :region => "region",
307
+ :postal_code => "postal_code",
308
+ :country => "country",
309
+ :address => "address"
310
+ }
311
+ expect(@venue.geocode_address).to eq "street_address, locality region postal_code country"
312
+ end
313
+
314
+ it "should fall back to 'address' field if street address fields are blank" do
315
+ @venue.attributes = {:street_address => "", :address => "address"}
316
+ expect(@venue.geocode_address).to eq "address"
317
+ end
318
+
319
+ describe "when versioning" do
320
+ it "should have versions" do
321
+ expect(Venue.new.versions).to eq []
322
+ end
323
+
324
+ it "should create a new version after updating" do
325
+ venue = FactoryGirl.create :venue
326
+ expect(venue.versions.count).to eq 1
327
+
328
+ venue.title += " (change)"
329
+
330
+ venue.save!
331
+ expect(venue.versions.count).to eq 2
332
+ end
333
+
334
+ it "should store old content in past versions" do
335
+ venue = FactoryGirl.create :venue
336
+ original_title = venue.title
337
+
338
+ venue.title += " (change)"
339
+
340
+ venue.save!
341
+ expect(venue.versions.last.reify.title).to eq original_title
342
+ end
343
+ end
344
+ end
345
+
346
+ end