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,69 @@
1
+ require 'spec_helper'
2
+
3
+ module Calagator
4
+
5
+ describe Source::Parser::Meetup, :type => :model do
6
+ describe "with a meetup.com API key in secrets.yml" do
7
+ before do
8
+ SECRETS.meetup_api_key = "foo"
9
+ end
10
+
11
+ before(:each) do
12
+ meetup_url = "http://www.meetup.com/pdxpython/events/ldhnqyplbnb/"
13
+ api_url = "https://api.meetup.com/2/event/ldhnqyplbnb?key=foo&sign=true"
14
+
15
+ stub_request(:get, api_url).to_return(body: read_sample('meetup.json'), headers: { content_type: "application/json" })
16
+ @events = Source::Parser::Meetup.to_events(url: meetup_url)
17
+ @event = @events.first
18
+ end
19
+
20
+ it "should find one event" do
21
+ expect(@events.size).to eq 1
22
+ end
23
+
24
+ it "should set event details" do
25
+ expect(@event.title).to eq "eLearning Network Meetup"
26
+ expect(@event.start_time).to eq Time.zone.parse("Thu Aug 11 00:00:00 UTC 2011")
27
+ end
28
+
29
+ it "should tag Meetup events with automagic machine tags" do
30
+ expect(@event.tag_list).to eq ["meetup:event=ldhnqyplbnb", "meetup:group=eLearningNetwork"]
31
+ end
32
+
33
+ it "should populate a venue when structured data is provided" do
34
+ expect(@event.venue).to be_a Venue
35
+ expect(@event.venue.title).to eq "Green Dragon Bistro and Brewpub"
36
+ expect(@event.venue.street_address).to eq "928 SE 9th Ave"
37
+ expect(@event.venue.tag_list).to eq ["meetup:venue=774133"]
38
+ end
39
+ end
40
+
41
+ context "without a meetup API key" do
42
+ before do
43
+ SECRETS.meetup_api_key = nil
44
+ end
45
+
46
+ before(:each) do
47
+ url = "http://www.meetup.com/pdxpython/events/ldhnqyplbnb/ical"
48
+ stub_request(:get, url).to_return(body: read_sample('meetup.ics'))
49
+ @events = Source::Parser::Meetup.to_events(url: url)
50
+ @event = @events.first
51
+ end
52
+
53
+ it "should find one event" do
54
+ expect(@events.size).to eq 1
55
+ end
56
+
57
+ it "should set event details" do
58
+ expect(@event.title).to eq "eLearning Network Meetup"
59
+ expect(@event.start_time.to_date).to eq Date.parse("3011-08-11")
60
+ end
61
+
62
+ it "should populate a venue when structured data is provided" do
63
+ expect(@event.venue).to be_a Venue
64
+ expect(@event.venue.title).to eq "Green Dragon Bistro and Brewpub"
65
+ end
66
+ end
67
+ end
68
+
69
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ module Calagator
4
+
5
+ describe Source::Parser::Plancast, :type => :model do
6
+
7
+ context do
8
+ before(:each) do
9
+ plancast_url = 'http://plancast.com/p/3cos/indiewebcamp'
10
+ api_url = 'http://api.plancast.com/02/plans/show.json?extensions=place&plan_id=3cos'
11
+ stub_request(:get, api_url).to_return(body: read_sample('plancast.json'), headers: { content_type: "application/json" })
12
+
13
+ @events = Source::Parser::Plancast.to_events(url: plancast_url)
14
+ @event = @events.first
15
+ end
16
+
17
+ it "should find one event" do
18
+ expect(@events.size).to eq 1
19
+ end
20
+
21
+ it "should set event details" do
22
+ expect(@event.title).to eq "IndieWebCamp"
23
+ expect(@event.start_time).to eq Time.zone.parse("Sat, 25 Jun 2011 00:00:00")
24
+ end
25
+
26
+ it "should tag Plancast events with automagic machine tags" do
27
+ expect(@event.tag_list).to eq ["plancast:plan=3cos"]
28
+ end
29
+
30
+ it "should populate a venue when structured data is provided" do
31
+ expect(@event.venue.title).to eq "Urban Airship"
32
+ expect(@event.venue.address).to eq "334 Northwest 11th Avenue, Portland, Oregon, United States"
33
+ expect(@event.venue.tag_list).to eq ["plancast:place=1520153"]
34
+ end
35
+ end
36
+
37
+ context "with missing venue" do
38
+ before(:each) do
39
+ plancast_url = 'http://plancast.com/p/3cos/indiewebcamp'
40
+ api_url = 'http://api.plancast.com/02/plans/show.json?extensions=place&plan_id=3cos'
41
+ stub_request(:get, api_url).to_return(body: read_sample('plancast_with_missing_venue.json'), headers: { content_type: "application/json" })
42
+ @events = Source::Parser::Plancast.to_events(url: plancast_url)
43
+ @event = @events.first
44
+ end
45
+
46
+ it "uses fallback when no venue is detected" do
47
+ expect(@event.venue.title).to eq "The Center of the Earth"
48
+ expect(@event.venue.address).to be_blank
49
+ end
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,238 @@
1
+ require 'spec_helper'
2
+
3
+ module Calagator
4
+
5
+ describe Source::Parser, "when reading content", :type => :model do
6
+ it "should read from a normal URL" do
7
+ stub_request(:get, "http://a.real/~url").to_return(body: "42")
8
+ expect(Source::Parser.read_url("http://a.real/~url")).to eq "42"
9
+ end
10
+
11
+ it "should raise an error when unauthorized" do
12
+ stub_request(:get, "http://a.private/~url").to_return(status: [401, "Forbidden"])
13
+ expect {
14
+ Source::Parser.read_url("http://a.private/~url")
15
+ }.to raise_error Source::Parser::HttpAuthenticationRequiredError
16
+ end
17
+ end
18
+
19
+ describe Source::Parser, "when subclassing", :type => :model do
20
+ it "should demand that #to_events is implemented" do
21
+ expect{ Source::Parser.new.to_events }.to raise_error NotImplementedError
22
+ end
23
+ end
24
+
25
+ describe Source::Parser, "when parsing events", :type => :model do
26
+ it "should have site-specific parsers first, then generics" do
27
+ expect(Source::Parser.parsers.to_a).to eq [
28
+ Source::Parser::Facebook,
29
+ Source::Parser::Meetup,
30
+ Source::Parser::Plancast,
31
+ Source::Parser::Hcal,
32
+ Source::Parser::Ical,
33
+ ]
34
+ end
35
+
36
+ it "should use first successful parser's results" do
37
+ events = [double]
38
+
39
+ body = {
40
+ name: "event",
41
+ start_time: "2010-01-01 12:00:00 UTC",
42
+ end_time: "2010-01-01 13:00:00 UTC"
43
+ }.to_json
44
+ stub_request(:get, "http://graph.facebook.com/omg").to_return(body: body, headers: { content_type: "application/json" })
45
+
46
+ expect(Source::Parser.to_events(url: "http://www.facebook.com/events/omg")).to have(1).event
47
+ end
48
+ end
49
+
50
+ describe Source::Parser, "checking duplicates when importing", :type => :model do
51
+ describe "with two identical events" do
52
+ before :each do
53
+ @venue_size_before_import = Venue.count
54
+ url = "http://mysample.hcal/"
55
+ @cal_source = Source.new(title: "Calendar event feed", url: url)
56
+ @cal_content = (%{
57
+ <div class="vevent">
58
+ <abbr class="dtstart" title="20080714"></abbr>
59
+ <abbr class="summary" title="Bastille Day"></abbr>
60
+ <abbr class="location" title="Arc de Triomphe"></abbr>
61
+ </div>
62
+ <div class="vevent">
63
+ <abbr class="dtstart" title="20080714"></abbr>
64
+ <abbr class="summary" title="Bastille Day"></abbr>
65
+ <abbr class="location" title="Arc de Triomphe"></abbr>
66
+ </div>})
67
+ stub_request(:get, url).to_return(body: @cal_content)
68
+ @events = @cal_source.to_events
69
+ @created_events = @cal_source.create_events!(:skip_old => false)
70
+ end
71
+
72
+ it "should only parse one event" do
73
+ expect(@events.size).to eq 1
74
+ end
75
+
76
+ it "should create only one event" do
77
+ expect(@created_events.size).to eq 1
78
+ end
79
+
80
+ it "should create only one venue" do
81
+ expect(Venue.count).to eq @venue_size_before_import + 1
82
+ end
83
+ end
84
+
85
+ describe "with an event" do
86
+ it "should retrieve an existing event if it's an exact duplicate" do
87
+ url = "http://mysample.hcal/"
88
+ hcal_source = Source.new(title: "Calendar event feed", url: url)
89
+ stub_request(:get, url).to_return(body: read_sample('hcal_event_duplicates_fixture.xml'))
90
+
91
+ event = hcal_source.to_events.first
92
+ event.save!
93
+
94
+ event2 = hcal_source.to_events.first
95
+ expect(event2).not_to be_a_new_record
96
+ end
97
+
98
+ it "an event with a orphaned exact duplicate should should remove duplicate marking" do
99
+ orphan = Event.create!(:title => "orphan", :start_time => Time.parse("July 14 2008"), :duplicate_of_id => 7142008 )
100
+ cal_content = %(
101
+ <div class="vevent">
102
+ <abbr class="summary" title="orphan"></abbr>
103
+ <abbr class="dtstart" title="20080714"></abbr>
104
+ </div>
105
+ )
106
+ url = "http://mysample.hcal/"
107
+ stub_request(:get, url).to_return(body: cal_content)
108
+
109
+ cal_source = Source.new(title: "Calendar event feed", url: url)
110
+ imported_event = cal_source.create_events!(:skip_old => false).first
111
+ expect(imported_event).not_to be_marked_as_duplicate
112
+ end
113
+ end
114
+
115
+ describe "should create two events when importing two non-identical events" do
116
+ # This behavior is tested under
117
+ # describe Source::Parser::Hcal, "with hCalendar events" do
118
+ # 'it "should parse a page with multiple events" '
119
+ end
120
+
121
+ describe "two identical events with different venues" do
122
+ before(:each) do
123
+ cal_content = %(
124
+ <div class="vevent">
125
+ <abbr class="dtstart" title="20080714"></abbr>
126
+ <abbr class="summary" title="Bastille Day"></abbr>
127
+ <abbr class="location" title="Arc de Triomphe"></abbr>
128
+ </div>
129
+ <div class="vevent">
130
+ <abbr class="dtstart" title="20080714"></abbr>
131
+ <abbr class="summary" title="Bastille Day"></abbr>
132
+ <abbr class="location" title="Bastille"></abbr>
133
+ </div>
134
+ )
135
+ url = "http://mysample.hcal/"
136
+ stub_request(:get, url).to_return(body: cal_content)
137
+
138
+ cal_source = Source.new(title: "Calendar event feed", url: url)
139
+ @parsed_events = cal_source.to_events
140
+ @created_events = cal_source.create_events!(:skip_old => false)
141
+ end
142
+
143
+ it "should parse two events" do
144
+ expect(@parsed_events.size).to eq 2
145
+ end
146
+
147
+ it "should create two events" do
148
+ expect(@created_events.size).to eq 2
149
+ end
150
+
151
+ it "should have different venues for the parsed events" do
152
+ expect(@parsed_events[0].venue).not_to eq @parsed_events[1].venue
153
+ end
154
+
155
+ it "should have different venues for the created events" do
156
+ expect(@created_events[0].venue).not_to eq @created_events[1].venue
157
+ end
158
+ end
159
+
160
+ it "should use an existing venue when importing an event whose venue matches a squashed duplicate" do
161
+ dummy_source = Source.create!(:title => "Dummy", :url => "http://IcalEventWithSquashedVenue.com/")
162
+ master_venue = Venue.create!(:title => "Master")
163
+ squashed_venue = Venue.create!(
164
+ :title => "Squashed Duplicate Venue",
165
+ :duplicate_of_id => master_venue.id)
166
+
167
+ cal_content = %(
168
+ <div class="vevent">
169
+ <abbr class="dtstart" title="20090117"></abbr>
170
+ <abbr class="summary" title="Event with cloned venue"></abbr>
171
+ <abbr class="location" title="Squashed Duplicate Venue"></abbr>
172
+ </div>
173
+ )
174
+
175
+ url = "http://mysample.hcal/"
176
+ stub_request(:get, url).to_return(body: cal_content)
177
+
178
+ source = Source.new(title: "Event with squashed venue", url: url)
179
+
180
+ event = source.to_events(:skip_old => false).first
181
+ expect(event.venue.title).to eq "Master"
182
+ end
183
+
184
+ it "should use an existing venue when importing an event with a matching machine tag that describes a venue" do
185
+ venue = Venue.create!(:title => "Custom Urban Airship", :tag_list => "plancast:place=1520153")
186
+
187
+ plancast_url = 'http://plancast.com/p/3cos/indiewebcamp'
188
+ api_url = 'http://api.plancast.com/02/plans/show.json?extensions=place&plan_id=3cos'
189
+ stub_request(:get, api_url).to_return(body: read_sample('plancast.json'), headers: { content_type: "application/json" })
190
+
191
+ source = Source.new(title: "Event with duplicate machine-tagged venue", url: plancast_url)
192
+
193
+ event = source.to_events(:skip_old => false).first
194
+
195
+ expect(event.venue).to eq venue
196
+ end
197
+
198
+ describe "choosing parsers by matching URLs" do
199
+ { "Calagator::Source::Parser::Plancast" => "http://plancast.com/p/3cos/indiewebcamp",
200
+ "Calagator::Source::Parser::Meetup" => "http://www.meetup.com/pdxweb/events/23287271/" }.each do |parser_name, url|
201
+
202
+ it "should only invoke the #{parser_name} parser when given #{url}" do
203
+ parser = parser_name.constantize
204
+ expect_any_instance_of(parser).to receive(:to_events).and_return([Event.new])
205
+ Source::Parser.parsers.reject{|p| p == parser }.each do |other_parser|
206
+ expect_any_instance_of(other_parser).not_to receive :to_events
207
+ end
208
+
209
+ stub_request(:get, url)
210
+ Source.new(:title => parser_name, :url => url).to_events
211
+ end
212
+ end
213
+ end
214
+ end
215
+
216
+ describe Source::Parser, "labels", :type => :model do
217
+ it "should have labels" do
218
+ expect(Source::Parser.labels).not_to be_blank
219
+ end
220
+
221
+ it "should have labels for each parser" do
222
+ expect(Source::Parser.labels.size).to eq Source::Parser.parsers.size
223
+ end
224
+
225
+ it "should use the label of the parser, as a string" do
226
+ label = Source::Parser.parsers.first.label.to_s
227
+ expect(Source::Parser.labels).to include label
228
+ end
229
+
230
+ it "should have sorted labels" do
231
+ labels = Source::Parser.labels
232
+ sorted = labels.sort_by(&:downcase)
233
+
234
+ expect(labels).to eq sorted
235
+ end
236
+ end
237
+
238
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+
3
+ module Calagator
4
+
5
+ describe Source, "in general", :type => :model do
6
+ before(:each) do
7
+ @event = mock_model(Event,
8
+ :title => "Title",
9
+ :description => "Description",
10
+ :url => "http://my.url/",
11
+ :start_time => Time.now + 1.day,
12
+ :end_time => nil,
13
+ :venue => nil,
14
+ :duplicate_of_id => nil)
15
+ end
16
+
17
+ it "should create events for source from URL" do
18
+ expect(@event).to receive(:save!)
19
+
20
+ source = Source.new(:url => "http://my.url/")
21
+ expect(source).to receive(:to_events).and_return([@event])
22
+ expect(source.create_events!).to eq [@event]
23
+ end
24
+
25
+ it "should fail to create events for invalid sources" do
26
+ source = Source.new(:url => '\not valid/')
27
+ expect{ source.to_events }.to raise_error(ActiveRecord::RecordInvalid, /Url has invalid format/i)
28
+ end
29
+ end
30
+
31
+ describe Source, "when reading name", :type => :model do
32
+ before(:each) do
33
+ @title = "title"
34
+ @url = "http://my.url/"
35
+ end
36
+
37
+ before(:each) do
38
+ @source = Source.new
39
+ end
40
+
41
+ it "should return nil if no title is available" do
42
+ expect(@source.name).to be_nil
43
+ end
44
+
45
+ it "should use title if available" do
46
+ @source.title = @title
47
+ expect(@source.name).to eq @title
48
+ end
49
+
50
+ it "should use URL if available" do
51
+ @source.url = @url
52
+ expect(@source.name).to eq @url
53
+ end
54
+
55
+ it "should prefer to use title over URL if both are available" do
56
+ @source.title = @title
57
+ @source.url = @url
58
+
59
+ expect(@source.name).to eq @title
60
+ end
61
+ end
62
+
63
+ describe Source, "when parsing URLs", :type => :model do
64
+ before(:each) do
65
+ @http_url = 'http://upcoming.yahoo.com/event/390164/'
66
+ @ical_url = 'webcal://upcoming.yahoo.com/event/390164/'
67
+ @base_url = 'upcoming.yahoo.com/event/390164/'
68
+ end
69
+
70
+ before(:each) do
71
+ @source = Source.new
72
+ end
73
+
74
+ it "should not modify supported url schemes" do
75
+ @source.url = @http_url
76
+
77
+ expect(@source.url).to eq @http_url
78
+ end
79
+
80
+ it "should substitute http for unsupported url schemes" do
81
+ @source.url = @ical_url
82
+
83
+ expect(@source.url).to eq @http_url
84
+ end
85
+
86
+ it "should add the http prefix to urls without one" do
87
+ @source.url = @base_url
88
+
89
+ expect(@source.url).to eq @http_url
90
+ end
91
+
92
+ it "should strip leading and trailing whitespace from URL" do
93
+ source = Source.new
94
+ source.url = " #{@http_url} "
95
+ expect(source.url).to eq @http_url
96
+ end
97
+
98
+ it "should be invalid if given invalid URL" do
99
+ source = Source.new
100
+ source.url = '\O.o/'
101
+ expect(source.url).to be_nil
102
+ expect(source).not_to be_valid
103
+ end
104
+ end
105
+
106
+ describe Source, "find_or_create_from", :type => :model do
107
+ before do
108
+ @url = "http://foo.bar"
109
+ end
110
+
111
+ it "should return new, unsaved record if given no arguments" do
112
+ source = Source.find_or_create_from()
113
+
114
+ expect(source).to be_a_new_record
115
+ end
116
+
117
+ it "should return an existing or newly-created record" do
118
+ record = Source.new(:url => @url)
119
+ expect(Source).to receive(:find_or_create_by_url).and_return(record)
120
+
121
+ result = Source.find_or_create_from(:url => @url)
122
+ expect(record).to eq result
123
+ end
124
+
125
+ it "should set re-import flag if given" do
126
+ record = Source.new(:url => @url)
127
+ expect(record).to receive(:save)
128
+ expect(Source).to receive(:find_or_create_by_url).and_return(record)
129
+
130
+ result = Source.find_or_create_from(:url => @url, :reimport => true)
131
+ expect(result.reimport).to be_truthy
132
+ end
133
+ end
134
+
135
+ end