├── .bummr-build.sh
├── .circleci
└── config.yml
├── .codeclimate.yml
├── .dockerignore
├── .foreman.example
├── .gitattributes
├── .gitignore
├── .haml-lint.yml
├── .hound.yml
├── .node-version
├── .reek
├── .reek.yml
├── .rspec
├── .rubocop.yml
├── .rubocop_todo.yml
├── .ruby-gemset
├── .ruby-version
├── .scss-lint.yml
├── .snyk
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── INSTALL.md
├── LICENSE.md
├── Procfile
├── README.md
├── Rakefile
├── VERSION
├── app
├── assets
│ ├── config
│ │ └── manifest.js
│ ├── javascripts
│ │ ├── admin
│ │ │ ├── accepted_payments.js
│ │ │ ├── application.js
│ │ │ ├── categories_form.js
│ │ │ ├── form.js.coffee
│ │ │ ├── funding_sources.js
│ │ │ ├── languages.js
│ │ │ ├── org_autocomplete.js
│ │ │ ├── required_documents.js
│ │ │ ├── service_areas.js
│ │ │ └── tags.js
│ │ └── application.js
│ └── stylesheets
│ │ ├── _variables.scss
│ │ ├── admin
│ │ └── application.css.scss
│ │ ├── application.css.scss
│ │ └── framework_and_overrides.css.scss
├── controllers
│ ├── admin
│ │ ├── contacts_controller.rb
│ │ ├── csv_controller.rb
│ │ ├── dashboard_controller.rb
│ │ ├── locations_controller.rb
│ │ ├── organization_contacts_controller.rb
│ │ ├── organizations_controller.rb
│ │ ├── programs_controller.rb
│ │ ├── registrations_controller.rb
│ │ ├── service_contacts_controller.rb
│ │ └── services_controller.rb
│ ├── api
│ │ └── v1
│ │ │ ├── address_controller.rb
│ │ │ ├── categories_controller.rb
│ │ │ ├── contact_phones_controller.rb
│ │ │ ├── contacts_controller.rb
│ │ │ ├── cors_controller.rb
│ │ │ ├── errors_controller.rb
│ │ │ ├── location_phones_controller.rb
│ │ │ ├── locations_controller.rb
│ │ │ ├── mail_address_controller.rb
│ │ │ ├── organizations_controller.rb
│ │ │ ├── root_controller.rb
│ │ │ ├── search_controller.rb
│ │ │ ├── services_controller.rb
│ │ │ └── status_controller.rb
│ ├── api_applications_controller.rb
│ ├── application_controller.rb
│ ├── concerns
│ │ ├── cacheable.rb
│ │ ├── custom_errors.rb
│ │ ├── pagination_headers.rb
│ │ ├── private_registration.rb
│ │ ├── taggable.rb
│ │ └── token_validator.rb
│ ├── home_controller.rb
│ └── user
│ │ └── registrations_controller.rb
├── helpers
│ └── admin
│ │ ├── csv_download_helper.rb
│ │ └── form_helper.rb
├── mailers
│ ├── .gitkeep
│ ├── admin_mailer.rb
│ ├── application_mailer.rb
│ └── custom_mailer.rb
├── models
│ ├── .gitkeep
│ ├── address.rb
│ ├── admin.rb
│ ├── api_application.rb
│ ├── application_record.rb
│ ├── category.rb
│ ├── concerns
│ │ ├── parent_presence_validatable.rb
│ │ └── search.rb
│ ├── contact.rb
│ ├── holiday_schedule.rb
│ ├── location.rb
│ ├── mail_address.rb
│ ├── nested_categories.rb
│ ├── organization.rb
│ ├── phone.rb
│ ├── program.rb
│ ├── regular_schedule.rb
│ ├── service.rb
│ └── user.rb
├── policies
│ ├── application_policy.rb
│ ├── contact_policy.rb
│ ├── location_policy.rb
│ ├── organization_policy.rb
│ ├── program_policy.rb
│ └── service_policy.rb
├── serializers
│ ├── address_serializer.rb
│ ├── category_serializer.rb
│ ├── contact_serializer.rb
│ ├── holiday_schedule_serializer.rb
│ ├── location_serializer.rb
│ ├── locations_organization_serializer.rb
│ ├── locations_serializer.rb
│ ├── mail_address_serializer.rb
│ ├── nearby_serializer.rb
│ ├── organization_serializer.rb
│ ├── phone_serializer.rb
│ ├── regular_schedule_serializer.rb
│ ├── service_serializer.rb
│ └── summarized_organization_serializer.rb
├── validators
│ ├── array_validator.rb
│ ├── date_validator.rb
│ ├── email_validator.rb
│ ├── pg_array_validator.rb
│ ├── phone_validator.rb
│ ├── regex_validator.rb
│ ├── service_area_validator.rb
│ ├── state_province_validator.rb
│ ├── url_validator.rb
│ ├── weekday_validator.rb
│ └── zip_validator.rb
└── views
│ ├── admin
│ ├── contacts
│ │ ├── _form.html.haml
│ │ ├── edit.html.haml
│ │ ├── forms
│ │ │ ├── _danger.html.haml
│ │ │ ├── _department.html.haml
│ │ │ ├── _editing.html.haml
│ │ │ ├── _email.html.haml
│ │ │ ├── _fields.html.haml
│ │ │ ├── _new_contact.html.haml
│ │ │ ├── _new_org_contact.html.haml
│ │ │ ├── _new_service_contact.html.haml
│ │ │ └── _title.html.haml
│ │ └── new.html.haml
│ ├── csv
│ │ ├── addresses.csv.shaper
│ │ ├── contacts.csv.shaper
│ │ ├── holiday_schedules.csv.shaper
│ │ ├── locations.csv.shaper
│ │ ├── mail_addresses.csv.shaper
│ │ ├── organizations.csv.shaper
│ │ ├── phones.csv.shaper
│ │ ├── programs.csv.shaper
│ │ ├── regular_schedules.csv.shaper
│ │ └── services.csv.shaper
│ ├── dashboard
│ │ └── index.html.haml
│ ├── locations
│ │ ├── _confirm_delete_location.html.haml
│ │ ├── _form.html.haml
│ │ ├── edit.html.haml
│ │ ├── forms
│ │ │ ├── _accessibility.html.haml
│ │ │ ├── _address.html.haml
│ │ │ ├── _address_fields.html.haml
│ │ │ ├── _admin_email_fields.html.haml
│ │ │ ├── _admin_email_fields_for_new_location.html.haml
│ │ │ ├── _admin_emails.html.haml
│ │ │ ├── _description.html.haml
│ │ │ ├── _fields.html.haml
│ │ │ ├── _holiday_schedule_fields.html.haml
│ │ │ ├── _languages.html.haml
│ │ │ ├── _mail_address.html.haml
│ │ │ ├── _mail_address_fields.html.haml
│ │ │ ├── _new_location_form.html.haml
│ │ │ ├── _phone_fields.html.haml
│ │ │ ├── _phones.html.haml
│ │ │ ├── _regular_schedule_fields.html.haml
│ │ │ ├── _short_desc.html.haml
│ │ │ ├── _transportation.html.haml
│ │ │ └── _virtual.html.haml
│ │ ├── index.html.haml
│ │ └── new.html.haml
│ ├── mailer
│ │ ├── confirmation_instructions.html.haml
│ │ └── reset_password_instructions.html.haml
│ ├── organization_contacts
│ │ ├── edit.html.haml
│ │ └── new.html.haml
│ ├── organizations
│ │ ├── _confirm_delete_organization.html.haml
│ │ ├── _form.html.haml
│ │ ├── edit.html.haml
│ │ ├── forms
│ │ │ ├── _accreditations.html.haml
│ │ │ ├── _date_incorporated.html.haml
│ │ │ ├── _description.html.haml
│ │ │ ├── _fields.html.haml
│ │ │ ├── _legal_status.html.haml
│ │ │ ├── _licenses.html.haml
│ │ │ ├── _new_organization_form.html.haml
│ │ │ ├── _tax_id.html.haml
│ │ │ └── _tax_status.html.haml
│ │ ├── index.html.haml
│ │ └── new.html.haml
│ ├── programs
│ │ ├── _confirm_delete_program.html.haml
│ │ ├── _form.html.haml
│ │ ├── edit.html.haml
│ │ ├── forms
│ │ │ ├── _danger.html.haml
│ │ │ ├── _editing.html.haml
│ │ │ ├── _fields.html.haml
│ │ │ └── _new_program_form.html.haml
│ │ ├── index.html.haml
│ │ └── new.html.haml
│ ├── service_contacts
│ │ ├── edit.html.haml
│ │ └── new.html.haml
│ ├── services
│ │ ├── _confirm_delete_service.html.haml
│ │ ├── _form.html.haml
│ │ ├── edit.html.haml
│ │ ├── forms
│ │ │ ├── _accepted_payments.html.haml
│ │ │ ├── _application_process.haml
│ │ │ ├── _audience.html.haml
│ │ │ ├── _categories.html.haml
│ │ │ ├── _description.html.haml
│ │ │ ├── _eligibility.html.haml
│ │ │ ├── _fees.html.haml
│ │ │ ├── _fields.html.haml
│ │ │ ├── _interpretation_services.html.haml
│ │ │ ├── _keywords.html.haml
│ │ │ ├── _languages.html.haml
│ │ │ ├── _new_service_form.html.haml
│ │ │ ├── _program.html.haml
│ │ │ ├── _required_documents.html.haml
│ │ │ ├── _service_areas.html.haml
│ │ │ ├── _service_locations.html.haml
│ │ │ ├── _status.html.haml
│ │ │ └── _wait.html.haml
│ │ ├── index.html.haml
│ │ └── new.html.haml
│ └── shared
│ │ ├── _navigation.html.haml
│ │ ├── _navigation_links.html.haml
│ │ └── forms
│ │ ├── _alternate_name.html.haml
│ │ ├── _choose_org.html.haml
│ │ ├── _email.html.haml
│ │ ├── _funding_sources.html.haml
│ │ ├── _holiday_hours.html.haml
│ │ ├── _hours.html.haml
│ │ ├── _name.html.haml
│ │ └── _website.html.haml
│ ├── admin_mailer
│ └── existing_email_signup.html.haml
│ ├── api_applications
│ ├── _form.html.haml
│ ├── edit.html.haml
│ ├── index.html.haml
│ └── new.html.haml
│ ├── devise
│ ├── confirmations
│ │ └── new.html.haml
│ ├── mailer
│ │ ├── confirmation_instructions.html.haml
│ │ └── reset_password_instructions.html.haml
│ ├── passwords
│ │ ├── edit.html.haml
│ │ └── new.html.haml
│ ├── registrations
│ │ ├── edit.html.haml
│ │ └── new.html.haml
│ ├── sessions
│ │ └── new.html.haml
│ └── shared
│ │ ├── _error_messages.html.erb
│ │ └── _links.html.haml
│ ├── home
│ └── index.html.haml
│ ├── layouts
│ ├── admin.html.haml
│ └── application.html.haml
│ └── shared
│ ├── _messages.html.haml
│ ├── _navigation.html.haml
│ └── _navigation_links.html.haml
├── bin
├── autospec
├── bundle
├── rails
├── rake
├── rspec
├── setup
├── spring
├── update
└── yarn
├── categories-in-ohana-api-admin.png
├── config.ru
├── config
├── application.example.yml
├── application.rb
├── boot.rb
├── database.travis.yml
├── database.vagrant.yml
├── database.yml
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ └── test.rb
├── initializers
│ ├── active_model_serializers.rb
│ ├── application_controller_renderer.rb
│ ├── assets.rb
│ ├── auto_strip_attributes.rb
│ ├── backtrace_silencers.rb
│ ├── content_security_policy.rb
│ ├── cookies_serializer.rb
│ ├── csv_shaper.rb
│ ├── devise.rb
│ ├── figaro.rb
│ ├── filter_parameter_logging.rb
│ ├── friendly_id.rb
│ ├── generators.rb
│ ├── geocoder.rb
│ ├── inflections.rb
│ ├── kaminari.rb
│ ├── kaminari_config.rb
│ ├── mime_types.rb
│ ├── new_framework_defaults_5_2.rb
│ ├── secret_token.rb
│ ├── session_store.rb
│ ├── version.rb
│ └── wrap_parameters.rb
├── locales
│ ├── devise.en.yml
│ └── en.yml
├── puma.rb
├── routes.rb
├── settings.example.yml
├── settings.yml
├── spring.rb
└── storage.yml
├── data
├── oe.json
├── ohana_api_development.dump
└── sample-csv
│ ├── addresses.csv
│ ├── contacts.csv
│ ├── holiday_schedules.csv
│ ├── locations.csv
│ ├── mail_addresses.csv
│ ├── organizations.csv
│ ├── phones.csv
│ ├── programs.csv
│ ├── regular_schedules.csv
│ ├── services.csv
│ └── taxonomy.csv
├── db
├── migrate
│ ├── 20140328034023_create_addresses.rb
│ ├── 20140328034531_create_organizations.rb
│ ├── 20140328034754_create_locations.rb
│ ├── 20140328035528_create_users.rb
│ ├── 20140328041648_create_api_applications.rb
│ ├── 20140328041859_create_contacts.rb
│ ├── 20140328042108_create_faxes.rb
│ ├── 20140328042218_create_mail_addresses.rb
│ ├── 20140328042359_create_phones.rb
│ ├── 20140328043104_create_services.rb
│ ├── 20140328044447_create_categories.rb
│ ├── 20140328052427_create_friendly_id_slugs.rb
│ ├── 20140402222453_create_categories_services.rb
│ ├── 20140404220233_add_ancestry_to_category.rb
│ ├── 20140424182454_remove_kind_from_locations.rb
│ ├── 20140505011725_add_search_vector_to_locations.rb
│ ├── 20140508030435_add_search_indexes_to_locations.rb
│ ├── 20140508030926_add_search_index_to_organizations.rb
│ ├── 20140508031024_add_search_index_to_categories.rb
│ ├── 20140508194831_update_search_vector_migration.rb
│ ├── 20140522153640_add_number_type_to_phones.rb
│ ├── 20140629181523_devise_create_admins.rb
│ ├── 20140630171418_add_super_admin_to_admins.rb
│ ├── 20140829154350_add_search_indexes_to_services.rb
│ ├── 20140909031145_convert_languages_to_array_type.rb
│ ├── 20140929221750_add_fields_to_organization.rb
│ ├── 20141007144757_add_fields_to_location.rb
│ ├── 20141009185459_add_fields_to_address.rb
│ ├── 20141009204519_add_fields_to_mail_address.rb
│ ├── 20141010031124_add_country_prefix_to_phones.rb
│ ├── 20141010155451_drop_faxes_table.rb
│ ├── 20141010171020_update_contact_fields.rb
│ ├── 20141010171817_add_contact_ref_to_phones.rb
│ ├── 20141017154640_add_fields_to_services.rb
│ ├── 20141021195019_update_service_status.rb
│ ├── 20141023040419_add_active_to_locations.rb
│ ├── 20141024022657_create_programs.rb
│ ├── 20141024025404_add_program_ref_to_services.rb
│ ├── 20141027154101_create_regular_schedules.rb
│ ├── 20141029170109_remove_hours_from_locations.rb
│ ├── 20141030012617_create_holiday_schedules.rb
│ ├── 20141030204742_add_array_fields_to_organization.rb
│ ├── 20141106215928_add_organization_ref_to_contacts.rb
│ ├── 20141107161835_add_service_ref_to_contacts.rb
│ ├── 20141108042551_add_organization_ref_to_phones.rb
│ ├── 20141108183056_add_service_ref_to_phones.rb
│ ├── 20141108194838_replace_location_urls_with_website.rb
│ ├── 20141108214741_add_website_index_to_locations.rb
│ ├── 20141109021540_replace_location_emails_with_email.rb
│ ├── 20141109022202_add_email_index_to_locations.rb
│ ├── 20141118132537_rename_wait_to_wait_time.rb
│ ├── 20141208165502_rename_oe_id_to_taxonomy_id.rb
│ ├── 20150107163352_add_interpretation_services_to_services.rb
│ ├── 20150314204202_rename_fields.rb
│ ├── 20150315202808_update_null_constraints.rb
│ └── 20180212023953_convert_admin_emails_to_array.rb
├── seeds.rb
└── structure.sql
├── docker-compose.yml
├── lib
├── address_extractor.rb
├── api_constraints.rb
├── asset_hosts.rb
├── assets
│ └── .gitkeep
├── category_id_collector.rb
├── category_importer.rb
├── category_presenter.rb
├── config_validator.rb
├── contact_importer.rb
├── contact_presenter.rb
├── default_host.rb
├── email_filter.rb
├── entity_importer.rb
├── entity_presenter.rb
├── exceptions.rb
├── file_checker.rb
├── holiday_schedule_importer.rb
├── holiday_schedule_presenter.rb
├── importer_errors.rb
├── location_filter.rb
├── location_importer.rb
├── location_presenter.rb
├── mail_address_importer.rb
├── mail_address_presenter.rb
├── organization_importer.rb
├── organization_presenter.rb
├── parent_assigner.rb
├── phone_importer.rb
├── phone_presenter.rb
├── program_importer.rb
├── program_presenter.rb
├── regular_schedule_importer.rb
├── regular_schedule_presenter.rb
├── service_importer.rb
├── service_presenter.rb
├── subdomain_constraints.rb
└── tasks
│ ├── .gitkeep
│ ├── import.rake
│ ├── oe.rake
│ ├── remove_test_users_and_admins.rake
│ └── taxonomy.rake
├── public
├── 404.html
├── 422.html
├── 500.html
├── favicon.ico
├── humans.txt
└── robots.txt
├── script
├── bootstrap
├── export
├── export_prod_db
├── geocode
├── import
├── reset
├── reset_test_db
├── restore
├── restore_prod_db
├── setup_db
├── setup_heroku
├── test
└── users
├── spec
├── active_record_spec_helper.rb
├── api
│ ├── contacts
│ │ ├── create_phone_spec.rb
│ │ ├── delete_phone_spec.rb
│ │ └── patch_phone_spec.rb
│ ├── cors_spec.rb
│ ├── create_address_spec.rb
│ ├── create_contact_spec.rb
│ ├── create_location_spec.rb
│ ├── create_mail_address_spec.rb
│ ├── create_organization_spec.rb
│ ├── create_service_spec.rb
│ ├── delete_address_spec.rb
│ ├── delete_contact_spec.rb
│ ├── delete_location_spec.rb
│ ├── delete_mail_address_spec.rb
│ ├── delete_organization_spec.rb
│ ├── delete_service_spec.rb
│ ├── get_categories_spec.rb
│ ├── get_category_children_spec.rb
│ ├── get_location_contacts_spec.rb
│ ├── get_location_services_spec.rb
│ ├── get_location_spec.rb
│ ├── get_locations_spec.rb
│ ├── get_organization_locations_spec.rb
│ ├── get_organization_spec.rb
│ ├── get_organizations_spec.rb
│ ├── locations
│ │ ├── create_phone_spec.rb
│ │ ├── delete_phone_spec.rb
│ │ ├── get_location_phones_spec.rb
│ │ └── patch_phone_spec.rb
│ ├── nearby_spec.rb
│ ├── pagination_headers_spec.rb
│ ├── patch_address_spec.rb
│ ├── patch_contact_spec.rb
│ ├── patch_location_spec.rb
│ ├── patch_mail_address_spec.rb
│ ├── patch_organization_spec.rb
│ ├── patch_service_spec.rb
│ ├── put_service_categories_spec.rb
│ ├── root_endpoint_spec.rb
│ ├── search
│ │ ├── category_spec.rb
│ │ ├── language_spec.rb
│ │ ├── location_status_spec.rb
│ │ └── service_area_spec.rb
│ ├── search_spec.rb
│ └── services
│ │ └── update_service_categories_spec.rb
├── controllers
│ ├── admin
│ │ ├── contacts_controller_spec.rb
│ │ ├── csv_controller_spec.rb
│ │ ├── locations_controller_spec.rb
│ │ ├── organization_contacts_controller_spec.rb
│ │ ├── organizations_controller_spec.rb
│ │ ├── programs_controller_spec.rb
│ │ ├── service_contacts_controller_spec.rb
│ │ └── services_controller_spec.rb
│ ├── api_applications_controller_spec.rb
│ └── home_controller_spec.rb
├── factories
│ ├── addresses.rb
│ ├── admins.rb
│ ├── api_applications.rb
│ ├── categories.rb
│ ├── contacts.rb
│ ├── holiday_schedules.rb
│ ├── locations.rb
│ ├── mail_addresses.rb
│ ├── organizations.rb
│ ├── phones.rb
│ ├── programs.rb
│ ├── regular_schedules.rb
│ ├── services.rb
│ └── users.rb
├── features
│ ├── admin
│ │ ├── contacts
│ │ │ ├── create_contact_spec.rb
│ │ │ ├── delete_contact_spec.rb
│ │ │ └── update_contact_spec.rb
│ │ ├── csv
│ │ │ ├── download_addresses_csv_spec.rb
│ │ │ ├── download_contacts_csv_spec.rb
│ │ │ ├── download_holiday_schedules_csv_spec.rb
│ │ │ ├── download_locations_csv_spec.rb
│ │ │ ├── download_mail_addresses_csv_spec.rb
│ │ │ ├── download_organizations_csv_spec.rb
│ │ │ ├── download_phones_csv_spec.rb
│ │ │ ├── download_programs_csv_spec.rb
│ │ │ ├── download_regular_schedules_csv_spec.rb
│ │ │ └── download_services_csv_spec.rb
│ │ ├── dashboard_spec.rb
│ │ ├── edit_account_spec.rb
│ │ ├── locations
│ │ │ ├── ability_to_add_admin_email_spec.rb
│ │ │ ├── create_location_spec.rb
│ │ │ ├── delete_location_spec.rb
│ │ │ ├── update_accessibility_spec.rb
│ │ │ ├── update_address_spec.rb
│ │ │ ├── update_admin_emails_spec.rb
│ │ │ ├── update_alternate_name_spec.rb
│ │ │ ├── update_description_spec.rb
│ │ │ ├── update_email_spec.rb
│ │ │ ├── update_holiday_hours_spec.rb
│ │ │ ├── update_hours_spec.rb
│ │ │ ├── update_languages_spec.rb
│ │ │ ├── update_mail_address_spec.rb
│ │ │ ├── update_name_spec.rb
│ │ │ ├── update_phone_numbers_spec.rb
│ │ │ ├── update_short_description_spec.rb
│ │ │ ├── update_transportation_spec.rb
│ │ │ ├── update_virtual_spec.rb
│ │ │ ├── update_website_spec.rb
│ │ │ ├── visit_location_spec.rb
│ │ │ └── visit_locations_spec.rb
│ │ ├── organizations
│ │ │ ├── create_contact_spec.rb
│ │ │ ├── create_organization_spec.rb
│ │ │ ├── delete_contact_spec.rb
│ │ │ ├── delete_organization_spec.rb
│ │ │ ├── update_accreditations_spec.rb
│ │ │ ├── update_alternate_name_spec.rb
│ │ │ ├── update_contact_name_spec.rb
│ │ │ ├── update_date_incorporated_spec.rb
│ │ │ ├── update_description_spec.rb
│ │ │ ├── update_email_spec.rb
│ │ │ ├── update_funding_sources_spec.rb
│ │ │ ├── update_legal_status_spec.rb
│ │ │ ├── update_licenses_spec.rb
│ │ │ ├── update_name_spec.rb
│ │ │ ├── update_phone_numbers_spec.rb
│ │ │ ├── update_tax_id_spec.rb
│ │ │ ├── update_tax_status_spec.rb
│ │ │ ├── update_website_spec.rb
│ │ │ ├── visit_organization_spec.rb
│ │ │ └── visit_organizations_spec.rb
│ │ ├── programs
│ │ │ ├── create_program_spec.rb
│ │ │ ├── delete_program_spec.rb
│ │ │ ├── update_alternate_name_spec.rb
│ │ │ ├── update_name_spec.rb
│ │ │ └── visit_programs_spec.rb
│ │ ├── services
│ │ │ ├── copy_service_to_location_spec.rb
│ │ │ ├── create_contact_spec.rb
│ │ │ ├── create_service_spec.rb
│ │ │ ├── delete_contact_spec.rb
│ │ │ ├── delete_service_spec.rb
│ │ │ ├── update_accepted_payments_spec.rb
│ │ │ ├── update_alternate_name_spec.rb
│ │ │ ├── update_audience_spec.rb
│ │ │ ├── update_categories_spec.rb
│ │ │ ├── update_contact_name_spec.rb
│ │ │ ├── update_description_spec.rb
│ │ │ ├── update_eligibility_spec.rb
│ │ │ ├── update_email_spec.rb
│ │ │ ├── update_fees_spec.rb
│ │ │ ├── update_funding_sources_spec.rb
│ │ │ ├── update_holiday_hours_spec.rb
│ │ │ ├── update_hours_spec.rb
│ │ │ ├── update_how_to_apply_spec.rb
│ │ │ ├── update_interpretation_services_spec.rb
│ │ │ ├── update_keywords_spec.rb
│ │ │ ├── update_languages_spec.rb
│ │ │ ├── update_name_spec.rb
│ │ │ ├── update_phone_numbers_spec.rb
│ │ │ ├── update_program_spec.rb
│ │ │ ├── update_required_documents_spec.rb
│ │ │ ├── update_service_areas_spec.rb
│ │ │ ├── update_status_spec.rb
│ │ │ ├── update_wait_time_spec.rb
│ │ │ ├── update_website_spec.rb
│ │ │ └── visit_services_spec.rb
│ │ ├── sign_in_spec.rb
│ │ ├── sign_out_spec.rb
│ │ └── sign_up_spec.rb
│ ├── create_new_api_application_spec.rb
│ ├── delete_api_application_spec.rb
│ ├── edit_account_spec.rb
│ ├── homepage_text_spec.rb
│ ├── sign_out_spec.rb
│ ├── signin_spec.rb
│ ├── signup_spec.rb
│ └── update_api_application_spec.rb
├── lib
│ ├── address_extractor_spec.rb
│ ├── api_constraints_spec.rb
│ ├── asset_hosts_spec.rb
│ ├── category_importer_spec.rb
│ ├── config_validator_spec.rb
│ ├── contact_importer_spec.rb
│ ├── default_host_spec.rb
│ ├── file_checker_spec.rb
│ ├── holiday_schedule_importer_spec.rb
│ ├── importer_errors_spec.rb
│ ├── location_importer_spec.rb
│ ├── location_presenter_spec.rb
│ ├── mail_address_importer_spec.rb
│ ├── organization_importer_spec.rb
│ ├── organization_presenter_spec.rb
│ ├── phone_importer_spec.rb
│ ├── program_importer_spec.rb
│ ├── regular_schedule_importer_spec.rb
│ ├── service_importer_spec.rb
│ ├── service_presenter_spec.rb
│ └── tasks
│ │ └── remove_test_users_and_admins_rake_spec.rb
├── models
│ ├── address_spec.rb
│ ├── admin_spec.rb
│ ├── api_application_spec.rb
│ ├── category_spec.rb
│ ├── contact_spec.rb
│ ├── holiday_schedule_spec.rb
│ ├── location_spec.rb
│ ├── mail_address_spec.rb
│ ├── organization_spec.rb
│ ├── phone_spec.rb
│ ├── program_spec.rb
│ ├── regular_schedule_spec.rb
│ ├── service_spec.rb
│ └── user_spec.rb
├── rails_helper.rb
├── requests
│ ├── api_applications_spec.rb
│ └── status_spec.rb
├── routing
│ └── api_applications_routing_spec.rb
├── spec_helper.rb
└── support
│ ├── admin_controller_helper.rb
│ ├── api
│ └── model_helpers.rb
│ ├── api_constraints_spec_helper.rb
│ ├── bullet.rb
│ ├── capybara.rb
│ ├── database_cleaner.rb
│ ├── default_headers.rb
│ ├── features
│ ├── contact_helpers.rb
│ ├── form_helpers.rb
│ ├── phone_helpers.rb
│ ├── schedule_helpers.rb
│ └── session_helpers.rb
│ ├── fixtures
│ ├── contact_with_no_parent.csv
│ ├── existing_category.csv
│ ├── holiday_schedule_with_no_parent.csv
│ ├── hs_with_2_digit_year.csv
│ ├── hs_with_invalid_date.csv
│ ├── hs_with_spelled_out_date.csv
│ ├── invalid_address.csv
│ ├── invalid_category.csv
│ ├── invalid_contact.csv
│ ├── invalid_holiday_schedule.csv
│ ├── invalid_location.csv
│ ├── invalid_location_org.csv
│ ├── invalid_mail_address.csv
│ ├── invalid_mail_address_location.csv
│ ├── invalid_org.csv
│ ├── invalid_parent_category.csv
│ ├── invalid_phone.csv
│ ├── invalid_program.csv
│ ├── invalid_program_headers.csv
│ ├── invalid_regular_schedule.csv
│ ├── invalid_service.csv
│ ├── invalid_service_location.csv
│ ├── locations.csv
│ ├── missing_address.csv
│ ├── org_with_2_digit_year.csv
│ ├── org_with_invalid_date.csv
│ ├── org_with_spelled_out_date.csv
│ ├── phone_with_no_parent.csv
│ ├── program_with_no_parent.csv
│ ├── programs.csv
│ ├── regular_schedule_with_no_parent.csv
│ ├── services.csv
│ ├── taxonomy.csv
│ ├── valid_address.csv
│ ├── valid_category.csv
│ ├── valid_contact_phone.csv
│ ├── valid_location.csv
│ ├── valid_location_contact.csv
│ ├── valid_location_holiday_schedule.csv
│ ├── valid_location_phone.csv
│ ├── valid_location_regular_schedule.csv
│ ├── valid_mail_address.csv
│ ├── valid_org.csv
│ ├── valid_org_contact.csv
│ ├── valid_org_phone.csv
│ ├── valid_program.csv
│ ├── valid_service.csv
│ ├── valid_service_contact.csv
│ ├── valid_service_holiday_schedule.csv
│ ├── valid_service_phone.csv
│ └── valid_service_regular_schedule.csv
│ ├── geocoder_stubs.rb
│ ├── mailer_macros.rb
│ ├── request_helpers.rb
│ ├── shared_contexts
│ └── rake.rb
│ ├── shared_examples_for_schedules.rb
│ ├── shoulda_matchers.rb
│ ├── warden.rb
│ └── webmock.rb
└── vendor
├── assets
├── javascripts
│ └── .gitkeep
└── stylesheets
│ └── .gitkeep
└── plugins
└── .gitkeep
/.bummr-build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | bundle exec rspec --fail-fast
3 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | Dockerfile
2 | tmp
3 | log
4 | *.md
5 | .git*
6 |
--------------------------------------------------------------------------------
/.foreman.example:
--------------------------------------------------------------------------------
1 | formation: release=0,web=1
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text eol=lf
2 | *.csv eol=crlf
3 | *.dump binary
4 | *.png binary
5 | *.jpg binary
6 | *.gif binary
7 |
--------------------------------------------------------------------------------
/.hound.yml:
--------------------------------------------------------------------------------
1 | ruby:
2 | config_file: .rubocop.yml
3 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | 16.13.1
2 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --require spec_helper
3 |
--------------------------------------------------------------------------------
/.ruby-gemset:
--------------------------------------------------------------------------------
1 | ohana-api
2 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.7.5
2 |
--------------------------------------------------------------------------------
/.scss-lint.yml:
--------------------------------------------------------------------------------
1 | linters:
2 | Comment:
3 | exclude:
4 | - 'app/assets/stylesheets/application.css.scss'
5 | - 'app/assets/stylesheets/admin/application.css.scss'
6 | PlaceholderInExtend:
7 | exclude:
8 | - 'app/assets/stylesheets/framework_and_overrides.css.scss'
9 |
--------------------------------------------------------------------------------
/.snyk:
--------------------------------------------------------------------------------
1 | version: v1.38.1
2 | ignore:
3 | 'SNYK-RUBY-NOKOGIRI-20299':
4 | - '* > nokogiri':
5 | reason: 'We do not opt in to DTDLOAD and do not opt out of NONET'
6 | expires: '2019-02-14T00:00:00.000Z'
7 | 'SNYK-RUBY-ACTIONCABLE-20338':
8 | - '* > actioncable':
9 | reason: 'We do not use actioncable'
10 | expires: '2019-02-14T00:00:00.000Z'
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use the official Ruby image because the Rails images have been deprecated
2 | FROM ruby:2.7.5
3 |
4 | RUN apt-get update \
5 | && apt-get install -y --no-install-recommends postgresql-client \
6 | && rm -rf /var/lib/apt/lists/*
7 |
8 | RUN mkdir /usr/local/node \
9 | && curl -L https://nodejs.org/dist/v4.4.7/node-v4.4.7-linux-x64.tar.xz | tar Jx -C /usr/local/node --strip-components=1
10 | RUN ln -s ../node/bin/node /usr/local/bin/
11 |
12 | WORKDIR /ohana-api
13 |
14 | COPY Gemfile /ohana-api
15 | COPY Gemfile.lock /ohana-api
16 |
17 | RUN gem install bundler
18 | RUN bundle install --jobs 20 --retry 5 --without production
19 |
20 | COPY . /ohana-api
21 |
22 | EXPOSE 8080
23 | CMD ["rails", "server", "-b", "0.0.0.0", "-p", "8080"]
24 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec puma -C ./config/puma.rb
2 | release: bundle exec rake db:migrate
3 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically
3 | # be available to Rake.
4 |
5 | require File.expand_path('config/application', __dir__)
6 |
7 | Rails.application.load_tasks
8 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 1.1.0
2 |
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_directory ../javascripts .js
2 | //= link_directory ../javascripts/admin .js
3 | //= link_directory ../stylesheets .css
4 | //= link_directory ../stylesheets/admin .css
5 | //= link application.css
6 | //= link admin/application.css
7 |
--------------------------------------------------------------------------------
/app/assets/javascripts/admin/accepted_payments.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | $('#service_accepted_payments').select2();
3 | });
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/admin/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file.
9 | //
10 | // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require jquery
14 | //= require jquery_ujs
15 | //= require bootstrap-sprockets
16 | //= require select2
17 | //= require_tree .
18 |
--------------------------------------------------------------------------------
/app/assets/javascripts/admin/funding_sources.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | $('#service_funding_sources,#organization_funding_sources').select2();
3 | });
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/admin/languages.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | $('#location_languages,#service_languages').select2();
3 | });
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/admin/org_autocomplete.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | $('#org-name').select2({
3 | minimumInputLength: 3,
4 | ajax: {
5 | url: $(this).data('url'),
6 | dataType: "json",
7 | data: function(term, page) {
8 | return {
9 | q: term,
10 | }
11 | },
12 | results: function(data, page) {
13 | return {
14 | results: $.map( data, function(org, i) {
15 | return { id: org[0], text: org[1] }
16 | } )
17 | }
18 | }
19 | }
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/app/assets/javascripts/admin/required_documents.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | $('#service_required_documents').select2();
3 | });
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/admin/service_areas.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | $('#service_service_areas').select2();
3 | });
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/admin/tags.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | $("#service_keywords,#organization_accreditations,#organization_licenses").select2({
3 | tags: [''],
4 | tokenSeparators: [',']
5 | });
6 | });
7 |
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require jquery
14 | //= require jquery_ujs
15 | //= require bootstrap-sprockets
16 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/_variables.scss:
--------------------------------------------------------------------------------
1 | $light-blue: #c8d4e8;
2 | $lighter-blue: #cbe0f3;
3 |
4 | $black: #000;
5 | $white: #fff;
6 |
7 | $red: #b5121b;
8 | $light-red: #df3e3e;
9 | $lighter-red: #de9292;
10 |
11 | $gray: #fcfcfc;
12 |
13 | $light-green: #c8e8ca;
14 |
--------------------------------------------------------------------------------
/app/controllers/admin/dashboard_controller.rb:
--------------------------------------------------------------------------------
1 | class Admin
2 | class DashboardController < ApplicationController
3 | layout 'admin'
4 |
5 | def index
6 | redirect_to new_admin_session_url unless admin_signed_in?
7 | @orgs = policy_scope(Organization) if current_admin
8 | @version = OHANA_API_VERSION
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/controllers/admin/registrations_controller.rb:
--------------------------------------------------------------------------------
1 | class Admin
2 | class RegistrationsController < Devise::RegistrationsController
3 | before_action :configure_permitted_parameters
4 |
5 | include PrivateRegistration
6 |
7 | protected
8 |
9 | def configure_permitted_parameters
10 | devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
11 | devise_parameter_sanitizer.permit(:account_update, keys: [:name])
12 | end
13 |
14 | def after_inactive_sign_up_url_for(_resource)
15 | new_admin_session_url
16 | end
17 |
18 | def after_update_path_for(_resource)
19 | edit_admin_registration_url
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/controllers/api/v1/categories_controller.rb:
--------------------------------------------------------------------------------
1 | module Api
2 | module V1
3 | class CategoriesController < ApplicationController
4 | def index
5 | categories = Category.all
6 | render json: categories, status: :ok
7 | end
8 |
9 | def children
10 | children = Category.find_by(taxonomy_id: params[:taxonomy_id]).children
11 | render json: children, status: :ok
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/controllers/api/v1/cors_controller.rb:
--------------------------------------------------------------------------------
1 | module Api
2 | module V1
3 | class CorsController < ApplicationController
4 | def render_204
5 | head :no_content
6 | end
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/controllers/api/v1/errors_controller.rb:
--------------------------------------------------------------------------------
1 | module Api
2 | module V1
3 | class ErrorsController < ApplicationController
4 | def raise_not_found!
5 | message = "No route matches #{params[:unmatched_route]}"
6 | raise ActionController::RoutingError, message
7 | end
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/controllers/concerns/cacheable.rb:
--------------------------------------------------------------------------------
1 | module Cacheable
2 | def cache_time
3 | ENV['EXPIRES_IN'].to_i.minutes
4 | end
5 |
6 | def set_cache_control
7 | expires_in cache_time, public: true
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/controllers/concerns/taggable.rb:
--------------------------------------------------------------------------------
1 | module Taggable
2 | def shift_and_split_params(params, *fields)
3 | fields.each do |key|
4 | params[key] = params[key].shift.split(',')
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/concerns/token_validator.rb:
--------------------------------------------------------------------------------
1 | module TokenValidator
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | before_action :validate_token!, only: %i[update destroy create]
6 | end
7 |
8 | def validate_token!
9 | render_401 unless valid_api_token?
10 | end
11 |
12 | def valid_api_token?
13 | token = request.env['HTTP_X_API_TOKEN'].to_s
14 | token.present? && token == ENV['ADMIN_APP_TOKEN']
15 | end
16 |
17 | def render_401
18 | hash =
19 | {
20 | 'message' => 'This action requires a valid X-API-Token header.',
21 | 'status' => 401
22 | }
23 | render json: hash, status: :unauthorized
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
1 | class HomeController < ApplicationController
2 | # If the user is signed in, we'd like to greet
3 | # them on the Home page
4 | def index
5 | @user = current_user
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/controllers/user/registrations_controller.rb:
--------------------------------------------------------------------------------
1 | class User
2 | class RegistrationsController < Devise::RegistrationsController
3 | before_action :configure_permitted_parameters
4 |
5 | include PrivateRegistration
6 |
7 | protected
8 |
9 | def configure_permitted_parameters
10 | devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
11 | devise_parameter_sanitizer.permit(:account_update, keys: [:name])
12 | end
13 |
14 | def after_update_path_for(_resource)
15 | edit_user_registration_url
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/helpers/admin/csv_download_helper.rb:
--------------------------------------------------------------------------------
1 | class Admin
2 | module CsvDownloadHelper
3 | def csv_tables
4 | %w[addresses contacts holiday_schedules locations mail_addresses
5 | organizations phones programs regular_schedules services]
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/mailers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeforamerica/ohana-api/e3ab28075a5fc2f2e15ce06403db5c1e785a7778/app/mailers/.gitkeep
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | end
3 |
--------------------------------------------------------------------------------
/app/mailers/custom_mailer.rb:
--------------------------------------------------------------------------------
1 | class CustomMailer < Devise::Mailer
2 | def confirmation_instructions(record, token, options = {})
3 | options[:template_path] = template_path(record)
4 | super
5 | end
6 |
7 | def reset_password_instructions(record, token, options = {})
8 | options[:template_path] = template_path(record)
9 | super
10 | end
11 |
12 | private
13 |
14 | def template_path(resource)
15 | return 'admin/mailer' if resource.is_a?(Admin)
16 | return 'devise/mailer' if resource.is_a?(User)
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/models/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeforamerica/ohana-api/e3ab28075a5fc2f2e15ce06403db5c1e785a7778/app/models/.gitkeep
--------------------------------------------------------------------------------
/app/models/admin.rb:
--------------------------------------------------------------------------------
1 | class Admin < ApplicationRecord
2 | # Devise already checks for presence of email and password.
3 | validates :name, presence: true
4 | validates :email, email: true, uniqueness: { case_sensitive: false }
5 |
6 | devise :database_authenticatable, :registerable,
7 | :recoverable, :rememberable, :trackable, :validatable,
8 | :confirmable
9 | end
10 |
--------------------------------------------------------------------------------
/app/models/api_application.rb:
--------------------------------------------------------------------------------
1 | class ApiApplication < ApplicationRecord
2 | belongs_to :user
3 |
4 | validates :name, :api_token, uniqueness: true
5 | validates :name, :main_url, presence: true
6 | validates :main_url, url: true
7 | validates :callback_url, url: true, allow_blank: true
8 |
9 | auto_strip_attributes :name, squish: true
10 |
11 | before_create :generate_api_token
12 |
13 | private
14 |
15 | def generate_api_token
16 | self.api_token = loop do
17 | random_token = SecureRandom.hex
18 | break random_token unless self.class.exists?(api_token: random_token)
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/app/models/category.rb:
--------------------------------------------------------------------------------
1 | class Category < ApplicationRecord
2 | has_and_belongs_to_many :services
3 |
4 | validates :name, :taxonomy_id,
5 | presence: { message: I18n.t('errors.messages.blank_for_category') }
6 |
7 | validates :taxonomy_id,
8 | uniqueness: {
9 | message: I18n.t('errors.messages.duplicate_taxonomy_id'),
10 | case_sensitive: false
11 | }
12 |
13 | has_ancestry
14 | end
15 |
--------------------------------------------------------------------------------
/app/models/concerns/parent_presence_validatable.rb:
--------------------------------------------------------------------------------
1 | module ParentPresenceValidatable
2 | def self.included(base)
3 | base.class_eval do
4 | validate :parent_presence
5 | end
6 | end
7 |
8 | private
9 |
10 | def parent_presence
11 | return if parents.any?(&:present?)
12 |
13 | errors.add(:base, "#{self.class.name.titleize} is missing a parent: #{list_of_parents}")
14 | end
15 |
16 | def parents
17 | belongs_to_associations.map { |a| send(a.name) }
18 | end
19 |
20 | def belongs_to_associations
21 | @belongs_to_associations ||= self.class.reflect_on_all_associations(:belongs_to)
22 | end
23 |
24 | def list_of_parents
25 | belongs_to_associations.map { |a| a.name.to_s.titleize }.join(' or ')
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/models/contact.rb:
--------------------------------------------------------------------------------
1 | class Contact < ApplicationRecord
2 | include ParentPresenceValidatable
3 |
4 | default_scope { order('id ASC') }
5 |
6 | belongs_to :location, optional: true, touch: true
7 | belongs_to :organization, optional: true
8 | belongs_to :service, optional: true, touch: true
9 |
10 | has_many :phones, dependent: :destroy, inverse_of: :contact
11 | accepts_nested_attributes_for :phones,
12 | allow_destroy: true, reject_if: :all_blank
13 |
14 | validates :name,
15 | presence: { message: I18n.t('errors.messages.blank_for_contact') }
16 |
17 | validates :email, email: true, allow_blank: true
18 |
19 | auto_strip_attributes :department, :email, :name, :title, squish: true
20 | end
21 |
--------------------------------------------------------------------------------
/app/models/holiday_schedule.rb:
--------------------------------------------------------------------------------
1 | class HolidaySchedule < ApplicationRecord
2 | include ParentPresenceValidatable
3 |
4 | belongs_to :location, optional: true, touch: true, inverse_of: :holiday_schedules
5 | belongs_to :service, optional: true, touch: true, inverse_of: :holiday_schedules
6 |
7 | validates :start_date, :end_date,
8 | date: true,
9 | presence: { message: I18n.t('errors.messages.blank_for_hs') }
10 |
11 | validates :opens_at, :closes_at,
12 | presence: { message: I18n.t('errors.messages.blank_for_hs_open') },
13 | unless: :closed?
14 | end
15 |
--------------------------------------------------------------------------------
/app/models/mail_address.rb:
--------------------------------------------------------------------------------
1 | class MailAddress < ApplicationRecord
2 | belongs_to :location, touch: true, inverse_of: :mail_address, optional: false
3 |
4 | validates :address_1,
5 | :city,
6 | :postal_code,
7 | :country,
8 | presence: { message: I18n.t('errors.messages.blank_for_mail_address') }
9 |
10 | validates :state_province, state_province: true
11 |
12 | validates :country, length: { allow_blank: true, maximum: 2, minimum: 2 }
13 |
14 | validates :postal_code, zip: { allow_blank: true }
15 |
16 | auto_strip_attributes :attention, :address_1, :address_2, :city, :state_province,
17 | :postal_code, :country, squish: true
18 | end
19 |
--------------------------------------------------------------------------------
/app/models/program.rb:
--------------------------------------------------------------------------------
1 | class Program < ApplicationRecord
2 | belongs_to :organization, optional: false
3 | has_many :services, dependent: :destroy
4 |
5 | validates :name,
6 | presence: { message: I18n.t('errors.messages.blank_for_program') }
7 |
8 | auto_strip_attributes :alternate_name, :name
9 |
10 | def self.with_orgs(ids)
11 | joins(:organization).where(organization_id: ids).distinct
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/models/regular_schedule.rb:
--------------------------------------------------------------------------------
1 | class RegularSchedule < ApplicationRecord
2 | include ParentPresenceValidatable
3 |
4 | default_scope { order('weekday ASC') }
5 |
6 | belongs_to :location, optional: true, touch: true, inverse_of: :regular_schedules
7 | belongs_to :service, optional: true, touch: true, inverse_of: :regular_schedules
8 |
9 | validates :weekday, :opens_at, :closes_at,
10 | presence: { message: I18n.t('errors.messages.blank_for_rs') }
11 |
12 | validates :weekday, weekday: { allow_blank: true }
13 | end
14 |
--------------------------------------------------------------------------------
/app/models/user.rb:
--------------------------------------------------------------------------------
1 | class User < ApplicationRecord
2 | has_many :api_applications, dependent: :destroy
3 | accepts_nested_attributes_for :api_applications
4 |
5 | # Devise checks for presence of email and password by default
6 | validates :name, presence: true
7 | validates :email, email: true, uniqueness: { case_sensitive: false }
8 |
9 | devise :database_authenticatable, :registerable,
10 | :recoverable, :rememberable, :trackable, :validatable,
11 | :confirmable
12 | end
13 |
--------------------------------------------------------------------------------
/app/policies/contact_policy.rb:
--------------------------------------------------------------------------------
1 | class ContactPolicy < ApplicationPolicy
2 | delegate :create?, :destroy?, :edit?, :update?, to: :organization_policy
3 |
4 | private
5 |
6 | def organization_policy
7 | @organization_policy ||= Pundit.policy!(user, record.organization)
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/policies/location_policy.rb:
--------------------------------------------------------------------------------
1 | class LocationPolicy < ApplicationPolicy
2 | def new?
3 | return true if user.super_admin?
4 | return super if record.id.present?
5 |
6 | can_access_at_least_one_organization?
7 | end
8 |
9 | class Scope < Scope
10 | def resolve
11 | return scope.pluck(:id, :name, :slug).sort_by(&:second) if user.super_admin?
12 |
13 | scope.with_email(user.email).pluck(:id, :name, :slug)
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/policies/organization_policy.rb:
--------------------------------------------------------------------------------
1 | class OrganizationPolicy < ApplicationPolicy
2 | class Scope < Scope
3 | def resolve
4 | return scope.pluck(:id, :name, :slug).sort_by(&:second) if user.super_admin?
5 |
6 | scope.with_locations(location_ids).pluck(:id, :name, :slug)
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/policies/program_policy.rb:
--------------------------------------------------------------------------------
1 | class ProgramPolicy < ApplicationPolicy
2 | delegate :create?, to: :organization_policy
3 |
4 | def new?
5 | can_access_at_least_one_organization?
6 | end
7 |
8 | private
9 |
10 | def organization_policy
11 | @organization_policy ||= Pundit.policy!(user, record.organization)
12 | end
13 |
14 | class Scope < Scope
15 | def resolve
16 | return scope.pluck(:id, :name).sort_by(&:second) if user.super_admin?
17 |
18 | scope.with_orgs(org_ids).pluck(:id, :name)
19 | end
20 |
21 | def org_ids
22 | Pundit.policy_scope!(user, Organization).map(&:first).flatten
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/policies/service_policy.rb:
--------------------------------------------------------------------------------
1 | class ServicePolicy < ApplicationPolicy
2 | class Scope < Scope
3 | def resolve
4 | return scope.pluck(:location_id, :id, :name).sort_by(&:third) if user.super_admin?
5 |
6 | scope.with_locations(location_ids).pluck(:location_id, :id, :name)
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/serializers/address_serializer.rb:
--------------------------------------------------------------------------------
1 | class AddressSerializer < ActiveModel::Serializer
2 | attributes :id, :address_1, :address_2, :city, :state_province, :postal_code
3 | end
4 |
--------------------------------------------------------------------------------
/app/serializers/category_serializer.rb:
--------------------------------------------------------------------------------
1 | class CategorySerializer < ActiveModel::Serializer
2 | attributes :id, :depth, :taxonomy_id, :name, :parent_id
3 | end
4 |
--------------------------------------------------------------------------------
/app/serializers/contact_serializer.rb:
--------------------------------------------------------------------------------
1 | class ContactSerializer < ActiveModel::Serializer
2 | attributes :department, :email, :id, :name, :title
3 |
4 | has_many :phones
5 | end
6 |
--------------------------------------------------------------------------------
/app/serializers/holiday_schedule_serializer.rb:
--------------------------------------------------------------------------------
1 | class HolidayScheduleSerializer < ActiveModel::Serializer
2 | attributes :closed, :start_date, :end_date, :opens_at, :closes_at
3 | end
4 |
--------------------------------------------------------------------------------
/app/serializers/location_serializer.rb:
--------------------------------------------------------------------------------
1 | class LocationSerializer < LocationsSerializer
2 | attributes :accessibility, :email, :languages, :transportation
3 |
4 | has_many :contacts
5 | has_one :mail_address
6 | has_many :regular_schedules
7 | has_many :holiday_schedules
8 | has_many :services
9 |
10 | has_one :organization, serializer: SummarizedOrganizationSerializer
11 |
12 | def accessibility
13 | object.accessibility.map(&:text)
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/serializers/locations_organization_serializer.rb:
--------------------------------------------------------------------------------
1 | class LocationsOrganizationSerializer < ActiveModel::Serializer
2 | attributes :id, :accreditations, :alternate_name, :date_incorporated,
3 | :description, :email, :funding_sources, :licenses, :name,
4 | :website, :slug, :url, :locations_url
5 |
6 | def url
7 | api_organization_url(object)
8 | end
9 |
10 | def locations_url
11 | api_org_locations_url(object)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/serializers/locations_serializer.rb:
--------------------------------------------------------------------------------
1 | class LocationsSerializer < ActiveModel::Serializer
2 | attributes :id, :active, :admin_emails, :alternate_name, :coordinates,
3 | :description, :latitude, :longitude, :name, :short_desc, :slug,
4 | :website, :updated_at, :url
5 |
6 | has_one :address
7 | has_one :organization, serializer: LocationsOrganizationSerializer
8 | has_many :phones
9 |
10 | def coordinates
11 | return [] unless object.longitude.present? && object.latitude.present?
12 |
13 | [object.longitude, object.latitude]
14 | end
15 |
16 | def url
17 | api_location_url(object)
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/serializers/mail_address_serializer.rb:
--------------------------------------------------------------------------------
1 | class MailAddressSerializer < ActiveModel::Serializer
2 | attributes :id, :attention, :address_1, :address_2, :city, :state_province, :postal_code
3 | end
4 |
--------------------------------------------------------------------------------
/app/serializers/nearby_serializer.rb:
--------------------------------------------------------------------------------
1 | class NearbySerializer < ActiveModel::Serializer
2 | attributes :id, :alternate_name, :latitude, :longitude, :name, :slug
3 |
4 | has_one :address
5 | end
6 |
--------------------------------------------------------------------------------
/app/serializers/organization_serializer.rb:
--------------------------------------------------------------------------------
1 | class OrganizationSerializer < LocationsOrganizationSerializer
2 | has_many :contacts
3 | has_many :phones
4 | end
5 |
--------------------------------------------------------------------------------
/app/serializers/phone_serializer.rb:
--------------------------------------------------------------------------------
1 | class PhoneSerializer < ActiveModel::Serializer
2 | attributes :id, :department, :extension, :number, :number_type, :vanity_number
3 | end
4 |
--------------------------------------------------------------------------------
/app/serializers/regular_schedule_serializer.rb:
--------------------------------------------------------------------------------
1 | class RegularScheduleSerializer < ActiveModel::Serializer
2 | attributes :weekday, :opens_at, :closes_at
3 | end
4 |
--------------------------------------------------------------------------------
/app/serializers/service_serializer.rb:
--------------------------------------------------------------------------------
1 | class ServiceSerializer < ActiveModel::Serializer
2 | attributes :id, :accepted_payments, :alternate_name, :audience, :description,
3 | :eligibility, :email, :fees, :funding_sources, :application_process,
4 | :interpretation_services, :keywords, :languages, :name,
5 | :required_documents, :service_areas, :status, :website,
6 | :wait_time, :updated_at
7 |
8 | # embed :ids, include: true
9 | has_many :categories
10 | has_many :contacts
11 | has_many :phones
12 | has_many :regular_schedules
13 | has_many :holiday_schedules
14 | end
15 |
--------------------------------------------------------------------------------
/app/serializers/summarized_organization_serializer.rb:
--------------------------------------------------------------------------------
1 | class SummarizedOrganizationSerializer < ActiveModel::Serializer
2 | attributes :id, :alternate_name, :name, :slug, :url
3 |
4 | def url
5 | api_organization_url(object)
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/validators/email_validator.rb:
--------------------------------------------------------------------------------
1 | class EmailValidator < RegexValidator
2 | def validate_each(record, attribute, value)
3 | default_message = "#{value} #{I18n.t('errors.messages.invalid_email')}"
4 |
5 | regex = /\A([^@\s]+)@((?:(?!-)[-a-z0-9]+(? 'true' }
6 | ×
7 | %span.sr-only
8 | Close
9 | %h4.modal-title#my-modal-label
10 | Are you ABSOLUTELY sure?
11 | .modal-body
12 | %p
13 | This action CANNOT be undone. This will delete
14 | %strong
15 | = org_name
16 | and all of its associated locations permanently.
17 | .modal-footer
18 | %button.btn.btn-default{ 'data-dismiss' => 'modal' } Close
19 | = link_to t('admin.buttons.confirm_delete_organization'), { action: :destroy, id: org_id }, method: :delete, class: 'btn btn-danger'
20 |
--------------------------------------------------------------------------------
/app/views/admin/organizations/edit.html.haml:
--------------------------------------------------------------------------------
1 | .content-box
2 | %h2= @organization.name
3 | = form_for [:admin, @organization], html: { class: 'edit-entry' } do |f|
4 | = render 'admin/organizations/form', f: f, organization: @organization
5 |
--------------------------------------------------------------------------------
/app/views/admin/organizations/forms/_accreditations.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | %strong
4 | = f.label :accreditations
5 | %p.desc
6 | = t('.description')
7 |
8 | = field_set_tag do
9 | = text_field_tag 'organization[accreditations][]', organization.accreditations.join(','), id: 'organization_accreditations', class: 'form-control'
10 |
--------------------------------------------------------------------------------
/app/views/admin/organizations/forms/_date_incorporated.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :date_incorporated
4 | %span.desc
5 | = t('.description')
6 |
7 | = field_set_tag do
8 | .row
9 | .col-sm-6
10 | = f.date_select :date_incorporated, include_blank: true, start_year: 1970, class: 'form-control'
11 |
--------------------------------------------------------------------------------
/app/views/admin/organizations/forms/_description.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :description
4 | %span.desc
5 | = t('.description')
6 |
7 | = field_set_tag do
8 | = f.text_area :description, required: false, class: 'form-control', rows: 10
9 |
--------------------------------------------------------------------------------
/app/views/admin/organizations/forms/_legal_status.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :legal_status
4 | %span.desc
5 | = t('.description')
6 |
7 | = field_set_tag do
8 | .row
9 | .col-sm-6
10 | = f.text_field :legal_status, required: false, maxlength: 255, class: 'form-control'
11 |
--------------------------------------------------------------------------------
/app/views/admin/organizations/forms/_licenses.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | %strong
4 | = f.label :licenses
5 | %p.desc
6 | = t('.description')
7 |
8 | = field_set_tag do
9 | = text_field_tag 'organization[licenses][]', organization.licenses.join(','), id: 'organization_licenses', class: 'form-control'
10 |
--------------------------------------------------------------------------------
/app/views/admin/organizations/forms/_new_organization_form.html.haml:
--------------------------------------------------------------------------------
1 | = render 'admin/organizations/forms/fields', f: f, organization: organization
2 |
3 | = f.submit t('admin.buttons.create_organization'), class: 'btn btn-primary btn-lg', data: { disable_with: 'Please wait...' }
4 |
--------------------------------------------------------------------------------
/app/views/admin/organizations/forms/_tax_id.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :tax_id
4 | %span.desc
5 | = t('.description')
6 |
7 | = field_set_tag do
8 | .row
9 | .col-sm-6
10 | = f.text_field :tax_id, required: false, maxlength: 255, class: 'form-control'
11 |
--------------------------------------------------------------------------------
/app/views/admin/organizations/forms/_tax_status.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :tax_status
4 | %span.desc
5 | = t('.description')
6 |
7 | = field_set_tag do
8 | .row
9 | .col-sm-6
10 | = f.text_field :tax_status, required: false, maxlength: 255, class: 'form-control'
11 |
--------------------------------------------------------------------------------
/app/views/admin/organizations/index.html.haml:
--------------------------------------------------------------------------------
1 | %p
2 | Below you should see a list of organizations that you are allowed to administer.
3 | If there are any entries missing,
4 | please #{mail_to SETTINGS[:admin_support_email], 'let us know'}.
5 | %p
6 | To start updating, click on one of the links, which will take you to the details page
7 | for the organization.
8 |
9 | - if current_admin.super_admin?
10 | %p
11 | As a super admin, you have access to all locations in the database. Please make updates responsibly.
12 |
13 | %p
14 | - @orgs.each do |org|
15 | %ul
16 | = link_to org.second, edit_admin_organization_path(org.third)
17 | = paginate @orgs
18 |
--------------------------------------------------------------------------------
/app/views/admin/organizations/new.html.haml:
--------------------------------------------------------------------------------
1 | .content-box
2 | %h1 Create a new organization
3 |
4 | = form_for [:admin, @organization], url: admin_organizations_path, html: { method: :post, class: 'edit-entry' } do |f|
5 | = render 'admin/organizations/forms/new_organization_form', f: f, organization: @organization
6 |
--------------------------------------------------------------------------------
/app/views/admin/programs/_confirm_delete_program.html.haml:
--------------------------------------------------------------------------------
1 | .modal-dialog
2 | .modal-content
3 | .modal-header
4 | = button_tag(type: 'button', class: 'close', data: { dismiss: 'modal' }) do
5 | %span{ 'aria-hidden' => 'true' }
6 | ×
7 | %span.sr-only
8 | Close
9 | %h4.modal-title#my-modal-label
10 | Are you ABSOLUTELY sure?
11 | .modal-body
12 | %p
13 | This action CANNOT be undone. This will delete
14 | %strong
15 | = program_name
16 | and all of its associated services permanently.
17 | .modal-footer
18 | %button.btn.btn-default{ 'data-dismiss' => 'modal' } Close
19 | = link_to t('admin.buttons.confirm_delete_program'), { action: :destroy, id: program_id }, method: :delete, class: 'btn btn-danger'
20 |
--------------------------------------------------------------------------------
/app/views/admin/programs/_form.html.haml:
--------------------------------------------------------------------------------
1 | = render 'admin/programs/forms/fields', f: f, program: program
2 | = render 'admin/programs/forms/danger', program: program
3 | = render 'admin/programs/forms/editing', f: f, program: program
4 |
--------------------------------------------------------------------------------
/app/views/admin/programs/edit.html.haml:
--------------------------------------------------------------------------------
1 | .content-box
2 | %h2 #{@program.organization.try(:name)} / #{@program.name}
3 | = form_for [:admin, @program], html: { class: 'edit-entry' } do |f|
4 | = render 'admin/programs/form', f: f, program: @program
5 |
--------------------------------------------------------------------------------
/app/views/admin/programs/forms/_danger.html.haml:
--------------------------------------------------------------------------------
1 | .danger-zone
2 | %header
3 | %strong
4 | Danger Zone
5 | %h4
6 | Delete this program
7 | %p
8 | Once you delete a program, there is no going back. All of the program's services will be deleted as well. Please be certain.
9 | %p
10 | = link_to t('admin.buttons.delete_program'), {}, href: '', data: { toggle: 'modal', target: '#modal-window' }, class: 'boxed-action'
11 | .modal.fade#modal-window{ 'aria-hidden' => 'true', 'aria-labelledby' => 'my-modal-label', 'role' => 'dialog', 'tabindex' => '-1' }
12 | = render 'admin/programs/confirm_delete_program', program_id: program.id, program_name: program.name
13 |
--------------------------------------------------------------------------------
/app/views/admin/programs/forms/_editing.html.haml:
--------------------------------------------------------------------------------
1 | .save-box.navbar-default
2 | %p
3 | Editing
4 | %strong
5 | = program.name
6 | = f.submit t('admin.buttons.save_changes'), class: 'btn btn-success', data: { disable_with: 'Please wait...' }
7 |
--------------------------------------------------------------------------------
/app/views/admin/programs/forms/_fields.html.haml:
--------------------------------------------------------------------------------
1 | - if program.errors.any?
2 | .alert.alert-danger
3 | %h2 #{pluralize(program.errors.count, 'error')} prohibited this program from being saved:
4 | %ul
5 | - program.errors.full_messages.each do |msg|
6 | %li= msg
7 |
8 | = render 'admin/shared/forms/choose_org', f: f
9 | = render 'admin/shared/forms/name', f: f
10 | = render 'admin/shared/forms/alternate_name', f: f
11 |
--------------------------------------------------------------------------------
/app/views/admin/programs/forms/_new_program_form.html.haml:
--------------------------------------------------------------------------------
1 | = render 'admin/programs/forms/fields', f: f, program: program
2 |
3 | = f.submit t('admin.buttons.create_program'), class: 'btn btn-primary btn-lg', data: { disable_with: 'Please wait...' }
4 |
--------------------------------------------------------------------------------
/app/views/admin/programs/new.html.haml:
--------------------------------------------------------------------------------
1 | .content-box
2 | %h1 Create a new program
3 |
4 | = form_for [:admin, @program], url: admin_programs_path, html: { method: :post, class: 'edit-entry' } do |f|
5 | = render 'admin/programs/forms/new_program_form', f: f, program: @program
6 |
--------------------------------------------------------------------------------
/app/views/admin/service_contacts/edit.html.haml:
--------------------------------------------------------------------------------
1 | .content-box
2 | %h2 #{@contact.try(:name)} / #{@service.name}
3 | = form_for [:admin, @service.location, @service, @contact], html: { class: 'edit-entry' } do |f|
4 | = render 'admin/contacts/form', f: f, contact: @contact
5 |
--------------------------------------------------------------------------------
/app/views/admin/service_contacts/new.html.haml:
--------------------------------------------------------------------------------
1 | .content-box
2 | %h1 Create a new contact
3 |
4 | = form_for [:admin, @service.location, @service, @contact], html: { method: :post, class: 'edit-entry' } do |f|
5 | = render 'admin/contacts/forms/new_service_contact', f: f, service: @service, contact: @contact
6 |
--------------------------------------------------------------------------------
/app/views/admin/services/_confirm_delete_service.html.haml:
--------------------------------------------------------------------------------
1 | .modal-dialog
2 | .modal-content
3 | .modal-header
4 | = button_tag(type: 'button', class: 'close', data: { dismiss: 'modal' }) do
5 | %span{ 'aria-hidden' => 'true' }
6 | ×
7 | %span.sr-only
8 | Close
9 | %h4.modal-title#my-modal-label
10 | Are you ABSOLUTELY sure?
11 | .modal-body
12 | %p
13 | This action CANNOT be undone. This will delete
14 | %strong
15 | #{service_name || service_id}.
16 | .modal-footer
17 | %button.btn.btn-default{ 'data-dismiss' => 'modal' } Close
18 | = link_to t('admin.buttons.confirm_delete_service'), { action: :destroy, id: service_id, name: service_name }, method: :delete, class: 'btn btn-danger'
19 |
--------------------------------------------------------------------------------
/app/views/admin/services/edit.html.haml:
--------------------------------------------------------------------------------
1 | .content-box
2 | %h2 #{@service.try(:name)} / #{@location.name}
3 | = form_for [:admin, @location, @service], html: { class: 'edit-entry' } do |f|
4 | = render 'admin/services/form', f: f, service: @service, location: @location, nested_categories: @nested_categories
5 |
6 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_accepted_payments.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box.payments
2 | %header
3 | %strong
4 | Accepted Payments
5 |
6 | = field_set_tag do
7 | = f.select :accepted_payments, SETTINGS.try(:[], :accepted_payments), {}, multiple: true, class: 'form-control', data: { placeholder: t('.placeholder') }
8 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_application_process.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :application_process
4 | %p.desc
5 | = t('.description')
6 |
7 | %p
8 | = f.text_area :application_process, class: 'form-control'
9 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_audience.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :audience
4 | %p.desc
5 | = t('.description')
6 |
7 | %p
8 | = f.text_area :audience, class: 'form-control'
9 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_categories.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | %strong
4 | = f.label :categories
5 | %p.desc
6 | = t('.description')
7 |
8 | = hidden_field_tag 'service[category_ids][]', nil
9 | = field_set_tag nil, id: 'categories' do
10 | = nested_categories
11 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_description.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :description
4 | %span.desc
5 | = t('.description')
6 |
7 | = field_set_tag do
8 | = f.text_area :description, required: false, class: 'form-control', rows: 5
9 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_eligibility.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :eligibility
4 | %p.desc
5 | = t('.description')
6 |
7 | %p
8 | = f.text_area :eligibility, class: 'form-control'
9 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_fees.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :fees
4 | %p.desc
5 | = t('.description')
6 |
7 | %p
8 | = f.text_area :fees, class: 'form-control'
9 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_interpretation_services.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :interpretation_services
4 | %p.desc
5 | = t('.description')
6 |
7 | %p
8 | = f.text_area :interpretation_services, class: 'form-control'
9 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_keywords.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | %strong
4 | = f.label :keywords
5 | %p.desc
6 | = t('.description')
7 |
8 | = field_set_tag do
9 | = text_field_tag 'service[keywords][]', service.keywords.join(','), id: 'service_keywords', class: 'form-control'
10 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_languages.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box.languages
2 | %header
3 | %strong
4 | Languages this service is provided in.
5 |
6 | = field_set_tag do
7 | = f.select :languages, SETTINGS.try(:[], :languages), {}, multiple: true, class: 'form-control', data: { placeholder: t('.placeholder') }
8 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_new_service_form.html.haml:
--------------------------------------------------------------------------------
1 | = render 'admin/services/forms/fields', f: f, service: service, nested_categories: nested_categories, location: location
2 |
3 | = render 'admin/services/forms/service_locations', location: location, service: service
4 |
5 | .save-box.navbar-default
6 | %p
7 | Creating service for
8 | %strong
9 | = location.name
10 | = f.submit t('admin.buttons.create_service'), class: 'btn btn-success', data: { disable_with: 'Please wait...' }
11 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_program.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box.program
2 | %header
3 | = f.label :program_id
4 | %span.desc
5 | = t('.description')
6 |
7 | %p
8 | = program_autocomplete_field_for(f, location)
9 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_required_documents.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box.documents
2 | %header
3 | %strong
4 | The documents required to receive this service.
5 |
6 | = field_set_tag do
7 | = f.select :required_documents, SETTINGS.try(:[], :required_documents), {}, multiple: true, class: 'form-control', data: { placeholder: t('.placeholder') }
8 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_service_areas.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | %strong
4 | = f.label :service_areas
5 | %p.desc
6 | = t('.description')
7 |
8 | = field_set_tag do
9 | = f.select :service_areas, SETTINGS.try(:[], :valid_service_areas), {}, multiple: true, class: 'form-control', data: { placeholder: t('.placeholder') }
10 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_service_locations.html.haml:
--------------------------------------------------------------------------------
1 | - if location.organization.locations.size > 1
2 | .content-box
3 | %h2 Copy this service to other Locations
4 | %p
5 | If this service is offered at other locations, you can copy it to those
6 | locations. Then, make sure to update the service at the other locations
7 | with any information that is different, such as the hours of operation.
8 |
9 | - locs ||= location.organization.locations.pluck(:id, :name)
10 |
11 | - locs.each do |loc|
12 | - next if loc.first == location.id
13 | - next if Location.find(loc.first).services.pluck(:name).include?(service.name)
14 | = check_box_tag 'service[locations][]', loc.first, false, id: loc.first
15 | = label_tag loc.first, loc.second
16 | %br
17 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_status.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | %strong
4 | = f.label :status
5 | %p.desc
6 | = t('.description')
7 |
8 | = field_set_tag do
9 | - if f.object.new_record?
10 | = f.select :status, Service.status.options, { selected: 'active' }, class: 'form-control'
11 | - else
12 | = f.select :status, Service.status.options, {}, class: 'form-control'
13 |
--------------------------------------------------------------------------------
/app/views/admin/services/forms/_wait.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :wait_time
4 | %p.desc
5 | = t('.description')
6 |
7 | %p
8 | = f.text_area :wait_time, class: 'form-control'
9 |
--------------------------------------------------------------------------------
/app/views/admin/services/index.html.haml:
--------------------------------------------------------------------------------
1 | %p
2 | Below you should see a list of services (and their corresponding location) that you are allowed to administer.
3 | If there are any entries missing,
4 | please #{mail_to SETTINGS[:admin_support_email], 'let us know'}.
5 | %p
6 | To start updating, click on one of the links, which will take you to the details page
7 | for the service.
8 |
9 | - if current_admin.super_admin?
10 | %p
11 | As a super admin, you have access to all services in the database. Please make updates responsibly.
12 |
13 | %p
14 | - @services.each do |service|
15 | %ul
16 | - location_name ||= Location.find(service[0]).name
17 | = link_to "#{service[2]} / #{location_name}", edit_admin_location_service_path(service[0], service[1])
18 | = paginate @services
19 |
--------------------------------------------------------------------------------
/app/views/admin/services/new.html.haml:
--------------------------------------------------------------------------------
1 | .content-box
2 | %h1 Create a new service
3 |
4 | = form_for [:admin, @location, @service], html: { method: :post, class: 'edit-entry' } do |f|
5 | = render 'admin/services/forms/new_service_form', f: f, location: @location, service: @service, nested_categories: @nested_categories
6 |
--------------------------------------------------------------------------------
/app/views/admin/shared/_navigation.html.haml:
--------------------------------------------------------------------------------
1 | %header.navbar.navbar-default.navbar-fixed-top
2 | .container
3 | .navbar-header
4 | = button_tag type: 'button', class: 'navbar-toggle', data: { toggle: 'collapse', target: '.navbar-collapse' } do
5 | %span.sr-only
6 | Toggle navigation
7 | %span.icon-bar
8 | %span.icon-bar
9 | %span.icon-bar
10 | = link_to t('titles.admin', brand: t('titles.brand')),
11 | admin_dashboard_url, class: 'navbar-brand'
12 | %nav.collapse.navbar-collapse
13 | %ul.nav.navbar-nav
14 | = render 'admin/shared/navigation_links'
15 |
--------------------------------------------------------------------------------
/app/views/admin/shared/_navigation_links.html.haml:
--------------------------------------------------------------------------------
1 | - if admin_signed_in?
2 | %li
3 | = link_to t('navigation.edit_account'), edit_admin_registration_path
4 | %li
5 | = link_to t('admin.buttons.organizations'), admin_organizations_path
6 | %li
7 | = link_to t('admin.buttons.locations'), admin_locations_path
8 | %li
9 | = link_to t('admin.buttons.services'), admin_services_path
10 | %li
11 | = link_to t('navigation.sign_out'), destroy_admin_session_path, method: 'delete'
12 | %ul.nav.navbar-nav.navbar-right
13 | %li
14 | = link_to "Hi, #{current_admin.name}", '#', class: 'logged-in'
15 |
--------------------------------------------------------------------------------
/app/views/admin/shared/forms/_alternate_name.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :alternate_name
4 | %span.desc
5 | = t('.description', type: f.object.model_name.human.downcase)
6 |
7 | = field_set_tag do
8 | .row
9 | .col-sm-6
10 | = f.text_field :alternate_name, required: false, maxlength: 255, class: 'form-control'
11 |
--------------------------------------------------------------------------------
/app/views/admin/shared/forms/_choose_org.html.haml:
--------------------------------------------------------------------------------
1 | - if f.object.new_record?
2 | .inst-box
3 | %header
4 | = f.label :organization_id
5 | %p
6 | = org_autocomplete_field_for(f, current_admin)
7 |
--------------------------------------------------------------------------------
/app/views/admin/shared/forms/_email.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :email
4 | %span.desc
5 | = t('.description', type: f.object.model_name.human.downcase)
6 |
7 | = field_set_tag do
8 | .row
9 | .col-sm-6
10 | = f.email_field :email, required: false, maxlength: 255, class: 'form-control'
11 |
--------------------------------------------------------------------------------
/app/views/admin/shared/forms/_funding_sources.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box.funding_sources
2 | %header
3 | %strong
4 | = f.label :funding_sources
5 | %p.desc
6 | = t('.description', type: f.object.model_name.human.downcase)
7 |
8 | = field_set_tag do
9 | = f.select :funding_sources, SETTINGS.try(:[], :funding_sources), {}, multiple: true, class: 'form-control', data: { placeholder: t('.placeholder') }
10 |
--------------------------------------------------------------------------------
/app/views/admin/shared/forms/_holiday_hours.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box.holiday-hours
2 | %header
3 | %strong
4 | Holiday Hours of operation
5 | = f.fields_for :holiday_schedules do |builder|
6 | = render 'admin/locations/forms/holiday_schedule_fields', f: builder
7 | = link_to_add_fields I18n.t('admin.buttons.add_holiday_schedule'), f, :holiday_schedules
8 |
--------------------------------------------------------------------------------
/app/views/admin/shared/forms/_hours.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box.hours
2 | %header
3 | %strong
4 | Hours of operation
5 | = f.fields_for :regular_schedules do |builder|
6 | = render 'admin/locations/forms/regular_schedule_fields', f: builder
7 | = link_to_add_fields I18n.t('admin.buttons.add_hours_of_operation'), f, :regular_schedules
8 |
--------------------------------------------------------------------------------
/app/views/admin/shared/forms/_name.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :name
4 | = field_set_tag do
5 | .row
6 | .col-sm-6
7 | = f.text_field :name, required: false, maxlength: 255, class: 'form-control'
8 |
--------------------------------------------------------------------------------
/app/views/admin/shared/forms/_website.html.haml:
--------------------------------------------------------------------------------
1 | .inst-box
2 | %header
3 | = f.label :website
4 | = field_set_tag do
5 | .row
6 | .col-sm-6
7 | = f.url_field :website, required: false, maxlength: 255, class: 'form-control'
8 |
--------------------------------------------------------------------------------
/app/views/admin_mailer/existing_email_signup.html.haml:
--------------------------------------------------------------------------------
1 | %p A request was made to use this email address to sign up for an account on #{@portal}. This email address is already in use. If you requested this change, please use the following link to sign in to #{@portal}:
2 |
3 | %p= link_to @sign_in_url, @sign_in_url, target: '_blank', rel: 'noopener'
4 |
5 | %p If you would like to create a new account on #{@portal}, please #{link_to 'sign up', @sign_up_url, target: '_blank', rel: 'noopener'} with a different email address.
6 |
7 | %p If you cannot remember your password, please follow the instructions for #{link_to 'resetting your password', @password_url, target: '_blank', rel: 'noopener'}.
8 |
9 | %p If you did not request a new account, please ignore this email.
10 |
--------------------------------------------------------------------------------
/app/views/api_applications/edit.html.haml:
--------------------------------------------------------------------------------
1 | %h1
2 | = @api_application.name
3 | %div
4 | %p API Token: #{@api_application.api_token}
5 | = render 'form', api_application: @api_application
6 |
--------------------------------------------------------------------------------
/app/views/api_applications/index.html.haml:
--------------------------------------------------------------------------------
1 | %h1 Developer Applications
2 | %p These are the applications you have registered to use the Ohana API.
3 |
4 | %ul
5 | - @api_applications.each do |api_application|
6 | %li
7 | = link_to "#{api_application.name} (#{api_application.main_url})", api_application_path(api_application)
8 | %br
9 | = link_to t('buttons.register_new_application'), new_api_application_path, class: 'btn btn-primary'
10 |
--------------------------------------------------------------------------------
/app/views/api_applications/new.html.haml:
--------------------------------------------------------------------------------
1 | %h3 Register a new application
2 | = render 'form', api_application: @api_application
3 |
--------------------------------------------------------------------------------
/app/views/devise/confirmations/new.html.haml:
--------------------------------------------------------------------------------
1 | .authform
2 | = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, role: 'form' }) do |f|
3 | %h3 Resend confirmation instructions
4 | = render 'devise/shared/error_messages', resource: resource
5 | .form-group
6 | = f.label :email
7 | = f.email_field :email, autofocus: true, class: 'form-control'
8 | = f.submit t('buttons.resend_confirmation_instructions'), class: 'button center'
9 | %br
10 | = render 'devise/shared/links'
11 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/confirmation_instructions.html.haml:
--------------------------------------------------------------------------------
1 | %p Hi #{@resource.name},
2 | %p Thanks for signing up for a developer account on #{t('titles.brand')}!
3 | %p You can confirm your account email through the link below:
4 | %p= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token, subdomain: ENV.fetch('DEV_SUBDOMAIN', nil))
5 | %p
6 | After you've confirmed your account, check out the
7 | = link_to 'documentation', 'http://codeforamerica.github.io/ohana-api-docs/'
8 | to get started.
9 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/reset_password_instructions.html.haml:
--------------------------------------------------------------------------------
1 | %p Hello #{@resource.name}!
2 | %p Someone has requested a link to change your password. You can do this through the link below.
3 | %p= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token, subdomain: ENV.fetch('DEV_SUBDOMAIN', nil))
4 | %p If you didn't request this, please ignore this email.
5 | %p Your password won't change until you access the link above and create a new one.
6 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/edit.html.haml:
--------------------------------------------------------------------------------
1 | .authform
2 | = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, role: 'form' }) do |f|
3 | %h3 Change your password
4 | = render 'devise/shared/error_messages', resource: resource
5 | = f.hidden_field :reset_password_token
6 | .form-group
7 | = f.label :password
8 | = f.password_field :password, autofocus: true, autocomplete: 'off', class: 'form-control'
9 | .form-group
10 | = f.label :password_confirmation
11 | = f.password_field :password_confirmation, autocomplete: 'off', class: 'form-control'
12 | = f.submit t('buttons.change_my_password'), class: 'button right'
13 | %br
14 | = render 'devise/shared/links'
15 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/new.html.haml:
--------------------------------------------------------------------------------
1 | .authform
2 | = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, role: 'form' }) do |f|
3 | %h3 Forgot your password?
4 | %p
5 | Enter the email you signed up with, and we'll send you password reset instructions.
6 | = render 'devise/shared/error_messages', resource: resource
7 | .form-group
8 | = f.label :email
9 | = f.email_field :email, autofocus: true, class: 'form-control'
10 | = f.submit t('buttons.send_reset_password_instructions'), class: 'button center'
11 | %br
12 | = render 'devise/shared/links'
13 |
--------------------------------------------------------------------------------
/app/views/devise/shared/_error_messages.html.erb:
--------------------------------------------------------------------------------
1 | <% if resource.errors.any? %>
2 |
3 |
4 | <%= I18n.t("errors.messages.not_saved",
5 | count: resource.errors.count,
6 | resource: resource.class.model_name.human.downcase)
7 | %>
8 |
9 |
10 | <% resource.errors.full_messages.each do |message| %>
11 | - <%= message %>
12 | <% end %>
13 |
14 |
15 | <% end %>
16 |
--------------------------------------------------------------------------------
/app/views/layouts/admin.html.haml:
--------------------------------------------------------------------------------
1 | !!!
2 | %html
3 | %head
4 | %meta{ content: 'width=device-width, initial-scale=1.0', name: 'viewport' }
5 | %title= content_for?(:title) ? yield(:title) : t('titles.admin', brand: t('titles.brand'))
6 | %meta{ content: content_for?(:description) ? yield(:description) : "Admin Interface for #{t('titles.brand')}", name: 'description' }
7 | = stylesheet_link_tag 'admin/application', media: 'all'
8 | = csrf_meta_tags
9 | = yield(:head)
10 | %body{ class: "#{controller_name} #{action_name}" }
11 | = render 'admin/shared/navigation'
12 | #main{ role: 'main' }
13 | .content
14 | .container
15 | .row
16 | = render 'shared/messages'
17 | = yield
18 | = javascript_include_tag 'admin/application'
19 |
--------------------------------------------------------------------------------
/app/views/shared/_messages.html.haml:
--------------------------------------------------------------------------------
1 | - flash.each do |name, msg|
2 | - if msg.is_a?(String)
3 | .alert-dismissible{ class: "alert alert-#{name.to_s == 'notice' ? 'success' : 'danger'}", role: 'alert' }
4 | = button_tag(type: 'button', class: 'close', data: { dismiss: 'alert' }) do
5 | %span{ 'aria-hidden' => 'true' }
6 | ×
7 | %span.sr-only
8 | Close
9 | = content_tag :div, msg, id: "flash_#{name}"
10 |
--------------------------------------------------------------------------------
/app/views/shared/_navigation.html.haml:
--------------------------------------------------------------------------------
1 | %header.navbar.navbar-default.navbar-fixed-top
2 | .container
3 | .navbar-header
4 | = button_tag type: 'button', class: 'navbar-toggle', data: { toggle: 'collapse', target: '.navbar-collapse' } do
5 | %span.sr-only
6 | Toggle navigation
7 | %span.icon-bar
8 | %span.icon-bar
9 | %span.icon-bar
10 | = link_to t('titles.developer', brand: t('titles.brand')), root_path, class: 'navbar-brand'
11 | %nav.collapse.navbar-collapse
12 | %ul.nav.navbar-nav
13 | = render 'shared/navigation_links'
14 |
--------------------------------------------------------------------------------
/app/views/shared/_navigation_links.html.haml:
--------------------------------------------------------------------------------
1 | - if user_signed_in?
2 | %li
3 | = link_to t('navigation.edit_account'), edit_user_registration_path
4 | %li
5 | = link_to t('navigation.your_apps'), api_applications_path
6 | %li
7 | = link_to t('navigation.sign_out'), destroy_user_session_path, method: 'delete'
8 | %ul.nav.navbar-nav.navbar-right
9 | %li
10 | = link_to "Hi, #{current_user.name}", '#', class: 'logged-in'
11 | - else
12 | %li
13 | = link_to t('navigation.sign_in'), new_user_session_path
14 | %li
15 | = link_to t('navigation.sign_up'), new_user_registration_path
16 |
--------------------------------------------------------------------------------
/bin/autospec:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #
3 | # This file was generated by Bundler.
4 | #
5 | # The application 'autospec' is installed as part of a gem, and
6 | # this file is here to facilitate running it.
7 | #
8 |
9 | require 'pathname'
10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11 | Pathname.new(__FILE__).realpath)
12 |
13 | require 'rubygems'
14 | require 'bundler/setup'
15 |
16 | load Gem.bin_path('rspec-core', 'autospec')
17 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../config/application', __dir__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/bin/rspec:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path("../spring", __FILE__)
4 | rescue LoadError
5 | end
6 | #
7 | # This file was generated by Bundler.
8 | #
9 | # The application 'rspec' is installed as part of a gem, and
10 | # this file is here to facilitate running it.
11 | #
12 |
13 | require 'pathname'
14 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
15 | Pathname.new(__FILE__).realpath)
16 |
17 | require 'rubygems'
18 | require 'bundler/setup'
19 |
20 | load Gem.bin_path('rspec-core', 'rspec')
21 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast
4 | # It gets overwritten when you run the `spring binstub` command
5 |
6 | unless defined?(Spring)
7 | require "rubygems"
8 | require "bundler"
9 |
10 | if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ spring \((.*?)\)$.*?^$/m)
11 | ENV["GEM_PATH"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR)
12 | ENV["GEM_HOME"] = ""
13 | Gem.paths = ENV
14 |
15 | gem "spring", match[1]
16 | require "spring/binstub"
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | begin
5 | exec "yarnpkg", *ARGV
6 | rescue Errno::ENOENT
7 | $stderr.puts "Yarn executable was not detected in the system."
8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9 | exit 1
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/categories-in-ohana-api-admin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeforamerica/ohana-api/e3ab28075a5fc2f2e15ce06403db5c1e785a7778/categories-in-ohana-api-admin.png
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('config/environment', __dir__)
4 |
5 | run Rails.application
6 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
2 |
3 | require 'bundler/setup' # Set up gems listed in the Gemfile.
4 |
--------------------------------------------------------------------------------
/config/database.travis.yml:
--------------------------------------------------------------------------------
1 | test:
2 | adapter: postgresql
3 | database: ohana_api_test
4 | username: postgres
--------------------------------------------------------------------------------
/config/database.vagrant.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: postgresql
3 | database: ohana_api_development
4 | template: template0
5 | encoding: utf8
6 | host: localhost
7 | port: 5432
8 | pool: 5
9 | timeout: 5000
10 | username: vagrant
11 | password: ohanatest
12 |
13 | # Warning: The database defined as "test" will be erased and
14 | # re-generated from your development database when you run "rake".
15 | # Do not set this db to the same as development or production.
16 | test:
17 | adapter: postgresql
18 | database: ohana_api_test
19 | template: template0
20 | encoding: utf8
21 | host: localhost
22 | port: 5432
23 | pool: 5
24 | timeout: 5000
25 | username: vagrant
26 | password: ohanatest
27 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | adapter: postgresql
3 | encoding: utf8
4 | host: localhost
5 | port: 5432
6 | pool: <%= ENV['DB_POOL'] || ENV['MAX_THREADS'] || 5 %>
7 | url: <%= ENV['DATABASE_URL'] %>
8 | timeout: 5000
9 |
10 | development:
11 | <<: *default
12 | database: ohana_api_development
13 |
14 | # Warning: The database defined as "test" will be erased and
15 | # re-generated from your development database when you run "rake".
16 | # Do not set this db to the same as development or production.
17 | test:
18 | <<: *default
19 | database: ohana_api_test
20 |
21 | production:
22 | pool: <%= ENV['DB_POOL'] || ENV['MAX_THREADS'] || 5 %>
23 | url: <%= ENV['DATABASE_URL'] %>
24 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/config/initializers/active_model_serializers.rb:
--------------------------------------------------------------------------------
1 | ActiveSupport.on_load(:active_model_serializers) do
2 | # Disable for all serializers (except ArraySerializer)
3 | ActiveModel::Serializer.root = false
4 |
5 | # Disable for ArraySerializer
6 | ActiveModel::ArraySerializer.root = false
7 | end
8 |
--------------------------------------------------------------------------------
/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
9 | # Add Yarn node_modules folder to the asset load path.
10 | # Rails.application.config.assets.paths << Rails.root.join('node_modules')
11 |
12 | # Precompile additional assets.
13 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
14 | # Rails.application.config.assets.precompile += %w( search.js )
15 |
--------------------------------------------------------------------------------
/config/initializers/auto_strip_attributes.rb:
--------------------------------------------------------------------------------
1 | AutoStripAttributes::Config.setup do
2 | filters_enabled[:squish] = true
3 | end
4 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't
4 | # wish to see in your backtraces.
5 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
6 |
7 | # You can also remove all the silencers if you're trying to debug a problem
8 | # that might stem from framework code.
9 | # Rails.backtrace_cleaner.remove_silencers!
10 |
--------------------------------------------------------------------------------
/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.action_dispatch.cookies_serializer = :hybrid
4 |
--------------------------------------------------------------------------------
/config/initializers/csv_shaper.rb:
--------------------------------------------------------------------------------
1 | CsvShaper.configure do |config|
2 | config.header_inflector = :underscore
3 | end
4 |
--------------------------------------------------------------------------------
/config/initializers/figaro.rb:
--------------------------------------------------------------------------------
1 | require Rails.root.join('lib/config_validator.rb')
2 |
3 | # Define the environment variables that should be set in config/application.yml.
4 | # See config/application.example.yml if you don't have config/application.yml.
5 | Figaro.require_keys('API_PATH') if ENV['API_SUBDOMAIN'].blank?
6 | Figaro.require_keys('ADMIN_PATH') if ENV['ADMIN_SUBDOMAIN'].blank?
7 | Figaro.require_keys(
8 | 'ASSET_HOST',
9 | 'DEFAULT_PER_PAGE',
10 | 'DOMAIN_NAME',
11 | 'MAX_PER_PAGE'
12 | )
13 | ConfigValidator.new.validate
14 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += %i[
5 | password password_confirmation api_token email
6 | ]
7 |
--------------------------------------------------------------------------------
/config/initializers/generators.rb:
--------------------------------------------------------------------------------
1 | # Rails.application.config.generators do |g|
2 | # end
3 |
--------------------------------------------------------------------------------
/config/initializers/geocoder.rb:
--------------------------------------------------------------------------------
1 | Geocoder.configure(
2 | lookup: :google,
3 | api_key: ENV.fetch('GOOGLE_GEOCODING_API_KEY', nil),
4 | http_proxy: ENV.fetch('QUOTAGUARD_URL', nil),
5 | use_https: true,
6 | cache: Rails.cache,
7 | always_raise: [
8 | Geocoder::OverQueryLimitError,
9 | Geocoder::RequestDenied,
10 | Geocoder::InvalidRequest,
11 | Geocoder::InvalidApiKey
12 | ]
13 | )
14 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/config/initializers/kaminari.rb:
--------------------------------------------------------------------------------
1 | module Kaminari
2 | # Kaminar's built-in methods for getting the next page
3 | # and determining if a page is the last page are wrong,
4 | # so I'm overriding them.
5 | module PageScopeMethods
6 | def next_page
7 | current_page < total_pages ? (current_page + 1) : nil
8 | end
9 |
10 | def last_page?
11 | current_page == total_pages
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/config/initializers/kaminari_config.rb:
--------------------------------------------------------------------------------
1 | # See Kaminari README for more details about config options:
2 | # https://github.com/amatsuda/kaminari#general-configuration-options
3 | Kaminari.configure do |config|
4 | config.default_per_page = ENV['DEFAULT_PER_PAGE'].to_i
5 | config.max_per_page = ENV['MAX_PER_PAGE'].to_i
6 | end
7 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.session_store :cookie_store, key: '_ohana-api_session'
4 |
--------------------------------------------------------------------------------
/config/initializers/version.rb:
--------------------------------------------------------------------------------
1 | OHANA_API_VERSION = File.read('VERSION').chomp
2 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/config/spring.rb:
--------------------------------------------------------------------------------
1 | %w[
2 | .ruby-version
3 | .rbenv-vars
4 | tmp/restart.txt
5 | tmp/caching-dev.txt
6 | config/application.yml
7 | config/settings.yml
8 | ].each { |path| Spring.watch(path) }
9 |
10 | Spring.watch_method = :listen
11 |
--------------------------------------------------------------------------------
/data/ohana_api_development.dump:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeforamerica/ohana-api/e3ab28075a5fc2f2e15ce06403db5c1e785a7778/data/ohana_api_development.dump
--------------------------------------------------------------------------------
/data/sample-csv/holiday_schedules.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,start_date,end_date,closed,opens_at,closes_at
2 | 1,22,,"January 01, 0001","January 01, 0001",true,,
3 | 2,22,,"12/24/2015","12/24/15",false,10:00,16:00
4 | 3,22,,"June 01, 0001","September 01, 0001",true,,
5 | 4,,22,"January 01, 0001","January 01, 0001",true,,
6 |
--------------------------------------------------------------------------------
/data/sample-csv/programs.csv:
--------------------------------------------------------------------------------
1 | id,organization_id,name,alternate_name
2 | 1,1,Defeat Hunger,
3 |
--------------------------------------------------------------------------------
/data/sample-csv/regular_schedules.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,weekday,opens_at,closes_at
2 | 1,22,,Monday,08:00,17:00
3 | 2,22,,Tuesday,08:00,17:00
4 | 3,22,,Wednesday,08:00,17:00
5 | 4,22,,Thursday,08:00,17:00
6 | 5,22,,Friday,08:00,17:00
7 | 6,22,,Saturday,10:00,18:00
8 | 7,22,,Sunday,11:00,17:00
9 | 8,,22,Monday,08:00,12:00
10 | 9,,22,Monday,14:00,4:00 PM
11 | 10,,22,Wednesday,10:00,3pm
12 | 13,1,,Monday,9am,03:00
13 |
--------------------------------------------------------------------------------
/db/migrate/20140328034023_create_addresses.rb:
--------------------------------------------------------------------------------
1 | class CreateAddresses < ActiveRecord::Migration
2 | def change
3 | create_table :addresses do |t|
4 | t.belongs_to :location
5 | t.text :street
6 | t.text :city
7 | t.text :state
8 | t.text :zip
9 |
10 | t.timestamps
11 | end
12 | add_index :addresses, :location_id
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20140328034531_create_organizations.rb:
--------------------------------------------------------------------------------
1 | class CreateOrganizations < ActiveRecord::Migration
2 | def change
3 | create_table :organizations do |t|
4 | t.text :name
5 | t.text :urls
6 | t.text :slug
7 |
8 | t.timestamps
9 | end
10 | add_index :organizations, :slug, unique: true
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20140328034754_create_locations.rb:
--------------------------------------------------------------------------------
1 | class CreateLocations < ActiveRecord::Migration
2 | def change
3 | create_table :locations do |t|
4 | t.belongs_to :organization
5 | t.text :accessibility
6 | t.text :admin_emails
7 | t.text :description
8 | t.text :emails
9 | t.text :hours
10 | t.text :kind
11 | t.float :latitude
12 | t.float :longitude
13 | t.text :languages
14 | t.text :name
15 | t.text :short_desc
16 | t.text :transportation
17 | t.text :urls
18 | t.text :slug
19 |
20 | t.timestamps
21 | end
22 | add_index :locations, :slug, unique: true
23 | add_index :locations, :organization_id
24 | add_index :locations, [:latitude, :longitude]
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/db/migrate/20140328041648_create_api_applications.rb:
--------------------------------------------------------------------------------
1 | class CreateApiApplications < ActiveRecord::Migration
2 | def change
3 | create_table :api_applications do |t|
4 | t.belongs_to :user
5 | t.text :name
6 | t.text :main_url
7 | t.text :callback_url
8 | t.text :api_token
9 |
10 | t.timestamps
11 | end
12 | add_index :api_applications, :user_id
13 | add_index :api_applications, :api_token, unique: true
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20140328041859_create_contacts.rb:
--------------------------------------------------------------------------------
1 | class CreateContacts < ActiveRecord::Migration
2 | def change
3 | create_table :contacts do |t|
4 | t.belongs_to :location
5 | t.text :name
6 | t.text :title
7 | t.text :email
8 | t.text :fax
9 | t.text :phone
10 | t.text :extension
11 |
12 | t.timestamps
13 | end
14 | add_index :contacts, :location_id
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/db/migrate/20140328042108_create_faxes.rb:
--------------------------------------------------------------------------------
1 | class CreateFaxes < ActiveRecord::Migration
2 | def change
3 | create_table :faxes do |t|
4 | t.belongs_to :location
5 | t.text :number
6 | t.text :department
7 |
8 | t.timestamps
9 | end
10 | add_index :faxes, :location_id
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20140328042218_create_mail_addresses.rb:
--------------------------------------------------------------------------------
1 | class CreateMailAddresses < ActiveRecord::Migration
2 | def change
3 | create_table :mail_addresses do |t|
4 | t.belongs_to :location
5 | t.text :attention
6 | t.text :street
7 | t.text :city
8 | t.text :state
9 | t.text :zip
10 |
11 | t.timestamps
12 | end
13 | add_index :mail_addresses, :location_id
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20140328042359_create_phones.rb:
--------------------------------------------------------------------------------
1 | class CreatePhones < ActiveRecord::Migration
2 | def change
3 | create_table :phones do |t|
4 | t.belongs_to :location
5 | t.text :number
6 | t.text :department
7 | t.text :extension
8 | t.text :vanity_number
9 |
10 | t.timestamps
11 | end
12 | add_index :phones, :location_id
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20140328043104_create_services.rb:
--------------------------------------------------------------------------------
1 | class CreateServices < ActiveRecord::Migration
2 | def change
3 | create_table :services do |t|
4 | t.belongs_to :location
5 | t.text :audience
6 | t.text :description
7 | t.text :eligibility
8 | t.text :fees
9 | t.text :how_to_apply
10 | t.text :name
11 | t.text :short_desc
12 | t.text :urls
13 | t.text :wait
14 | t.text :funding_sources
15 | t.text :service_areas
16 | t.text :keywords
17 |
18 | t.timestamps
19 | end
20 | add_index :services, :location_id
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/db/migrate/20140328044447_create_categories.rb:
--------------------------------------------------------------------------------
1 | class CreateCategories < ActiveRecord::Migration
2 | def change
3 | create_table :categories do |t|
4 | t.text :name
5 | t.text :oe_id
6 | t.text :slug
7 |
8 | t.timestamps
9 | end
10 | add_index :categories, :slug, unique: true
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20140328052427_create_friendly_id_slugs.rb:
--------------------------------------------------------------------------------
1 | class CreateFriendlyIdSlugs < ActiveRecord::Migration
2 |
3 | def self.up
4 | create_table :friendly_id_slugs do |t|
5 | t.string :slug, :null => false
6 | t.integer :sluggable_id, :null => false
7 | t.string :sluggable_type, :limit => 40
8 | t.datetime :created_at
9 | end
10 | add_index :friendly_id_slugs, :sluggable_id
11 | add_index :friendly_id_slugs, [:slug, :sluggable_type], :unique => true
12 | add_index :friendly_id_slugs, :sluggable_type
13 | end
14 |
15 | def self.down
16 | drop_table :friendly_id_slugs
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/db/migrate/20140402222453_create_categories_services.rb:
--------------------------------------------------------------------------------
1 | class CreateCategoriesServices < ActiveRecord::Migration
2 | def change
3 | create_table :categories_services, id: false do |t|
4 | t.belongs_to :category, null: false
5 | t.belongs_to :service, null: false
6 | end
7 | add_index(:categories_services, [:service_id, :category_id], unique: true)
8 | add_index(:categories_services, [:category_id, :service_id], unique: true)
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20140404220233_add_ancestry_to_category.rb:
--------------------------------------------------------------------------------
1 | class AddAncestryToCategory < ActiveRecord::Migration
2 | def change
3 | add_column :categories, :ancestry, :string
4 | add_index :categories, :ancestry
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20140424182454_remove_kind_from_locations.rb:
--------------------------------------------------------------------------------
1 | class RemoveKindFromLocations < ActiveRecord::Migration
2 | def change
3 | remove_column :locations, :kind, :text
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20140508030926_add_search_index_to_organizations.rb:
--------------------------------------------------------------------------------
1 | class AddSearchIndexToOrganizations < ActiveRecord::Migration
2 | def up
3 | execute "create index organizations_name on organizations using gin(to_tsvector('english', name))"
4 | end
5 |
6 | def down
7 | execute "drop index organizations_name"
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20140508031024_add_search_index_to_categories.rb:
--------------------------------------------------------------------------------
1 | class AddSearchIndexToCategories < ActiveRecord::Migration
2 | def up
3 | execute "create index categories_name on categories using gin(to_tsvector('english', name))"
4 | end
5 |
6 | def down
7 | execute "drop index categories_name"
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20140522153640_add_number_type_to_phones.rb:
--------------------------------------------------------------------------------
1 | class AddNumberTypeToPhones < ActiveRecord::Migration
2 | def change
3 | add_column :phones, :number_type, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20140630171418_add_super_admin_to_admins.rb:
--------------------------------------------------------------------------------
1 | class AddSuperAdminToAdmins < ActiveRecord::Migration
2 | def change
3 | add_column :admins, :super_admin, :boolean, default: false
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20140829154350_add_search_indexes_to_services.rb:
--------------------------------------------------------------------------------
1 | class AddSearchIndexesToServices < ActiveRecord::Migration
2 | def up
3 | execute "create index services_service_areas on services using gin(to_tsvector('english', service_areas))"
4 | end
5 |
6 | def down
7 | execute "drop index services_service_areas"
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20140929221750_add_fields_to_organization.rb:
--------------------------------------------------------------------------------
1 | class AddFieldsToOrganization < ActiveRecord::Migration
2 | def change
3 | add_column :organizations, :alternate_name, :string
4 | add_column :organizations, :date_incorporated, :date
5 | add_column :organizations, :description, :text, null: false
6 | add_column :organizations, :email, :string
7 | add_column :organizations, :legal_status, :string
8 | add_column :organizations, :tax_id, :string
9 | add_column :organizations, :tax_status, :string
10 | add_column :organizations, :website, :string
11 |
12 | remove_column :organizations, :urls, :string
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20141007144757_add_fields_to_location.rb:
--------------------------------------------------------------------------------
1 | class AddFieldsToLocation < ActiveRecord::Migration
2 | def change
3 | add_column :locations, :alternate_name, :string
4 | add_column :locations, :virtual, :boolean, default: false
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20141009185459_add_fields_to_address.rb:
--------------------------------------------------------------------------------
1 | class AddFieldsToAddress < ActiveRecord::Migration
2 | def change
3 | add_column :addresses, :country_code, :string, null: false
4 | add_column :addresses, :street_2, :string
5 | rename_column :addresses, :zip, :postal_code
6 | rename_column :addresses, :street, :street_1
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20141009204519_add_fields_to_mail_address.rb:
--------------------------------------------------------------------------------
1 | class AddFieldsToMailAddress < ActiveRecord::Migration
2 | def change
3 | add_column :mail_addresses, :country_code, :string, null: false
4 | add_column :mail_addresses, :street_2, :string
5 | rename_column :mail_addresses, :zip, :postal_code
6 | rename_column :mail_addresses, :street, :street_1
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20141010031124_add_country_prefix_to_phones.rb:
--------------------------------------------------------------------------------
1 | class AddCountryPrefixToPhones < ActiveRecord::Migration
2 | def change
3 | add_column :phones, :country_prefix, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20141010155451_drop_faxes_table.rb:
--------------------------------------------------------------------------------
1 | class DropFaxesTable < ActiveRecord::Migration
2 | def up
3 | drop_table :faxes
4 | end
5 |
6 | def down
7 | create_table :faxes do |t|
8 | t.belongs_to :location
9 | t.text :number
10 | t.text :department
11 |
12 | t.timestamps
13 | end
14 | add_index :faxes, :location_id
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/db/migrate/20141010171020_update_contact_fields.rb:
--------------------------------------------------------------------------------
1 | class UpdateContactFields < ActiveRecord::Migration
2 | def change
3 | add_column :contacts, :department, :string
4 | remove_column :contacts, :fax, :string
5 | remove_column :contacts, :phone, :string
6 | remove_column :contacts, :extension, :string
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20141010171817_add_contact_ref_to_phones.rb:
--------------------------------------------------------------------------------
1 | class AddContactRefToPhones < ActiveRecord::Migration
2 | def change
3 | add_reference :phones, :contact, index: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20141021195019_update_service_status.rb:
--------------------------------------------------------------------------------
1 | class UpdateServiceStatus < ActiveRecord::Migration
2 | def change
3 | change_column_null :services, :status, false
4 | change_column_default :services, :status, 'active'
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20141023040419_add_active_to_locations.rb:
--------------------------------------------------------------------------------
1 | class AddActiveToLocations < ActiveRecord::Migration
2 | def change
3 | add_column :locations, :active, :boolean, default: true
4 | add_index :locations, :active
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20141024022657_create_programs.rb:
--------------------------------------------------------------------------------
1 | class CreatePrograms < ActiveRecord::Migration
2 | def change
3 | create_table :programs do |t|
4 | t.belongs_to :organization
5 | t.string :name
6 | t.string :alternate_name
7 |
8 | t.timestamps
9 | end
10 | add_index :programs, :organization_id
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20141024025404_add_program_ref_to_services.rb:
--------------------------------------------------------------------------------
1 | class AddProgramRefToServices < ActiveRecord::Migration
2 | def change
3 | add_reference :services, :program, index: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20141027154101_create_regular_schedules.rb:
--------------------------------------------------------------------------------
1 | class CreateRegularSchedules < ActiveRecord::Migration
2 | def change
3 | create_table :regular_schedules do |t|
4 | t.integer :weekday
5 | t.time :opens_at
6 | t.time :closes_at
7 | t.references :service, index: true
8 | t.references :location, index: true
9 | end
10 | add_index :regular_schedules, :weekday
11 | add_index :regular_schedules, :opens_at
12 | add_index :regular_schedules, :closes_at
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20141029170109_remove_hours_from_locations.rb:
--------------------------------------------------------------------------------
1 | class RemoveHoursFromLocations < ActiveRecord::Migration
2 | def change
3 | remove_column :locations, :hours, :text
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20141030012617_create_holiday_schedules.rb:
--------------------------------------------------------------------------------
1 | class CreateHolidaySchedules < ActiveRecord::Migration
2 | def change
3 | create_table :holiday_schedules do |t|
4 | t.references :location, index: true
5 | t.references :service, index: true
6 | t.boolean :closed, null: false
7 | t.date :start_date, null: false
8 | t.date :end_date, null: false
9 | t.time :opens_at
10 | t.time :closes_at
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20141030204742_add_array_fields_to_organization.rb:
--------------------------------------------------------------------------------
1 | class AddArrayFieldsToOrganization < ActiveRecord::Migration
2 | def change
3 | add_column :organizations, :funding_sources, :string, array: true, default: []
4 | add_column :organizations, :accreditations, :string, array: true, default: []
5 | add_column :organizations, :licenses, :string, array: true, default: []
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20141106215928_add_organization_ref_to_contacts.rb:
--------------------------------------------------------------------------------
1 | class AddOrganizationRefToContacts < ActiveRecord::Migration
2 | def change
3 | add_reference :contacts, :organization, index: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20141107161835_add_service_ref_to_contacts.rb:
--------------------------------------------------------------------------------
1 | class AddServiceRefToContacts < ActiveRecord::Migration
2 | def change
3 | add_reference :contacts, :service, index: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20141108042551_add_organization_ref_to_phones.rb:
--------------------------------------------------------------------------------
1 | class AddOrganizationRefToPhones < ActiveRecord::Migration
2 | def change
3 | add_reference :phones, :organization, index: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20141108183056_add_service_ref_to_phones.rb:
--------------------------------------------------------------------------------
1 | class AddServiceRefToPhones < ActiveRecord::Migration
2 | def change
3 | add_reference :phones, :service, index: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20141108194838_replace_location_urls_with_website.rb:
--------------------------------------------------------------------------------
1 | class ReplaceLocationUrlsWithWebsite < ActiveRecord::Migration
2 | def change
3 | add_column :locations, :website, :string
4 | remove_column :locations, :urls, :string
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20141108214741_add_website_index_to_locations.rb:
--------------------------------------------------------------------------------
1 | class AddWebsiteIndexToLocations < ActiveRecord::Migration
2 | def up
3 | execute "CREATE INDEX locations_website_with_varchar_pattern_ops ON locations (website varchar_pattern_ops);"
4 | end
5 |
6 | def down
7 | execute "DROP INDEX locations_website_with_varchar_pattern_ops"
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20141109021540_replace_location_emails_with_email.rb:
--------------------------------------------------------------------------------
1 | class ReplaceLocationEmailsWithEmail < ActiveRecord::Migration
2 | def change
3 | add_column :locations, :email, :string
4 | remove_column :locations, :emails, :text
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20141109022202_add_email_index_to_locations.rb:
--------------------------------------------------------------------------------
1 | class AddEmailIndexToLocations < ActiveRecord::Migration
2 | def up
3 | execute "CREATE INDEX locations_email_with_varchar_pattern_ops ON locations (email varchar_pattern_ops);"
4 | end
5 |
6 | def down
7 | execute "DROP INDEX locations_email_with_varchar_pattern_ops"
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20141118132537_rename_wait_to_wait_time.rb:
--------------------------------------------------------------------------------
1 | class RenameWaitToWaitTime < ActiveRecord::Migration
2 | def change
3 | rename_column :services, :wait, :wait_time
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20141208165502_rename_oe_id_to_taxonomy_id.rb:
--------------------------------------------------------------------------------
1 | class RenameOeIdToTaxonomyId < ActiveRecord::Migration
2 | def change
3 | rename_column :categories, :oe_id, :taxonomy_id
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20150107163352_add_interpretation_services_to_services.rb:
--------------------------------------------------------------------------------
1 | class AddInterpretationServicesToServices < ActiveRecord::Migration
2 | def change
3 | add_column :services, :interpretation_services, :text
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20150314204202_rename_fields.rb:
--------------------------------------------------------------------------------
1 | class RenameFields < ActiveRecord::Migration
2 | def change
3 | rename_column :services, :how_to_apply, :application_process
4 | rename_column :addresses, :street_1, :address_1
5 | rename_column :addresses, :street_2, :address_2
6 | rename_column :addresses, :state, :state_province
7 | rename_column :addresses, :country_code, :country
8 | rename_column :mail_addresses, :street_1, :address_1
9 | rename_column :mail_addresses, :street_2, :address_2
10 | rename_column :mail_addresses, :state, :state_province
11 | rename_column :mail_addresses, :country_code, :country
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20150315202808_update_null_constraints.rb:
--------------------------------------------------------------------------------
1 | class UpdateNullConstraints < ActiveRecord::Migration
2 | def change
3 | change_column_null :services, :application_process, true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | web:
4 | build: .
5 | volumes:
6 | - .:/ohana-api
7 | ports:
8 | - "8080:8080"
9 | environment:
10 | DATABASE_URL: "postgres://postgres@db"
11 | depends_on:
12 | - db
13 | db:
14 | image: postgres
15 |
--------------------------------------------------------------------------------
/lib/address_extractor.rb:
--------------------------------------------------------------------------------
1 | AddressExtractor = Struct.new(:path) do
2 | def self.extract_addresses(path)
3 | new(path).csv_entries
4 | end
5 |
6 | def csv_entries
7 | @csv_entries ||= SmarterCSV.process(path, convert_values_to_numeric: false)
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/api_constraints.rb:
--------------------------------------------------------------------------------
1 | class ApiConstraints
2 | def initialize(options)
3 | @version = options[:version]
4 | end
5 |
6 | def matches?(request)
7 | versioned_accept_header?(request) || @version == 1
8 | end
9 |
10 | private
11 |
12 | def versioned_accept_header?(request)
13 | accept = request.headers['Accept']
14 | return false if accept.blank?
15 |
16 | mime_type, version = accept.gsub(/\s/, '').split(';')
17 | mime_type.match(/vnd\.ohanapi\+json/) && version == "version=#{@version}"
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeforamerica/ohana-api/e3ab28075a5fc2f2e15ce06403db5c1e785a7778/lib/assets/.gitkeep
--------------------------------------------------------------------------------
/lib/category_id_collector.rb:
--------------------------------------------------------------------------------
1 | module CategoryIdCollector
2 | def cat_ids(taxonomy_ids)
3 | return [] if taxonomy_ids.blank?
4 |
5 | Category.where(taxonomy_id: taxonomy_ids).pluck(:id)
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/category_importer.rb:
--------------------------------------------------------------------------------
1 | class CategoryImporter < EntityImporter
2 | def valid?
3 | @valid ||= categories.all?(&:valid?)
4 | end
5 |
6 | def errors
7 | ImporterErrors.messages_for(categories)
8 | end
9 |
10 | def import
11 | ActiveRecord::Base.no_touching do
12 | categories.each(&:save)
13 | end
14 | end
15 |
16 | def self.required_headers
17 | %w[taxonomy_id name parent_id parent_name]
18 | end
19 |
20 | protected
21 |
22 | def categories
23 | @categories ||= csv_entries.each_with_object([]) do |chunks, result|
24 | chunks.each { |row| result << CategoryPresenter.new(row).to_category }
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/category_presenter.rb:
--------------------------------------------------------------------------------
1 | CategoryPresenter = Struct.new(:row) do
2 | def to_category
3 | return Category.create(category_params) if row[:parent_id].blank?
4 | return parent_category unless parent_category.valid?
5 |
6 | child_category
7 | end
8 |
9 | def parent_category
10 | @parent_category ||= Category.find_or_create_by(name: row[:parent_name],
11 | taxonomy_id: row[:parent_id])
12 | end
13 |
14 | def child_category
15 | parent_category.children.create(category_params)
16 | end
17 |
18 | private
19 |
20 | def category_params
21 | raw_params = ActionController::Parameters.new(row)
22 | raw_params.permit(:taxonomy_id, :name)
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/contact_importer.rb:
--------------------------------------------------------------------------------
1 | class ContactImporter < EntityImporter
2 | def valid?
3 | @valid ||= contacts.all?(&:valid?)
4 | end
5 |
6 | def errors
7 | ImporterErrors.messages_for(contacts)
8 | end
9 |
10 | def import
11 | ActiveRecord::Base.no_touching do
12 | contacts.each(&:save)
13 | end
14 | end
15 |
16 | def self.required_headers
17 | %w[id location_id organization_id service_id department email name
18 | title]
19 | end
20 |
21 | protected
22 |
23 | def contacts
24 | @contacts ||= csv_entries.each_with_object([]) do |chunks, result|
25 | chunks.each { |row| result << ContactPresenter.new(row).to_contact }
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/contact_presenter.rb:
--------------------------------------------------------------------------------
1 | ContactPresenter = Struct.new(:row) do
2 | include ParentAssigner
3 |
4 | def to_contact
5 | contact = Contact.find_or_initialize_by(id: row[:id].to_i)
6 | contact.attributes = row
7 | assign_parents_for(contact, row)
8 | contact
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/default_host.rb:
--------------------------------------------------------------------------------
1 | class DefaultHost
2 | def call(request)
3 | host = request.host
4 | return host if host.end_with?(default_host)
5 |
6 | default_host
7 | end
8 |
9 | private
10 |
11 | def default_host
12 | @default_host ||= Figaro.env.domain_name
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/entity_importer.rb:
--------------------------------------------------------------------------------
1 | require 'csv'
2 |
3 | EntityImporter = Struct.new(:path) do
4 | def self.import_file(path)
5 | new(path).tap(&:import)
6 | end
7 |
8 | def self.check_and_import_file(path)
9 | check = FileChecker.new(path, required_headers).validate
10 |
11 | return if check == 'skip import'
12 |
13 | Kernel.puts("\n===> Importing #{path.to_s.split('/').last}")
14 | process_import(path)
15 | end
16 |
17 | def self.process_import(path)
18 | importer = import_file(path)
19 | importer.errors.each { |e| Kernel.puts(e) } unless importer.valid?
20 | end
21 |
22 | protected
23 |
24 | def csv_entries
25 | @csv_entries ||= SmarterCSV.process(path, chunk_size: 100)
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/entity_presenter.rb:
--------------------------------------------------------------------------------
1 | module EntityPresenter
2 | def to_array(row, *fields)
3 | fields.each do |field|
4 | row[field] =
5 | if row[field].blank?
6 | []
7 | else
8 | row[field].split(',').map(&:squish)
9 | end
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/lib/exceptions.rb:
--------------------------------------------------------------------------------
1 | module Exceptions
2 | class InvalidRadius < ArgumentError
3 | end
4 |
5 | class InvalidLatLon < ArgumentError
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/holiday_schedule_importer.rb:
--------------------------------------------------------------------------------
1 | class HolidayScheduleImporter < EntityImporter
2 | def valid?
3 | @valid ||= holiday_schedules.all?(&:valid?)
4 | end
5 |
6 | def errors
7 | ImporterErrors.messages_for(holiday_schedules)
8 | end
9 |
10 | def import
11 | ActiveRecord::Base.no_touching do
12 | holiday_schedules.each(&:save)
13 | end
14 | end
15 |
16 | def self.required_headers
17 | %w[id location_id service_id closed start_date end_date opens_at
18 | closes_at]
19 | end
20 |
21 | protected
22 |
23 | def holiday_schedules
24 | @holiday_schedules ||= csv_entries.each_with_object([]) do |chunks, result|
25 | chunks.each { |row| result << HolidaySchedulePresenter.new(row).to_holiday_schedule }
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/holiday_schedule_presenter.rb:
--------------------------------------------------------------------------------
1 | HolidaySchedulePresenter = Struct.new(:row) do
2 | include ParentAssigner
3 |
4 | def to_holiday_schedule
5 | holiday_schedule = HolidaySchedule.find_or_initialize_by(id: row[:id].to_i)
6 | holiday_schedule.attributes = row
7 | assign_parents_for(holiday_schedule, row)
8 | holiday_schedule
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/importer_errors.rb:
--------------------------------------------------------------------------------
1 | ImporterErrors = Struct.new(:record, :line_number) do
2 | def self.messages_for(records = [])
3 | records.each_with_index.filter_map do |record, index|
4 | new(record, index + 2).message
5 | end
6 | end
7 |
8 | def message
9 | "Line #{line_number}: #{error_messages}" unless record.valid?
10 | end
11 |
12 | protected
13 |
14 | def error_messages
15 | record.errors.full_messages.uniq.join(', ')
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/mail_address_importer.rb:
--------------------------------------------------------------------------------
1 | class MailAddressImporter < EntityImporter
2 | def valid?
3 | @valid ||= mail_addresses.all?(&:valid?)
4 | end
5 |
6 | def errors
7 | ImporterErrors.messages_for(mail_addresses)
8 | end
9 |
10 | def import
11 | ActiveRecord::Base.no_touching do
12 | mail_addresses.each(&:save)
13 | end
14 | end
15 |
16 | def self.required_headers
17 | %w[id location_id attention address_1 address_2 city state_province postal_code
18 | country]
19 | end
20 |
21 | protected
22 |
23 | def mail_addresses
24 | @mail_addresses ||= csv_entries.each_with_object([]) do |chunks, result|
25 | chunks.each { |row| result << MailAddressPresenter.new(row).to_mail_address }
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/mail_address_presenter.rb:
--------------------------------------------------------------------------------
1 | MailAddressPresenter = Struct.new(:row) do
2 | include ParentAssigner
3 |
4 | def to_mail_address
5 | mail_address = MailAddress.find_or_initialize_by(id: row[:id].to_i)
6 | mail_address.attributes = row
7 | assign_parents_for(mail_address, row)
8 | mail_address
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/organization_importer.rb:
--------------------------------------------------------------------------------
1 | class OrganizationImporter < EntityImporter
2 | def valid?
3 | @valid ||= organizations.all?(&:valid?)
4 | end
5 |
6 | def errors
7 | ImporterErrors.messages_for(organizations)
8 | end
9 |
10 | def import
11 | ActiveRecord::Base.no_touching do
12 | organizations.each(&:save)
13 | end
14 | end
15 |
16 | def self.required_headers
17 | %w[id accreditations alternate_name date_incorporated
18 | description email funding_sources legal_status
19 | licenses name tax_id tax_status website]
20 | end
21 |
22 | private
23 |
24 | def organizations
25 | @organizations ||= csv_entries.each_with_object([]) do |chunks, orgs|
26 | chunks.each { |row| orgs << OrganizationPresenter.new(row).to_org }
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/organization_presenter.rb:
--------------------------------------------------------------------------------
1 | OrganizationPresenter = Struct.new(:row) do
2 | include EntityPresenter
3 |
4 | def to_org
5 | org = Organization.find_or_initialize_by(id: row[:id].to_i)
6 | to_array(row, :accreditations, :licenses, :funding_sources)
7 | org.attributes = row
8 | org.id = row[:id].to_i
9 | org
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/parent_assigner.rb:
--------------------------------------------------------------------------------
1 | module ParentAssigner
2 | private
3 |
4 | def assign_parents_for(record, row)
5 | foreign_keys_in(row).each do |key|
6 | next if row[key].nil?
7 |
8 | record[key] = row[key].to_i
9 | end
10 | end
11 |
12 | def foreign_keys_in(row)
13 | row.keys.select { |key| key.to_s == 'id' || key.to_s.include?('_id') }
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/phone_importer.rb:
--------------------------------------------------------------------------------
1 | class PhoneImporter < EntityImporter
2 | def valid?
3 | @valid ||= phones.all?(&:valid?)
4 | end
5 |
6 | def errors
7 | ImporterErrors.messages_for(phones)
8 | end
9 |
10 | def import
11 | ActiveRecord::Base.no_touching do
12 | phones.each(&:save)
13 | end
14 | end
15 |
16 | def self.required_headers
17 | %w[id location_id organization_id service_id contact_id department
18 | extension number number_type vanity_number country_prefix]
19 | end
20 |
21 | protected
22 |
23 | def phones
24 | @phones ||= csv_entries.each_with_object([]) do |chunks, result|
25 | chunks.each { |row| result << PhonePresenter.new(row).to_phone }
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/phone_presenter.rb:
--------------------------------------------------------------------------------
1 | PhonePresenter = Struct.new(:row) do
2 | include ParentAssigner
3 |
4 | def to_phone
5 | phone = Phone.find_or_initialize_by(id: row[:id].to_i)
6 | phone.attributes = row
7 | assign_parents_for(phone, row)
8 | phone
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/program_importer.rb:
--------------------------------------------------------------------------------
1 | class ProgramImporter < EntityImporter
2 | def valid?
3 | @valid ||= programs.all?(&:valid?)
4 | end
5 |
6 | def errors
7 | ImporterErrors.messages_for(programs)
8 | end
9 |
10 | def import
11 | ActiveRecord::Base.no_touching do
12 | programs.each(&:save)
13 | end
14 | end
15 |
16 | def self.required_headers
17 | %w[id organization_id name alternate_name]
18 | end
19 |
20 | protected
21 |
22 | def programs
23 | @programs ||= csv_entries.each_with_object([]) do |chunks, result|
24 | chunks.each { |row| result << ProgramPresenter.new(row).to_program }
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/program_presenter.rb:
--------------------------------------------------------------------------------
1 | ProgramPresenter = Struct.new(:row) do
2 | include ParentAssigner
3 |
4 | def to_program
5 | program = Program.find_or_initialize_by(id: row[:id].to_i)
6 | program.attributes = row
7 | assign_parents_for(program, row)
8 | program
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/regular_schedule_importer.rb:
--------------------------------------------------------------------------------
1 | class RegularScheduleImporter < EntityImporter
2 | def valid?
3 | @valid ||= regular_schedules.all?(&:valid?)
4 | end
5 |
6 | def errors
7 | ImporterErrors.messages_for(regular_schedules)
8 | end
9 |
10 | def import
11 | ActiveRecord::Base.no_touching do
12 | regular_schedules.each(&:save)
13 | end
14 | end
15 |
16 | def self.required_headers
17 | %w[id location_id service_id weekday opens_at closes_at]
18 | end
19 |
20 | protected
21 |
22 | def regular_schedules
23 | @regular_schedules ||= csv_entries.each_with_object([]) do |chunks, result|
24 | chunks.each { |row| result << RegularSchedulePresenter.new(row).to_regular_schedule }
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/regular_schedule_presenter.rb:
--------------------------------------------------------------------------------
1 | RegularSchedulePresenter = Struct.new(:row) do
2 | include ParentAssigner
3 |
4 | def to_regular_schedule
5 | regular_schedule = RegularSchedule.find_or_initialize_by(id: row[:id].to_i)
6 | regular_schedule.attributes = row
7 | assign_parents_for(regular_schedule, row)
8 | regular_schedule
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/service_presenter.rb:
--------------------------------------------------------------------------------
1 | ServicePresenter = Struct.new(:row) do
2 | include EntityPresenter
3 | include ParentAssigner
4 | include CategoryIdCollector
5 |
6 | # rubocop:disable Metrics/AbcSize
7 | def to_service
8 | service = Service.find_or_initialize_by(id: row[:id].to_i)
9 | to_array(row, :accepted_payments, :funding_sources, :keywords, :languages,
10 | :required_documents, :service_areas, :taxonomy_ids)
11 | service.attributes = row.except(:taxonomy_ids)
12 | assign_categories_to(service, row[:taxonomy_ids])
13 | assign_parents_for(service, row.except(:taxonomy_ids))
14 | service
15 | end
16 | # rubocop:enable Metrics/AbcSize
17 |
18 | private
19 |
20 | def assign_categories_to(service, taxonomy_ids)
21 | service.category_ids = cat_ids(taxonomy_ids)
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/lib/subdomain_constraints.rb:
--------------------------------------------------------------------------------
1 | class SubdomainConstraints
2 | def initialize(options)
3 | @subdomain = options[:subdomain]
4 | end
5 |
6 | def matches?(request)
7 | if @subdomain.present?
8 | request.subdomain == @subdomain
9 | else
10 | request.subdomain.blank? || request.subdomain == 'www'
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/tasks/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeforamerica/ohana-api/e3ab28075a5fc2f2e15ce06403db5c1e785a7778/lib/tasks/.gitkeep
--------------------------------------------------------------------------------
/lib/tasks/remove_test_users_and_admins.rake:
--------------------------------------------------------------------------------
1 | task remove_test_users_and_admins: :environment do
2 | User.includes(:api_applications).
3 | where(email: %w[user@example.com user2@example.com]).destroy_all
4 |
5 | Admin.
6 | where(email: %w[ohana@samaritanhouse.com ohana@gmail.com masteradmin@ohanapi.org]).
7 | destroy_all
8 | end
9 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeforamerica/ohana-api/e3ab28075a5fc2f2e15ce06403db5c1e785a7778/public/favicon.ico
--------------------------------------------------------------------------------
/public/humans.txt:
--------------------------------------------------------------------------------
1 | /* the humans responsible & colophon */
2 | /* humanstxt.org */
3 |
4 |
5 | /* TEAM */
6 | :
7 | Site:
8 | Twitter:
9 | Location:
10 |
11 | /* THANKS */
12 | Daniel Kehoe (@rails_apps) for the RailsApps project
13 |
14 | /* SITE */
15 | Standards: HTML5, CSS3
16 | Components: jQuery
17 | Software: Ruby on Rails
18 |
19 | /* GENERATED BY */
20 | RailsApps application template: http://railsapps.github.io/
21 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-Agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/script/bootstrap:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | bin/setup --docker
6 |
7 | echo "===> Building the Docker image..."
8 | docker-compose build
9 |
10 | echo "===> Setting up the database on the Docker image..."
11 | docker-compose run --rm web bundle exec rake db:reset RAILS_ENV=development
12 | docker-compose run --rm web bundle exec rails db:environment:set RAILS_ENV=test
13 | docker-compose run --rm web bundle exec rake db:reset RAILS_ENV=test
14 | docker-compose run --rm web pg_restore -c --no-owner -d ohana_api_development /ohana-api/data/ohana_api_development.dump -U postgres -h db
15 |
16 | echo "===> Starting the Docker image..."
17 | docker-compose up
18 |
--------------------------------------------------------------------------------
/script/export:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo "===> Exporting the ohana_api_development DB into data/ohana_api_development.dump"
6 | pg_dump -Fc --no-acl --no-owner -h localhost ohana_api_development > data/ohana_api_development.dump
7 | echo "===> Done exporting the DB."
--------------------------------------------------------------------------------
/script/export_prod_db:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo 'Removing test users and admins'
6 | bin/rake remove_test_users_and_admins
7 |
8 | echo Exporting the ohana_api_development DB into data/ohana_api_production.dump...
9 | pg_dump -Fc --no-acl --no-owner -h localhost ohana_api_development > data/ohana_api_production.dump
10 |
11 | echo Done exporting the DB.
12 |
--------------------------------------------------------------------------------
/script/geocode:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo "===> Geocoding locations. This will take several minutes..."
6 | rake geocode:all CLASS=Location sleep=0.25
--------------------------------------------------------------------------------
/script/import:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | sleep_time="$1"
6 |
7 | bin/rake import:all sleep=$sleep_time
8 | echo 'All done, rock on!'
9 |
--------------------------------------------------------------------------------
/script/reset:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo Resetting the database...
6 | bin/rake db:reset
7 | echo Done resetting the database.
8 |
--------------------------------------------------------------------------------
/script/reset_test_db:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | dropdb ohana_api_test
6 | bin/rake db:create:all
7 | bin/rake db:migrate
8 |
--------------------------------------------------------------------------------
/script/restore:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo "===> Restoring the DB from data/ohana_api_development.dump..."
6 | pg_restore -c --no-owner -d ohana_api_development data/ohana_api_development.dump
7 | echo "===> Done restoring the DB."
8 |
--------------------------------------------------------------------------------
/script/restore_prod_db:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo "===> Restoring the DB from data/ohana_api_production.dump..."
6 | pg_restore -c --no-owner -d ohana_api_development data/ohana_api_production.dump
7 | echo "===> Done restoring the DB."
8 |
--------------------------------------------------------------------------------
/script/setup_db:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo "===> Creating ohana_api_development and ohana_api_test databases..."
6 | bin/rake db:create:all
7 |
8 | echo "===> Setting up the ohana_api_development database..."
9 | bin/rake db:structure:load
10 |
11 | echo "===> Populating the ohana_api_development database..."
12 | script/restore
13 |
--------------------------------------------------------------------------------
/script/test:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | spring rspec
6 | rubocop
7 | haml-lint app/views
8 |
--------------------------------------------------------------------------------
/script/users:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo "===> Seeding the DB with test users..."
6 | bin/rake db:seed
--------------------------------------------------------------------------------
/spec/active_record_spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'active_record'
2 |
3 | connection_info = YAML.load_file('config/database.yml')['test']
4 | ActiveRecord::Base.establish_connection(connection_info)
5 |
6 | RSpec.configure do |config|
7 | config.around do |example|
8 | ActiveRecord::Base.transaction do
9 | example.run
10 | raise ActiveRecord::Rollback
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/api/services/update_service_categories_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'PUT /services/:service_id/categories' do
4 | before(:all) do
5 | create_service
6 | end
7 |
8 | after(:all) do
9 | Organization.find_each(&:destroy)
10 | end
11 |
12 | it 'returns 200 when validations pass' do
13 | create(:category)
14 | create(:health)
15 | put(
16 | api_service_categories_url(@service, subdomain: ENV.fetch('API_SUBDOMAIN', nil)),
17 | taxonomy_ids: %w[101 102]
18 | )
19 |
20 | expect(response).to have_http_status(:ok)
21 | expect(json['categories'][0]['name']).to eq 'Food'
22 | expect(json['categories'][1]['name']).to eq 'Health'
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/controllers/home_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe HomeController do
4 | describe "GET 'index'" do
5 | it 'returns http success' do
6 | get 'index'
7 | expect(response).to be_successful
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/factories/api_applications.rb:
--------------------------------------------------------------------------------
1 | # Read about factories at https://github.com/thoughtbot/factory_girl
2 |
3 | FactoryBot.define do
4 | factory :api_application do
5 | name { 'test app' }
6 | main_url { 'http://cfa.org' }
7 | callback_url { 'http://cfa.org/callback' }
8 |
9 | factory :app_with_extra_whitespace do
10 | name { ' app with extra whitespace ' }
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/factories/categories.rb:
--------------------------------------------------------------------------------
1 | # Read about factories at https://github.com/thoughtbot/factory_girl
2 |
3 | FactoryBot.define do
4 | factory :category do
5 | name { 'Food' }
6 | taxonomy_id { '101' }
7 | end
8 |
9 | factory :health, class: 'Category' do
10 | name { 'Health' }
11 | taxonomy_id { '102' }
12 | end
13 |
14 | factory :jobs, class: 'Category' do
15 | name { 'Jobs' }
16 | taxonomy_id { '105' }
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/factories/contacts.rb:
--------------------------------------------------------------------------------
1 | # Read about factories at https://github.com/thoughtbot/factory_girl
2 |
3 | FactoryBot.define do
4 | factory :contact do
5 | name { 'Moncef Belyamani' }
6 | title { 'CTO' }
7 |
8 | factory :foobar do
9 | name { 'Foo' }
10 | title { 'Bar' }
11 | end
12 |
13 | factory :contact_with_extra_whitespace do
14 | name { 'Foo ' }
15 | title { ' Bar' }
16 | email { ' foo@bar.com ' }
17 | department { 'Screening ' }
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/factories/holiday_schedules.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :holiday_schedule do
3 | closed { true }
4 | start_date { 'December 24, 2014' }
5 | end_date { 'December 24, 2014' }
6 |
7 | trait :open do
8 | closed { false }
9 | opens_at { '9am' }
10 | closes_at { '17:00' }
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/factories/mail_addresses.rb:
--------------------------------------------------------------------------------
1 | # Read about factories at https://github.com/thoughtbot/factory_girl
2 |
3 | FactoryBot.define do
4 | factory :mail_address do
5 | attention { 'Monfresh' }
6 | address_1 { '1 davis dr' }
7 | city { 'Belmont' }
8 | state_province { 'CA' }
9 | postal_code { '90210' }
10 | country { 'US' }
11 | association :location, factory: :no_address
12 | end
13 |
14 | factory :mail_address_with_extra_whitespace, class: 'MailAddress' do
15 | attention { ' Moncef ' }
16 | address_1 { '8875 La Honda Road' }
17 | city { 'La Honda ' }
18 | state_province { ' CA ' }
19 | postal_code { ' 94020' }
20 | country { ' US ' }
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/factories/phones.rb:
--------------------------------------------------------------------------------
1 | # Read about factories at https://github.com/thoughtbot/factory_girl
2 |
3 | FactoryBot.define do
4 | factory :phone do
5 | number { '650 851-1210' }
6 | number_type { 'voice' }
7 | extension { '200' }
8 | end
9 |
10 | factory :phone_with_extra_whitespace, class: 'Phone' do
11 | country_prefix { '33 ' }
12 | number { '650 851-1210 ' }
13 | department { ' Information ' }
14 | extension { '2000 ' }
15 | vanity_number { ' 800-FLY-AWAY ' }
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/factories/programs.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :program do
3 | name { 'Collection of Services ' }
4 | alternate_name { ' Also Known As' }
5 | organization
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/factories/regular_schedules.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :regular_schedule do
3 | weekday { 7 }
4 | opens_at { '9:30' }
5 | closes_at { '5pm' }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/factories/users.rb:
--------------------------------------------------------------------------------
1 | # Read about factories at https://github.com/thoughtbot/factory_girl
2 |
3 | FactoryBot.define do
4 | factory :user do
5 | name { 'Test User' }
6 | email { 'valid@example.com' }
7 | password { 'mong01dtest' }
8 | password_confirmation { 'mong01dtest' }
9 | # required if the Devise Confirmable module is used
10 | confirmed_at { Time.zone.now }
11 |
12 | factory :user_with_app do
13 | after(:create) do |user|
14 | create(:api_application, user: user)
15 | end
16 | end
17 | end
18 |
19 | factory :unconfirmed_user, class: :user do
20 | name { 'Unconfirmed User' }
21 | email { 'invalid@example.com' }
22 | password { 'mong01dtest' }
23 | password_confirmation { 'mong01dtest' }
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/spec/features/admin/contacts/delete_contact_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Delete contact' do
4 | before do
5 | @location = create(:location)
6 | @location.contacts.create!(attributes_for(:contact))
7 | login_super_admin
8 | visit '/admin/locations/vrs-services'
9 | click_link 'Moncef Belyamani'
10 | end
11 |
12 | it 'when deleting contact' do
13 | find_link(I18n.t('admin.buttons.delete_contact')).click
14 | using_wait_time 5 do
15 | expect(page).to have_current_path admin_location_path(@location), ignore_query: true
16 | expect(page).not_to have_link 'Moncef Belyamani'
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/features/admin/csv/download_addresses_csv_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Downloading Addresses CSV' do
4 | before do
5 | login_super_admin
6 | @address = create(:address, location_id: 1)
7 | visit admin_csv_addresses_path(format: 'csv')
8 | end
9 |
10 | it 'contains the same headers as in the import Wiki' do
11 | expect(csv.first).to eq %w[id location_id address_1 address_2 city
12 | state_province postal_code country]
13 | end
14 |
15 | it 'populates address attribute values' do
16 | expect(csv.second).to eq [
17 | @address.id.to_s, @address.location_id.to_s, '1800 Easton Drive', nil,
18 | 'Burlingame', 'CA', '94010', 'US'
19 | ]
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/features/admin/csv/download_mail_addresses_csv_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Downloading Mail Addresses CSV' do
4 | before do
5 | login_super_admin
6 | @mail_address = create(:mail_address)
7 | visit admin_csv_mail_addresses_path(format: 'csv')
8 | end
9 |
10 | it 'contains the same headers as in the import Wiki' do
11 | expect(csv.first).to eq %w[id location_id attention address_1 address_2
12 | city state_province postal_code country]
13 | end
14 |
15 | it 'populates mail address attribute values' do
16 | expect(csv.second).to eq [
17 | @mail_address.id.to_s, @mail_address.location_id.to_s, 'Monfresh',
18 | '1 davis dr', nil, 'Belmont', 'CA', '90210', 'US'
19 | ]
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/features/admin/csv/download_programs_csv_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Downloading Programs CSV' do
4 | before do
5 | login_super_admin
6 | @program = create(:program)
7 | visit admin_csv_programs_path(format: 'csv')
8 | end
9 |
10 | it 'contains the same headers as in the import Wiki' do
11 | expect(csv.first).to eq %w[id organization_id alternate_name name]
12 | end
13 |
14 | it 'populates program attribute values' do
15 | expect(csv.second).to eq [
16 | @program.id.to_s, @program.organization_id.to_s, 'Also Known As',
17 | 'Collection of Services'
18 | ]
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/features/admin/csv/download_regular_schedules_csv_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Downloading Regular Schedules CSV' do
4 | before do
5 | login_super_admin
6 | create_service
7 | @regular_schedule = @location.regular_schedules.
8 | create!(attributes_for(:regular_schedule))
9 | visit admin_csv_regular_schedules_path(format: 'csv')
10 | end
11 |
12 | it 'contains the same headers as in the import Wiki' do
13 | expect(csv.first).to eq %w[id location_id service_id weekday opens_at
14 | closes_at]
15 | end
16 |
17 | it 'formats the date and time values' do
18 | expect(csv.second).to eq [
19 | @regular_schedule.id.to_s, @location.id.to_s, nil, 'Sunday', '09:30',
20 | '17:00'
21 | ]
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/features/admin/edit_account_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Editing Admin account' do
4 | before do
5 | login_admin
6 | visit '/admin/edit'
7 | end
8 |
9 | it 'allows the name to be changed' do
10 | fill_in 'admin_name', with: 'New Admin name'
11 | fill_in 'admin_current_password', with: @admin.password
12 | click_button I18n.t('buttons.update')
13 | visit '/admin/edit'
14 | expect(find_field('admin_name').value).to eq 'New Admin name'
15 | end
16 |
17 | it 'redirects to the admin edit page after update' do
18 | fill_in 'admin_name', with: 'New Admin name'
19 | fill_in 'admin_current_password', with: @admin.password
20 | click_button I18n.t('buttons.update')
21 | expect(page).to have_current_path edit_admin_registration_path, ignore_query: true
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/features/admin/locations/update_alternate_name_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update alternate name' do
4 | before do
5 | create(:location)
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with alternate name' do
11 | fill_in 'location_alternate_name', with: 'Juvenile Sexual Responsibility Program'
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(find_field('location_alternate_name').value).
14 | to eq 'Juvenile Sexual Responsibility Program'
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/features/admin/locations/update_description_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update description' do
4 | before do
5 | create(:location)
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with empty description' do
11 | fill_in 'location_description', with: ''
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(page).to have_content "Description can't be blank for Location"
14 | end
15 |
16 | it 'with valid description' do
17 | fill_in 'location_description', with: 'This is a description'
18 | click_button I18n.t('admin.buttons.save_changes')
19 | expect(find_field('location_description').value).to eq 'This is a description'
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/features/admin/locations/update_email_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update email' do
4 | before do
5 | @location = create(:location)
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with valid location email' do
11 | fill_in 'location_email', with: 'foo@bar.com'
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(find_field('location_email').value).to eq 'foo@bar.com'
14 | end
15 |
16 | it 'with invalid location email' do
17 | fill_in 'location_email', with: 'foobar.com'
18 | click_button I18n.t('admin.buttons.save_changes')
19 | expect(page).to have_content 'foobar.com is not a valid email'
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/features/admin/locations/update_name_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update name' do
4 | before do
5 | create(:location)
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with empty location name' do
11 | fill_in 'location_name', with: ''
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(page).to have_content "Name can't be blank for Location"
14 | expect(page).to have_css('.field_with_errors')
15 | end
16 |
17 | it 'with valid location name' do
18 | fill_in 'location_name', with: 'Juvenile Sexual Responsibility Program'
19 | click_button I18n.t('admin.buttons.save_changes')
20 | expect(find_field('location_name').value).
21 | to eq 'Juvenile Sexual Responsibility Program'
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/features/admin/locations/update_short_description_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update short description' do
4 | before do
5 | create(:location)
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with empty description' do
11 | fill_in 'location_short_desc', with: ''
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(find_field('location_short_desc').value).to eq ''
14 | end
15 |
16 | it 'with valid description' do
17 | fill_in 'location_short_desc', with: 'This is a short description'
18 | click_button I18n.t('admin.buttons.save_changes')
19 | expect(find_field('location_short_desc').value).
20 | to eq 'This is a short description'
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/features/admin/locations/update_transportation_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update transportation options' do
4 | before do
5 | create(:location)
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with empty transportation options' do
11 | fill_in 'location_transportation', with: ''
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(find_field('location_transportation').value).to eq ''
14 | end
15 |
16 | it 'with non-empty transportation options' do
17 | fill_in 'location_transportation', with: 'SAMTRANS stops within 1/2 mile.'
18 | click_button I18n.t('admin.buttons.save_changes')
19 | expect(find_field('location_transportation').value).
20 | to eq 'SAMTRANS stops within 1/2 mile.'
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/features/admin/locations/update_virtual_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update virtual attribute' do
4 | before do
5 | create(:location)
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'setting to true' do
11 | select('Does not have a physical address', from: 'location_virtual')
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(find_field('location_virtual').value).to eq 'true'
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/features/admin/locations/update_website_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update website' do
4 | before do
5 | @location = create(:location)
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with invalid website' do
11 | fill_in 'location_website', with: 'www.monfresh.com'
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(page).to have_content 'www.monfresh.com is not a valid URL'
14 | expect(page).to have_css('.field_with_errors')
15 | end
16 |
17 | it 'with valid website' do
18 | fill_in 'location_website', with: 'http://codeforamerica.org'
19 | click_button I18n.t('admin.buttons.save_changes')
20 | expect(find_field('location_website').value).
21 | to eq 'http://codeforamerica.org'
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/features/admin/organizations/delete_contact_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Delete contact' do
4 | before do
5 | @org = create(:organization)
6 | @org.contacts.create!(attributes_for(:contact))
7 | login_super_admin
8 | visit '/admin/organizations/parent-agency'
9 | click_link 'Moncef Belyamani'
10 | end
11 |
12 | it 'when deleting contact' do
13 | find_link(I18n.t('admin.buttons.delete_contact')).click
14 | using_wait_time 5 do
15 | expect(page).to have_current_path admin_organization_path(@org), ignore_query: true
16 | expect(page).not_to have_link 'Moncef Belyamani'
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/features/admin/organizations/update_alternate_name_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update alternate name' do
4 | before do
5 | create(:organization)
6 | login_super_admin
7 | visit '/admin/organizations/parent-agency'
8 | end
9 |
10 | it 'with alternate name' do
11 | fill_in 'organization_alternate_name', with: 'Juvenile Sexual Responsibility Program'
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(find_field('organization_alternate_name').value).
14 | to eq 'Juvenile Sexual Responsibility Program'
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/features/admin/organizations/update_date_incorporated_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update date incorporated' do
4 | before do
5 | create(:organization)
6 | login_super_admin
7 | visit '/admin/organizations/parent-agency'
8 | end
9 |
10 | it 'with date incorporated' do
11 | select_date(Time.zone.today, from: 'organization_date_incorporated')
12 | click_button I18n.t('admin.buttons.save_changes')
13 |
14 | expect(find_field('organization_date_incorporated_1i').value).
15 | to eq Time.zone.today.year.to_s
16 | expect(find_field('organization_date_incorporated_2i').value).
17 | to eq Time.zone.today.month.to_s
18 | expect(find_field('organization_date_incorporated_3i').value).
19 | to eq Time.zone.today.day.to_s
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/features/admin/organizations/update_email_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update email' do
4 | before do
5 | create(:organization)
6 | login_super_admin
7 | visit '/admin/organizations/parent-agency'
8 | end
9 |
10 | it 'with valid organization email' do
11 | fill_in 'organization_email', with: 'foo@bar.com'
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(find_field('organization_email').value).to eq 'foo@bar.com'
14 | end
15 |
16 | it 'with invalid organization email' do
17 | fill_in 'organization_email', with: 'foobar.com'
18 | click_button I18n.t('admin.buttons.save_changes')
19 | expect(page).to have_content 'foobar.com is not a valid email'
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/features/admin/organizations/update_legal_status_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update legal status' do
4 | before do
5 | create(:organization)
6 | login_super_admin
7 | visit '/admin/organizations/parent-agency'
8 | end
9 |
10 | it 'with legal status' do
11 | fill_in 'organization_legal_status', with: 'non-profit'
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(find_field('organization_legal_status').value).to eq 'non-profit'
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/features/admin/organizations/update_name_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update name' do
4 | before do
5 | create(:organization)
6 | login_super_admin
7 | visit '/admin/organizations/parent-agency'
8 | end
9 |
10 | it 'with empty organization name' do
11 | fill_in 'organization_name', with: ''
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(page).to have_content "Name can't be blank for Organization"
14 | end
15 |
16 | it 'with valid organization name' do
17 | fill_in 'organization_name', with: 'Juvenile Sexual Responsibility Program'
18 | click_button I18n.t('admin.buttons.save_changes')
19 | expect(find_field('organization_name').value).
20 | to eq 'Juvenile Sexual Responsibility Program'
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/features/admin/organizations/update_tax_id_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update tax id' do
4 | before do
5 | create(:organization)
6 | login_super_admin
7 | visit '/admin/organizations/parent-agency'
8 | end
9 |
10 | it 'with tax id' do
11 | fill_in 'organization_tax_id', with: '12-1234567'
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(find_field('organization_tax_id').value).to eq '12-1234567'
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/features/admin/organizations/update_tax_status_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update tax status' do
4 | before do
5 | create(:organization)
6 | login_super_admin
7 | visit '/admin/organizations/parent-agency'
8 | end
9 |
10 | it 'with tax status' do
11 | fill_in 'organization_tax_status', with: '501(c)(3)'
12 | click_button I18n.t('admin.buttons.save_changes')
13 | expect(find_field('organization_tax_status').value).to eq '501(c)(3)'
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/features/admin/programs/update_alternate_name_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update alternate name' do
4 | before do
5 | loc = create(:location)
6 | program = loc.organization.programs.create!(attributes_for(:program))
7 | login_super_admin
8 | visit admin_program_path(program)
9 | end
10 |
11 | it 'with valid alternate_name' do
12 | fill_in 'program_alternate_name', with: 'Youth Counseling'
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content t('admin.notices.program_updated')
15 | expect(find_field('program_alternate_name').value).to eq 'Youth Counseling'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/features/admin/programs/update_name_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update name' do
4 | before do
5 | loc = create(:location)
6 | program = loc.organization.programs.create!(attributes_for(:program))
7 | login_super_admin
8 | visit admin_program_path(program)
9 | end
10 |
11 | it 'with empty name' do
12 | fill_in 'program_name', with: ''
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content "Name can't be blank for Program"
15 | end
16 |
17 | it 'with valid name' do
18 | fill_in 'program_name', with: 'Youth Counseling'
19 | click_button I18n.t('admin.buttons.save_changes')
20 | expect(page).to have_content t('admin.notices.program_updated')
21 | expect(find_field('program_name').value).to eq 'Youth Counseling'
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/features/admin/services/delete_contact_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Delete contact' do
4 | before do
5 | create_service
6 | @service.contacts.create!(attributes_for(:contact))
7 | login_super_admin
8 | visit '/admin/locations/vrs-services'
9 | click_link 'Literacy Program'
10 | click_link 'Moncef Belyamani'
11 | end
12 |
13 | it 'when deleting contact' do
14 | find_link(I18n.t('admin.buttons.delete_contact')).click
15 | using_wait_time 5 do
16 | expect(page).to have_current_path admin_location_service_path(@location, @service),
17 | ignore_query: true
18 | expect(page).not_to have_link 'Moncef Belyamani'
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/features/admin/services/update_alternate_name_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update alternate_name' do
4 | before do
5 | create_service
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | click_link 'Literacy Program'
9 | end
10 |
11 | it 'with valid alternate_name' do
12 | fill_in 'service_alternate_name', with: 'Youth Counseling'
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content 'Service was successfully updated.'
15 | expect(find_field('service_alternate_name').value).to eq 'Youth Counseling'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/features/admin/services/update_audience_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update audience' do
4 | before do
5 | create_service
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with valid audience' do
11 | click_link 'Literacy Program'
12 | fill_in 'service_audience', with: 'Youth Counseling'
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content 'Service was successfully updated.'
15 | expect(find_field('service_audience').value).to eq 'Youth Counseling'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/features/admin/services/update_eligibility_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update eligibility' do
4 | before do
5 | create_service
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with valid eligibility' do
11 | click_link 'Literacy Program'
12 | fill_in 'service_eligibility', with: 'Youth Counseling'
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content 'Service was successfully updated.'
15 | expect(find_field('service_eligibility').value).to eq 'Youth Counseling'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/features/admin/services/update_email_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update email' do
4 | before do
5 | create_service
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | click_link 'Literacy Program'
9 | end
10 |
11 | it 'with invalid email' do
12 | fill_in 'service_email', with: 'foobar.com'
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content 'is not a valid email'
15 | end
16 |
17 | it 'with valid email' do
18 | fill_in 'service_email', with: 'ruby@good.com'
19 | click_button I18n.t('admin.buttons.save_changes')
20 | expect(page).to have_content 'Service was successfully updated.'
21 | expect(find_field('service_email').value).to eq 'ruby@good.com'
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/features/admin/services/update_fees_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update fees' do
4 | before do
5 | create_service
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with valid fees' do
11 | click_link 'Literacy Program'
12 | fill_in 'service_fees', with: 'Youth Counseling'
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content 'Service was successfully updated.'
15 | expect(find_field('service_fees').value).to eq 'Youth Counseling'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/features/admin/services/update_how_to_apply_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update application_process' do
4 | before do
5 | create_service
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with valid application_process' do
11 | click_link 'Literacy Program'
12 | fill_in 'service_application_process', with: 'Youth Counseling'
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content 'Service was successfully updated.'
15 | expect(find_field('service_application_process').value).to eq 'Youth Counseling'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/features/admin/services/update_interpretation_services_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update interpretation_services' do
4 | before do
5 | create_service
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with valid interpretation_services' do
11 | click_link 'Literacy Program'
12 | fill_in 'service_interpretation_services', with: 'CTS LanguageLink'
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content 'Service was successfully updated.'
15 | expect(find_field('service_interpretation_services').value).to eq 'CTS LanguageLink'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/features/admin/services/update_name_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update name' do
4 | before do
5 | create_service
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | click_link 'Literacy Program'
9 | end
10 |
11 | it 'with empty name' do
12 | fill_in 'service_name', with: ''
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content "Name can't be blank for Service"
15 | end
16 |
17 | it 'with valid name' do
18 | fill_in 'service_name', with: 'Youth Counseling'
19 | click_button I18n.t('admin.buttons.save_changes')
20 | expect(page).to have_content 'Service was successfully updated.'
21 | expect(find_field('service_name').value).to eq 'Youth Counseling'
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/features/admin/services/update_status_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update status' do
4 | before do
5 | create_service
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with valid status' do
11 | click_link 'Literacy Program'
12 | select 'Defunct', from: 'service_status'
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content 'Service was successfully updated.'
15 | expect(find_field('service_status').value).to eq 'defunct'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/features/admin/services/update_wait_time_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Update wait_time' do
4 | before do
5 | create_service
6 | login_super_admin
7 | visit '/admin/locations/vrs-services'
8 | end
9 |
10 | it 'with valid wait_time' do
11 | click_link 'Literacy Program'
12 | fill_in 'service_wait_time', with: 'Youth Counseling'
13 | click_button I18n.t('admin.buttons.save_changes')
14 | expect(page).to have_content 'Service was successfully updated.'
15 | expect(find_field('service_wait_time').value).to eq 'Youth Counseling'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/features/admin/sign_out_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Signing out' do
4 | before do
5 | login_admin
6 | visit edit_admin_registration_path
7 | end
8 |
9 | it 'redirects to the admin sign in page' do
10 | click_link I18n.t('navigation.sign_out')
11 | expect(page).to have_current_path(new_admin_session_path, ignore_query: true)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/features/delete_api_application_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Delete an API Application' do
4 | before do
5 | user = create(:user_with_app)
6 | login_as(user, scope: :user)
7 | name = user.api_applications.first.name
8 | main_url = user.api_applications.first.main_url
9 | visit_app(name, main_url)
10 | end
11 |
12 | it 'deletes the application when the delete button is clicked' do
13 | click_link I18n.t('buttons.delete_application')
14 |
15 | expect(page).to have_content 'Application was successfully deleted.'
16 | expect(page).to have_current_path api_applications_path, ignore_query: true
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/features/sign_out_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'Signing out' do
4 | before do
5 | login_user
6 | visit edit_user_registration_path
7 | end
8 |
9 | it 'redirects to the user home page' do
10 | click_link I18n.t('navigation.sign_out')
11 | expect(page).to have_current_path(root_path, ignore_query: true)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/lib/importer_errors_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe ImporterErrors do
4 | subject(:importer_errors) { ImporterErrors.new(invalid, 6) }
5 |
6 | let(:errors) { double(:errors, full_messages: ['not enough cowbell']) }
7 | let(:invalid) { double(:record, valid?: false, errors: errors) }
8 |
9 | its(:line_number) { is_expected.to eq 6 }
10 | its(:message) { is_expected.to eq 'Line 6: not enough cowbell' }
11 |
12 | describe '.messages_for' do
13 | let(:valid) { double(:record, valid?: true) }
14 |
15 | it 'collects the messages for all records by order of appearance' do
16 | messages = ImporterErrors.messages_for([valid, invalid])
17 | expect(messages).to eq ['Line 3: not enough cowbell']
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/spec/models/category_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe Category do
4 | subject { build(:category) }
5 |
6 | it { is_expected.to be_valid }
7 |
8 | it { is_expected.to have_and_belong_to_many(:services) }
9 |
10 | it do
11 | expect(subject).to validate_presence_of(:name).
12 | with_message("can't be blank for Category")
13 | end
14 |
15 | it do
16 | expect(subject).to validate_presence_of(:taxonomy_id).
17 | with_message("can't be blank for Category")
18 | end
19 |
20 | it do
21 | expect(subject).to validate_uniqueness_of(:taxonomy_id).
22 | case_insensitive.with_message('id has already been taken')
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/models/program_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe Program do
4 | subject { build(:program) }
5 |
6 | it { is_expected.to be_valid }
7 |
8 | it { is_expected.to validate_presence_of(:name).with_message("can't be blank for Program") }
9 |
10 | it { is_expected.to belong_to(:organization).required }
11 | it { is_expected.to have_many(:services).dependent(:destroy) }
12 |
13 | describe 'auto_strip_attributes' do
14 | it 'strips extra whitespace before validation' do
15 | program = build(:program)
16 | program.valid?
17 | expect(program.name).to eq('Collection of Services')
18 | expect(program.alternate_name).to eq('Also Known As')
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/requests/api_applications_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | describe 'ApiApplications' do
4 | describe 'GET /api_applications' do
5 | context 'when not signed in' do
6 | it "redirects to 'users/sign_in'" do
7 | get api_applications_path
8 | expect(response.status).to be(302)
9 | expect(response).to redirect_to(new_user_session_path)
10 | end
11 | end
12 |
13 | context 'when signed in' do
14 | it 'returns a 200' do
15 | user = create(:user)
16 | post(
17 | user_session_path,
18 | params: { user: { email: user.email, password: user.password } }
19 | )
20 | get api_applications_url
21 | follow_redirect!
22 | expect(response.status).to be(200)
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/spec/support/admin_controller_helper.rb:
--------------------------------------------------------------------------------
1 | module AdminControllerHelper
2 | def log_in_as_admin(admin)
3 | @request.env['devise.mapping'] = Devise.mappings[:admin]
4 | sign_in FactoryBot.create(admin)
5 | end
6 | end
7 |
8 | RSpec.configure do |config|
9 | config.include Devise::Test::ControllerHelpers, type: :controller
10 | config.include AdminControllerHelper, type: :controller
11 | end
12 |
--------------------------------------------------------------------------------
/spec/support/api/model_helpers.rb:
--------------------------------------------------------------------------------
1 | def create_service
2 | @location = create(:location)
3 | @service = @location.services.create!(attributes_for(:service))
4 | end
5 |
--------------------------------------------------------------------------------
/spec/support/api_constraints_spec_helper.rb:
--------------------------------------------------------------------------------
1 | def header_for_version(version)
2 | "application/vnd.ohanapi+json;version=#{version}"
3 | end
4 |
--------------------------------------------------------------------------------
/spec/support/bullet.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | if Bullet.enable?
3 | config.before do
4 | Bullet.start_request
5 | end
6 |
7 | config.after do
8 | Bullet.perform_out_of_channel_notifications if Bullet.notification?
9 | Bullet.end_request
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/support/capybara.rb:
--------------------------------------------------------------------------------
1 | require 'webdrivers/chromedriver'
2 | require 'selenium/webdriver'
3 |
4 | Capybara.configure do |config|
5 | config.default_max_wait_time = 5
6 | config.always_include_port = true
7 | end
8 |
9 | Capybara.register_driver :headless_chrome do |app|
10 | browser_options = Selenium::WebDriver::Chrome::Options.new
11 | browser_options.add_argument('--headless')
12 | browser_options.add_argument('--disable-gpu')
13 |
14 | Capybara::Selenium::Driver.new app,
15 | browser: :chrome,
16 | capabilities: [browser_options]
17 | end
18 |
19 | Capybara.javascript_driver = :headless_chrome
20 | Capybara.default_driver = :rack_test
21 |
22 | Webdrivers.cache_time = 86_400
23 |
--------------------------------------------------------------------------------
/spec/support/database_cleaner.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | config.before(:suite) do
3 | DatabaseCleaner.clean_with(:truncation)
4 | end
5 |
6 | config.before do
7 | DatabaseCleaner.strategy = :transaction
8 | end
9 |
10 | config.before(:each, js: true) do
11 | DatabaseCleaner.strategy = :truncation
12 | end
13 |
14 | config.before do
15 | DatabaseCleaner.start
16 | end
17 |
18 | config.after do
19 | DatabaseCleaner.clean
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/support/fixtures/contact_with_no_parent.csv:
--------------------------------------------------------------------------------
1 | id,location_id,organization_id,service_id,name,title,email,department
2 | 1,,,,John Smith,Food Pantry Manager,john@example.org,Food Pantry
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/existing_category.csv:
--------------------------------------------------------------------------------
1 | taxonomy_id,name,parent_id,parent_name
2 | 101,Emergency,,
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/holiday_schedule_with_no_parent.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,start_date,end_date,closed,opens_at,closes_at
2 | 1,,,11/27/2014,11/27/2014,FALSE,10:00,3:00 PM
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/hs_with_2_digit_year.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,start_date,end_date,closed,opens_at,closes_at
2 | 1,1,,1/2/14,1/12/14,TRUE,,
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/hs_with_invalid_date.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,start_date,end_date,closed,opens_at,closes_at
2 | 1,1,,1/11/2014,13/27/2014,FALSE,10:00,3:00 PM
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/hs_with_spelled_out_date.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,start_date,end_date,closed,opens_at,closes_at
2 | 1,1,,"December 24, 2014","December 24, 2014",TRUE,,
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_address.csv:
--------------------------------------------------------------------------------
1 | id,location_id,address_1,address_2,city,state_province,postal_code,country
2 | 1,1,123 Main Street,Suite 101,,VA,22031,US
3 | 2,2,123 Main Street,Suite 101,Fairfax,VA,22031,US
4 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_category.csv:
--------------------------------------------------------------------------------
1 | taxonomy_id,name,parent_id,parent_name
2 | 101-01,,,
3 | 102,Food,,
4 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_contact.csv:
--------------------------------------------------------------------------------
1 | id,location_id,organization_id,service_id,name,title,email,department
2 | 1,1,,,,Food Pantry Manager,john@example.org,Food Pantry
3 | 2,,1,,Jane Doe,Food Pantry Manager,jane@example.org,Food Pantry
4 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_holiday_schedule.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,start_date,end_date,closed,opens_at,closes_at
2 | 1,1,,12/24/2014,12/24/2014,,9:30,
3 | 3,1,,11/27/2014,11/27/2014,FALSE,10:00,3:00 PM
4 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_location.csv:
--------------------------------------------------------------------------------
1 | id,organization_id,accessibility,admin_emails,alternate_name,description,email,languages,latitude,longitude,name,transportation,virtual,website
2 | 1,1,"disabled_parking, elevator, wheelchair","admin@example.org, john@example.org",,The Palo Alto location of the Harvest Food Bank distributes canned goods only.,info@location.org,"English, Spanish, Tagalog",37.7726402,-122.4099154,,,,http://www.example.org
3 | 3,1,,,,The Atherton location of the Harvest Food Bank distributes canned goods only.,info@location.org,,,,Harvest Food Bank of Atherton,SAMTrans stops 1 block away,false,http://www.example.org
4 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_location_org.csv:
--------------------------------------------------------------------------------
1 | id,organization_id,accessibility,admin_emails,alternate_name,description,email,languages,latitude,longitude,name,transportation,virtual,website
2 | 1,100,,,,The Palo Alto location of the Harvest Food Bank distributes canned goods only.,,,37.7726402,-122.4099154,Harvest Food Bank,,,
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_mail_address.csv:
--------------------------------------------------------------------------------
1 | id,location_id,attention,address_1,address_2,city,state_province,postal_code,country
2 | 1,1,John Smith,,Suite 101,Fairfax,VA,22031,US
3 | 2,1,John Smith,123 Main Street,Suite 101,Fairfax,VA,22031,US
4 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_mail_address_location.csv:
--------------------------------------------------------------------------------
1 | id,location_id,attention,address_1,address_2,city,state_province,postal_code,country
2 | 1,100,John Smith,123 Main Street,Suite 101,Fairfax,VA,22031,US
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_org.csv:
--------------------------------------------------------------------------------
1 | id,accreditations,alternate_name,date_incorporated,description,email,funding_sources,legal_status,licenses,name,tax_id,tax_status,website
2 | 1,"BBB, State Board of Education",HFB,1/1/1970,"Harvest Food Bank provides fresh produce, dairy, and canned goods to food pantries throughout the city.",info@example.org,"Donations, Grants",Nonprofit,State Health Inspection License,,12-456789,501(c)3,http://www.example.org
3 | 2,,,1/2/2014,Example org description,info@fakeorg.org,Donations,Nonprofit,,Example Agency,,,http://www.example.org
4 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_parent_category.csv:
--------------------------------------------------------------------------------
1 | taxonomy_id,name,parent_id,parent_name
2 | 101-01,Disaster,0,
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_phone.csv:
--------------------------------------------------------------------------------
1 | id,contact_id,location_id,organization_id,service_id,number,extension,department,number_type,vanity_number,country_prefix
2 | 1,,1,,,703-555-1212,123,Food Pantry,,703-555-FOOD,1
3 | 2,,1,,,202-555-1212,,,fax,,
4 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_program.csv:
--------------------------------------------------------------------------------
1 | id,organization_id,name,alternate_name
2 | 1,1,,
3 | 2,1,Foo,
4 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_program_headers.csv:
--------------------------------------------------------------------------------
1 | id,organization_id,name
2 | 1,1,Defeat Hunger,
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_regular_schedule.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,weekday,opens_at,closes_at
2 | 1,1,,Monday,9:30,
3 | 2,1,,Tuesday,9:30,5:00 PM
4 | 3,1,,Wednesday,9:30,5:00 PM
5 | 4,1,,Thursday,9:30,5:00 PM
6 | 5,1,,Friday,9:00,4:00 PM
7 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_service.csv:
--------------------------------------------------------------------------------
1 | id,location_id,program_id,accepted_payments,alternate_name,description,eligibility,email,fees,funding_sources,application_process,keywords,languages,name,required_documents,service_areas,status,wait_time,website
2 | 1,1,,"Cash, Check, Credit Card",,Provides free hot meals to the homeless.,Homeless and low-income residents.,info@service.org,,"DC Government, Donations",Call or apply in person,"hot meels, hungry","English, Spanish, Tagalog",,,"Atherton, Belmont",active,No wait.,http://example.org/service
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/invalid_service_location.csv:
--------------------------------------------------------------------------------
1 | id,location_id,program_id,accepted_payments,alternate_name,description,eligibility,email,fees,funding_sources,application_process,keywords,languages,name,required_documents,service_areas,status,wait_time,website
2 | 1,100,,"Cash, Check, Credit Card",,Provides free hot meals to the homeless.,Homeless and low-income residents.,info@service.org,,"DC Government, Donations",Call or apply in person,"hot meels, hungry","English, Spanish, Tagalog",Example Service,,"Atherton, Belmont",active,No wait.,http://example.org/service
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/missing_address.csv:
--------------------------------------------------------------------------------
1 | id,location_id,address_1,address_2,city,state_province,postal_code,country
2 | 1,100,123 Main Street,Suite 101,,VA,22031,US
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/org_with_2_digit_year.csv:
--------------------------------------------------------------------------------
1 | id,accreditations,alternate_name,date_incorporated,description,email,funding_sources,legal_status,licenses,name,tax_id,tax_status,website
2 | 1,"BBB, State Board of Education",HFB,1/24/70,"Harvest Food Bank provides fresh produce, dairy, and canned goods to food pantries throughout the city.",info@example.org,"Donations, Grants",Nonprofit,State Health Inspection License,Parent Agency,12-456789,501(c)3,http://www.example.org
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/org_with_invalid_date.csv:
--------------------------------------------------------------------------------
1 | id,accreditations,alternate_name,date_incorporated,description,email,funding_sources,legal_status,licenses,name,tax_id,tax_status,website
2 | 1,"BBB, State Board of Education",HFB,24/2/70,"Harvest Food Bank provides fresh produce, dairy, and canned goods to food pantries throughout the city.",info@example.org,"Donations, Grants",Nonprofit,State Health Inspection License,Parent Agency,12-456789,501(c)3,http://www.example.org
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/org_with_spelled_out_date.csv:
--------------------------------------------------------------------------------
1 | id,accreditations,alternate_name,date_incorporated,description,email,funding_sources,legal_status,licenses,name,tax_id,tax_status,website
2 | 1,"BBB, State Board of Education",HFB,"January 20, 1970","Harvest Food Bank provides fresh produce, dairy, and canned goods to food pantries throughout the city.",info@example.org,"Donations, Grants",Nonprofit,State Health Inspection License,Parent Agency,12-456789,501(c)3,http://www.example.org
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/phone_with_no_parent.csv:
--------------------------------------------------------------------------------
1 | id,contact_id,location_id,organization_id,service_id,number,extension,department,number_type,vanity_number,country_prefix
2 | 1,,,,,703-555-1212,123,Food Pantry,voice,703-555-FOOD,1
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/program_with_no_parent.csv:
--------------------------------------------------------------------------------
1 | id,organization_id,name,alternate_name
2 | 1,,Defeat Hunger,
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/programs.csv:
--------------------------------------------------------------------------------
1 | id,organization_id,name,alternate_name
2 | 1,1,Defeat Hunger,
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/regular_schedule_with_no_parent.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,weekday,opens_at,closes_at
2 | 1,,,Monday,9:30,5:00 PM
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/services.csv:
--------------------------------------------------------------------------------
1 | id,location_id,program_id,accepted_payments,alternate_name,description,eligibility,email,fees,funding_sources,application_process,keywords,languages,name,required_documents,service_areas,status,wait_time,website
2 |
--------------------------------------------------------------------------------
/spec/support/fixtures/taxonomy.csv:
--------------------------------------------------------------------------------
1 | taxonomy_id,name,parent_id,parent_name
2 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_address.csv:
--------------------------------------------------------------------------------
1 | id,location_id,address_1,address_2,city,state_province,postal_code,country
2 | 1,2,123 Main Street,Suite 101,Fairfax,VA,22031,US
3 | 2,3,123 Main Street,Suite 101,Fairfax,VA,22031,US
4 | 4,1,123 Main Street,Suite 101,Fairfax,VA,22031,US
5 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_category.csv:
--------------------------------------------------------------------------------
1 | taxonomy_id,name,parent_id,parent_name
2 | 101,Emergency,,
3 | 101-01,Disaster Response,101,Emergency
4 | 101-02,Emergency Cash,101,Emergency
5 | 101-02-01,Help Pay for Food,101-02,Emergency Cash
6 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_contact_phone.csv:
--------------------------------------------------------------------------------
1 | id,contact_id,location_id,organization_id,service_id,number,extension,department,number_type,vanity_number,country_prefix
2 | 1,1,,,,703-555-1212,123,Food Pantry,voice,703-555-FOOD,1
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_location.csv:
--------------------------------------------------------------------------------
1 | id,organization_id,accessibility,admin_emails,alternate_name,description,email,languages,latitude,longitude,name,transportation,virtual,website
2 | 1,1,"cd, ramp","test@test.com, foo@bar.com",,The Palo Alto location of the Harvest Food Bank distributes canned goods only.,,"French, spanish",37.7726402,-122.4099154,Harvest Food Bank,,,
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_location_contact.csv:
--------------------------------------------------------------------------------
1 | id,location_id,organization_id,service_id,name,title,email,department
2 | 2,1,,,John Smith,Food Pantry Manager,john@example.org,Food Pantry
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_location_holiday_schedule.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,start_date,end_date,closed,opens_at,closes_at
2 | 2,1,,1/11/2014,11/27/2014,FALSE,10:00,3:00 PM
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_location_phone.csv:
--------------------------------------------------------------------------------
1 | id,contact_id,location_id,organization_id,service_id,number,extension,department,number_type,vanity_number,country_prefix
2 | 2,,1,,,703-555-1212,123,Food Pantry,voice,703-555-FOOD,1
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_location_regular_schedule.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,weekday,opens_at,closes_at
2 | 2,1,,Monday,9:30,5:00 PM
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_mail_address.csv:
--------------------------------------------------------------------------------
1 | id,location_id,attention,address_1,address_2,city,state_province,postal_code,country
2 | 2,1,John Smith,123 Main Street,Suite 101,Fairfax,VA,22031,US
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_org.csv:
--------------------------------------------------------------------------------
1 | id,accreditations,alternate_name,date_incorporated,description,email,funding_sources,legal_status,licenses,name,tax_id,tax_status,website
2 | 2,"BBB, State Board of Education",HFB,1/2/1970,"Harvest Food Bank provides fresh produce, dairy, and canned goods to food pantries throughout the city.",info@example.org,"Donations, Grants",Nonprofit,State Health Inspection License,Parent Agency,12-456789,501(c)3,http://www.example.org
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_org_contact.csv:
--------------------------------------------------------------------------------
1 | id,location_id,organization_id,service_id,name,title,email,department
2 | 1,,1,,John Smith,Food Pantry Manager,john@example.org,Food Pantry
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_org_phone.csv:
--------------------------------------------------------------------------------
1 | id,contact_id,location_id,organization_id,service_id,number,extension,department,number_type,vanity_number,country_prefix
2 | 1,,,1,,703-555-1212,123,Food Pantry,voice,703-555-FOOD,1
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_program.csv:
--------------------------------------------------------------------------------
1 | id,organization_id,name,alternate_name
2 | 2,1,Defeat Hunger,
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_service.csv:
--------------------------------------------------------------------------------
1 | id,location_id,program_id,accepted_payments,alternate_name,description,eligibility,email,fees,funding_sources,application_process,interpretation_services,keywords,languages,name,required_documents,service_areas,status,wait_time,website,taxonomy_ids
2 | 1,1,1,"Cash, Check, Credit Card",,Provides free hot meals to the homeless.,Homeless and low-income residents.,info@service.org,,"DC Government, Donations",Call or apply in person,We offer 3-way interpretation services over the phone via Propio Language Services (http://propio-ls.com).,"hot meels, hungry","English, Spanish, Tagalog",Harvest Food Bank of Palo Alto,"Passport,Driver's License","Atherton, Belmont",active,No wait.,http://example.org/service,"101, 102"
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_service_contact.csv:
--------------------------------------------------------------------------------
1 | id,location_id,organization_id,service_id,name,title,email,department
2 | 1,,,1,John Smith,Food Pantry Manager,john@example.org,Food Pantry
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_service_holiday_schedule.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,start_date,end_date,closed,opens_at,closes_at
2 | 1,,1,11/27/2014,11/27/2014,FALSE,10:00,3:00 PM
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_service_phone.csv:
--------------------------------------------------------------------------------
1 | id,contact_id,location_id,organization_id,service_id,number,extension,department,number_type,vanity_number,country_prefix
2 | 1,,,,1,703-555-1212,123,Food Pantry,voice,703-555-FOOD,1
3 |
--------------------------------------------------------------------------------
/spec/support/fixtures/valid_service_regular_schedule.csv:
--------------------------------------------------------------------------------
1 | id,location_id,service_id,weekday,opens_at,closes_at
2 | 1,,1,Monday,9:30,5:00 PM
3 |
--------------------------------------------------------------------------------
/spec/support/mailer_macros.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | config.before(:each, email: true) do
3 | ActionMailer::Base.deliveries = []
4 | end
5 | end
6 |
7 | module MailerMacros
8 | def first_email
9 | ActionMailer::Base.deliveries.first
10 | end
11 |
12 | def last_email
13 | ActionMailer::Base.deliveries.last
14 | end
15 |
16 | def reset_email
17 | ActionMailer::Base.deliveries = []
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/support/request_helpers.rb:
--------------------------------------------------------------------------------
1 | module Requests
2 | # This module allows you to use 'json' within specs as a shortcut for
3 | # 'JSON.parse(response.body)'
4 | module RequestHelpers
5 | def json
6 | @json ||= JSON.parse(response.body)
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/support/shared_contexts/rake.rb:
--------------------------------------------------------------------------------
1 | require 'rake'
2 |
3 | shared_context 'rake' do
4 | subject { rake[task_name] }
5 |
6 | let(:rake) { Rake::Application.new }
7 | let(:task_name) { self.class.top_level_description }
8 | let(:task_path) { "lib/tasks/#{task_name.split(':').first}" }
9 |
10 | def loaded_files_excluding_current_rake_file
11 | $LOADED_FEATURES.reject { |file| file == Rails.root.join("#{task_path}.rake").to_s }
12 | end
13 |
14 | before do
15 | Rake.application = rake
16 | Rake.application.rake_require(
17 | task_path, [Rails.root.to_s], loaded_files_excluding_current_rake_file
18 | )
19 |
20 | Rake::Task.define_task(:environment)
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/support/shared_examples_for_schedules.rb:
--------------------------------------------------------------------------------
1 | shared_examples 'validating opens_at and closes_at values' do |model, attribute|
2 | it 'converts 18:00' do
3 | rs = build(model, attribute => '18:00')
4 | rs.valid?
5 | expect(rs.send(attribute)).to eq(Time.utc(2000, 1, 1, 18, 00, 0))
6 | end
7 |
8 | it 'converts 5am' do
9 | rs = build(model, attribute => '5am')
10 | rs.valid?
11 | expect(rs.send(attribute)).to eq(Time.utc(2000, 1, 1, 5, 00, 0))
12 | end
13 |
14 | it 'converts 7pm' do
15 | rs = build(model, attribute => '7pm')
16 | rs.valid?
17 | expect(rs.send(attribute)).to eq(Time.utc(2000, 1, 1, 19, 00, 0))
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/support/shoulda_matchers.rb:
--------------------------------------------------------------------------------
1 | Shoulda::Matchers.configure do |config|
2 | config.integrate do |with|
3 | # Choose a test framework:
4 | with.test_framework :rspec
5 |
6 | # Choose one or more libraries:
7 | with.library :active_record
8 | with.library :active_model
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/support/warden.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | config.include Warden::Test::Helpers, type: :feature
3 |
4 | config.before do
5 | Warden.test_mode!
6 | end
7 |
8 | config.after do
9 | Warden.test_reset!
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/support/webmock.rb:
--------------------------------------------------------------------------------
1 | require 'webmock/rspec'
2 |
3 | WebMock.disable_net_connect!(
4 | allow: [
5 | /localhost/,
6 | /127\.0\.0\.1/,
7 | /codeclimate.com/, # For uploading coverage reports
8 | /chromedriver\.storage\.googleapis\.com/ # For fetching a chromedriver binary
9 | ]
10 | )
11 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeforamerica/ohana-api/e3ab28075a5fc2f2e15ce06403db5c1e785a7778/vendor/assets/javascripts/.gitkeep
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeforamerica/ohana-api/e3ab28075a5fc2f2e15ce06403db5c1e785a7778/vendor/assets/stylesheets/.gitkeep
--------------------------------------------------------------------------------
/vendor/plugins/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeforamerica/ohana-api/e3ab28075a5fc2f2e15ce06403db5c1e785a7778/vendor/plugins/.gitkeep
--------------------------------------------------------------------------------