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,51 @@
1
+ module Calagator
2
+
3
+ class Venue < ActiveRecord::Base
4
+ class Geocoder < Struct.new(:venue, :geo)
5
+ cattr_accessor(:perform_geocoding) { true }
6
+ class << self
7
+ alias_method :perform_geocoding?, :perform_geocoding
8
+ end
9
+
10
+ def self.geocode(venue)
11
+ new(venue).geocode
12
+ end
13
+
14
+ def geocode
15
+ return true unless should_geocode?
16
+
17
+ self.geo = GeoKit::Geocoders::MultiGeocoder.geocode(venue.geocode_address)
18
+ if geo.success
19
+ venue.latitude = geo.lat
20
+ venue.longitude = geo.lng
21
+ venue.street_address = geo.street_address if venue.street_address.blank?
22
+ venue.locality = geo.city if venue.locality.blank?
23
+ venue.region = geo.state if venue.region.blank?
24
+ venue.postal_code = geo.zip if venue.postal_code.blank?
25
+ venue.country = geo.country_code if venue.country.blank?
26
+ end
27
+
28
+ log
29
+ end
30
+
31
+ private
32
+
33
+ def should_geocode?
34
+ [
35
+ self.class.perform_geocoding?,
36
+ (venue.location.blank? || venue.force_geocoding == "1"),
37
+ venue.geocode_address.present?,
38
+ venue.duplicate_of.blank?
39
+ ].all?
40
+ end
41
+
42
+ def log
43
+ venue_id = venue.new_record? ? "new record" : "record #{venue.id}"
44
+ status = geo.success ? "was successful" : "failed"
45
+ message = "Venue#add_geocoding for #{venue} #{status}, response was: #{geo.inspect}"
46
+ Rails.logger.info message
47
+ end
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,63 @@
1
+ module Calagator
2
+
3
+ class Venue < ActiveRecord::Base
4
+ class Search < Struct.new(:tag, :query, :wifi, :all, :closed, :include_closed)
5
+ def initialize attributes = {}
6
+ members.each do |key|
7
+ send "#{key}=", attributes[key]
8
+ end
9
+ end
10
+
11
+ def venues
12
+ @venues ||= if query
13
+ Venue.search(query, include_closed: include_closed, wifi: wifi)
14
+ else
15
+ base.business.wifi_status.search.scope
16
+ end
17
+ end
18
+
19
+ def most_active_venues
20
+ base.business.wifi_status.scope.order('events_count DESC').limit(10)
21
+ end
22
+
23
+ def newest_venues
24
+ base.business.wifi_status.scope.order('created_at DESC').limit(10)
25
+ end
26
+
27
+ def results?
28
+ query || tag || all
29
+ end
30
+
31
+ protected
32
+
33
+ def base
34
+ @scope = Venue.non_duplicates
35
+ self
36
+ end
37
+
38
+ def business
39
+ if closed
40
+ @scope = @scope.out_of_business
41
+ elsif !include_closed
42
+ @scope = @scope.in_business
43
+ end
44
+ self
45
+ end
46
+
47
+ def wifi_status
48
+ @scope = @scope.with_public_wifi if wifi
49
+ self
50
+ end
51
+
52
+ def search
53
+ @scope = @scope.tagged_with(tag) if tag.present? # searching by tag
54
+ self
55
+ end
56
+
57
+ def scope
58
+ @scope
59
+ end
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,24 @@
1
+ module Calagator
2
+
3
+ class Venue < ActiveRecord::Base
4
+ class SearchEngine
5
+ cattr_accessor(:kind) { :sql }
6
+
7
+ def self.search(*args)
8
+ search_engine.search(*args)
9
+ end
10
+
11
+ def self.use(kind)
12
+ self.kind = kind
13
+ search_engine.configure if search_engine.respond_to?(:configure)
14
+ end
15
+
16
+ private_class_method
17
+
18
+ def self.search_engine
19
+ kind == :sunspot ? ApacheSunspot : Sql
20
+ end
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,85 @@
1
+ module Calagator
2
+
3
+ class Venue < ActiveRecord::Base
4
+ class SearchEngine
5
+ class ApacheSunspot < Struct.new(:query, :opts)
6
+ # Return an Array of non-duplicate Venue instances matching the search +query+..
7
+ #
8
+ # Options:
9
+ # * :order => How to order the entries? Defaults to :score. Permitted values:
10
+ # * :score => Sort with most relevant matches first
11
+ # * :name => Sort by event title
12
+ # * :title => same as :name
13
+ # * :limit => Maximum number of entries to return. Defaults to 50.
14
+ # * :wifi => Require wifi
15
+ # * :include_closed => Include closed venues? Defaults to false.
16
+ def self.search(*args)
17
+ new(*args).search
18
+ end
19
+
20
+ def self.configure
21
+ Venue.searchable do
22
+ text :title, :default_boost => 3
23
+ string :title
24
+ text :description
25
+ text :address
26
+ text :street_address
27
+ text :postal_code
28
+ text :locality
29
+ text :region
30
+ text :tag_list, :default_boost => 3
31
+ text :url
32
+ boolean :closed
33
+ boolean :wifi
34
+ boolean :duplicate_for_solr do |record|
35
+ record.duplicate_of_id.present?
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.configured?
41
+ Venue.respond_to?(:solr_search)
42
+ end
43
+
44
+ def initialize(*args)
45
+ super
46
+ self.class.configure unless self.class.configured?
47
+ end
48
+
49
+ def search
50
+ Venue.solr_search do
51
+ keywords query
52
+ order_by *order
53
+ with :duplicate_for_solr, false
54
+ with :wifi, true if wifi
55
+ with :closed, false unless include_closed
56
+ end.results.take(limit)
57
+ end
58
+
59
+ private
60
+
61
+ def order
62
+ case opts[:order].try(:to_sym)
63
+ when :name, :title
64
+ [:title, :asc]
65
+ else
66
+ [:score, :desc]
67
+ end
68
+ end
69
+
70
+ def wifi
71
+ opts[:wifi]
72
+ end
73
+
74
+ def include_closed
75
+ opts[:include_closed]
76
+ end
77
+
78
+ def limit
79
+ opts[:limit] || 50
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,68 @@
1
+ module Calagator
2
+
3
+ class Venue < ActiveRecord::Base
4
+ class SearchEngine
5
+ class Sql < Struct.new(:query, :opts)
6
+ def self.search(*args)
7
+ new(*args).search
8
+ end
9
+
10
+ def search
11
+ base.keywords.non_duplicates.with_wifi.in_business.order.limit.scope.to_a
12
+ end
13
+
14
+ protected
15
+
16
+ attr_accessor :scope
17
+
18
+ def base
19
+ column_names = Venue.column_names.map { |name| "venues.#{name}" }
20
+ @scope = Venue.scoped
21
+ .group(column_names)
22
+ .joins("LEFT OUTER JOIN taggings on taggings.taggable_id = venues.id AND taggings.taggable_type LIKE '%Venue'")
23
+ .joins("LEFT OUTER JOIN tags ON tags.id = taggings.tag_id")
24
+ self
25
+ end
26
+
27
+ def keywords
28
+ query_conditions = @scope
29
+ .where(['LOWER(title) LIKE ?', "%#{query.downcase}%"])
30
+ .where(['LOWER(description) LIKE ?', "%#{query.downcase}%"])
31
+
32
+ query_conditions = query.split.inject(query_conditions) do |query_conditions, keyword|
33
+ query_conditions.where(['LOWER(tags.name) = ?', keyword])
34
+ end
35
+
36
+ @scope = @scope.where(query_conditions.where_values.join(' OR '))
37
+ self
38
+ end
39
+
40
+ def non_duplicates
41
+ @scope = @scope.non_duplicates
42
+ self
43
+ end
44
+
45
+ def order
46
+ @scope = @scope.order('LOWER(venues.title) ASC')
47
+ self
48
+ end
49
+
50
+ def limit
51
+ @scope = @scope.limit(opts[:limit] || 50)
52
+ self
53
+ end
54
+
55
+ def with_wifi
56
+ @scope = @scope.with_public_wifi if opts[:wifi]
57
+ self
58
+ end
59
+
60
+ def in_business
61
+ @scope = @scope.in_business unless opts[:include_closed]
62
+ self
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ end
File without changes
@@ -0,0 +1,37 @@
1
+ # = CacheObserver
2
+ #
3
+ # Expires caches.
4
+ module Calagator
5
+
6
+ class CacheObserver < ActiveRecord::Observer
7
+ observe Event, Venue
8
+
9
+ #---[ Unique methods ]--------------------------------------------------
10
+
11
+ # Returns a cache key string for the day, e.g. "20080730". It's used
12
+ # primarily by the #cache_if calls in views. The optional +request+ object
13
+ # provides a HTTP_HOST so that caching can be done for a particular hostname.
14
+ def self.daily_key_for(name, request=nil)
15
+ return "#{name}@#{Time.now.strftime('%Y%m%d')}"
16
+ end
17
+
18
+ # Expires all cached data.
19
+ def self.expire_all
20
+ Rails.logger.info "CacheObserver::expire_all: invoked"
21
+ Rails.cache.clear
22
+ end
23
+
24
+ #---[ Triggers ]--------------------------------------------------------
25
+
26
+ def after_save(record)
27
+ Rails.logger.info "CacheObserver#after_save: invoked"
28
+ self.class.expire_all
29
+ end
30
+
31
+ def after_destroy(record)
32
+ Rails.logger.info "CacheObserver#after_destroy: invoked"
33
+ self.class.expire_all
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,28 @@
1
+ <h2>Lock Events</h2>
2
+
3
+ <p>Locking an event prevents editing or deleting it.</p>
4
+
5
+ <table style="width: 60%; border: 1px solid black;">
6
+ <thead>
7
+ <tr style="background-color: #ccc;">
8
+ <th>Event name</th>
9
+ <th>Date</th>
10
+ <th>&nbsp;</th>
11
+ </tr>
12
+ </thead>
13
+ <tbody>
14
+ <% @events.each do |event| %>
15
+ <tr>
16
+ <td style="border: 1px solid gray"><%= link_to event.title, event_path(event) %></td>
17
+ <td style="border: 1px solid gray"><%= event.start_time %></td>
18
+ <td style="border: 1px solid gray">
19
+ <% if event.locked %>
20
+ <%= button_to "Unlock", lock_event_path(:event_id => event.id) %>
21
+ <% else %>
22
+ <%= button_to "Lock", lock_event_path(:event_id => event.id) %>
23
+ <% end %>
24
+ </td>
25
+ </tr>
26
+ <% end %>
27
+ </tbody>
28
+ </table>
@@ -0,0 +1,8 @@
1
+ <h2>Calagator Admin Tools</h2>
2
+
3
+ <ul>
4
+ <li><%= link_to "Recent changes", '/changes' %></li>
5
+ <li><%= link_to "Squash duplicate events", duplicates_events_path %></li>
6
+ <li><%= link_to "Squash duplicate vennues", duplicates_venues_path %></li>
7
+ <li><%= link_to "Lock events", admin_events_path %></li>
8
+ </ul>
@@ -0,0 +1,62 @@
1
+ <div class="vevent">
2
+ <h1 class="summary"><%= event.title %></h1>
3
+ <div class='date'><%= normalize_time(event) %></div>
4
+
5
+ <% unless event.venue.blank? %>
6
+ <div class="location vcard">
7
+ <a href='<%= url_for venue_path(event.venue) %>' class='url'>
8
+ <span class='fn org'><%= event.venue.title %></span>
9
+ </a>
10
+ <div class="adr">
11
+ <% if !event.venue.street_address.blank? %>
12
+ <div class="street-address"><%= event.venue.street_address %></div>
13
+ <% end %>
14
+ <% if !event.venue.locality.blank? %>
15
+ <span class="locality"><%= event.venue.locality %></span>
16
+ <% end %>
17
+ <% if !event.venue.region.blank? %>
18
+ , <span class="region"><%= event.venue.region %></span>
19
+ <% end %>
20
+ <% if !event.venue.postal_code.blank? %>
21
+ <span class="postal-code"><%= event.venue.postal_code %></span>
22
+ <% end %>
23
+ <% if !event.venue.country.blank? %>
24
+ <div class='country-name'><%= event.venue.country %><div>
25
+ <% end %>
26
+ <% if event.venue && event.venue.has_full_address? %>
27
+ (<a href='<%=google_maps_url(event.venue.full_address)%>'>map</a>)
28
+ <% end %>
29
+ </div>
30
+ </div>
31
+ <% end %>
32
+
33
+ <% unless event.description.blank? %>
34
+ <div class="description">
35
+ <%= format_description(event.description) %>
36
+ </div>
37
+ <% end %>
38
+
39
+ <% unless event.url.blank? %>
40
+ <h3>Links</h3>
41
+ <ul>
42
+ <li><%= link_to "Website", event.url, :class => "url" %></li>
43
+ </ul>
44
+ <% end %>
45
+
46
+ <% unless event.tags.blank? %>
47
+ <div class="tags">
48
+ <h3>Tags</h3>
49
+ <p><%= tag_links_for event %></p>
50
+ </div>
51
+ <% end %>
52
+
53
+ <div class='single_view_right'>
54
+ <a href='<%= event_url(event) %>.ics'>Download to iCal</a>
55
+ <div id='edit_link'>
56
+ <p>You can <%= link_to "edit this event", edit_event_url(event) %>.</p>
57
+ </div>
58
+ <div id='metadata'>
59
+ <%= datestamp(event) %>
60
+ </div>
61
+ </div>
62
+ </div>
@@ -0,0 +1,63 @@
1
+ <%
2
+ # USAGE: Display a form for creating or editing an event.
3
+ #
4
+ # VARIABLES:
5
+ # * event: An new or existing Event instance.
6
+ %>
7
+
8
+ <% if params[:preview] && event.valid? -%>
9
+ <div id="event_preview">
10
+ <h2>Preview your event</h2>
11
+
12
+ <%= render :partial => "item", :locals => { :event => event, :has_contentbar => false } %>
13
+ </div>
14
+ <% end -%>
15
+
16
+ <%= semantic_form_for(event, :html => {:id => 'event_form', :class => 'standard_form', :novalidate => 'novalidate'}) do |f| %>
17
+ <%= f.inputs :name => "#{'Editing: ' unless event.new_record?} <em>#{event.title}</em>".html_safe do %>
18
+ <%= f.input :title, :label => "Event Name" %>
19
+ <% focus_on '#event_title' %>
20
+ <li class="string input required stringish" id="event_venue_title_input">
21
+ <%= label_tag :venue_name, "Venue", :class => 'label' %>
22
+ <%= text_field_tag 'venue_name', '', :class=> 'autocomplete', :value => !event.venue.nil? ? event.venue.title : params[:venue_name] %>
23
+ <%= hidden_field(:event, :venue_id, :value => (!event.venue.nil? ? event.venue.id : params[:event_venue_id])) %>
24
+ <%= image_tag "spinner.gif", :id=> "event_venue_loading", :style => "display: none; margin-left: 0.5em;" %>
25
+ <p class='inline-hints'>
26
+ Type the name of a venue to search. If it doesn't exist, we'll create it for you.
27
+ </p>
28
+ <li>
29
+ <li id="event_times" class='input'>
30
+ <%= f.label :when, "When", :class => 'label' %>
31
+ <%
32
+ present = Time.now
33
+ # Provide initial default values for the start_time and end_time rounded to the upcoming hour.
34
+ event.start_time ||= present + (60.minutes - present.min.minutes) - present.sec
35
+ event.end_time ||= present + (60.minutes - present.min.minutes) - present.sec + 1.hour
36
+ %>
37
+ <%= text_field_tag 'start_date', event.start_time.to_date, :id => 'date_start', :class => 'date_picker' %>
38
+ <%= text_field_tag 'start_time', event.start_time.strftime('%I:%M %p'), :id => 'time_start', :class => 'time_picker' %>
39
+ <span class="to">to</span>
40
+ <%= text_field_tag 'end_time', event.end_time.strftime('%I:%M %p'), :id => 'time_end', :class => 'time_picker' %>
41
+ <%= text_field_tag 'end_date', event.end_time.to_date, :id => 'date_end', :class => 'date_picker' %>
42
+ <%= f.semantic_errors :start_date, :start_time, :end_date, :end_time %>
43
+ </li>
44
+ <%= f.input :url, :label => 'Website' %>
45
+ <%= f.input :description,
46
+ :input_html => {:rows => 12},
47
+ :hint => "(Format text using #{link_to("Markdown", "http://www.simpleeditions.com/59001/markdown-an-introduction", :target => "_blank")} and HTML)".html_safe %>
48
+ <%= f.input :venue_details, :required => false,
49
+ :input_html => {:rows => 4},
50
+ :hint => "(Event-specific details like the room number, or who to call to be let into the building.)" %>
51
+ <%= f.input :tag_list, :required => false,
52
+ :label => 'Tags',
53
+ :hint => "(Tags are comma-separated keywords that make it easier to find your event and boost its position in searches. You can also use a tag like <code>plancast:plan=ABCD</code> to associate this with a particular <a href='http://plancast.com/'>Plancast</a> event, or a tag like <code>epdx:group=1234</code> to associate the event with a particular <a href='http://epdx.org/'>ePDX</a> group)".html_safe %>
54
+ <li class='trap'>
55
+ <%= label 'trap', 'field', "Leave this field blank or we'll think you're a robot." %>
56
+ <%= text_field_tag 'trap_field', params[:trap_field] %>
57
+ </li>
58
+ <% end %>
59
+ <%= f.actions do %>
60
+ <%= f.action :submit, :label => "Preview", :button_html => {:name => 'preview'} %>
61
+ <%= f.action :submit %>
62
+ <% end %>
63
+ <% end %>