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.
- checksums.yaml +7 -0
- data/LICENSE.txt +23 -0
- data/README.md +75 -0
- data/Rakefile +12 -0
- data/app/assets/images/confused-alligator-sm.png +0 -0
- data/app/assets/images/disk.png +0 -0
- data/app/assets/images/edit.png +0 -0
- data/app/assets/images/external_sites/epdx.png +0 -0
- data/app/assets/images/external_sites/external.gif +0 -0
- data/app/assets/images/external_sites/facebook.png +0 -0
- data/app/assets/images/external_sites/foursquare.png +0 -0
- data/app/assets/images/external_sites/gowalla.png +0 -0
- data/app/assets/images/external_sites/lanyrd.png +0 -0
- data/app/assets/images/external_sites/meetup.png +0 -0
- data/app/assets/images/external_sites/plancast.png +0 -0
- data/app/assets/images/external_sites/shizzow.png +0 -0
- data/app/assets/images/external_sites/upcoming.png +0 -0
- data/app/assets/images/external_sites/yelp.png +0 -0
- data/app/assets/images/feed.png +0 -0
- data/app/assets/images/heart.png +0 -0
- data/app/assets/images/icon_ical.gif +0 -0
- data/app/assets/images/information.png +0 -0
- data/app/assets/images/nav_marker.gif +0 -0
- data/app/assets/images/nav_marker.png +0 -0
- data/app/assets/images/plus.png +0 -0
- data/app/assets/images/redx.png +0 -0
- data/app/assets/images/site-icon.png +0 -0
- data/app/assets/images/spinner.gif +0 -0
- data/app/assets/images/star.png +0 -0
- data/app/assets/images/subnav_marker.gif +0 -0
- data/app/assets/images/subnav_marker.png +0 -0
- data/app/assets/images/tag_blue.png +0 -0
- data/app/assets/images/tag_icons/angular.png +0 -0
- data/app/assets/images/tag_icons/beer.png +0 -0
- data/app/assets/images/tag_icons/bitcoin.png +0 -0
- data/app/assets/images/tag_icons/free.png +0 -0
- data/app/assets/images/tag_icons/golang.png +0 -0
- data/app/assets/images/tag_icons/html.png +0 -0
- data/app/assets/images/tag_icons/javascript.png +0 -0
- data/app/assets/images/tag_icons/linux.png +0 -0
- data/app/assets/images/tag_icons/php.png +0 -0
- data/app/assets/images/tag_icons/pizza.png +0 -0
- data/app/assets/images/tag_icons/python.png +0 -0
- data/app/assets/images/tag_icons/rails.png +0 -0
- data/app/assets/images/tag_icons/ruby.png +0 -0
- data/app/assets/images/tag_icons/sass.png +0 -0
- data/app/assets/images/tag_icons/swift.png +0 -0
- data/app/assets/images/transmit_blue.png +0 -0
- data/app/assets/images/weekday_background.gif +0 -0
- data/app/assets/javascripts/calagator.js +18 -0
- data/app/assets/javascripts/calagator/forms.js +60 -0
- data/app/assets/stylesheets/calagator.scss +8 -0
- data/app/assets/stylesheets/calagator/common.scss +14 -0
- data/app/assets/stylesheets/calagator/datepicker.scss +177 -0
- data/app/assets/stylesheets/calagator/errors.css +110 -0
- data/app/assets/stylesheets/calagator/forms.scss +4 -0
- data/app/assets/stylesheets/calagator/reset.css +40 -0
- data/app/assets/stylesheets/calagator/theme.css +0 -0
- data/app/controllers/calagator/admin_controller.rb +27 -0
- data/app/controllers/calagator/application_controller.rb +82 -0
- data/app/controllers/calagator/events_controller.rb +171 -0
- data/app/controllers/calagator/site_controller.rb +35 -0
- data/app/controllers/calagator/sources_controller.rb +94 -0
- data/app/controllers/calagator/venues_controller.rb +133 -0
- data/app/controllers/calagator/versions_controller.rb +20 -0
- data/app/helpers/calagator/application_helper.rb +128 -0
- data/app/helpers/calagator/events_helper.rb +184 -0
- data/app/helpers/calagator/google_event_export_helper.rb +67 -0
- data/app/helpers/calagator/mapping_helper.rb +103 -0
- data/app/helpers/calagator/sources_helper.rb +10 -0
- data/app/helpers/calagator/tags_helper.rb +42 -0
- data/app/helpers/calagator/time_range_helper.rb +166 -0
- data/app/models/calagator/event.rb +246 -0
- data/app/models/calagator/event/cloner.rb +39 -0
- data/app/models/calagator/event/ical_renderer.rb +155 -0
- data/app/models/calagator/event/overview.rb +45 -0
- data/app/models/calagator/event/saver.rb +58 -0
- data/app/models/calagator/event/search.rb +72 -0
- data/app/models/calagator/event/search_engine.rb +28 -0
- data/app/models/calagator/event/search_engine/apache_sunspot.rb +106 -0
- data/app/models/calagator/event/search_engine/sql.rb +107 -0
- data/app/models/calagator/source.rb +115 -0
- data/app/models/calagator/source/importer.rb +46 -0
- data/app/models/calagator/source/parser.rb +128 -0
- data/app/models/calagator/source/parser/facebook.rb +67 -0
- data/app/models/calagator/source/parser/hcal.rb +108 -0
- data/app/models/calagator/source/parser/http_authentication_required_error.rb +6 -0
- data/app/models/calagator/source/parser/ical.rb +186 -0
- data/app/models/calagator/source/parser/meetup.rb +72 -0
- data/app/models/calagator/source/parser/not_found.rb +9 -0
- data/app/models/calagator/source/parser/plancast.rb +59 -0
- data/app/models/calagator/venue.rb +161 -0
- data/app/models/calagator/venue/geocoder.rb +51 -0
- data/app/models/calagator/venue/search.rb +63 -0
- data/app/models/calagator/venue/search_engine.rb +24 -0
- data/app/models/calagator/venue/search_engine/apache_sunspot.rb +85 -0
- data/app/models/calagator/venue/search_engine/sql.rb +68 -0
- data/app/models/event/search_engine/base.rb +0 -0
- data/app/observers/calagator/cache_observer.rb +37 -0
- data/app/views/calagator/admin/events.html.erb +28 -0
- data/app/views/calagator/admin/index.html.erb +8 -0
- data/app/views/calagator/events/_feed_item.html.erb +62 -0
- data/app/views/calagator/events/_form.html.erb +63 -0
- data/app/views/calagator/events/_gcal_reminder.html.erb +1 -0
- data/app/views/calagator/events/_hcal.html.erb +41 -0
- data/app/views/calagator/events/_item.html.erb +120 -0
- data/app/views/calagator/events/_list.html.erb +30 -0
- data/app/views/calagator/events/_list_item.html.erb +37 -0
- data/app/views/calagator/events/_search_section.html.erb +15 -0
- data/app/views/calagator/events/_subnav.html.erb +13 -0
- data/app/views/calagator/events/_table.html.erb +74 -0
- data/app/views/calagator/events/duplicates.html.erb +58 -0
- data/app/views/calagator/events/edit.html.erb +3 -0
- data/app/views/calagator/events/index.atom.builder +32 -0
- data/app/views/calagator/events/index.html.erb +51 -0
- data/app/views/calagator/events/index.kml.erb +20 -0
- data/app/views/calagator/events/new.html.erb +7 -0
- data/app/views/calagator/events/search.html.erb +25 -0
- data/app/views/calagator/events/show.html.erb +81 -0
- data/app/views/calagator/site/_appropriateness_message.html.erb +15 -0
- data/app/views/calagator/site/_description.html.erb +3 -0
- data/app/views/calagator/site/about.html.erb +7 -0
- data/app/views/calagator/site/defunct.html.erb +15 -0
- data/app/views/calagator/site/export.html.erb +4 -0
- data/app/views/calagator/site/index.html.erb +40 -0
- data/app/views/calagator/site/opensearch.xml.builder +8 -0
- data/app/views/calagator/sources/_subnav.html.erb +0 -0
- data/app/views/calagator/sources/edit.html.erb +12 -0
- data/app/views/calagator/sources/import.html.erb +10 -0
- data/app/views/calagator/sources/index.html.erb +22 -0
- data/app/views/calagator/sources/new.html.erb +32 -0
- data/app/views/calagator/sources/show.html.erb +16 -0
- data/app/views/calagator/venues/_form.html.erb +57 -0
- data/app/views/calagator/venues/_subnav.html.erb +10 -0
- data/app/views/calagator/venues/duplicates.html.erb +65 -0
- data/app/views/calagator/venues/edit.html.erb +3 -0
- data/app/views/calagator/venues/index.html.erb +93 -0
- data/app/views/calagator/venues/index.kml.erb +15 -0
- data/app/views/calagator/venues/map.html.erb +4 -0
- data/app/views/calagator/venues/new.html.erb +5 -0
- data/app/views/calagator/venues/show.html.erb +113 -0
- data/app/views/calagator/versions/_chooser.html.erb +28 -0
- data/app/views/calagator/versions/_edit_with_chooser.html.erb +16 -0
- data/app/views/layouts/calagator/application.html.erb +110 -0
- data/config/deploy/local.rb +37 -0
- data/config/deploy/lucca.rb +33 -0
- data/config/initializers/dates.rb +7 -0
- data/config/initializers/formtastic.rb +82 -0
- data/config/initializers/geokit.rb +74 -0
- data/config/initializers/ics_renderer.rb +6 -0
- data/config/initializers/load_tag_model_extensions.rb +3 -0
- data/config/initializers/mime_types.rb +9 -0
- data/config/initializers/observers.rb +1 -0
- data/config/initializers/search_engine.rb +4 -0
- data/config/initializers/set_default_url_host.rb +5 -0
- data/config/initializers/time_get_zone.rb +8 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +55 -0
- data/config/secrets.yml.blag +75 -0
- data/config/sunspot.yml +23 -0
- data/db/development.sqlite3 +0 -0
- data/db/development.sqlite3.bak +0 -0
- data/db/development.sqlite3.old +0 -0
- data/db/development~20111112@110950.sqlite3 +0 -0
- data/db/migrate/001_create_events.rb +17 -0
- data/db/migrate/002_create_venues.rb +17 -0
- data/db/migrate/003_create_sources.rb +16 -0
- data/db/migrate/004_add_detailed_fields_to_venue.rb +19 -0
- data/db/migrate/005_add_end_time_to_events.rb +9 -0
- data/db/migrate/006_add_source_id_to_events.rb +9 -0
- data/db/migrate/008_add_source_id_to_venues.rb +10 -0
- data/db/migrate/009_add_duplicate_of_column_to_venues.rb +9 -0
- data/db/migrate/010_add_duplicate_of_column_to_events.rb +9 -0
- data/db/migrate/011_change_lat_long_type.rb +12 -0
- data/db/migrate/012_add_source_reimport.rb +9 -0
- data/db/migrate/013_change_end_time_to_duration.rb +11 -0
- data/db/migrate/014_remove_format_type_from_source.rb +9 -0
- data/db/migrate/015_create_updates.rb +15 -0
- data/db/migrate/016_remove_next_update_from_source.rb +9 -0
- data/db/migrate/20080705163959_change_duration_to_end_time.rb +11 -0
- data/db/migrate/20080705164959_create_tags_and_taggings.rb +28 -0
- data/db/migrate/20081011181519_create_versioned_events.rb +25 -0
- data/db/migrate/20081011193124_create_versioned_venues.rb +32 -0
- data/db/migrate/20081115190515_add_rrule_to_events.rb +15 -0
- data/db/migrate/20090912082129_create_versions.rb +18 -0
- data/db/migrate/20110219205156_add_closed_flag_to_venues.rb +9 -0
- data/db/migrate/20110220001008_add_wifi_flag_to_venues.rb +9 -0
- data/db/migrate/20110220011427_add_access_notes_to_venues.rb +9 -0
- data/db/migrate/20110220031117_add_events_count_to_venues.rb +8 -0
- data/db/migrate/20110604174521_add_venue_details_to_events.rb +9 -0
- data/db/migrate/20110717231316_acts_as_taggable_on_migration.rb +50 -0
- data/db/migrate/20120709092821_cleanup.rb +14 -0
- data/db/migrate/20120831234448_specify_venues_latitude_and_longitude_precision.rb +11 -0
- data/db/migrate/20150206085809_remove_updates.rb +13 -0
- data/db/migrate/20150207231355_add_locked_status_to_events.rb +5 -0
- data/db/production.sqlite3 +0 -0
- data/db/schema.rb +102 -0
- data/db/seeds.rb +16 -0
- data/db/test.sqlite3 +0 -0
- data/db/test2.sqlite3 +0 -0
- data/lib/calagator.rb +30 -0
- data/lib/calagator/blacklist_validator.rb +69 -0
- data/lib/calagator/decode_html_entities_hack.rb +33 -0
- data/lib/calagator/duplicate_checking.rb +133 -0
- data/lib/calagator/duplicate_checking/controller_actions.rb +38 -0
- data/lib/calagator/duplicate_checking/duplicate_finder.rb +83 -0
- data/lib/calagator/duplicate_checking/duplicate_squasher.rb +74 -0
- data/lib/calagator/engine.rb +30 -0
- data/lib/calagator/strip_whitespace.rb +19 -0
- data/lib/calagator/tag_model_extensions.rb +104 -0
- data/lib/calagator/url_prefixer.rb +9 -0
- data/lib/calagator/version.rb +3 -0
- data/lib/generators/calagator/install_generator.rb +39 -0
- data/lib/generators/calagator/templates/config/calagator.rb +26 -0
- data/lib/generators/calagator/templates/config/secrets.yml.sample +83 -0
- data/lib/secrets_reader.rb +76 -0
- data/lib/tasks/spec_db.rake +53 -0
- data/lib/tasks/sunspot_reindex_calagator.rake +6 -0
- data/lib/tasks/sunspot_solr_restart_enhancements.rake +19 -0
- data/lib/tasks/update_counter_caches.rake +13 -0
- data/lib/templates/erb/scaffold/_form.html.erb +11 -0
- data/lib/theme_reader.rb +17 -0
- data/lib/wait_for_solr.rb +25 -0
- data/spec/controllers/calagator/application_controller_spec.rb +47 -0
- data/spec/controllers/calagator/events_controller_spec.rb +794 -0
- data/spec/controllers/calagator/site_controller_spec.rb +59 -0
- data/spec/controllers/calagator/sources_controller_spec.rb +439 -0
- data/spec/controllers/calagator/venues_controller_spec.rb +319 -0
- data/spec/controllers/calagator/versions_controller_spec.rb +82 -0
- data/spec/controllers/squash_many_duplicates_examples.rb +49 -0
- data/spec/dummy/Gemfile +39 -0
- data/spec/dummy/Gemfile.lock +195 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/images/rails.png +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +16 -0
- data/spec/dummy/app/assets/stylesheets/application.css +14 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +62 -0
- data/spec/dummy/config/boot.rb +6 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/calagator.rb +26 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +59 -0
- data/spec/dummy/config/secrets.yml +83 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20150309023304_create_events.calagator.rb +18 -0
- data/spec/dummy/db/migrate/20150309023305_create_venues.calagator.rb +18 -0
- data/spec/dummy/db/migrate/20150309023306_create_sources.calagator.rb +17 -0
- data/spec/dummy/db/migrate/20150309023307_add_detailed_fields_to_venue.calagator.rb +20 -0
- data/spec/dummy/db/migrate/20150309023308_add_end_time_to_events.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023309_add_source_id_to_events.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023310_add_source_id_to_venues.calagator.rb +11 -0
- data/spec/dummy/db/migrate/20150309023311_add_duplicate_of_column_to_venues.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023312_add_duplicate_of_column_to_events.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023313_change_lat_long_type.calagator.rb +13 -0
- data/spec/dummy/db/migrate/20150309023314_add_source_reimport.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023315_change_end_time_to_duration.calagator.rb +12 -0
- data/spec/dummy/db/migrate/20150309023316_remove_format_type_from_source.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023317_create_updates.calagator.rb +16 -0
- data/spec/dummy/db/migrate/20150309023318_remove_next_update_from_source.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023319_change_duration_to_end_time.calagator.rb +12 -0
- data/spec/dummy/db/migrate/20150309023320_create_tags_and_taggings.calagator.rb +29 -0
- data/spec/dummy/db/migrate/20150309023321_create_versioned_events.calagator.rb +26 -0
- data/spec/dummy/db/migrate/20150309023322_create_versioned_venues.calagator.rb +33 -0
- data/spec/dummy/db/migrate/20150309023323_add_rrule_to_events.calagator.rb +16 -0
- data/spec/dummy/db/migrate/20150309023324_create_versions.calagator.rb +19 -0
- data/spec/dummy/db/migrate/20150309023325_add_closed_flag_to_venues.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023326_add_wifi_flag_to_venues.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023327_add_access_notes_to_venues.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023328_add_events_count_to_venues.calagator.rb +9 -0
- data/spec/dummy/db/migrate/20150309023329_add_venue_details_to_events.calagator.rb +10 -0
- data/spec/dummy/db/migrate/20150309023330_acts_as_taggable_on_migration.calagator.rb +51 -0
- data/spec/dummy/db/migrate/20150309023331_cleanup.calagator.rb +15 -0
- data/spec/dummy/db/migrate/20150309023332_specify_venues_latitude_and_longitude_precision.calagator.rb +12 -0
- data/spec/dummy/db/migrate/20150309023333_remove_updates.calagator.rb +14 -0
- data/spec/dummy/db/migrate/20150309023334_add_locked_status_to_events.calagator.rb +6 -0
- data/spec/dummy/db/schema.rb +95 -0
- data/spec/dummy/db/seeds.rb +7 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/doc/README_FOR_APP +2 -0
- data/spec/dummy/log/development.log +273 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/robots.txt +5 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/test/performance/browsing_test.rb +12 -0
- data/spec/dummy/test/test_helper.rb +13 -0
- data/spec/factories.rb +93 -0
- data/spec/features/add_event_spec.rb +99 -0
- data/spec/features/add_venue_spec.rb +34 -0
- data/spec/features/admin_auth_spec.rb +22 -0
- data/spec/features/admin_lock_event_spec.rb +41 -0
- data/spec/features/import_events_from_feed_spec.rb +43 -0
- data/spec/features/managing_event_spec.rb +111 -0
- data/spec/features/managing_venue_spec.rb +71 -0
- data/spec/features/search_event_spec.rb +27 -0
- data/spec/helpers/calagator/application_helper_spec.rb +82 -0
- data/spec/helpers/calagator/events_helper_spec.rb +172 -0
- data/spec/helpers/calagator/google_event_export_helper_spec.rb +70 -0
- data/spec/helpers/calagator/sources_helper_spec.rb +12 -0
- data/spec/helpers/calagator/tags_helper_spec.rb +85 -0
- data/spec/helpers/calagator/time_range_helper_spec.rb +59 -0
- data/spec/lib/calagator/blacklist_validator_spec.rb +65 -0
- data/spec/lib/calagator/decode_html_entities_hack_spec.rb +54 -0
- data/spec/lib/calagator/settings_spec.rb +20 -0
- data/spec/lib/calagator/url_prefixer_spec.rb +33 -0
- data/spec/lib/secrets_reader_spec.rb +65 -0
- data/spec/models/calagator/event/cloner_spec.rb +43 -0
- data/spec/models/calagator/event/overview_spec.rb +79 -0
- data/spec/models/calagator/event/search_spec.rb +103 -0
- data/spec/models/calagator/event_search_spec.rb +149 -0
- data/spec/models/calagator/event_spec.rb +859 -0
- data/spec/models/calagator/source/parser_facebook_spec.rb +73 -0
- data/spec/models/calagator/source/parser_hcal_spec.rb +69 -0
- data/spec/models/calagator/source/parser_ical_non_standard_spec.rb +91 -0
- data/spec/models/calagator/source/parser_ical_spec.rb +322 -0
- data/spec/models/calagator/source/parser_meetup_spec.rb +69 -0
- data/spec/models/calagator/source/parser_plancast_spec.rb +53 -0
- data/spec/models/calagator/source/parser_spec.rb +238 -0
- data/spec/models/calagator/source_spec.rb +135 -0
- data/spec/models/calagator/venue/search_spec.rb +92 -0
- data/spec/models/calagator/venue_search_spec.rb +124 -0
- data/spec/models/calagator/venue_spec.rb +346 -0
- data/spec/models/tag_spec.rb +35 -0
- data/spec/rails_helper.rb +39 -0
- data/spec/spec_helper.rb +140 -0
- data/spec/support/disable_geocoding.rb +1 -0
- data/spec/support/http_samples.rb +5 -0
- data/spec/support/samples/facebook.json +23 -0
- data/spec/support/samples/hcal_basic.xml +6 -0
- data/spec/support/samples/hcal_dup_event_dup_venue.xml +13 -0
- data/spec/support/samples/hcal_event_duplicates_fixture.xml +13 -0
- data/spec/support/samples/hcal_event_wo_lat_and_long.xml +14 -0
- data/spec/support/samples/hcal_multiple.xml +16 -0
- data/spec/support/samples/hcal_same_event_twice_with_different_venues.xml +12 -0
- data/spec/support/samples/hcal_single.xml +8 -0
- data/spec/support/samples/hcal_two_identical_events.xml +14 -0
- data/spec/support/samples/hcal_upcoming_v1.html +412 -0
- data/spec/support/samples/hcal_upcoming_v2.html +749 -0
- data/spec/support/samples/hcal_upcoming_v3.html +685 -0
- data/spec/support/samples/hcal_upcoming_v4.html +761 -0
- data/spec/support/samples/ical_apple.ics +22 -0
- data/spec/support/samples/ical_apple_v3.ics +37 -0
- data/spec/support/samples/ical_basic.ics +15 -0
- data/spec/support/samples/ical_basic_with_duration.ics +16 -0
- data/spec/support/samples/ical_event_with_squashed_venue.ics +12 -0
- data/spec/support/samples/ical_eventful_many.ics +504 -0
- data/spec/support/samples/ical_gmt.ics +12 -0
- data/spec/support/samples/ical_google.ics +786 -0
- data/spec/support/samples/ical_multiple_calendars.ics +111 -0
- data/spec/support/samples/ical_upcoming.ics +36 -0
- data/spec/support/samples/ical_upcoming_many.ics +682 -0
- data/spec/support/samples/ical_upcoming_v2.ics +43 -0
- data/spec/support/samples/ical_z.ics +10 -0
- data/spec/support/samples/meetup.ics +16 -0
- data/spec/support/samples/meetup.json +31 -0
- data/spec/support/samples/plancast.ics +59 -0
- data/spec/support/samples/plancast.json +51 -0
- data/spec/support/samples/plancast_with_missing_venue.json +39 -0
- data/spec/support/samples/upcoming_v1.xml +8 -0
- data/spec/support/samples/upcoming_v2_with_invalid_utc_dates.xml +8 -0
- data/spec/support/time_convenience_methods.rb +8 -0
- data/spec/support/time_zones.rb +12 -0
- data/spec/support/url_helpers.rb +5 -0
- data/spec/support/wait_for_ajax.rb +15 -0
- data/spec/support/webmock.rb +8 -0
- data/spec/travis_spec.rb +7 -0
- metadata +1194 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Calagator
|
|
4
|
+
|
|
5
|
+
describe Event::Cloner do
|
|
6
|
+
describe "when cloning" do
|
|
7
|
+
let :original do
|
|
8
|
+
FactoryGirl.build(:event,
|
|
9
|
+
:id => 42,
|
|
10
|
+
:start_time => Time.zone.parse("2008-01-19 10:00:00"),
|
|
11
|
+
:end_time => Time.zone.parse("2008-01-19 17:00:00"),
|
|
12
|
+
:tag_list => "foo, bar, baz",
|
|
13
|
+
:venue_details => "Details")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
subject do
|
|
17
|
+
Event::Cloner.clone(original)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it { should be_new_record }
|
|
21
|
+
its(:id) { should be_nil }
|
|
22
|
+
|
|
23
|
+
describe "#start_time" do
|
|
24
|
+
it "should equal todays date with the same time" do
|
|
25
|
+
start_time = Date.today + original.start_time.hour.hours
|
|
26
|
+
subject.start_time.should == start_time
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "#end_time" do
|
|
31
|
+
it "should equal todays date with the same time" do
|
|
32
|
+
end_time = Date.today + original.end_time.hour.hours
|
|
33
|
+
subject.end_time.should == end_time
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
%w[title description url venue_id venue_details tag_list].each do |field|
|
|
38
|
+
its(field) { should eq original.send(field) }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Calagator
|
|
4
|
+
|
|
5
|
+
describe Event::Overview, :type => :model do
|
|
6
|
+
describe "#times_to_events" do
|
|
7
|
+
before do
|
|
8
|
+
@today_midnight = today
|
|
9
|
+
@yesterday = @today_midnight.yesterday
|
|
10
|
+
@tomorrow = @today_midnight.tomorrow
|
|
11
|
+
@day_after_tomorrow = @tomorrow.tomorrow
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe "#today" do
|
|
15
|
+
it "should include events that started before today and end after today" do
|
|
16
|
+
event = FactoryGirl.create(:event, start_time: @yesterday, end_time: @tomorrow)
|
|
17
|
+
expect(subject.today).to include event
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should include events that started earlier today" do
|
|
21
|
+
event = FactoryGirl.create(:event, start_time: @today_midnight)
|
|
22
|
+
expect(subject.today).to include event
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should not include events that ended before today" do
|
|
26
|
+
event = FactoryGirl.create(:event, start_time: @yesterday, end_time: @yesterday.end_of_day)
|
|
27
|
+
expect(subject.today).not_to include event
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should not include events that start tomorrow" do
|
|
31
|
+
event = FactoryGirl.create(:event, start_time: @tomorrow)
|
|
32
|
+
expect(subject.today).not_to include event
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should not include events that ended at midnight today" do
|
|
36
|
+
event = FactoryGirl.create(:event, start_time: @yesterday, end_time: @today_midnight)
|
|
37
|
+
expect(subject.today).not_to include event
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe "#tomorrow" do
|
|
42
|
+
it "should include events that start tomorrow" do
|
|
43
|
+
event = FactoryGirl.create(:event, start_time: @tomorrow)
|
|
44
|
+
expect(subject.tomorrow).to include event
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should not include events that start after tomorrow" do
|
|
48
|
+
event = FactoryGirl.create(:event, start_time: @day_after_tomorrow)
|
|
49
|
+
expect(subject.tomorrow).not_to include event
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe "#later" do
|
|
54
|
+
it "should include events that start after tomorrow" do
|
|
55
|
+
event = FactoryGirl.create(:event, start_time: @day_after_tomorrow)
|
|
56
|
+
expect(subject.later).to include event
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "should not include events that start after two weeks" do
|
|
60
|
+
event = FactoryGirl.create(:event, start_time: 2.weeks.from_now)
|
|
61
|
+
expect(subject.later).not_to include event
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe "#more" do
|
|
66
|
+
it "should provide an event if there are events past the future cutoff" do
|
|
67
|
+
event = FactoryGirl.create(:event, start_time: 2.weeks.from_now)
|
|
68
|
+
expect(subject.more).to eq(event)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "should be nil if there are no events past the future cutoff" do
|
|
72
|
+
event = FactoryGirl.create(:event, start_time: 2.weeks.from_now - 1.day)
|
|
73
|
+
expect(subject.more).to be_blank
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Calagator
|
|
4
|
+
|
|
5
|
+
describe Event::Search, :type => :model do
|
|
6
|
+
describe "by keyword" do
|
|
7
|
+
it "should be able to only return events that include a specific keyword" do
|
|
8
|
+
events = double
|
|
9
|
+
expect(Event).to receive(:search).with("myquery", skip_old: false, order: "date").and_return(events)
|
|
10
|
+
|
|
11
|
+
subject = Event::Search.new query: "myquery"
|
|
12
|
+
expect(subject.events).to eq(events)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should be able to only return current events" do
|
|
16
|
+
events = double
|
|
17
|
+
expect(Event).to receive(:search).with("myquery", order: "date", skip_old: true).and_return(events)
|
|
18
|
+
|
|
19
|
+
subject = Event::Search.new query: "myquery", current: "1"
|
|
20
|
+
expect(subject.events).to eq(events)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "should warn if user tries ordering by invalid order" do
|
|
24
|
+
subject = Event::Search.new query: "myquery", order: "kittens"
|
|
25
|
+
expect(subject.failure_message).to eq("Unknown ordering option \"kittens\", sorting by date instead.")
|
|
26
|
+
expect(subject).not_to be_hard_failure
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "by tag" do
|
|
31
|
+
it "should be able to only return events matching specific tag" do
|
|
32
|
+
events = double
|
|
33
|
+
expect(Event).to receive(:search_tag).with("foo", current: false, order: "date").and_return(events)
|
|
34
|
+
|
|
35
|
+
subject = Event::Search.new tag: "foo"
|
|
36
|
+
expect(subject.events).to eq(events)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should warn if user tries ordering by invalid order" do
|
|
40
|
+
subject = Event::Search.new tag: "omg", order: "kittens"
|
|
41
|
+
expect(subject.failure_message).to eq("Unknown ordering option \"kittens\", sorting by date instead.")
|
|
42
|
+
expect(subject).not_to be_hard_failure
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "should warn if user tries ordering tags by score" do
|
|
46
|
+
subject = Event::Search.new tag: "omg", order: "score"
|
|
47
|
+
expect(subject.failure_message).to eq("You cannot sort tags by score")
|
|
48
|
+
expect(subject).not_to be_hard_failure
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe "#grouped_events" do
|
|
53
|
+
it "groups events into a hash by currentness" do
|
|
54
|
+
past_event = double(:event, current?: false)
|
|
55
|
+
current_event = double(:event, current?: true)
|
|
56
|
+
events = [past_event, current_event]
|
|
57
|
+
expect(Event).to receive(:search).and_return(events)
|
|
58
|
+
|
|
59
|
+
expect(subject.grouped_events).to eq({
|
|
60
|
+
past: [past_event],
|
|
61
|
+
current: [current_event],
|
|
62
|
+
})
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "discards past events when passed the current option" do
|
|
66
|
+
past_event = double(:event, current?: false)
|
|
67
|
+
current_event = double(:event, current?: true)
|
|
68
|
+
events = [past_event, current_event]
|
|
69
|
+
expect(Event).to receive(:search).and_return(events)
|
|
70
|
+
|
|
71
|
+
subject = expect(Event::Search.new(current: "true").grouped_events).to eq({
|
|
72
|
+
past: [],
|
|
73
|
+
current: [current_event],
|
|
74
|
+
})
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "orders past events by date desc if passed date to the order option" do
|
|
78
|
+
current_event = double(:event, current?: true)
|
|
79
|
+
past_event = double(:event, current?: false)
|
|
80
|
+
other_past_event = double(:event, current?: false)
|
|
81
|
+
expect(Event).to receive(:search).and_return([current_event, past_event, other_past_event])
|
|
82
|
+
expect(Event::Search.new(order: "date").grouped_events).to eq({
|
|
83
|
+
current: [current_event],
|
|
84
|
+
past: [past_event, other_past_event],
|
|
85
|
+
})
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "hard failures" do
|
|
90
|
+
it "should hard fail if given no search query" do
|
|
91
|
+
expect(subject.failure_message).to eq("You must enter a search query")
|
|
92
|
+
expect(subject).to be_hard_failure
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should hard fail if searching by both query and tag" do
|
|
96
|
+
subject = Event::Search.new query: "omg", tag: "bbq"
|
|
97
|
+
expect(subject.failure_message).to eq("You can't search by tag and query at the same time")
|
|
98
|
+
expect(subject).to be_hard_failure
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Calagator
|
|
4
|
+
|
|
5
|
+
describe Event, :type => :model do
|
|
6
|
+
shared_examples_for "#search" do
|
|
7
|
+
it "returns everything when searching by empty string" do
|
|
8
|
+
event1 = FactoryGirl.create(:event)
|
|
9
|
+
event2 = FactoryGirl.create(:event)
|
|
10
|
+
expect(Event.search("")).to match_array([event1, event2])
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "searches event titles by substring" do
|
|
14
|
+
event1 = FactoryGirl.create(:event, title: "wtfbbq")
|
|
15
|
+
event2 = FactoryGirl.create(:event, title: "zomg!")
|
|
16
|
+
expect(Event.search("zomg")).to eq([event2])
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "searches event descriptions by substring" do
|
|
20
|
+
event1 = FactoryGirl.create(:event, description: "wtfbbq")
|
|
21
|
+
event2 = FactoryGirl.create(:event, description: "zomg!")
|
|
22
|
+
expect(Event.search("zomg")).to eq([event2])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "searches event tags by exact match" do
|
|
26
|
+
event1 = FactoryGirl.create(:event, tag_list: ["wtf", "bbq", "zomg"])
|
|
27
|
+
event2 = FactoryGirl.create(:event, tag_list: ["wtf", "bbq", "omg"])
|
|
28
|
+
expect(Event.search("omg")).to eq([event2])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "does not search multiple terms" do
|
|
32
|
+
event1 = FactoryGirl.create(:event, title: "wtf")
|
|
33
|
+
event2 = FactoryGirl.create(:event, title: "zomg!")
|
|
34
|
+
event3 = FactoryGirl.create(:event, title: "bbq")
|
|
35
|
+
expect(Event.search("wtf zomg")).to match_array([])
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "searches case-insensitively" do
|
|
39
|
+
event1 = FactoryGirl.create(:event, title: "WTFBBQ")
|
|
40
|
+
event2 = FactoryGirl.create(:event, title: "ZOMG!")
|
|
41
|
+
expect(Event.search("zomg")).to eq([event2])
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "sorts by start time descending" do
|
|
45
|
+
event2 = FactoryGirl.create(:event, start_time: 1.day.ago)
|
|
46
|
+
event1 = FactoryGirl.create(:event, start_time: 1.day.from_now)
|
|
47
|
+
expect(Event.search("")).to eq([event1, event2])
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "can sort by event title" do
|
|
51
|
+
event2 = FactoryGirl.create(:event, title: "zomg")
|
|
52
|
+
event1 = FactoryGirl.create(:event, title: "omg")
|
|
53
|
+
expect(Event.search("", order: "name")).to eq([event1, event2])
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "can sort by venue title" do
|
|
57
|
+
event2 = FactoryGirl.create(:event, venue: FactoryGirl.create(:venue, title: "zomg"))
|
|
58
|
+
event1 = FactoryGirl.create(:event, venue: FactoryGirl.create(:venue, title: "omg"))
|
|
59
|
+
expect(Event.search("", order: "venue")).to eq([event1, event2])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "can sort by start date" do
|
|
63
|
+
event2 = FactoryGirl.create(:event, start_time: 1.year.ago)
|
|
64
|
+
event1 = FactoryGirl.create(:event, start_time: 1.year.from_now)
|
|
65
|
+
expect(Event.search("", order: "date")).to eq([event1, event2])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "can limit to current and upcoming events" do
|
|
69
|
+
event1 = FactoryGirl.create(:event, start_time: 1.year.ago, end_time: 1.year.ago + 1.hour)
|
|
70
|
+
event2 = FactoryGirl.create(:event, start_time: 1.hour.ago, end_time: 1.hour.from_now)
|
|
71
|
+
event3 = FactoryGirl.create(:event, start_time: 1.year.from_now, end_time: 1.year.from_now + 1.hour)
|
|
72
|
+
expect(Event.search("", skip_old: true)).to eq([event3, event2])
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "can limit number of events" do
|
|
76
|
+
2.times { FactoryGirl.create(:event) }
|
|
77
|
+
expect(Event.search("", limit: 1).count).to eq(1)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "limit applies to current and past queries separately" do
|
|
81
|
+
event1 = FactoryGirl.create(:event, title: "omg", start_time: 1.year.ago)
|
|
82
|
+
event2 = FactoryGirl.create(:event, title: "omg", start_time: 1.year.ago)
|
|
83
|
+
event3 = FactoryGirl.create(:event, title: "omg", start_time: 1.year.from_now)
|
|
84
|
+
event4 = FactoryGirl.create(:event, title: "omg", start_time: 1.year.from_now)
|
|
85
|
+
expect(Event.search("omg", limit: 1).to_a.count).to eq(2)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "ANDs terms together to narrow search results" do
|
|
89
|
+
event1 = FactoryGirl.create(:event, title: "women who hack")
|
|
90
|
+
event2 = FactoryGirl.create(:event, title: "women who bike")
|
|
91
|
+
event3 = FactoryGirl.create(:event, title: "omg")
|
|
92
|
+
expect(Event.search("women who hack")).to eq([event1])
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe "Sql" do
|
|
97
|
+
# spec_helper defaults all tests to sql
|
|
98
|
+
|
|
99
|
+
it_should_behave_like "#search"
|
|
100
|
+
|
|
101
|
+
it "searches event urls by substring" do
|
|
102
|
+
event1 = FactoryGirl.create(:event, url: "http://example.com/wtfbbq.html")
|
|
103
|
+
event2 = FactoryGirl.create(:event, url: "http://example.com/zomg.html")
|
|
104
|
+
expect(Event.search("zomg")).to eq([event2])
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "is using the sql search engine" do
|
|
108
|
+
expect(Event::SearchEngine.kind).to eq(:sql)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "does not provide a score" do
|
|
112
|
+
expect(Event::SearchEngine.score?).to be_falsey
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
describe "Sunspot" do
|
|
117
|
+
around do |example|
|
|
118
|
+
server_running = begin
|
|
119
|
+
# Try opening the configured port. If it works, it's running.
|
|
120
|
+
TCPSocket.new('127.0.0.1', Sunspot::Rails.configuration.port).close
|
|
121
|
+
true
|
|
122
|
+
rescue Errno::ECONNREFUSED
|
|
123
|
+
false
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
if server_running
|
|
127
|
+
Event::SearchEngine.use(:sunspot)
|
|
128
|
+
Venue::SearchEngine.use(:sunspot)
|
|
129
|
+
Event.reindex
|
|
130
|
+
Venue.reindex
|
|
131
|
+
example.run
|
|
132
|
+
else
|
|
133
|
+
pending "Solr not running. Start with `rake sunspot:solr:start RAILS_ENV=test`"
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it_should_behave_like "#search"
|
|
138
|
+
|
|
139
|
+
it "is using the sunspot search engine" do
|
|
140
|
+
expect(Event::SearchEngine.kind).to eq(:sunspot)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "provides a score" do
|
|
144
|
+
expect(Event::SearchEngine.score?).to be_truthy
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
end
|
|
@@ -0,0 +1,859 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Calagator
|
|
4
|
+
|
|
5
|
+
describe Event, :type => :model do
|
|
6
|
+
describe "in general" do
|
|
7
|
+
it "should be valid" do
|
|
8
|
+
event = Event.new(:title => "Event title", :start_time => Time.zone.parse('2008.04.12'))
|
|
9
|
+
expect(event).to be_valid
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "should add a http:// prefix to urls without one" do
|
|
13
|
+
event = Event.new(:title => "Event title", :start_time => Time.zone.parse('2008.04.12'), :url => 'google.com')
|
|
14
|
+
expect(event).to be_valid
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "validates blacklisted words" do
|
|
18
|
+
BlacklistValidator.any_instance.stub(patterns: [/\bcialis\b/, /\bviagra\b/])
|
|
19
|
+
event = Event.new(:title => "Foo bar cialis", :start_time => Time.zone.parse('2008.04.12'), :url => 'google.com')
|
|
20
|
+
expect(event).not_to be_valid
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "can be locked" do
|
|
24
|
+
event = Event.create(:title => "Event title", :start_time => Time.zone.parse('2008.04.12'))
|
|
25
|
+
event.lock_editing!
|
|
26
|
+
expect(event.locked).to eq(true)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "can be unlocked" do
|
|
30
|
+
event = Event.create(:title => "Event title", :start_time => Time.zone.parse('2008.04.12'), :locked => true)
|
|
31
|
+
event.unlock_editing!
|
|
32
|
+
expect(event.locked).to eq(false)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "can't be deleted if it's locked" do
|
|
36
|
+
event = Event.create(:title => "Event title", :start_time => Time.zone.parse('2008.04.12'))
|
|
37
|
+
event.lock_editing!
|
|
38
|
+
expect(event.destroy).to eq(false)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe "when checking time status" do
|
|
43
|
+
it "should be old if event ended before today" do
|
|
44
|
+
expect(FactoryGirl.build(:event, start_time: 2.days.ago, end_time: 1.day.ago)).to be_old
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should be current if event is happening today" do
|
|
48
|
+
expect(FactoryGirl.build(:event, start_time: 1.hour.from_now)).to be_current
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "should be ongoing if it began before today but ends today or later" do
|
|
52
|
+
expect(FactoryGirl.build(:event, start_time: 1.day.ago, end_time: 1.day.from_now)).to be_ongoing
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe "dealing with tags" do
|
|
57
|
+
before do
|
|
58
|
+
@tags = "some, tags"
|
|
59
|
+
@event = Event.new(:title => "Tagging Day", :start_time => now)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should be taggable" do
|
|
63
|
+
expect(@event.tag_list).to eq []
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should just cache tagging if it is a new record" do
|
|
67
|
+
expect(@event).not_to receive :save
|
|
68
|
+
expect(@event).to be_new_record
|
|
69
|
+
@event.tag_list = @tags
|
|
70
|
+
expect(@event.tag_list.to_s).to eq @tags
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should use tags with punctuation" do
|
|
74
|
+
tags = [".net", "foo-bar"]
|
|
75
|
+
@event.tag_list = tags.join(", ")
|
|
76
|
+
@event.save
|
|
77
|
+
|
|
78
|
+
@event.reload
|
|
79
|
+
expect(@event.tags.map(&:name).sort).to eq tags.sort
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "should not interpret numeric tags as IDs" do
|
|
83
|
+
tag = "123"
|
|
84
|
+
@event.tag_list = tag
|
|
85
|
+
@event.save
|
|
86
|
+
|
|
87
|
+
@event.reload
|
|
88
|
+
expect(@event.tags.first.name).to eq "123"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should return a collection of events for a given tag" do
|
|
92
|
+
@event.tag_list = @tags
|
|
93
|
+
@event.save
|
|
94
|
+
expect(Event.tagged_with('tags')).to eq [@event]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe "when parsing" do
|
|
99
|
+
|
|
100
|
+
before do
|
|
101
|
+
@basic_hcal = read_sample('hcal_basic.xml')
|
|
102
|
+
@basic_venue = mock_model(Venue, :title => 'Argent Hotel, San Francisco, CA', :full_address => '50 3rd St, San Francisco, CA 94103')
|
|
103
|
+
@basic_event = Event.new(
|
|
104
|
+
:title => 'Web 2.0 Conference',
|
|
105
|
+
:url => 'http://www.web2con.com/',
|
|
106
|
+
:start_time => Time.zone.parse('2007-10-05'),
|
|
107
|
+
:end_time => nil,
|
|
108
|
+
:venue => @basic_venue)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "should parse an iCalendar into an Event" do
|
|
112
|
+
url = "http://foo.bar/"
|
|
113
|
+
actual_ical = Event::IcalRenderer.render(@basic_event)
|
|
114
|
+
stub_request(:get, url).to_return(body: actual_ical)
|
|
115
|
+
|
|
116
|
+
events = Source::Parser.to_events(url: url, skip_old: false)
|
|
117
|
+
|
|
118
|
+
expect(events.size).to eq 1
|
|
119
|
+
event = events.first
|
|
120
|
+
expect(event.title).to eq @basic_event.title
|
|
121
|
+
expect(event.url).to eq @basic_event.url
|
|
122
|
+
expect(event.description).to be_blank
|
|
123
|
+
|
|
124
|
+
expect(event.venue.title).to match "#{@basic_event.venue.title}: #{@basic_event.venue.full_address}"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "should parse an iCalendar into an Event without a URL and generate it" do
|
|
128
|
+
generated_url = "http://foo.bar/"
|
|
129
|
+
@basic_event.url = nil
|
|
130
|
+
actual_ical = Event::IcalRenderer.render(@basic_event, :url_helper => lambda{|event| generated_url})
|
|
131
|
+
url = "http://foo.bar/"
|
|
132
|
+
stub_request(:get, url).to_return(body: actual_ical)
|
|
133
|
+
|
|
134
|
+
events = Source::Parser.to_events(url: url, skip_old: false)
|
|
135
|
+
|
|
136
|
+
expect(events.size).to eq 1
|
|
137
|
+
event = events.first
|
|
138
|
+
expect(event.title).to eq @basic_event.title
|
|
139
|
+
expect(event.url).to eq @basic_event.url
|
|
140
|
+
expect(event.description).to match /Imported from: #{generated_url}/
|
|
141
|
+
|
|
142
|
+
expect(event.venue.title).to match "#{@basic_event.venue.title}: #{@basic_event.venue.full_address}"
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
describe "when finding duplicates by type" do
|
|
147
|
+
def assert_default_find_duplicates_by_type(type)
|
|
148
|
+
expect(Event).to receive(:future).and_return 42
|
|
149
|
+
expect(Event.find_duplicates_by_type(type)).to eq({ [] => 42 })
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it "should find all future events if called with nil" do
|
|
153
|
+
assert_default_find_duplicates_by_type(nil)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "should find all future events if called with empty string" do
|
|
157
|
+
assert_default_find_duplicates_by_type('')
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "should find all future events if called with 'na'" do
|
|
161
|
+
assert_default_find_duplicates_by_type('na')
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def assert_specific_find_by_duplicates_by(type, queried)
|
|
165
|
+
expect(Event).to receive(:find_duplicates_by).with(queried, {:grouped => true, :where => anything()})
|
|
166
|
+
Event.find_duplicates_by_type(type)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it "should find events with all duplicate fields if called with 'all'" do
|
|
170
|
+
assert_specific_find_by_duplicates_by('all', :all)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "should find events with any duplicate fields if called with 'any'" do
|
|
174
|
+
assert_specific_find_by_duplicates_by('any', :any)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "should find events with duplicate titles if called with 'title'" do
|
|
178
|
+
assert_specific_find_by_duplicates_by('title', [:title])
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
describe "when processing date" do
|
|
183
|
+
before do
|
|
184
|
+
@event = Event.new(:title => "MyEvent")
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
it "should fail to validate if start time is nil" do
|
|
188
|
+
@event.start_time = nil
|
|
189
|
+
expect(@event).not_to be_valid
|
|
190
|
+
expect(@event.errors[:start_time].size).to eq(1)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "should fail to validate if start time is blank" do
|
|
194
|
+
@event.start_time = ""
|
|
195
|
+
expect(@event).not_to be_valid
|
|
196
|
+
expect(@event.errors[:start_time].size).to eq(1)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it "should fail to validate if end_time is earlier than start time " do
|
|
200
|
+
@event.start_time = now
|
|
201
|
+
@event.end_time = @event.start_time - 2.hours
|
|
202
|
+
expect(@event).to be_invalid
|
|
203
|
+
expect(@event.errors[:end_time].size).to eq(1)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
describe "when processing url" do
|
|
208
|
+
before do
|
|
209
|
+
@event = Event.new(:title => 'MyEvent', :start_time => now)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
let(:valid_urls) {[
|
|
213
|
+
"hackoregon.org",
|
|
214
|
+
"http://www.meetup.com/Hack_Oregon-Data/events/",
|
|
215
|
+
"example.com",
|
|
216
|
+
"sub.example.com/",
|
|
217
|
+
"sub.domain.my-example.com",
|
|
218
|
+
"example.com/?stuff=true",
|
|
219
|
+
"example.com:5000/?stuff=true",
|
|
220
|
+
"sub.domain.my-example.com/path/to/file/hello.html",
|
|
221
|
+
"hello.museum",
|
|
222
|
+
"http://example.com",
|
|
223
|
+
]}
|
|
224
|
+
|
|
225
|
+
let(:invalid_urls){[
|
|
226
|
+
"hackoregon.org, http://www.meetup.com/Hack_Oregon-Data/events/",
|
|
227
|
+
"hackoregon.org\nhttp://www.meetup.com/",
|
|
228
|
+
"htttp://www.example.com"
|
|
229
|
+
]}
|
|
230
|
+
|
|
231
|
+
it "should validate with valid urls (with scheme included or not)" do
|
|
232
|
+
valid_urls.each do |valid_url|
|
|
233
|
+
@event.url = valid_url
|
|
234
|
+
expect(@event).to be_valid
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
it "should fail to validate with invalid urls (with scheme included or not)" do
|
|
239
|
+
invalid_urls.each do |invalid_url|
|
|
240
|
+
@event.url = invalid_url
|
|
241
|
+
expect(@event).to be_invalid
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
describe "#start_time=" do
|
|
247
|
+
it "should clear with nil" do
|
|
248
|
+
expect(Event.new(:start_time => nil).start_time).to be_nil
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
it "should set from date String" do
|
|
252
|
+
event = Event.new(:start_time => "2009-01-02")
|
|
253
|
+
expect(event.start_time).to eq Time.zone.parse("2009-01-02")
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
it "should set from date-time String" do
|
|
257
|
+
event = Event.new(:start_time => "2009-01-02 03:45")
|
|
258
|
+
expect(event.start_time).to eq Time.zone.parse("2009-01-02 03:45")
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
it "should set from an Array of Strings" do
|
|
262
|
+
event = Event.new(:start_time => ["2009-01-03", "02:14"])
|
|
263
|
+
expect(event.start_time).to eq Time.zone.parse("2009-01-03 02:14")
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it "should set from Date" do
|
|
267
|
+
event = Event.new(:start_time => Date.parse("2009-02-01"))
|
|
268
|
+
expect(event.start_time).to eq Time.parse("2009-02-01")
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
it "should set from DateTime" do
|
|
272
|
+
event = Event.new(:start_time => Time.zone.parse("2009-01-01 05:30"))
|
|
273
|
+
expect(event.start_time).to eq Time.zone.parse("2009-01-01 05:30")
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it "should flag an invalid time and reset to nil" do
|
|
277
|
+
event = Event.new(:start_time => "2010/1/1")
|
|
278
|
+
event.start_time = "1/0"
|
|
279
|
+
expect(event.start_time).to be_nil
|
|
280
|
+
expect(event.errors[:start_time]).to be_present
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
describe "#end_time=" do
|
|
285
|
+
it "should clear with nil" do
|
|
286
|
+
expect(Event.new(:end_time => nil).end_time).to be_nil
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
it "should set from date String" do
|
|
290
|
+
event = Event.new(:end_time => "2009-01-02")
|
|
291
|
+
expect(event.end_time).to eq Time.zone.parse("2009-01-02")
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
it "should set from date-time String" do
|
|
295
|
+
event = Event.new(:end_time => "2009-01-02 03:45")
|
|
296
|
+
expect(event.end_time).to eq Time.zone.parse("2009-01-02 03:45")
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
it "should set from an Array of Strings" do
|
|
300
|
+
event = Event.new(:end_time => ["2009-01-03", "02:14"])
|
|
301
|
+
expect(event.end_time).to eq Time.zone.parse("2009-01-03 02:14")
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
it "should set from Date" do
|
|
305
|
+
event = Event.new(:end_time => Date.parse("2009-02-01"))
|
|
306
|
+
expect(event.end_time).to eq Time.parse("2009-02-01")
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
it "should set from DateTime" do
|
|
310
|
+
event = Event.new(:end_time => Time.zone.parse("2009-01-01 05:30"))
|
|
311
|
+
expect(event.end_time).to eq Time.zone.parse("2009-01-01 05:30")
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
it "should flag an invalid time" do
|
|
315
|
+
event = Event.new(:end_time => "1/0")
|
|
316
|
+
expect(event.errors[:end_time]).to be_present
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
describe "#dates" do
|
|
321
|
+
it "returns an array of dates spanned by the event" do
|
|
322
|
+
event = Event.new(start_time: "2010-01-01", end_time: "2010-01-03")
|
|
323
|
+
expect(event.dates).to eq([
|
|
324
|
+
Date.parse("2010-01-01"),
|
|
325
|
+
Date.parse("2010-01-02"),
|
|
326
|
+
Date.parse("2010-01-03"),
|
|
327
|
+
])
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
it "returns an array of one date when there is no end time" do
|
|
331
|
+
event = Event.new(start_time: "2010-01-01")
|
|
332
|
+
expect(event.dates).to eq([Date.parse("2010-01-01")])
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
it "throws ArgumentError when there is no start time" do
|
|
336
|
+
expect { Event.new.dates }.to raise_error(ArgumentError)
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
describe "#duration" do
|
|
341
|
+
it "returns the event length in seconds" do
|
|
342
|
+
event = Event.new(start_time: "2010-01-01", end_time: "2010-01-03")
|
|
343
|
+
expect(event.duration).to eq(172800)
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
it "returns zero if start and end times aren't present" do
|
|
347
|
+
expect(Event.new.duration).to eq(0)
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
describe "#location" do
|
|
352
|
+
it "delegates to the venue's location" do
|
|
353
|
+
event = Event.new
|
|
354
|
+
event.build_venue latitude: 45.5200, longitude: 122.6819
|
|
355
|
+
expect(event.location).to eq([45.5200, 122.6819])
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
describe ".search_tag" do
|
|
360
|
+
before do
|
|
361
|
+
@c = FactoryGirl.create(:event, title: "c", tag_list: ["tag", "wtf"], start_time: 3.minutes.ago)
|
|
362
|
+
@b = FactoryGirl.create(:event, title: "b", tag_list: ["omg", "wtf"], start_time: 2.minutes.ago)
|
|
363
|
+
@a = FactoryGirl.create(:event, title: "a", tag_list: ["tag", "omg"], start_time: 1.minutes.ago)
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
it "finds events with the given tag" do
|
|
367
|
+
Event.search_tag("tag").should == [@c,@a]
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
it "accepts an order option" do
|
|
371
|
+
Event.search_tag("tag", order: "name").should == [@a,@c]
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
describe "when finding by dates" do
|
|
376
|
+
|
|
377
|
+
before do
|
|
378
|
+
@today_midnight = today
|
|
379
|
+
@yesterday = @today_midnight.yesterday
|
|
380
|
+
@tomorrow = @today_midnight.tomorrow
|
|
381
|
+
|
|
382
|
+
@this_venue = Venue.create!(:title => "This venue")
|
|
383
|
+
|
|
384
|
+
@started_before_today_and_ends_after_today = Event.create!(
|
|
385
|
+
:title => "Event in progress",
|
|
386
|
+
:start_time => @yesterday,
|
|
387
|
+
:end_time => @tomorrow,
|
|
388
|
+
:venue_id => @this_venue.id)
|
|
389
|
+
|
|
390
|
+
@started_midnight_and_continuing_after = Event.create!(
|
|
391
|
+
:title => "Midnight start",
|
|
392
|
+
:start_time => @today_midnight,
|
|
393
|
+
:end_time => @tomorrow,
|
|
394
|
+
:venue_id => @this_venue.id)
|
|
395
|
+
|
|
396
|
+
@started_and_ended_yesterday = Event.create!(
|
|
397
|
+
:title => "Yesterday start",
|
|
398
|
+
:start_time => @yesterday,
|
|
399
|
+
:end_time => @yesterday.end_of_day,
|
|
400
|
+
:venue_id => @this_venue.id)
|
|
401
|
+
|
|
402
|
+
@started_today_and_no_end_time = Event.create!(
|
|
403
|
+
:title => "nil end time",
|
|
404
|
+
:start_time => @today_midnight,
|
|
405
|
+
:end_time => nil,
|
|
406
|
+
:venue_id => @this_venue.id)
|
|
407
|
+
|
|
408
|
+
@starts_and_ends_tomorrow = Event.create!(
|
|
409
|
+
:title => "starts and ends tomorrow",
|
|
410
|
+
:start_time => @tomorrow,
|
|
411
|
+
:end_time => @tomorrow.end_of_day,
|
|
412
|
+
:venue_id => @this_venue.id)
|
|
413
|
+
|
|
414
|
+
@starts_after_tomorrow = Event.create!(
|
|
415
|
+
:title => "Starting after tomorrow",
|
|
416
|
+
:start_time => @tomorrow + 1.day,
|
|
417
|
+
:venue_id => @this_venue.id)
|
|
418
|
+
|
|
419
|
+
@started_before_today_and_ends_at_midnight = Event.create!(
|
|
420
|
+
:title => "Midnight end",
|
|
421
|
+
:start_time => @yesterday,
|
|
422
|
+
:end_time => @today_midnight,
|
|
423
|
+
:venue_id => @this_venue.id)
|
|
424
|
+
|
|
425
|
+
@future_events_for_this_venue = @this_venue.events.future
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
describe "for future events" do
|
|
429
|
+
before do
|
|
430
|
+
@future_events = Event.future
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
it "should include events that started earlier today" do
|
|
434
|
+
expect(@future_events).to include @started_midnight_and_continuing_after
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
it "should include events with no end time that started today" do
|
|
438
|
+
expect(@future_events).to include @started_today_and_no_end_time
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
it "should include events that started before today and ended after today" do
|
|
442
|
+
events = Event.future
|
|
443
|
+
expect(events).to include @started_before_today_and_ends_after_today
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
it "should include events with no end time that started today" do
|
|
447
|
+
expect(@future_events).to include @started_today_and_no_end_time
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
it "should not include events that ended before today" do
|
|
451
|
+
expect(@future_events).not_to include @started_and_ended_yesterday
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
describe "for future events with venue" do
|
|
456
|
+
before do
|
|
457
|
+
@another_venue = Venue.create!(:title => "Another venue")
|
|
458
|
+
|
|
459
|
+
@future_event_another_venue = Event.create!(
|
|
460
|
+
:title => "Starting after tomorrow",
|
|
461
|
+
:start_time => @tomorrow + 1.day,
|
|
462
|
+
:venue_id => @another_venue.id)
|
|
463
|
+
|
|
464
|
+
@future_event_no_venue = Event.create!(
|
|
465
|
+
:title => "Starting after tomorrow",
|
|
466
|
+
:start_time => @tomorrow + 1.day)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# TODO Consider moving these examples elsewhere because they don't appear to relate to this scope. This comment applies to the examples from here...
|
|
470
|
+
it "should include events that started earlier today" do
|
|
471
|
+
expect(@future_events_for_this_venue).to include @started_midnight_and_continuing_after
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
it "should include events with no end time that started today" do
|
|
475
|
+
expect(@future_events_for_this_venue).to include @started_today_and_no_end_time
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
it "should include events that started before today and ended after today" do
|
|
479
|
+
expect(@future_events_for_this_venue).to include @started_before_today_and_ends_after_today
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
it "should not include events that ended before today" do
|
|
483
|
+
expect(@future_events_for_this_venue).not_to include @started_and_ended_yesterday
|
|
484
|
+
end
|
|
485
|
+
# TODO ...to here.
|
|
486
|
+
|
|
487
|
+
it "should not include events for another venue" do
|
|
488
|
+
expect(@future_events_for_this_venue).not_to include @future_event_another_venue
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
it "should not include events with no venue" do
|
|
492
|
+
expect(@future_events_for_this_venue).not_to include @future_event_no_venue
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
describe "for date range" do
|
|
497
|
+
it "should include events that started earlier today" do
|
|
498
|
+
events = Event.within_dates(@today_midnight, @tomorrow)
|
|
499
|
+
expect(events).to include @started_midnight_and_continuing_after
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
it "should include events that started before today and end after today" do
|
|
503
|
+
events = Event.within_dates(@today_midnight, @tomorrow)
|
|
504
|
+
expect(events).to include @started_before_today_and_ends_after_today
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
it "should not include past events" do
|
|
508
|
+
events = Event.within_dates(@today_midnight, @tomorrow)
|
|
509
|
+
expect(events).not_to include @started_and_ended_yesterday
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
it "should exclude events that start after the end of the range" do
|
|
513
|
+
events = Event.within_dates(@tomorrow, @tomorrow)
|
|
514
|
+
expect(events).not_to include @started_today_and_no_end_time
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
describe "when ordering" do
|
|
520
|
+
describe "with .ordered_by_ui_field" do
|
|
521
|
+
it "defaults to order by start time" do
|
|
522
|
+
event1 = FactoryGirl.create(:event, start_time: Time.zone.parse("2003-01-01"))
|
|
523
|
+
event2 = FactoryGirl.create(:event, start_time: Time.zone.parse("2002-01-01"))
|
|
524
|
+
event3 = FactoryGirl.create(:event, start_time: Time.zone.parse("2001-01-01"))
|
|
525
|
+
|
|
526
|
+
events = Event.ordered_by_ui_field(nil)
|
|
527
|
+
expect(events).to eq([event3, event2, event1])
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
it "can order by event name" do
|
|
531
|
+
event1 = FactoryGirl.create(:event, title: "CU there")
|
|
532
|
+
event2 = FactoryGirl.create(:event, title: "Be there")
|
|
533
|
+
event3 = FactoryGirl.create(:event, title: "An event")
|
|
534
|
+
|
|
535
|
+
events = Event.ordered_by_ui_field("name")
|
|
536
|
+
expect(events).to eq([event3, event2, event1])
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
it "can order by venue name" do
|
|
540
|
+
event1 = FactoryGirl.create(:event, venue: FactoryGirl.create(:venue, title: "C venue"))
|
|
541
|
+
event2 = FactoryGirl.create(:event, venue: FactoryGirl.create(:venue, title: "B venue"))
|
|
542
|
+
event3 = FactoryGirl.create(:event, venue: FactoryGirl.create(:venue, title: "A venue"))
|
|
543
|
+
|
|
544
|
+
events = Event.ordered_by_ui_field("venue")
|
|
545
|
+
expect(events).to eq([event3, event2, event1])
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
describe "with finding duplicates" do
|
|
551
|
+
before do
|
|
552
|
+
@non_duplicate_event = FactoryGirl.create(:event)
|
|
553
|
+
@duplicate_event = FactoryGirl.create(:duplicate_event)
|
|
554
|
+
@events = [@non_duplicate_event, @duplicate_event]
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
it "should find all events that have not been marked as duplicate" do
|
|
558
|
+
non_duplicates = Event.non_duplicates
|
|
559
|
+
expect(non_duplicates).to include @non_duplicate_event
|
|
560
|
+
expect(non_duplicates).not_to include @duplicate_event
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
it "should find all events that have been marked as duplicate" do
|
|
564
|
+
duplicates = Event.marked_duplicates
|
|
565
|
+
expect(duplicates).to include @duplicate_event
|
|
566
|
+
expect(duplicates).not_to include @non_duplicate_event
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
describe "with finding duplicates (integration test)" do
|
|
571
|
+
before do
|
|
572
|
+
@event = FactoryGirl.create(:event)
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
# Find duplicates, create another event with the given attributes, and find duplicates again
|
|
576
|
+
# TODO Refactor #find_duplicates_create_a_clone_and_find_again and its uses into something simpler, like #assert_duplicate_count.
|
|
577
|
+
def find_duplicates_create_a_clone_and_find_again(find_duplicates_arguments, clone_attributes, create_class = Event)
|
|
578
|
+
before_results = create_class.find_duplicates_by( find_duplicates_arguments)
|
|
579
|
+
clone = create_class.create!(clone_attributes)
|
|
580
|
+
after_results = Event.find_duplicates_by(find_duplicates_arguments)
|
|
581
|
+
return [before_results.sort_by(&:created_at), after_results.sort_by(&:created_at)]
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
it "should find duplicate title by title" do
|
|
585
|
+
pre, post = find_duplicates_create_a_clone_and_find_again(:title, {:title => @event.title, :start_time => @event.start_time} )
|
|
586
|
+
expect(post.size).to eq(pre.size + 2)
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
it "should find duplicate title by any" do
|
|
590
|
+
# TODO figure out why the #find_duplicates_create_a_clone_and_find_again isn't giving expected results and a workaround was needed.
|
|
591
|
+
#pre, post = find_duplicates_create_a_clone_and_find_again(:any, {:title => @event.title, :start_time => @event.start_time} )
|
|
592
|
+
#post.size.should eq(pre.size + 2)
|
|
593
|
+
dup_title = Event.create!({:title => @event.title, :start_time => @event.start_time + 1.minute})
|
|
594
|
+
expect(Event.find_duplicates_by(:any)).to include dup_title
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
it "should not find duplicate title by url" do
|
|
598
|
+
pre, post = find_duplicates_create_a_clone_and_find_again(:url, {:title => @event.title, :start_time => @event.start_time} )
|
|
599
|
+
expect(post.size).to eq pre.size
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
it "should find complete duplicates by all" do
|
|
603
|
+
pre, post = find_duplicates_create_a_clone_and_find_again(:all, @event.attributes)
|
|
604
|
+
expect(post.size).to eq(pre.size + 2)
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
it "should not find incomplete duplicates by all" do
|
|
608
|
+
pre, post = find_duplicates_create_a_clone_and_find_again(:all, @event.attributes.merge(:title => "SpaceCube", :start_time => @event.start_time ))
|
|
609
|
+
expect(post.size).to eq pre.size
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
it "should find duplicate for matching multiple fields" do
|
|
613
|
+
pre, post = find_duplicates_create_a_clone_and_find_again([:title, :start_time], {:title => @event.title, :start_time => @event.start_time })
|
|
614
|
+
expect(post.size).to eq(pre.size + 2)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
it "should not find duplicates for mismatching multiple fields" do
|
|
618
|
+
pre, post = find_duplicates_create_a_clone_and_find_again([:title, :start_time], {:title => "SpaceCube", :start_time => @event.start_time })
|
|
619
|
+
expect(post.size).to eq pre.size
|
|
620
|
+
end
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
describe "when squashing duplicates (integration test)" do
|
|
624
|
+
before do
|
|
625
|
+
@event = FactoryGirl.create(:event)
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
it "should consolidate associations, and merge tags" do
|
|
629
|
+
@event.tag_list = %w[first second] # master event contains one duplicate tag, and one unique tag
|
|
630
|
+
|
|
631
|
+
clone = Event.create!(@event.attributes)
|
|
632
|
+
clone.tag_list.replace %w[second third] # duplicate event also contains one duplicate tag, and one unique tag
|
|
633
|
+
clone.save!
|
|
634
|
+
clone.reload
|
|
635
|
+
expect(clone).not_to be_duplicate
|
|
636
|
+
|
|
637
|
+
Event.squash(@event, clone)
|
|
638
|
+
expect(@event.tag_list.to_a.sort).to eq %w[first second third] # master now contains all three tags
|
|
639
|
+
expect(clone.duplicate_of).to eq @event
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
describe "when checking for squashing" do
|
|
644
|
+
before do
|
|
645
|
+
@today = today
|
|
646
|
+
@master = Event.create!(:title => "Master", :start_time => @today)
|
|
647
|
+
@slave1 = Event.create!(:title => "1st slave", :start_time => @today, :duplicate_of_id => @master.id)
|
|
648
|
+
@slave2 = Event.create!(:title => "2nd slave", :start_time => @today, :duplicate_of_id => @slave1.id)
|
|
649
|
+
@orphan = Event.create!(:title => "orphan", :start_time => @today, :duplicate_of_id => 999999)
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
it "should recognize a master" do
|
|
653
|
+
expect(@master).to be_a_master
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
it "should recognize a slave" do
|
|
657
|
+
expect(@slave1).to be_a_slave
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
it "should not think that a slave is a master" do
|
|
661
|
+
expect(@slave2).not_to be_a_master
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
it "should not think that a master is a slave" do
|
|
665
|
+
expect(@master).not_to be_a_slave
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
it "should return the progenitor of a child" do
|
|
669
|
+
expect(@slave1.progenitor).to eq @master
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
it "should return the progenitor of a grandchild" do
|
|
673
|
+
expect(@slave2.progenitor).to eq @master
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
it "should return a master as its own progenitor" do
|
|
677
|
+
expect(@master.progenitor).to eq @master
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
it "should return a marked duplicate as progenitor if it is orphaned" do
|
|
681
|
+
expect(@orphan.progenitor).to eq @orphan
|
|
682
|
+
end
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
describe "when versioning" do
|
|
686
|
+
it "should have versions" do
|
|
687
|
+
expect(Event.new.versions).to eq []
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
it "should create a new version after updating" do
|
|
691
|
+
event = Event.create!(:title => "Event title", :start_time => Time.zone.parse('2008.04.12'))
|
|
692
|
+
expect(event.versions.count).to eq 1
|
|
693
|
+
|
|
694
|
+
event.title = "New Title"
|
|
695
|
+
event.save!
|
|
696
|
+
expect(event.versions.count).to eq 2
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
describe "when normalizing line-endings in the description" do
|
|
701
|
+
before do
|
|
702
|
+
@event = Event.new
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
it "should not molest contents without carriage-returns" do
|
|
706
|
+
@event.description = "foo\nbar"
|
|
707
|
+
expect(@event.description).to eq "foo\nbar"
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
it "should replace CRLF with LF" do
|
|
711
|
+
@event.description = "foo\r\nbar"
|
|
712
|
+
expect(@event.description).to eq "foo\nbar"
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
it "should replace stand-alone CR with LF" do
|
|
716
|
+
@event.description = "foo\rbar"
|
|
717
|
+
expect(@event.description).to eq "foo\nbar"
|
|
718
|
+
end
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
describe "when converting to iCal" do
|
|
722
|
+
def ical_roundtrip(events, opts = {})
|
|
723
|
+
parsed_events = RiCal.parse_string(Event::IcalRenderer.render(events, opts)).first.events
|
|
724
|
+
if events.is_a?(Event)
|
|
725
|
+
parsed_events.first
|
|
726
|
+
else
|
|
727
|
+
parsed_events
|
|
728
|
+
end
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
it "should produce parsable iCal output" do
|
|
732
|
+
expect { ical_roundtrip( FactoryGirl.build(:event) ) }.not_to raise_error
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
it "should represent an event without an end time as a 1-hour block" do
|
|
736
|
+
event = FactoryGirl.build(:event, :start_time => now, :end_time => nil)
|
|
737
|
+
expect(event.end_time).to be_blank
|
|
738
|
+
|
|
739
|
+
rt = ical_roundtrip(event)
|
|
740
|
+
expect(rt.dtend - rt.dtstart).to eq 1.hour
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
it "should set the appropriate end time if one is given" do
|
|
744
|
+
event = FactoryGirl.build(:event, :start_time => now, :end_time => now + 2.hours)
|
|
745
|
+
|
|
746
|
+
rt = ical_roundtrip(event)
|
|
747
|
+
expect(rt.dtend - rt.dtstart).to eq 2.hours
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
describe "when comparing Event's attributes to its iCalendar output" do
|
|
751
|
+
let(:event) { FactoryGirl.build(:event, :id => 123, :created_at => now) }
|
|
752
|
+
let(:ical) { ical_roundtrip(event) }
|
|
753
|
+
|
|
754
|
+
{ :summary => :title,
|
|
755
|
+
:created => :created_at,
|
|
756
|
+
:last_modified => :updated_at,
|
|
757
|
+
:description => :description,
|
|
758
|
+
:url => :url,
|
|
759
|
+
:dtstart => :start_time,
|
|
760
|
+
:dtstamp => :created_at
|
|
761
|
+
}.each do |ical_attribute, model_attribute|
|
|
762
|
+
it "should map the Event's #{model_attribute} attribute to '#{ical_attribute}' in the iCalendar output" do
|
|
763
|
+
model_value = event.send(model_attribute)
|
|
764
|
+
ical_value = ical.send(ical_attribute)
|
|
765
|
+
|
|
766
|
+
case model_value
|
|
767
|
+
when ActiveSupport::TimeWithZone
|
|
768
|
+
# Compare raw time because one is using local time zone, while other is using UTC time.
|
|
769
|
+
expect(model_value.to_i).to eq ical_value.to_i
|
|
770
|
+
else
|
|
771
|
+
expect(model_value).to eq ical_value
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
it "should call the URL helper to generate a UID" do
|
|
778
|
+
event = FactoryGirl.build(:event)
|
|
779
|
+
expect(ical_roundtrip(event, :url_helper => lambda {|e| "UID'D!" }).uid).to eq "UID'D!"
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
it "should strip HTML from the description" do
|
|
783
|
+
event = FactoryGirl.create(:event, :description => "<blink>OMFG HTML IS TEH AWESOME</blink>")
|
|
784
|
+
expect(ical_roundtrip(event).description).not_to include "<blink>"
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
it "should include tags in the description" do
|
|
788
|
+
event = FactoryGirl.build(:event)
|
|
789
|
+
event.tag_list = "tags, folksonomy, categorization"
|
|
790
|
+
expect(ical_roundtrip(event).description).to include event.tag_list.to_s
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
it "should leave URL blank if no URL is provided" do
|
|
794
|
+
event = FactoryGirl.build(:event, :url => nil)
|
|
795
|
+
expect(ical_roundtrip(event).url).to be_nil
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
it "should have Source URL if URL helper is given)" do
|
|
799
|
+
event = FactoryGirl.build(:event)
|
|
800
|
+
expect(ical_roundtrip(event, :url_helper => lambda{|e| "FAKE"} ).description).to match /FAKE/
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
it "should create multi-day entries for multi-day events" do
|
|
804
|
+
time = Time.zone.now
|
|
805
|
+
event = FactoryGirl.build(:event, :start_time => time, :end_time => time + 4.days)
|
|
806
|
+
parsed_event = ical_roundtrip( event )
|
|
807
|
+
|
|
808
|
+
start_time = Date.current
|
|
809
|
+
expect(parsed_event.dtstart).to eq start_time
|
|
810
|
+
expect(parsed_event.dtend).to eq(start_time + 5.days)
|
|
811
|
+
end
|
|
812
|
+
|
|
813
|
+
describe "sequence" do
|
|
814
|
+
def event_to_ical(event)
|
|
815
|
+
return RiCal.parse_string(Event::IcalRenderer.render([event])).first.events.first
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
it "should set an initial sequence on a new event" do
|
|
819
|
+
event = FactoryGirl.create(:event)
|
|
820
|
+
ical = event_to_ical(event)
|
|
821
|
+
expect(ical.sequence).to eq 1
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
it "should increment the sequence if it is updated" do
|
|
825
|
+
event = FactoryGirl.create(:event)
|
|
826
|
+
event.update_attribute(:title, "Update 1")
|
|
827
|
+
ical = event_to_ical(event)
|
|
828
|
+
expect(ical.sequence).to eq 2
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
# it "should offset the squence based the global SECRETS.icalendar_sequence_offset" do
|
|
832
|
+
# SECRETS.should_receive(:icalendar_sequence_offset).and_return(41)
|
|
833
|
+
# event = FactoryGirl.build(:event)
|
|
834
|
+
# ical = event_to_ical(event)
|
|
835
|
+
# ical.sequence.should eq 42
|
|
836
|
+
# end
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
describe "- the headers" do
|
|
840
|
+
before do
|
|
841
|
+
@data = Event::IcalRenderer.render(FactoryGirl.build(:event))
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
it "should include the calendar name" do
|
|
845
|
+
expect(@data).to match /\sX-WR-CALNAME:#{Calagator.title}\s/
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
it "should include the method" do
|
|
849
|
+
expect(@data).to match /\sMETHOD:PUBLISH\s/
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
it "should include the scale" do
|
|
853
|
+
expect(@data).to match /\sCALSCALE:Gregorian\s/i
|
|
854
|
+
end
|
|
855
|
+
end
|
|
856
|
+
end
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
end
|