├── .codeclimate.yml ├── .gitignore ├── .gitlab-ci.yml ├── .rubocop.yml ├── .rubocop_todo.yml ├── .ruby-version ├── .travis.yml ├── CONTRIBUTING.md ├── CUSTOMIZING.md ├── Gemfile ├── LOCALES.md ├── MIT-LICENSE ├── README.md ├── Rakefile ├── app ├── controllers │ └── hello │ │ ├── application_controller.rb │ │ ├── authentication │ │ ├── sessions_controller.rb │ │ ├── sign_in_controller.rb │ │ └── sudo_mode_controller.rb │ │ ├── concerns │ │ ├── authentication │ │ │ └── sign_in.rb │ │ ├── management │ │ │ ├── forgot_password.rb │ │ │ └── reset_password.rb │ │ └── registration │ │ │ └── sign_up.rb │ │ ├── internationalization │ │ └── locale_controller.rb │ │ ├── management │ │ ├── accesses_controller.rb │ │ ├── confirm_emails_controller.rb │ │ ├── emails_controller.rb │ │ ├── forgot_password_controller.rb │ │ ├── passwords_controller.rb │ │ ├── profiles_controller.rb │ │ └── reset_password_controller.rb │ │ └── registration │ │ └── sign_up_controller.rb ├── helpers │ └── hello │ │ └── application_helper.rb ├── mailers │ └── hello │ │ └── mailer.rb ├── models │ ├── access.rb │ ├── credential.rb │ ├── email_credential.rb │ ├── password_credential.rb │ └── user.rb └── views │ ├── hello │ ├── authentication │ │ ├── _sign_in.html.erb │ │ ├── new_session.html.erb │ │ ├── sessions.html.erb │ │ ├── sign_in.html.erb │ │ └── sudo_mode.html.erb │ ├── internationalization │ │ └── locales.html.erb │ ├── mailer │ │ ├── confirm_email.html.erb │ │ ├── forgot_password.html.erb │ │ └── welcome.html.erb │ ├── management │ │ ├── accesses.html.erb │ │ ├── cancel.html.erb │ │ ├── email_credentials │ │ │ ├── expired_confirmation_token.html.erb │ │ │ └── index.html.erb │ │ ├── password_credentials │ │ │ ├── _forgot_form.html.erb │ │ │ ├── _reset_form.html.erb │ │ │ ├── forgot.html.erb │ │ │ ├── forgot_success.html.erb │ │ │ ├── reset.html.erb │ │ │ └── show.html.erb │ │ └── user.html.erb │ ├── registration │ │ ├── _sign_up.html.erb │ │ ├── sign_up.html.erb │ │ └── sign_up_widget.html.erb │ └── shared │ │ ├── _errors.html.erb │ │ ├── _flash.html.erb │ │ ├── _nav_pills.html.erb │ │ ├── _session_expiration.html.erb │ │ └── _settings.html.erb │ └── layouts │ └── hello │ └── .keep ├── bin └── rails ├── config ├── locales │ ├── hello.en.yml │ ├── hello.es.yml │ ├── hello.fr.yml │ ├── hello.pl.yml │ ├── hello.pt-BR.yml │ ├── hello.zh-CN.yml │ └── hello.zh-TW.yml └── routes.rb ├── db └── migrate │ ├── 1_create_credentials.rb │ ├── 2_create_accesses.rb │ └── 3_create_users.rb ├── dummy ├── .rspec ├── README.rdoc ├── Rakefile ├── app │ ├── assets │ │ ├── images │ │ │ └── .keep │ │ ├── javascripts │ │ │ └── application.js │ │ └── stylesheets │ │ │ └── application.css │ ├── controllers │ │ ├── application_controller.rb │ │ ├── concerns │ │ │ └── .keep │ │ ├── middleware_controller.rb │ │ ├── my_areas_controller.rb │ │ ├── onboarding_controller.rb │ │ ├── root_controller.rb │ │ └── users_controller.rb │ ├── helpers │ │ ├── application_helper.rb │ │ └── hello_helper.rb │ ├── mailers │ │ └── .keep │ ├── models │ │ ├── .keep │ │ ├── address.rb │ │ ├── application_record.rb │ │ ├── concerns │ │ │ ├── .keep │ │ │ └── user │ │ │ │ └── authorization.rb │ │ ├── credential.rb │ │ ├── some_credential_datum.rb │ │ └── user.rb │ └── views │ │ ├── layouts │ │ └── application.html.erb │ │ ├── my_authenticated_area │ │ └── index.html.erb │ │ ├── my_non_webmaster_area │ │ └── index.html.erb │ │ ├── onboarding │ │ └── index.html.erb │ │ ├── root │ │ └── index.html.erb │ │ └── users │ │ ├── index.html.erb │ │ ├── list.html.erb │ │ ├── new.html.erb │ │ └── show.html.erb ├── bin │ ├── bundle │ ├── rails │ └── rake ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── backtrace_silencers.rb │ │ ├── filter_parameter_logging.rb │ │ ├── hello.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ ├── missings_translations.rb │ │ ├── secret_token.rb │ │ ├── session_store.rb │ │ └── wrap_parameters.rb │ └── routes.rb ├── db │ ├── migrate │ │ ├── 20140502043302_create_credentials.hello.rb │ │ ├── 20140502043303_create_accesses.hello.rb │ │ ├── 20140502043304_create_users.hello.rb │ │ └── 20140920192959_create_tables_to_test_deactivation_with_associations.rb │ └── schema.rb ├── lib │ └── assets │ │ └── .keep ├── log │ └── .keep └── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ └── favicon.ico ├── gemfiles ├── README.md ├── rails-5-0-stable.gemfile ├── rails-5-1-stable.gemfile ├── rails-5-2-stable.gemfile └── rails-master.gemfile ├── hello-rails.gemspec ├── lib ├── data │ └── locales.yml ├── generators │ └── hello │ │ ├── concerns │ │ ├── USAGE │ │ └── concerns_generator.rb │ │ ├── install │ │ ├── USAGE │ │ ├── install_generator.rb │ │ └── templates │ │ │ ├── application.html.erb │ │ │ ├── hello_helper.rb │ │ │ ├── initializer.rb │ │ │ ├── models │ │ │ ├── concerns │ │ │ │ └── user │ │ │ │ │ └── authorization.rb │ │ │ └── user.rb │ │ │ ├── onboarding │ │ │ ├── index.html.erb │ │ │ └── onboarding_controller.rb │ │ │ └── root │ │ │ ├── index.html.erb │ │ │ └── root_controller.rb │ │ ├── locales │ │ ├── USAGE │ │ └── locales_generator.rb │ │ ├── users │ │ ├── USAGE │ │ ├── templates │ │ │ └── app │ │ │ │ ├── controllers │ │ │ │ └── users_controller.rb │ │ │ │ └── views │ │ │ │ └── users │ │ │ │ ├── index.html.erb │ │ │ │ ├── list.html.erb │ │ │ │ ├── new.html.erb │ │ │ │ └── show.html.erb │ │ └── users_generator.rb │ │ └── views │ │ ├── USAGE │ │ └── views_generator.rb ├── hello-rails.rb ├── hello.rb ├── hello │ ├── CHANGES.md │ ├── business.rb │ ├── business │ │ ├── authentication │ │ │ ├── sign_in.rb │ │ │ ├── sign_out.rb │ │ │ ├── sudo_mode_authentication.rb │ │ │ └── sudo_mode_expiration.rb │ │ ├── base.rb │ │ ├── internationalization │ │ │ └── update_locale.rb │ │ ├── management │ │ │ ├── add_email.rb │ │ │ ├── cancel_account.rb │ │ │ ├── confirm_email.rb │ │ │ ├── forgot_password.rb │ │ │ ├── remove_email.rb │ │ │ ├── reset_password.rb │ │ │ ├── send_confirmation_email.rb │ │ │ ├── unlink_access.rb │ │ │ └── update_profile.rb │ │ └── registration │ │ │ └── sign_up.rb │ ├── configuration.rb │ ├── encryptors.rb │ ├── encryptors │ │ ├── complex.rb │ │ └── simple.rb │ ├── engine.rb │ ├── errors.rb │ ├── locales.rb │ ├── middleware.rb │ ├── rails_active_record.rb │ ├── rails_active_record │ │ ├── access.rb │ │ ├── credential.rb │ │ ├── email_credential.rb │ │ ├── password_credential.rb │ │ └── user.rb │ ├── rails_controller.rb │ ├── rails_controller │ │ └── restrict_by_role.rb │ ├── rails_helper.rb │ ├── request_manager.rb │ ├── request_manager │ │ ├── abstract.rb │ │ ├── factory.rb │ │ ├── stateful.rb │ │ ├── stateful │ │ │ ├── finder.rb │ │ │ └── session_wrapper.rb │ │ └── stateless.rb │ ├── time_zones.rb │ ├── utils.rb │ ├── utils │ │ └── device_name.rb │ └── version.rb └── tasks │ └── hello_tasks.rake └── spec ├── bdd └── hello │ ├── authentication │ ├── authorization │ │ ├── authorization_role_restriction_spec.rb │ │ ├── authorization_router_constraints_spec.rb │ │ ├── authorization_sensitive_restriction_spec.rb │ │ └── bdd.yml │ ├── bdd.yml │ ├── classic_sign_in_spec.rb │ ├── manage_sessions_spec.rb │ └── sign_out_spec.rb │ ├── bdd.yml │ ├── internalionalization │ ├── anyone_can_change_their_locale │ │ ├── bdd.yml │ │ ├── change_locale_on_the_locale_page_spec.rb │ │ ├── change_locale_on_the_profile_page_spec.rb │ │ ├── change_locale_on_the_sign_in_form_spec.rb │ │ └── change_locale_on_the_sign_up_form_spec.rb │ ├── anyone_can_change_their_timezone │ │ ├── bdd.yml │ │ ├── change_timezone_on_the_profile_page_spec.rb │ │ ├── change_timezone_on_the_sign_in_form_spec.rb │ │ └── change_timezone_on_the_sign_up_form_spec.rb │ └── bdd.yml │ ├── management │ ├── bdd.yml │ ├── cancel_account_spec.rb │ ├── manage_email_credentials │ │ ├── bdd.yml │ │ ├── manage_email_credentials_emails_api_spec.rb │ │ └── manage_email_credentials_emails_page_spec.rb │ ├── manage_password_credentials │ │ ├── bdd.yml │ │ ├── manage_password_forgot_password_spec.rb │ │ ├── manage_password_page_spec.rb │ │ └── manage_password_reset_password_spec.rb │ ├── manage_profile │ │ ├── bdd.yml │ │ ├── manage_profile_api_spec.rb │ │ └── manage_profile_page_spec.rb │ ├── manage_social_credentials │ │ ├── bdd.yml │ │ └── manage_social_credentials_pending_spec.rb │ └── unlink_sessions_spec.rb │ ├── other │ ├── bdd.yml │ ├── create_user_spec.rb │ ├── impersonate_user_spec.rb │ └── list_users_spec.rb │ ├── registration │ ├── bdd.yml │ ├── classic_sign_up_spec.rb │ └── onboarding_process_spec.rb │ └── support.rb ├── business └── hello │ ├── authentication │ └── sign_in_spec.rb │ └── registration │ └── sign_up_spec.rb ├── controllers ├── authentication_spec.rb ├── localization_spec.rb └── request_can_carry_an_access_token_spec.rb ├── fixtures └── hello │ └── password_mailer │ ├── confirmation │ ├── forgot │ └── sign_up ├── mailers └── hello │ └── mailer_spec.rb ├── models ├── access_spec.rb ├── create_and_destroy │ ├── email_credential_create_and_destroy_spec.rb │ ├── password_credential_create_and_destroy_spec.rb │ └── user_create_and_destroy_spec.rb ├── credential_spec.rb ├── email_credential_spec.rb ├── password_credential_spec.rb └── user_spec.rb ├── others ├── configuration_spec.rb ├── encryptors │ ├── complex_spec.rb │ └── simple_spec.rb ├── helper_spec.rb └── localization_consistency_spec.rb ├── requests ├── forgot_password_spec.rb ├── reset_password_spec.rb └── security │ └── user_spec.rb ├── routing └── hello │ ├── accesses_routing_spec.rb │ ├── emails_routing_spec.rb │ ├── locale_routing_spec.rb │ ├── profile_routing_spec.rb │ ├── registration_routing_spec.rb │ ├── sign_out_routing_spec.rb │ ├── sudo_mode_routing_spec.rb │ └── users_routing_spec.rb ├── spec_helper.rb ├── spec_helper ├── codeclimate.rb ├── configure_rspec.rb ├── create_database.rb ├── dummy_and_test_dependencies.rb └── support.rb ├── support ├── factories.rb ├── feature_injection.rb ├── features │ ├── feature_support_given.rb │ └── feature_support_then.rb ├── helpers │ ├── aliases.rb │ ├── configuration.rb │ ├── current.rb │ ├── expect.rb │ ├── given.rb │ ├── shortcuts.rb │ ├── then.rb │ └── when.rb └── requests │ └── request_support.rb └── utils └── device_name_spec.rb /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | rubocop: 3 | enabled: true 4 | ratings: 5 | paths: 6 | - Gemfile.lock 7 | - "**.rb" 8 | 9 | exclude_paths: 10 | - spec/ 11 | - examples/ 12 | - dummy/ 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | pkg/ 4 | dummy/db/*.sqlite3 5 | dummy/db/*.sqlite3-journal 6 | dummy/log/*.log 7 | dummy/tmp/ 8 | dummy/.sass-cache 9 | coverage/ 10 | Gemfile.lock 11 | gemfiles/*.lock 12 | ignore/ 13 | *.gem 14 | .idea/ 15 | rspec.xml 16 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Include: 3 | - '**/Rakefile' 4 | Exclude: 5 | - 'spec/bdd/**/*' 6 | - 'vendor/**/*' 7 | 8 | Metrics/LineLength: 9 | Max: 120 10 | 11 | Style/Documentation: 12 | Enabled: false 13 | 14 | Style/TrivialAccessors: 15 | Enabled: false 16 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # http://lint.travis-ci.org/ 2 | # http://docs.travis-ci.com/user/build-configuration/ 3 | # rvm list known 4 | language: ruby 5 | sudo: false 6 | rvm: 7 | - 2.3.8 8 | - 2.4.5 9 | - 2.5.5 10 | - 2.6.2 11 | # - ruby-head 12 | # - jruby-head 13 | # - rbx-head 14 | 15 | gemfile: 16 | - gemfiles/rails-5-2-stable.gemfile 17 | - gemfiles/rails-5-1-stable.gemfile 18 | - gemfiles/rails-5-0-stable.gemfile 19 | - gemfiles/rails-master.gemfile 20 | 21 | matrix: 22 | fast_finish: true 23 | allow_failures: 24 | - gemfile: gemfiles/rails-master.gemfile 25 | 26 | cache: 27 | - bundler 28 | - apt 29 | addons: 30 | code_climate: 31 | repo_token: 6aaf7f088c020571433a2ddd872072efcbe05aba1b68d66ed96593e8d101a74d 32 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | 1. Fork it 4 | 2. Create your feature branch (`git checkout -b my-new-feature`) 5 | 3. Commit your changes (`git commit -am 'Add some feature'`) 6 | 4. Push to the branch (`git push origin my-new-feature`) 7 | 5. Create new Pull Request 8 | 9 | 10 | 11 | 12 | 13 | ## Contributing With Translations 14 | 15 | Link to [Translation files](https://github.com/hello-gem/hello/blob/master/config/locales/hello.en.yml) 16 | 17 | Link to [Our Locale Contributors](https://github.com/hello-gem/hello/blob/master/LOCALES.md) 18 | 19 | 1. change the initializer `config.locales = %w(en es pt-BR )` 20 | 1. dummy initializer `spec/dummy/config/initializers/hello.rb` 21 | 2. initializer template `lib/generators/hello/install/templates/initializer.rb` 22 | 2. update locale test in `spec/controllers/localization_spec.rb` 23 | 3. create and modify the locale file `config/locales/hello..yml` 24 | 4. ensure consistency with this test `bundle exec rspec spec/others/localization_consistency_spec.rb spec/controllers/localization_spec.rb` 25 | 5. Thank You! Submit your Pull Request `:)` 26 | 27 | -------------------------------------------------------------------------------- /CUSTOMIZING.md: -------------------------------------------------------------------------------- 1 | ## Customizing 2 | 3 | It's easy to customize behavior! 4 | 5 | ```ruby 6 | class User < Hello::RailsActiveRecord::User 7 | def user? 8 | %w(user webmaster).include?(role) 9 | end 10 | end 11 | 12 | module Hello 13 | module Concerns 14 | module Registration 15 | module SignUp 16 | 17 | def on_success 18 | deliver_welcome_email 19 | deliver_confirmation_email 20 | redirect_to root_path 21 | end 22 | 23 | end 24 | end 25 | end 26 | end 27 | ``` 28 | 29 | 30 | 31 | 32 | 33 | ## Customizing - behavior and views 34 | 35 | These files are generated when you install this gem. 36 | 37 | They are simple to customize, just open them. 38 | 39 | ├── app/ 40 | │   ├── controllers/ 41 | │   │   ├── hello/ 42 | │   │   │ └── concerns/ 43 | │   │   │ └── [...optional...] 44 | │   │   ├── onboarding_controller.rb 45 | │   │   └── users_controller.rb 46 | │   │ 47 | │   ├── models/ 48 | │   │   └── user.rb 49 | │   │ 50 | │   ├── views/ 51 | │   │ ├── hello/ 52 | │   │ │ └── [...optional...] 53 | │   │ ├── layouts/ 54 | │   │ │   └── application.html.erb 55 | │   │ ├── onboarding/ 56 | │   │ │   └── index.html.erb 57 | │   │ └── users/ 58 | │ │ ├── index.html.erb 59 | │ │ ├── show.html.erb 60 | │   │ └── new.html.erb 61 | │   │ 62 | ├── config/ 63 | │   └── initializers 64 | │   └── hello.rb 65 | │ 66 | └── db/ 67 |    └── migrate/ 68 |       ├── 1_create_credentials.hello.rb 69 |       ├── 2_create_accesses.hello.rb 70 |       └── 3_create_users.hello.rb 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '~>5.0' 4 | 5 | gemspec 6 | -------------------------------------------------------------------------------- /LOCALES.md: -------------------------------------------------------------------------------- 1 | # LOCALES 2 | 3 | Special Thanks to our locale contributors :) 4 | 5 | ## Available 6 | 7 | | Locale | Author | 8 | |---|---| 9 | | en | | 10 | | es | YYY | 11 | | fr | arnaud | 12 | | pl | @mpapis | 13 | | pt-BR | YYY | 14 | | zh-CN | David | 15 | | zh-TW | David | 16 | 17 | 18 | 19 | 20 | 21 | ## Needing 22 | 23 | | Locale | Author | 24 | |---|---| 25 | | af | | 26 | | ar | | 27 | | az | | 28 | | be | | 29 | | bg | | 30 | | bn | | 31 | | bs | | 32 | | ca | | 33 | | cs | | 34 | | cy | | 35 | | da | | 36 | | de | | 37 | | de-AT | | 38 | | de-CH | | 39 | | el | | 40 | | eo | | 41 | | et | | 42 | | eu | | 43 | | fa | | 44 | | fi | | 45 | | fr-CA | | 46 | | fr-CH | | 47 | | gl | | 48 | | he | | 49 | | hi | | 50 | | hr | | 51 | | hu | | 52 | | id | | 53 | | is | | 54 | | it | | 55 | | it-CH | | 56 | | ja | | 57 | | km | | 58 | | kn | | 59 | | ko | | 60 | | lb | | 61 | | lo | | 62 | | lt | | 63 | | lv | | 64 | | mk | | 65 | | mn | | 66 | | mr-IN | | 67 | | ms | | 68 | | nb | | 69 | | nb-NO | | 70 | | ne | | 71 | | nep | | 72 | | nl | | 73 | | nn | | 74 | | or | | 75 | | pa | | 76 | | pl | | 77 | | pt | | 78 | | rm | | 79 | | ro | | 80 | | ru | | 81 | | sk | | 82 | | sl | | 83 | | sr | | 84 | | sv | | 85 | | sw | | 86 | | ta | | 87 | | th | | 88 | | tl | | 89 | | tr | | 90 | | tt | | 91 | | ug | | 92 | | uk | | 93 | | ur | | 94 | | uz | | 95 | | vi | | 96 | | wo | | 97 | | zh-HK | | 98 | | zh-YUE | | 99 | 100 | ------- 101 | 102 | Source: `I18n.available_locales.sort` 103 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 YOURNAME 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # frozen_string_literal: true 3 | 4 | begin 5 | require 'bundler/setup' 6 | rescue LoadError 7 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 8 | end 9 | 10 | APP_RAKEFILE = File.expand_path('dummy/Rakefile', __dir__) 11 | load 'rails/tasks/engine.rake' 12 | 13 | Bundler::GemHelper.install_tasks 14 | 15 | Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each { |f| load f } 16 | 17 | require 'rspec/core' 18 | require 'rspec/core/rake_task' 19 | require 'rspec/rails' 20 | 21 | desc 'Run all specs in spec directory (excluding plugin specs)' 22 | RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare') 23 | 24 | task default: :spec 25 | -------------------------------------------------------------------------------- /app/controllers/hello/application_controller.rb: -------------------------------------------------------------------------------- 1 | class Hello::ApplicationController < ApplicationController 2 | rescue_from Hello::Errors::JsonNotSupported do |exception| 3 | render json: _json_data_for_exception(exception), status: :bad_request 4 | end 5 | 6 | rescue_from ActionController::ParameterMissing do |exception| 7 | respond_to do |format| 8 | format.html { fail exception } 9 | format.json { render json: _json_data_for_exception(exception), status: :bad_request } # 400 10 | end 11 | end 12 | 13 | private 14 | 15 | # Don't override this at home, kids 16 | def _json_data_for_exception(exception) 17 | { 18 | maintenance: false, 19 | action: "#{controller_name}##{action_name}", 20 | exception: { 21 | class: exception.class.name, 22 | message: exception.message, 23 | # backtrace: exception.backtrace 24 | } 25 | } 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/controllers/hello/authentication/sign_in_controller.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Authentication 3 | # you really should be overriding concerns instead of this file 4 | class SignInController < ApplicationController 5 | include Hello::Concerns::Authentication::SignIn 6 | 7 | kick :guest, only: [:authenticated] 8 | 9 | before_actions do 10 | only(:index, :authenticate) { @sign_in = Hello::Business::Authentication::SignIn.new } 11 | end 12 | 13 | # GET /hello/sign_in 14 | def index 15 | render_sign_in 16 | end 17 | 18 | # POST /hello/sign_in 19 | def authenticate 20 | if @sign_in.authenticate(sign_in_params[:login], sign_in_params[:password]) 21 | flash[:notice] = @sign_in.success_message 22 | on_success 23 | else 24 | on_failure 25 | end 26 | end 27 | 28 | private 29 | 30 | def sign_in_params 31 | params.require(:sign_in) 32 | end 33 | 34 | def render_sign_in 35 | render 'hello/authentication/sign_in' 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /app/controllers/hello/authentication/sudo_mode_controller.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Authentication 3 | class SudoModeController < ApplicationController 4 | kick :guest, :onboarding 5 | 6 | # GET /hello/sudo_mode 7 | def form 8 | render_sudo_mode_form 9 | end 10 | 11 | # PATCH /hello/sudo_mode 12 | def authenticate 13 | business = Business::Authentication::SudoModeAuthentication.new(current_access) 14 | 15 | if business.authenticate!(password_param) 16 | path_to_go = session[:url] || root_path 17 | flash[:notice] = business.success_message 18 | redirect_to path_to_go 19 | else 20 | flash.now[:alert] = business.alert_message 21 | render_sudo_mode_form 22 | end 23 | end 24 | 25 | # GET /hello/sudo_mode/expire 26 | def expire 27 | business = Business::Authentication::SudoModeExpiration.new(current_access) 28 | business.expire! 29 | flash[:notice] = business.success_message 30 | redirect_to '/' 31 | end 32 | 33 | private 34 | 35 | def password_param 36 | params.require(:user)[:password] 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/controllers/hello/concerns/authentication/sign_in.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Concerns 3 | module Authentication 4 | module SignIn 5 | 6 | def on_success 7 | access_token = sign_in!(@sign_in.user, expires_at, sudo_mode_expires_at) 8 | 9 | respond_to do |format| 10 | format.html { redirect_to path_to_go } 11 | format.json { render json: access_token.as_json_web_api, status: :created } 12 | end 13 | end 14 | 15 | def on_failure 16 | respond_to do |format| 17 | format.html { render_sign_in } 18 | format.json { render json: @sign_in.errors, status: :unprocessable_entity } 19 | end 20 | end 21 | 22 | 23 | private 24 | 25 | def expires_at 26 | if params[:keep_me] 27 | 30.days.from_now 28 | else 29 | 30.minutes.from_now 30 | end 31 | end 32 | 33 | def sudo_mode_expires_at 34 | Hello.configuration.sudo_expires_in.from_now 35 | end 36 | 37 | def path_to_go 38 | session.delete(:url) || '/' 39 | end 40 | 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/controllers/hello/concerns/management/forgot_password.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Concerns 3 | module Management 4 | module ForgotPassword 5 | 6 | def on_success 7 | reset_token_and_deliver_emails! 8 | 9 | respond_to do |format| 10 | format.html { render_success } 11 | format.json { render json: { sent: true }, status: :created } 12 | end 13 | end 14 | 15 | def on_failure 16 | respond_to do |format| 17 | format.html { render_form } 18 | format.json { render json: @forgot_password.errors, status: :unprocessable_entity } 19 | end 20 | end 21 | 22 | private 23 | 24 | def reset_token_and_deliver_emails! 25 | url = get_reset_password_url 26 | 27 | emails.each do |email| 28 | Mailer.forgot_password(email, @user, url).deliver 29 | end 30 | end 31 | 32 | def emails 33 | @user.email_credentials.map(&:email) 34 | end 35 | 36 | def get_reset_password_url 37 | p = @user.password_credential 38 | token = p.reset_verifying_token! 39 | hello.reset_password_url(p.id, @user.id, token) 40 | end 41 | 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /app/controllers/hello/concerns/management/reset_password.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Concerns 3 | module Management 4 | module ResetPassword 5 | 6 | def on_success 7 | access_token = sign_in!(@reset_password.user, expires_at, sudo_mode_expires_at) 8 | 9 | redirect_to path_to_go 10 | end 11 | 12 | def on_failure 13 | render_reset_form 14 | end 15 | 16 | private 17 | 18 | def expires_at 19 | 30.days.from_now 20 | end 21 | 22 | def sudo_mode_expires_at 23 | Hello.configuration.sudo_expires_in.from_now 24 | end 25 | 26 | def path_to_go 27 | '/' 28 | end 29 | 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/controllers/hello/concerns/registration/sign_up.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Concerns 3 | module Registration 4 | module SignUp 5 | 6 | def on_success 7 | deliver_welcome_email 8 | deliver_confirmation_email 9 | 10 | access_token = sign_in!(@sign_up.user, expires_at, sudo_mode_expires_at) 11 | 12 | respond_to do |format| 13 | format.html { redirect_to path_to_go } 14 | format.json { render json: access_token.as_json_web_api, status: :created } 15 | end 16 | end 17 | 18 | def on_failure 19 | respond_to do |format| 20 | format.html { render_sign_up } 21 | format.json { render json: @sign_up.errors, status: :unprocessable_entity } 22 | end 23 | end 24 | 25 | private 26 | 27 | def expires_at 28 | 30.days.from_now 29 | end 30 | 31 | def sudo_mode_expires_at 32 | Hello.configuration.sudo_expires_in.from_now 33 | end 34 | 35 | def path_to_go 36 | '/onboarding' 37 | end 38 | 39 | def deliver_welcome_email 40 | Mailer.welcome(email, user, password).deliver 41 | end 42 | 43 | def deliver_confirmation_email 44 | token = email_credential.reset_verifying_token! 45 | url = hello.confirm_email_url(email_credential, token) 46 | Mailer.confirm_email(email, user, url).deliver 47 | end 48 | 49 | def email 50 | email_credential.email 51 | end 52 | 53 | def email_credential 54 | @sign_up.email_credentials.first 55 | end 56 | 57 | def user 58 | @sign_up.user 59 | end 60 | 61 | def password 62 | @sign_up.password 63 | end 64 | 65 | end 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /app/controllers/hello/internationalization/locale_controller.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Internationalization 3 | class LocaleController < ApplicationController 4 | dont_kick_people 5 | 6 | # GET /hello/locale 7 | def index 8 | respond_to do |format| 9 | format.html { render 'hello/internationalization/locales' } 10 | format.json { render json: { locales: view_context.available_locales_with_names } } 11 | end 12 | end 13 | 14 | # POST /hello/locale 15 | def update 16 | business = Business::Internationalization::UpdateLocale.new(params['locale']) 17 | 18 | current_user && current_user.update!(locale: business.locale) 19 | use_locale(business.locale) 20 | 21 | respond_to do |format| 22 | format.html { redirect_back fallback_location: root_path, notice: business.success_message } 23 | format.json { fail Hello::Errors::JsonNotSupported } 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/hello/management/accesses_controller.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Management 3 | class AccessesController < ApplicationController 4 | kick :guest, :onboarding 5 | 6 | before_actions do 7 | all { sudo_mode } 8 | only(:index) { @accesses = current_user.accesses } 9 | only(:destroy) { @access = current_user.accesses.find(params[:id]) } 10 | end 11 | 12 | # GET /hello/accesses 13 | def index 14 | render 'hello/management/accesses' 15 | end 16 | 17 | # DELETE /hello/accesses/1 18 | def destroy 19 | business = Business::Management::UnlinkAccess.new 20 | if @access.destroy 21 | flash[:notice] = business.success_message 22 | else 23 | flash[:alert] = business.alert_message 24 | end 25 | redirect_to hello.accesses_url 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/controllers/hello/management/confirm_emails_controller.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Management 3 | class ConfirmEmailsController < ApplicationController 4 | dont_kick_people 5 | 6 | before_actions do 7 | all { sign_out! } 8 | only(:confirm) { @credential = ::EmailCredential.where(id: params[:id]).first } 9 | end 10 | 11 | # GET /hello/emails/1/confirm/:token 12 | def confirm 13 | business = Business::Management::ConfirmEmail.new(@credential) 14 | 15 | if business.confirm_with_token(params[:token]) 16 | _sign_in 17 | flash[:notice] = business.success_message 18 | redirect_to profile_path 19 | else 20 | flash[:alert] = business.alert_message 21 | redirect_to expired_confirmation_token_emails_path 22 | end 23 | end 24 | 25 | # GET /hello/emails/expired_confirmation_token 26 | def expired_confirmation_token 27 | render 'hello/management/email_credentials/expired_confirmation_token' 28 | end 29 | 30 | private 31 | 32 | def _sign_in 33 | # In RSpec and Capybara (Rails 4.2): 34 | # when the user gets access, the session of the next request will assume the values it had before, 35 | # if before you were a guest, you will be redirected as a user, but the following request will be as a guest again 36 | # if before you were a user1, you will be redirected as a user2, but the following request will be as a user1 again 37 | access_token = sign_in!(@credential.user, 1.hour.from_now) 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/controllers/hello/management/emails_controller.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Management 3 | class EmailsController < ApplicationController 4 | kick :guest, :onboarding 5 | sudo_mode 6 | 7 | helper_method :credentials 8 | 9 | before_actions do 10 | only(:index) { @credential = ::EmailCredential.new } 11 | only(:create) { @credential = current_user.email_credentials.build(email_credential_params) } 12 | only(:destroy, :deliver) { @credential = current_user.email_credentials.find(params[:id]) } 13 | end 14 | 15 | # GET /hello/emails 16 | def index 17 | render_list 18 | end 19 | 20 | # POST /hello/emails 21 | def create 22 | business = Business::Management::AddEmail.new(@credential) 23 | if @credential.save 24 | redirect_to hello.emails_path, notice: business.success_message 25 | else 26 | flash.now[:alert] = business.error_message 27 | render_list 28 | end 29 | end 30 | 31 | # DELETE /hello/emails/1 32 | def destroy 33 | business = Business::Management::RemoveEmail.new(@credential) 34 | if @credential.destroy 35 | redirect_to hello.emails_path, notice: business.success_message 36 | else 37 | flash.now[:alert] = business.error_message 38 | render_list 39 | end 40 | end 41 | 42 | # POST /hello/emails/1/deliver 43 | def deliver 44 | business = Business::Management::SendConfirmationEmail.new(self, @credential) 45 | business.deliver 46 | flash[:notice] = business.success_message 47 | redirect_to hello.emails_path 48 | end 49 | 50 | private 51 | 52 | # Only allow a trusted parameter "white list" through. 53 | def email_credential_params 54 | params.require(:email_credential).permit(:email) 55 | end 56 | 57 | def credentials 58 | # TODO: this is necessary to hide a temporary bug, must solve this later 59 | current_user.credentials.where(type: 'EmailCredential') 60 | end 61 | 62 | def render_list 63 | render 'hello/management/email_credentials/index' 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /app/controllers/hello/management/forgot_password_controller.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Management 3 | # you really should be overriding concerns instead of this file 4 | class ForgotPasswordController < ApplicationController 5 | include Hello::Concerns::Management::ForgotPassword 6 | 7 | dont_kick :guest 8 | 9 | before_action { @sender = Hello.configuration.mailer_sender } 10 | 11 | # GET /hello/passwords/forgot 12 | def index 13 | @forgot_password = Business::Management::ForgotPassword.new 14 | render 'hello/management/password_credentials/forgot' 15 | end 16 | 17 | # POST /hello/passwords/forgot 18 | def forgot 19 | @forgot_password = Business::Management::ForgotPassword.new(params.require(:forgot_password)) 20 | @user = @forgot_password.user 21 | 22 | if @forgot_password.reset 23 | on_success 24 | else 25 | on_failure 26 | end 27 | end 28 | 29 | private 30 | 31 | def render_success 32 | render 'hello/management/password_credentials/forgot_success' 33 | end 34 | 35 | def render_form 36 | render 'hello/management/password_credentials/forgot' 37 | end 38 | 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/controllers/hello/management/passwords_controller.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Management 3 | class PasswordsController < ApplicationController 4 | kick :guest, :onboarding 5 | sudo_mode 6 | 7 | before_action do 8 | @password_credential = current_user.password_credential || fail(ActiveRecord::NotFound) 9 | @update_profile = Business::Management::UpdateProfile.new(@password_credential) 10 | end 11 | 12 | # GET /hello/passwords 13 | def index 14 | respond_to do |format| 15 | format.html { redirect_to password_path(@password_credential.id) } 16 | format.json { head :no_content } 17 | end 18 | end 19 | 20 | # GET /hello/passwords/1 21 | def show 22 | respond_to do |format| 23 | format.html { render_password_view } 24 | format.json { head :no_content } 25 | end 26 | end 27 | 28 | # PATCH /hello/passwords/1 29 | def update 30 | @password_credential.password = password_credential_params[:password] 31 | # @password_credential.password_confirmation = password_credential_params[:password_confirmation] if password_credential_params[:password_confirmation] 32 | 33 | if @password_credential.save 34 | respond_to do |format| 35 | format.html { redirect_to hello.password_path(@password_credential), notice: @update_profile.success_message } 36 | format.json { head :no_content } 37 | end 38 | else 39 | respond_to do |format| 40 | format.html { render_password_view } 41 | format.json { render json: @password_credential.errors, status: :unprocessable_entity } 42 | end 43 | end 44 | end 45 | 46 | private 47 | 48 | def password_credential_params 49 | params.require(:password_credential) 50 | end 51 | 52 | def render_password_view 53 | render 'hello/management/password_credentials/show' 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /app/controllers/hello/management/profiles_controller.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Management 3 | class ProfilesController < ApplicationController 4 | 5 | kick :guest, :onboarding, only: [:show, :update] 6 | 7 | before_action(only: [:show, :update]) do 8 | @user = current_user 9 | @user_business = Business::Management::UpdateProfile.new(@user) 10 | end 11 | 12 | # GET /hello/user 13 | def show 14 | respond_to do |format| 15 | format.html { render 'hello/management/user' } 16 | format.json { render json: @user.as_json_web_api, status: :ok } 17 | end 18 | end 19 | 20 | # PATCH /hello/user 21 | def update 22 | if @user_business.update(user_params) 23 | use_locale(current_user.locale) 24 | flash[:notice] = @user_business.success_message 25 | respond_to do |format| 26 | format.html { redirect_to hello.profile_path } 27 | format.json { render json: @user.as_json_web_api, status: :ok } 28 | end 29 | else 30 | render 'hello/management/user' 31 | end 32 | end 33 | 34 | 35 | 36 | 37 | dont_kick :user, only: [:cancel, :destroy] 38 | sudo_mode only: [:cancel, :destroy] 39 | 40 | # GET /hello/user/cancel 41 | def cancel 42 | render 'hello/management/cancel' 43 | end 44 | 45 | # DELETE /hello/user 46 | def destroy 47 | @cancel_account = Business::Management::CancelAccount.new(current_user) 48 | 49 | if @cancel_account.cancel_account 50 | flash[:notice] = @cancel_account.success_message 51 | respond_to do |format| 52 | format.html { redirect_to '/' } 53 | format.json { render json: { cancelled: true }, status: :ok } 54 | end 55 | else 56 | flash.now[:alert] = @cancel_account.alert_message 57 | respond_to do |format| 58 | format.html { render 'hello/management/cancel' } 59 | format.json { render json: { message: @cancel_account.alert_message }, status: :unprocessable_entity } 60 | end 61 | end 62 | end 63 | 64 | private 65 | 66 | def user_params 67 | params.require(:user) 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /app/controllers/hello/management/reset_password_controller.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Management 3 | # you really should be overriding concerns instead of this file 4 | class ResetPasswordController < ApplicationController 5 | include Hello::Concerns::Management::ResetPassword 6 | 7 | sign_out! 8 | 9 | before_action do 10 | # a helping ivar 11 | @current_url = request.fullpath 12 | # find 13 | @user = ::User.find(params[:user_id]) 14 | @password_credential = @user.password_credentials.find(params[:id]) 15 | unless @password_credential.verifying_token_is?(params[:token]) 16 | fail ActiveRecord::RecordNotFound 17 | end 18 | # business 19 | @reset_password = Business::Management::ResetPassword.new(@password_credential) 20 | end 21 | 22 | # GET /passwords/:id/reset/:user_id/:token 23 | def index 24 | render_reset_form 25 | end 26 | 27 | # POST /passwords/:id/reset/:user_id/:token 28 | def update 29 | if @reset_password.update_password(new_password) 30 | flash[:notice] = @reset_password.success_message 31 | on_success 32 | else 33 | on_failure 34 | end 35 | end 36 | 37 | rescue_from ActiveRecord::RecordNotFound do 38 | flash[:alert] = Business::Management::ResetPassword.new(nil).alert_message 39 | redirect_to forgot_passwords_path 40 | end 41 | 42 | private 43 | 44 | def render_reset_form 45 | render 'hello/management/password_credentials/reset' 46 | end 47 | 48 | def new_password 49 | params.require(:reset_password)[:password] 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /app/controllers/hello/registration/sign_up_controller.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Registration 3 | # you really should be overriding concerns instead of this file 4 | class SignUpController < ApplicationController 5 | include Hello::Concerns::Registration::SignUp 6 | 7 | dont_kick_people 8 | 9 | before_action do 10 | @sign_up = Hello::Business::Registration::SignUp.new 11 | end 12 | 13 | # GET /hello/sign_up 14 | def index 15 | render_sign_up 16 | end 17 | 18 | # GET /hello/sign_up/widget 19 | def widget 20 | render 'hello/registration/sign_up_widget', layout: false 21 | end 22 | 23 | # POST /hello/sign_up 24 | def create 25 | if sign_up_disabled 26 | _create_disabled 27 | else 28 | _create_enabled 29 | end 30 | end 31 | 32 | # GET /hello/sign_up/disabled 33 | def disabled 34 | render_sign_up 35 | end 36 | 37 | protected 38 | 39 | def render_sign_up 40 | render 'hello/registration/sign_up' 41 | end 42 | 43 | def _create_enabled 44 | if @sign_up.register(sign_up_params) 45 | flash[:notice] = @sign_up.success_message 46 | on_success 47 | else 48 | on_failure 49 | end 50 | end 51 | 52 | def _create_disabled 53 | @sign_up.errors[:base] << "Email Registration is temporarily disabled" 54 | if sign_up_disabled.is_a?(Hash) 55 | sign_up_disabled.each do |k, v| 56 | @sign_up.errors[k] << Array(v).flatten 57 | end 58 | end 59 | on_failure 60 | end 61 | 62 | def sign_up_params 63 | params.require(:sign_up).permit(sign_up_params_permitted) 64 | end 65 | 66 | def sign_up_params_permitted 67 | Hello.configuration.sign_up_fields.map(&:to_s) 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /app/helpers/hello/application_helper.rb: -------------------------------------------------------------------------------- 1 | # this gem requires this file to be here 2 | module Hello 3 | module ApplicationHelper 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/mailers/hello/mailer.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | class Mailer < ActionMailer::Base 3 | default from: Hello.configuration.mailer_sender 4 | 5 | def welcome(email, user, password) 6 | @user = user 7 | @password = password 8 | 9 | mail to: email 10 | end 11 | 12 | def confirm_email(email, user, url) 13 | @user = user 14 | @url = url 15 | 16 | mail to: email 17 | end 18 | 19 | def forgot_password(email, user, url) 20 | @user = user 21 | @url = url 22 | 23 | mail to: email 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/models/access.rb: -------------------------------------------------------------------------------- 1 | class Access < ApplicationRecord 2 | include Hello::RailsActiveRecord::Access 3 | end 4 | -------------------------------------------------------------------------------- /app/models/credential.rb: -------------------------------------------------------------------------------- 1 | class Credential < ApplicationRecord 2 | include Hello::RailsActiveRecord::Credential 3 | end 4 | -------------------------------------------------------------------------------- /app/models/email_credential.rb: -------------------------------------------------------------------------------- 1 | class EmailCredential < Credential 2 | include Hello::RailsActiveRecord::EmailCredential 3 | end 4 | -------------------------------------------------------------------------------- /app/models/password_credential.rb: -------------------------------------------------------------------------------- 1 | class PasswordCredential < Credential 2 | include Hello::RailsActiveRecord::PasswordCredential 3 | end 4 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | include Hello::RailsActiveRecord::User 3 | end 4 | -------------------------------------------------------------------------------- /app/views/hello/authentication/_sign_in.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @sign_in, as: :sign_in, url: sign_in_path, html: {class: "form-horizontal", role: "form"} do |f| %> 2 | 3 | 4 | <% if @sign_in.errors.any? %> 5 | <%= render '/hello/shared/errors', business: @sign_in %> 6 | <% end %> 7 | 8 |
9 | <%= f.label :login, "Email or Username", class: "col-sm-4 control-label" %> 10 |
11 | <%= f.text_field :login, class: "form-control", placeholder: "Email or Username" %> 12 | 13 | <% if @sign_in.bad_login? %> 14 |
15 |

This login was not found in our database.

16 |

Do you want to <%= link_to "Sign Up", hello.sign_up_path %> instead?

17 |
18 | <% end %> 19 | 20 |
21 | 22 |
23 | 24 | 25 |
26 | <%= f.label :password, class: "col-sm-4 control-label" %> 27 |
28 | <%= f.password_field :password, class: "form-control", placeholder: "Password" %> 29 | 30 | <% if @sign_in.bad_password? %> 31 |
32 |

Your password does not match that in our database.

33 |
34 | Did you <%= link_to "forget your password", hello.forgot_passwords_path %>? 35 |
36 |
37 | <% end %> 38 | 39 |
40 |
41 | 42 |
43 |
44 |
45 | 49 |
50 |
51 |
52 | 59 |
60 |
61 | <%= f.submit "Sign In", class: "btn btn-primary" %> 62 |
63 |
64 | <% end %> 65 | -------------------------------------------------------------------------------- /app/views/hello/authentication/new_session.html.erb: -------------------------------------------------------------------------------- 1 |

Add Account

2 | 3 |

<%= link_to "Sign Up", hello.sign_up_path, class: 'btn btn-xs btn-info', style: 'width: 150px' %> 4 |

<%= link_to "Sign In", hello.sign_in_path, class: 'btn btn-xs btn-info', style: 'width: 150px' %>

5 | -------------------------------------------------------------------------------- /app/views/hello/authentication/sessions.html.erb: -------------------------------------------------------------------------------- 1 |

Switch Accounts

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <% @accesses.each do |a| %> 16 | 17 | 18 | 19 | 20 | 21 | 24 | 29 | 30 | <% end %> 31 | 32 | 33 | 34 | 35 | 36 |
UsernameNameRoleExpiryActions
<%= link_to a.user.username, a.user %><%= a.user.name %><%= a.user.role %><%= distance_of_time_in_words_to_now(a.expires_at) %><%= link_to "Switch!", 22 | hello.session_path(a.id), 23 | class: 'btn btn-xs btn-block btn-success' unless is_current_access?(a) %><%= button_to "Forget", 25 | hello.session_path(a.id), 26 | method: :delete, 27 | class: 'btn btn-xs btn-block btn-danger', 28 | data: { confirm: 'Are you sure?' } %>
<%= link_to "Add Account", hello.new_session_path, class: 'btn btn-xs btn-block btn-info' %>
37 | -------------------------------------------------------------------------------- /app/views/hello/authentication/sign_in.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= render 'hello/authentication/sign_in' %> 4 | -------------------------------------------------------------------------------- /app/views/hello/authentication/sudo_mode.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render '/hello/shared/settings' %> 3 | 4 |
5 |
6 |
Confirm Password to Continue
7 |
8 | 9 | <%= form_for current_user, url: hello.sudo_mode_path, html: {class: "form-horizontal", role: "form"} do |f| %> 10 | 11 |
12 | <%= f.label :username, class: "col-sm-4 control-label" %> 13 |
14 | 15 | <%= current_user.username %> 16 | 17 |
18 |
19 | 20 |
21 | <%= f.label :password, class: "col-sm-4 control-label" %> 22 |
23 | <%= f.password_field :password, class: "form-control", placeholder: "Password" %> 24 |
25 |
26 |
27 |
28 | <%= f.submit "Confirm Password", class: "btn btn-danger" %> 29 |
30 |
31 | <% end %> 32 |
33 | 34 |
35 |
36 | 37 |
38 | -------------------------------------------------------------------------------- /app/views/hello/internationalization/locales.html.erb: -------------------------------------------------------------------------------- 1 |

Choose your language

2 | 3 | 8 | -------------------------------------------------------------------------------- /app/views/hello/mailer/confirm_email.html.erb: -------------------------------------------------------------------------------- 1 |

Confirm this email address

2 | 3 |

Hello <%= @user.name %>,

4 |

Confirm this email address on the link bellow

5 | 6 |

<%= '-' * 40 %>

7 |

<%= link_to @url, @url %>

8 |

<%= '-' * 40 %>

9 | 10 |

Awesome Regards,

11 |

Awesome Team

12 |

Awesome Site

13 | -------------------------------------------------------------------------------- /app/views/hello/mailer/forgot_password.html.erb: -------------------------------------------------------------------------------- 1 |

Reset Your Password

2 | 3 |

Hello <%= @user.name %>,

4 |

Reset your password on the link bellow

5 | 6 |

<%= '-' * 40 %>

7 |

<%= link_to @url, @url %>

8 |

<%= '-' * 40 %>

9 | 10 |

Awesome Regards,

11 |

Awesome Team

12 |

Awesome Site

13 | -------------------------------------------------------------------------------- /app/views/hello/mailer/welcome.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome

2 | 3 |

Hello <%= @user.name %>,

4 |

We hope you love our website :)

5 | 6 |

Your password is: "<%= @password %>", it has been encrypted for storage.

7 |

if you forget it, you will have to ask for a new password.

8 | 9 |

Awesome Regards,

10 |

Awesome Team

11 |

Awesome Site

12 | -------------------------------------------------------------------------------- /app/views/hello/management/accesses.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | <%= render '/hello/shared/settings' %> 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <% @accesses.each_with_index do |access, i| %> 20 | <% is_current_access?(access).tap do |is_current_access| %> 21 | 22 | 23 | 24 | 25 | 26 | 37 | 38 | 39 | <% end %> 40 | <% end %> 41 | 42 | 43 | 44 |
#DeviceIP Address
Device #<%= i+1 %><%= access.full_device_name %><%= access.ip %> 27 | <% if is_current_access %> 28 | [this device] 29 | <% else %> 30 | <%= button_to 'Unlink', 31 | access, 32 | method: :delete, 33 | class: 'btn btn-danger', 34 | data: { confirm: 'Are you sure?' } %> 35 | <% end %> 36 |
45 | 46 |
47 |
48 | -------------------------------------------------------------------------------- /app/views/hello/management/cancel.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | <%= render '/hello/shared/settings' %> 4 | 5 |
6 | 7 |

Cancel Account

8 |

Are you sure you want to cancel your account?

9 | 10 |

<%= button_to "Cancel my Account", hello.profile_path, method: :delete %>

11 |

<%= link_to "No, I want to stay", root_path %>

12 | 13 |
14 |
15 | -------------------------------------------------------------------------------- /app/views/hello/management/email_credentials/expired_confirmation_token.html.erb: -------------------------------------------------------------------------------- 1 | . 2 | -------------------------------------------------------------------------------- /app/views/hello/management/password_credentials/_forgot_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @forgot_password, as: :forgot_password, url: forgot_passwords_path, html: {class: "form-horizontal", role: "form"} do |f| %> 2 | 3 | 4 | <% if @forgot_password.errors.any? %> 5 | <%= render '/hello/shared/errors', business: @forgot_password %> 6 | <% end %> 7 | 8 |
9 |
10 | <%= f.text_field :login, class: "form-control ", placeholder: "Email or Username" %> 11 |
12 |
13 | <%= f.submit "Continue", class: "btn btn-primary" %> 14 |
15 |
16 | 17 | <% end %> 18 | -------------------------------------------------------------------------------- /app/views/hello/management/password_credentials/_reset_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @reset_password, as: :reset_password, url: @current_url, html: {class: "form-horizontal", role: "form"} do |f| %> 2 | 3 | 4 | <% if @reset_password.errors.any? %> 5 | <%= render '/hello/shared/errors', business: @reset_password %> 6 | <% end %> 7 | 8 | <% @user.email_credentials.map(&:email).each do |email| %> 9 |
10 | <%= f.label :email, class: "col-sm-4 control-label" %> 11 |
12 | 13 | <%= email %> 14 | 15 |
16 |
17 | <% end %> 18 | 19 |
20 | <%= f.label :username, class: "col-sm-4 control-label" %> 21 |
22 | 23 | <%= @user.username %> 24 | 25 |
26 |
27 | 28 |
29 | <%= f.label :password, class: "col-sm-4 control-label" %> 30 |
31 | <%= f.password_field :password, class: "form-control", placeholder: "Password" %> 32 |
33 |
34 |
35 |
36 | <%= f.submit "Save", class: "btn btn-primary" %> 37 |
38 |
39 | <% end %> 40 | -------------------------------------------------------------------------------- /app/views/hello/management/password_credentials/forgot.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

To reset your password, enter the email address you use to sign in to this website.

5 |

This can be any email address associated with your account.

6 |

We will send you a verification email.

7 |
You can also enter your @username.
8 |
9 | 10 | <%= render 'hello/management/password_credentials/forgot_form' %> 11 | -------------------------------------------------------------------------------- /app/views/hello/management/password_credentials/forgot_success.html.erb: -------------------------------------------------------------------------------- 1 | <% if @forgot_password.login %> 2 |

Email sent to "<%= @forgot_password.login %>"

3 | <% end %> 4 | 5 | 8 | 9 | 10 |
11 |

Q: Didn't receive the password reset email?

12 |

13 | A: Check your spam folder for an email from 14 | <%= @sender %> 15 |

16 |
17 | 18 |
19 |

Q: You still don't see the email?

20 |

A: Try a different way to get into your account.

21 |
22 | Suggestion: 23 | <%= link_to "Click Here", hello.forgot_passwords_path %> 24 | and try again with a different email. 25 |
26 |
27 | -------------------------------------------------------------------------------- /app/views/hello/management/password_credentials/reset.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= render 'hello/management/password_credentials/reset_form' %> 4 | -------------------------------------------------------------------------------- /app/views/hello/management/password_credentials/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | <%= render '/hello/shared/settings' %> 4 | 5 |
6 | 7 | <%= form_for @password_credential, url: password_path, html: {class: "form-horizontal", role: "form"} do |f| %> 8 | <%= render '/hello/shared/errors', business: @update_profile %> 9 | 10 |
11 | <%= f.label :password, class: "col-sm-4 control-label" %> 12 |
13 | <%= f.password_field :password, class: "form-control", placeholder: "Password" %> 14 |
15 |
16 | 17 |
18 |
19 | <%= f.submit "Update Profile", class: "btn btn-default" %> 20 |
21 |
22 | <% end %> 23 | 24 |
25 |
26 | -------------------------------------------------------------------------------- /app/views/hello/registration/sign_up.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= render 'hello/registration/sign_up' %> 4 | -------------------------------------------------------------------------------- /app/views/hello/registration/sign_up_widget.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= render 'hello/registration/sign_up' %> 4 | -------------------------------------------------------------------------------- /app/views/hello/shared/_errors.html.erb: -------------------------------------------------------------------------------- 1 | <% if business.errors.any? %> 2 |
3 |

<%= business.error_message %>

4 | 5 |
    6 | <% business.errors.full_messages.each do |msg| %> 7 |
  • <%= msg %>
  • 8 | <% end %> 9 |
10 |
11 | <% end %> 12 | -------------------------------------------------------------------------------- /app/views/hello/shared/_flash.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <% if notice %> 3 |
<%= notice %>
4 | <% end %> 5 | 6 | <% if alert %> 7 |
<%= alert %>
8 | <% end %> 9 | -------------------------------------------------------------------------------- /app/views/hello/shared/_nav_pills.html.erb: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /app/views/hello/shared/_session_expiration.html.erb: -------------------------------------------------------------------------------- 1 | <% if signed_in? %> 2 |

3 | 4 | Your session expires in <%= time_ago_in_words current_access.expires_at %> 5 | 6 | 7 | <% if sudo_mode? %> 8 | 9 | | 10 | Sudo Mode expires in <%= time_ago_in_words current_access.sudo_expires_at %> 11 | (<%= link_to "expire it now", hello.sudo_mode_expire_path %>) 12 | 13 | <% end %> 14 |

15 | <% end %> 16 | -------------------------------------------------------------------------------- /app/views/hello/shared/_settings.html.erb: -------------------------------------------------------------------------------- 1 | <% if not signed_in? %> 2 | 3 | 4 |
5 |
6 | 7 | <% else %> 8 | 9 | 10 | 11 |
12 |
13 | <%= nav_link_to "Profile", 14 | hello.profile_path, 15 | {class: "list-group-item"}, 16 | {selected_class: "active"} 17 | %> 18 | <%= nav_link_to "Password", 19 | hello.passwords_path, 20 | {class: "list-group-item"}, 21 | {selected_class: "active"} 22 | %> 23 | <%= nav_link_to "Emails (#{current_user.email_credentials.count})", 24 | hello.emails_path, 25 | {class: "list-group-item"}, 26 | {selected_class: "active"} 27 | %> 28 | <%= nav_link_to "Devices (#{current_user.accesses_count})", 29 | hello.accesses_path, 30 | {class: "list-group-item"}, 31 | {selected_class: "active"} 32 | %> 33 | <%= nav_link_to "Cancel Account", 34 | hello.cancel_profile_path, 35 | {class: "list-group-item"}, 36 | {selected_class: "active"} 37 | %> 38 |
39 |
40 | 41 | <% end %> 42 | -------------------------------------------------------------------------------- /app/views/layouts/hello/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hello-gem/hello/32cb8bcf086c14b5ba6a23a5cccfc001a7e326f7/app/views/layouts/hello/.keep -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # This command will automatically be run when you run "rails" from the root of your application. 5 | 6 | ENGINE_ROOT = File.expand_path('..', __dir__) 7 | ENGINE_PATH = File.expand_path('../lib/hello/engine', __dir__) 8 | 9 | require 'rails/all' 10 | require 'rails/engine/commands' 11 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Hello::Engine.routes.draw do 2 | # root 'management/profiles#show' 3 | 4 | # 5 | # REGISTRATION 6 | # 7 | scope module: 'registration' do 8 | get 'sign_up' => 'sign_up#index' 9 | post 'sign_up' => 'sign_up#create' 10 | get 'sign_up/widget' => 'sign_up#widget' 11 | match 'sign_up/disabled' => 'sign_up#disabled', via: [:get, :post] 12 | end 13 | 14 | # 15 | # AUTHENTICATION 16 | # 17 | scope module: 'authentication' do 18 | resources :sessions, only: [:index, :new, :show, :destroy] 19 | delete 'sign_out' => 'sessions#sign_out' 20 | 21 | get 'sign_in' => 'sign_in#index' 22 | post 'sign_in' => 'sign_in#authenticate' 23 | 24 | get 'sudo_mode' => 'sudo_mode#form' 25 | patch 'sudo_mode' => 'sudo_mode#authenticate' 26 | get 'sudo_mode/expire' => 'sudo_mode#expire' 27 | end 28 | 29 | # 30 | # MANAGEMENT 31 | # 32 | scope module: 'management' do 33 | resources :accesses, only: [:index, :destroy] 34 | 35 | root 'profiles#show' 36 | resource :profile, only: [:show, :update, :destroy] do 37 | member do 38 | get :cancel 39 | end 40 | end 41 | 42 | resources :emails, only: [:index, :create, :destroy] do 43 | member do 44 | post 'deliver' 45 | get 'confirm/:token' => 'confirm_emails#confirm', as: 'confirm' 46 | end 47 | collection do 48 | get 'expired_confirmation_token' => 'confirm_emails#expired_confirmation_token' 49 | end 50 | end 51 | 52 | resources :passwords, only: [:index, :show, :update] do 53 | collection do 54 | get 'forgot' => 'forgot_password#index' 55 | post 'forgot' => 'forgot_password#forgot' 56 | end 57 | member do 58 | scope '/reset/:user_id/:token' do 59 | get '/' => 'reset_password#index', as: 'reset' 60 | post '/' => 'reset_password#update', as: nil 61 | end 62 | end 63 | end 64 | end 65 | 66 | # 67 | # INTERNATIONALIZATION 68 | # 69 | scope module: 'internationalization' do 70 | get 'locale' => 'locale#index' 71 | post 'locale' => 'locale#update' 72 | end 73 | 74 | end 75 | -------------------------------------------------------------------------------- /db/migrate/1_create_credentials.rb: -------------------------------------------------------------------------------- 1 | class CreateCredentials < ActiveRecord::Migration 2 | def change 3 | create_table :credentials do |t| 4 | t.references :user, index: true 5 | t.string :type 6 | 7 | t.string :email 8 | t.string :digest 9 | 10 | t.datetime :confirmed_at 11 | t.string :verifying_token_digest 12 | t.datetime :verifying_token_digested_at 13 | 14 | t.timestamps 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/2_create_accesses.rb: -------------------------------------------------------------------------------- 1 | class CreateAccesses < ActiveRecord::Migration 2 | def change 3 | create_table :accesses do |t| 4 | t.references :user, index: true 5 | t.string :user_agent_string 6 | t.string :token 7 | t.string :ip 8 | t.datetime :expires_at, default: DateTime.new(2000, 1, 1) 9 | t.datetime :sudo_expires_at, default: DateTime.new(2000, 1, 1) 10 | 11 | t.timestamps 12 | t.index :token 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/3_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration 2 | def change 3 | create_table :users do |t| 4 | t.string :name 5 | 6 | t.string :role, default: 'onboarding' 7 | 8 | t.string :locale 9 | t.string :time_zone 10 | t.string :username 11 | 12 | t.integer :credentials_count, default: 0 13 | t.integer :accesses_count, default: 0 14 | 15 | t.timestamps 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /dummy/.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /dummy/README.rdoc: -------------------------------------------------------------------------------- 1 | == README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | 26 | 27 | Please feel free to use a different markup language if you do not plan to run 28 | rake doc:app. 29 | -------------------------------------------------------------------------------- /dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Add your own tasks in files placed in lib/tasks ending in .rake, 4 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 5 | 6 | require File.expand_path('config/application', __dir__) 7 | 8 | Dummy::Application.load_tasks 9 | -------------------------------------------------------------------------------- /dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hello-gem/hello/32cb8bcf086c14b5ba6a23a5cccfc001a7e326f7/dummy/app/assets/images/.keep -------------------------------------------------------------------------------- /dummy/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 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_tree . 14 | -------------------------------------------------------------------------------- /dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require_tree . 13 | */ 14 | -------------------------------------------------------------------------------- /dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | end 6 | -------------------------------------------------------------------------------- /dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hello-gem/hello/32cb8bcf086c14b5ba6a23a5cccfc001a7e326f7/dummy/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /dummy/app/controllers/middleware_controller.rb: -------------------------------------------------------------------------------- 1 | class MiddlewareController < ApplicationController 2 | def bad_kitty 3 | render plain: '200 OK' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /dummy/app/controllers/my_areas_controller.rb: -------------------------------------------------------------------------------- 1 | class MyAreasController < ApplicationController 2 | # 3 | # KEEP ONE 4 | # 5 | dont_kick :guest, only: :guest_page 6 | dont_kick :onboarding, only: :onboarding_page 7 | dont_kick :webmaster, only: :webmaster_page 8 | 9 | # 10 | # KICK ONE 11 | # 12 | kick :webmaster, only: :non_webmaster_page 13 | kick :guest, only: :authenticated_page 14 | 15 | # 16 | # USER AREA 17 | # 18 | kick :guest, :onboarding, only: :user_page 19 | 20 | def guest_page 21 | yes 22 | end 23 | 24 | def authenticated_page 25 | yes 26 | end 27 | 28 | def onboarding_page 29 | yes 30 | end 31 | 32 | def user_page 33 | yes 34 | end 35 | 36 | def webmaster_page 37 | yes 38 | end 39 | 40 | def non_webmaster_page 41 | yes 42 | end 43 | 44 | private 45 | 46 | def yes 47 | render plain: 'yes!' 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /dummy/app/controllers/onboarding_controller.rb: -------------------------------------------------------------------------------- 1 | class OnboardingController < ApplicationController 2 | dont_kick :onboarding 3 | 4 | def index 5 | end 6 | 7 | def continue 8 | respond_to do |format| 9 | if update(params[:role]) 10 | format.html { redirect_to root_path, notice: 'Welcome!' } 11 | format.json { render json: { user: current_user.as_json_web_api }, status: :ok } 12 | else 13 | format.html { render action: 'index' } 14 | format.json { render json: { errors: 'invalid role supplied' }, status: :unprocessable_entity } 15 | end 16 | end 17 | end 18 | 19 | private 20 | 21 | def update(role) 22 | case role 23 | when 'user' 24 | current_user.update! role: 'user' 25 | return true 26 | when 'webmaster' 27 | current_user.update! role: 'webmaster' 28 | return true 29 | else 30 | return false 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /dummy/app/controllers/root_controller.rb: -------------------------------------------------------------------------------- 1 | class RootController < ApplicationController 2 | kick :onboarding 3 | 4 | def index 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /dummy/app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | # 3 | # 4 | # 5 | # any role 6 | # 7 | 8 | before_action :find_user, only: [:show, :impersonate] 9 | 10 | # GET /users 11 | def index 12 | @users = User.order(:id) 13 | @count = User.count 14 | end 15 | 16 | # GET /users/username 17 | # GET /users/id -> redirects to /users/username 18 | def show 19 | end 20 | 21 | # 22 | # 23 | # 24 | # webmaster role 25 | # 26 | 27 | dont_kick :webmaster, only: [:list, :new, :create, :impersonate] 28 | sudo_mode only: [:list, :new, :create, :impersonate] 29 | before_action :init_user, only: [:new, :create] 30 | 31 | # GET /users/list 32 | def list 33 | @users = User.order(:id) 34 | @count = User.count 35 | end 36 | 37 | # GET /users/new 38 | def new 39 | end 40 | 41 | # POST /users 42 | def create 43 | create_user_params = params.require(:user).permit! 44 | if @user.register(create_user_params) 45 | redirect_to new_user_path, notice: t('hello.business.registration.sign_up.success') 46 | else 47 | render action: :new 48 | end 49 | end 50 | 51 | # POST /users/1/impersonate 52 | def impersonate 53 | sign_in!(@user, 60.minutes.from_now, 60.minutes.from_now) 54 | 55 | redirect_to root_path, notice: t('hello.business.authentication.sign_in.success') 56 | end 57 | 58 | private 59 | 60 | def find_user 61 | @user = User.find_by_username!(params[:id]) 62 | rescue ActiveRecord::RecordNotFound 63 | redirect_to User.find_by_id!(params[:id]) # forces redirect to path with username if used id on URL 64 | end 65 | 66 | def init_user 67 | @user = Hello::Business::Registration::SignUp.new 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /dummy/app/helpers/hello_helper.rb: -------------------------------------------------------------------------------- 1 | module HelloHelper 2 | 3 | def nav_link_to(*args) 4 | args.pop 5 | link_to(*args) 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /dummy/app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hello-gem/hello/32cb8bcf086c14b5ba6a23a5cccfc001a7e326f7/dummy/app/mailers/.keep -------------------------------------------------------------------------------- /dummy/app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hello-gem/hello/32cb8bcf086c14b5ba6a23a5cccfc001a7e326f7/dummy/app/models/.keep -------------------------------------------------------------------------------- /dummy/app/models/address.rb: -------------------------------------------------------------------------------- 1 | # this model was created with the objective of testing account cancel 2 | class Address < ActiveRecord::Base 3 | belongs_to :user 4 | 5 | validates_presence_of :text, :user 6 | end 7 | -------------------------------------------------------------------------------- /dummy/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hello-gem/hello/32cb8bcf086c14b5ba6a23a5cccfc001a7e326f7/dummy/app/models/concerns/.keep -------------------------------------------------------------------------------- /dummy/app/models/concerns/user/authorization.rb: -------------------------------------------------------------------------------- 1 | module User::Authorization 2 | 3 | def role_is?(string) 4 | case string.to_s 5 | when 'guest' 6 | %w(guest).include?(role) 7 | when 'onboarding' 8 | %w(onboarding).include?(role) 9 | when 'user' 10 | %w(user webmaster).include?(role) 11 | when 'webmaster' 12 | %w(webmaster).include?(role) 13 | end 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /dummy/app/models/credential.rb: -------------------------------------------------------------------------------- 1 | class Credential < ApplicationRecord 2 | include Hello::RailsActiveRecord::Credential 3 | 4 | # this model was created with the objective of testing account cancel 5 | has_many :some_credential_data, dependent: :restrict_with_error 6 | end 7 | -------------------------------------------------------------------------------- /dummy/app/models/some_credential_datum.rb: -------------------------------------------------------------------------------- 1 | # this model was created with the objective of testing account cancel 2 | class SomeCredentialDatum < ActiveRecord::Base 3 | belongs_to :credential 4 | 5 | validates_presence_of :text, :credential 6 | end 7 | -------------------------------------------------------------------------------- /dummy/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | include Hello::RailsActiveRecord::User 3 | include Authorization 4 | 5 | def to_param 6 | username 7 | end 8 | 9 | validates_presence_of :name 10 | has_many :addresses, dependent: :restrict_with_error 11 | 12 | end 13 | -------------------------------------------------------------------------------- /dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> 6 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 | <%= render "/hello/shared/nav_pills" %> 23 | 24 |

Project name

25 |

Hello, <%= current_user && current_user.name || "Guest" %>!

26 | 27 | <%= render "/hello/shared/session_expiration" %> 28 |
29 | 30 | <%= render "/hello/shared/flash" %> 31 | 32 | 33 | 34 | <%= yield %> 35 | 36 | 37 | 38 |
39 | <%= debug session.to_hash %> 40 |

dummy-path: <%= request.fullpath %>

41 |

dummy-locale: <%= I18n.locale %>

42 | 43 |

dummy-accounts-<%= current_accesses.size %>

44 | <% current_accesses.each do |access_token| %> 45 |

dummy-account-<%= access_token.user.username %>

46 | <% end %> 47 | 48 | <% if signed_in? %> 49 |

dummy-logged-in-User#<%= current_user.id %>

50 |

dummy-logged-in-Access#<%= current_access.id %>

51 |

dummy-logged-in-role-<%= current_user.role %>

52 |

dummy-logged-in-<%= sudo_mode? ? 'with' : 'without' %>-sudo-mode

53 | <% else %> 54 |

dummy-logged-out

55 | <% end %> 56 |
57 | 58 |
59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /dummy/app/views/my_authenticated_area/index.html.erb: -------------------------------------------------------------------------------- 1 |

MyAuthenticatedArea#index

2 |

Find me in app/views/my_authenticated_area/index.html.erb

3 | -------------------------------------------------------------------------------- /dummy/app/views/my_non_webmaster_area/index.html.erb: -------------------------------------------------------------------------------- 1 |

MyNonMasterArea#index

2 |

Find me in app/views/my_non_master_area/index.html.erb

3 | -------------------------------------------------------------------------------- /dummy/app/views/onboarding/index.html.erb: -------------------------------------------------------------------------------- 1 |

Complete Onboarding Process

2 | 3 | <%= button_to "Continue As a User", onboarding_path(role: 'user') %> 4 | 5 | <%= button_to "Continue As a Webmaster", onboarding_path(role: 'webmaster') %> 6 | -------------------------------------------------------------------------------- /dummy/app/views/root/index.html.erb: -------------------------------------------------------------------------------- 1 |

Home Page

2 | 3 | <% if signed_in? %> 4 | 5 | <% else %> 6 | 7 | <% end %> 8 | -------------------------------------------------------------------------------- /dummy/app/views/users/index.html.erb: -------------------------------------------------------------------------------- 1 |

Listing Users (<%= @count %>)

2 | 3 | <% if current_user && current_user.role_is?(:webmaster) %> 4 |

<%= link_to "View User List as a Webmaster", list_users_path %>

5 | <% end %> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <% @users.each do |user| %> 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | <% end %> 28 | 29 |
UsernameNameRoleLocaleTime zone
<%= link_to user.username, user %><%= user.name %><%= user.role %><%= user.locale %><%= user.time_zone %>
30 | -------------------------------------------------------------------------------- /dummy/app/views/users/list.html.erb: -------------------------------------------------------------------------------- 1 |

Listing Users (<%= @count %>)

2 | 3 | <%= link_to('New User as a Webmaster', new_user_path) %> 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | <% @users.each do |user| %> 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | <% end %> 36 | 37 |
IDUsernameNameRoleLocaleTime zoneCredentialsAccesses
#<%= user.id %><%= link_to user.username, user %><%= user.name %><%= user.role %><%= user.locale %><%= user.time_zone %><%= user.credentials_count %><%= user.accesses_count %><%= button_to "Impersonate!", impersonate_user_path(user) if current_user != user %>
38 | -------------------------------------------------------------------------------- /dummy/app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @user, as: :user, url: users_path, html: {class: "form-horizontal", role: "form"} do |f| %> 2 | 3 | 4 | <% if @user.errors.any? %> 5 | <%= render '/hello/shared/errors', business: @user %> 6 | <% end %> 7 | 8 |
9 | <%= f.label :role, class: "col-sm-4 control-label" %> 10 |
11 | <%= f.text_field :role, class: "form-control", placeholder: "Role" %> 12 |
13 |
14 | 15 |
16 | <%= f.label :name, class: "col-sm-4 control-label" %> 17 |
18 | <%= f.text_field :name, class: "form-control", placeholder: "Name" %> 19 |
20 |
21 | 22 |
23 | <%= f.label :email, class: "col-sm-4 control-label" %> 24 |
25 | <%= f.text_field :email, class: "form-control", placeholder: "Email" %> 26 |
27 |
28 |
29 | <%= f.label :username, class: "col-sm-4 control-label" %> 30 |
31 | <%= f.text_field :username, class: "form-control", placeholder: "Username" %> 32 |
33 |
34 |
35 | <%= f.label :password, class: "col-sm-4 control-label" %> 36 |
37 | <%= f.password_field :password, class: "form-control", placeholder: "Password" %> 38 |
39 |
40 | 41 |
42 | <%= f.label :locale, class: "col-sm-4 control-label" %> 43 |
44 | <%= f.select :locale, hello_locale_select_options, {}, class: "form-control" %> 45 |
46 |
47 | 48 |
49 | <%= f.label :time_zone, class: "col-sm-4 control-label" %> 50 |
51 | <%= f.time_zone_select :time_zone, nil, {}, class: "form-control" %> 52 |
53 |
54 | 55 |
56 |
57 | <%= f.submit "Create User", class: "btn btn-primary" %> 58 |
59 |
60 | <% end %> 61 | -------------------------------------------------------------------------------- /dummy/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 5 | load Gem.bin_path('bundler', 'bundle') 6 | -------------------------------------------------------------------------------- /dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | APP_PATH = File.expand_path('../config/application', __dir__) 5 | require_relative '../config/boot' 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require_relative '../config/boot' 5 | require 'rake' 6 | Rake.application.run 7 | -------------------------------------------------------------------------------- /dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | # Pick the frameworks you want: 4 | require 'active_record/railtie' 5 | require 'action_controller/railtie' 6 | require 'action_mailer/railtie' 7 | require 'sprockets/railtie' 8 | # require "rails/test_unit/railtie" 9 | 10 | require 'rspec/core' 11 | Bundler.require(*Rails.groups) 12 | require 'hello-rails' 13 | 14 | module Dummy 15 | class Application < Rails::Application 16 | # Settings in config/environments/* take precedence over those specified here. 17 | # Application configuration should go into files in config/initializers 18 | # -- all .rb files in that directory are automatically loaded. 19 | 20 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 21 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 22 | # config.time_zone = 'Central Time (US & Canada)' 23 | 24 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 25 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 26 | # config.i18n.default_locale = :de 27 | 28 | # don't generate RSpec tests for views and helpers 29 | config.generators do |g| 30 | g.test_framework nil 31 | g.stylesheets = false 32 | g.javascripts = false 33 | g.helper = false 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__) 6 | -------------------------------------------------------------------------------- /dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | development: 7 | adapter: sqlite3 8 | database: db/development.sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | # Warning: The database defined as "test" will be erased and 13 | # re-generated from your development database when you run "rake". 14 | # Do not set this db to the same as development or production. 15 | test: 16 | adapter: sqlite3 17 | database: ":memory:" 18 | 19 | production: 20 | adapter: sqlite3 21 | database: db/production.sqlite3 22 | pool: 5 23 | timeout: 5000 24 | -------------------------------------------------------------------------------- /dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Dummy::Application.initialize! 6 | -------------------------------------------------------------------------------- /dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send. 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger. 20 | config.active_support.deprecation = :log 21 | 22 | # Raise an error on page load if there are pending migrations 23 | config.active_record.migration_error = :page_load 24 | 25 | # Debug mode disables concatenation and preprocessing of assets. 26 | # This option may cause significant delays in view rendering with a large 27 | # number of complex assets. 28 | config.assets.debug = true 29 | end 30 | -------------------------------------------------------------------------------- /dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Dummy::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static asset server for tests with Cache-Control for performance. 16 | # config.serve_static_files = true 17 | # config.public_file_server.enabled = true 18 | # config.static_cache_control = 'public, max-age=3600' 19 | 20 | # Show full error reports and disable caching. 21 | config.consider_all_requests_local = true 22 | config.action_controller.perform_caching = false 23 | 24 | # Raise exceptions instead of rendering exception templates. 25 | config.action_dispatch.show_exceptions = false 26 | 27 | # Disable request forgery protection in test environment. 28 | config.action_controller.allow_forgery_protection = false 29 | 30 | # Tell Action Mailer not to deliver emails to the real world. 31 | # The :test delivery method accumulates sent emails in the 32 | # ActionMailer::Base.deliveries array. 33 | config.action_mailer.delivery_method = :test 34 | 35 | # Print deprecation notices to the stderr. 36 | config.active_support.deprecation = :stderr 37 | end 38 | -------------------------------------------------------------------------------- /dummy/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 wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /dummy/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 += [:password] unless Rails.env.test? 5 | -------------------------------------------------------------------------------- /dummy/config/initializers/hello.rb: -------------------------------------------------------------------------------- 1 | Hello.configure do |config| 2 | config.mailer_sender = 'hello@example.com' 3 | 4 | config.email_presence = true 5 | config.email_regex = /\A[A-Z0-9._-]+@[A-Z0-9.-]+\.[A-Z0-9.-]+\z/i 6 | config.email_length = 4..250 7 | 8 | config.username_presence = true 9 | config.username_regex = /\A[a-z0-9_-]+\z/i 10 | config.username_length = 4..32 11 | 12 | config.password_presence = true 13 | config.password_regex = /\A[a-z0-9]+\z/i 14 | config.password_length = 4..250 15 | 16 | config.sign_up_disabled = false # {reason: "standard maintenance", until: "3PM"} 17 | config.sign_up_fields = %w(username email password time_zone locale name) 18 | 19 | config.locales = %w(en es fr pl pt-BR zh-CN) 20 | config.time_zones = Hello::TimeZones.all 21 | 22 | config.sudo_expires_in = 10.minutes 23 | 24 | end 25 | -------------------------------------------------------------------------------- /dummy/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 | -------------------------------------------------------------------------------- /dummy/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 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /dummy/config/initializers/missings_translations.rb: -------------------------------------------------------------------------------- 1 | 2 | I18n.exception_handler = lambda do |_exception, locale, key, _options| 3 | if key.to_s != 'i18n.plural.rule' 4 | fail "missing translation: #{locale} - #{key}" 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /dummy/config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure your secret_key_base is kept private 11 | # if you're sharing your code publicly. 12 | Dummy::Application.config.secret_key_base = '2c077500d55798a739945c97696367c3725ce90463131e1000379143f6732f2bcfaef023db841eea4b370f8599448b7a36d7baa389053d2207150120d0579eaf' 13 | -------------------------------------------------------------------------------- /dummy/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Dummy::Application.config.session_store :cookie_store, key: '_dummy_session' 4 | 5 | # Suggestion added by gem 'hello-rails' 6 | Rails.application.config.session_options[:expire_after] = 10.years 7 | -------------------------------------------------------------------------------- /dummy/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] if respond_to?(:wrap_parameters) 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 | -------------------------------------------------------------------------------- /dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | get 'middleware/bad_kitty', constraints: -> (request) { request.env['hello'].signed_in? } 3 | 4 | get 'my_areas/guest_page' 5 | get 'my_areas/authenticated_page' 6 | get 'my_areas/onboarding_page' 7 | get 'my_areas/user_page' 8 | get 'my_areas/webmaster_page' 9 | get 'my_areas/non_webmaster_page' 10 | 11 | get 'onboarding' => 'onboarding#index' 12 | post 'onboarding' => 'onboarding#continue' 13 | 14 | resources :users, only: [:index, :show, :new, :create] do 15 | collection do 16 | get 'list' 17 | end 18 | member do 19 | post 'impersonate' 20 | end 21 | end 22 | 23 | root to: 'root#index' 24 | mount Hello::Engine => '/hello' 25 | get '/hello/sign_out' => 'hello/authentication/sessions#sign_out' 26 | end 27 | -------------------------------------------------------------------------------- /dummy/db/migrate/20140502043302_create_credentials.hello.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from hello (originally 1) 2 | class CreateCredentials < ActiveRecord::Migration 3 | def change 4 | create_table :credentials do |t| 5 | t.references :user, index: true 6 | t.string :type 7 | 8 | t.string :email 9 | t.string :digest 10 | 11 | t.datetime :confirmed_at 12 | t.string :verifying_token_digest 13 | t.datetime :verifying_token_digested_at 14 | 15 | t.timestamps 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /dummy/db/migrate/20140502043303_create_accesses.hello.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from hello (originally 2) 2 | class CreateAccesses < ActiveRecord::Migration 3 | def change 4 | create_table :accesses do |t| 5 | t.references :user, index: true 6 | t.string :user_agent_string 7 | t.string :token 8 | t.string :ip 9 | t.datetime :expires_at, default: DateTime.new(2000, 1, 1) 10 | t.datetime :sudo_expires_at, default: DateTime.new(2000, 1, 1) 11 | 12 | t.timestamps 13 | t.index :token 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /dummy/db/migrate/20140502043304_create_users.hello.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from hello (originally 3) 2 | class CreateUsers < ActiveRecord::Migration 3 | def change 4 | create_table :users do |t| 5 | t.string :name 6 | 7 | t.string :role, default: 'onboarding' 8 | 9 | t.string :locale 10 | t.string :time_zone 11 | t.string :username 12 | 13 | t.integer :credentials_count, default: 0 14 | t.integer :accesses_count, default: 0 15 | 16 | t.timestamps 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /dummy/db/migrate/20140920192959_create_tables_to_test_deactivation_with_associations.rb: -------------------------------------------------------------------------------- 1 | class CreateTablesToTestDeactivationWithAssociations < ActiveRecord::Migration 2 | def change 3 | create_table :addresses do |t| 4 | t.references :user, index: true 5 | t.string :text 6 | 7 | t.timestamps 8 | end 9 | create_table :some_credential_data do |t| 10 | t.references :credential, index: true 11 | t.string :text 12 | 13 | t.timestamps 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /dummy/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hello-gem/hello/32cb8bcf086c14b5ba6a23a5cccfc001a7e326f7/dummy/lib/assets/.keep -------------------------------------------------------------------------------- /dummy/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hello-gem/hello/32cb8bcf086c14b5ba6a23a5cccfc001a7e326f7/dummy/log/.keep -------------------------------------------------------------------------------- /dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

The page you were looking for doesn't exist.

54 |

You may have mistyped the address or the page may have moved.

55 |
56 |

If you are the application owner check the logs for more information.

57 | 58 | 59 | -------------------------------------------------------------------------------- /dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

The change you wanted was rejected.

54 |

Maybe you tried to change something you didn't have access to.

55 |
56 |

If you are the application owner check the logs for more information.

57 | 58 | 59 | -------------------------------------------------------------------------------- /dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

We're sorry, but something went wrong.

54 |
55 |

If you are the application owner check the logs for more information.

56 | 57 | 58 | -------------------------------------------------------------------------------- /dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hello-gem/hello/32cb8bcf086c14b5ba6a23a5cccfc001a7e326f7/dummy/public/favicon.ico -------------------------------------------------------------------------------- /gemfiles/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Running Tests 3 | 4 | Note You do NOT need to run migrations to run tests 5 | 6 | ## With base gemfile 7 | 8 | ```bash 9 | bundle install 10 | bundle exec rspec 11 | ``` 12 | 13 | ## With different gemfiles 14 | 15 | 16 | ```bash 17 | BUNDLE_GEMFILE=gemfiles/rails-master.gemfile bundle install 18 | BUNDLE_GEMFILE=gemfiles/rails-master.gemfile bundle exec rspec 19 | ``` 20 | 21 | ```bash 22 | BUNDLE_GEMFILE=gemfiles/rails-5-0-stable.gemfile bundle install 23 | BUNDLE_GEMFILE=gemfiles/rails-5-0-stable.gemfile bundle exec rspec 24 | ``` 25 | 26 | 27 | ## Running the Dummy App on Development Mode 28 | 29 | Note: the dummy app uses SQLite3 30 | 31 | ```bash 32 | cd spec/dummy 33 | rake db:migrate 34 | rails server 35 | ``` 36 | -------------------------------------------------------------------------------- /gemfiles/rails-5-0-stable.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'rails', '~>5.0' 4 | 5 | gemspec :path => "../" 6 | -------------------------------------------------------------------------------- /gemfiles/rails-5-1-stable.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'rails', '~>5.1' 4 | 5 | gemspec :path => "../" 6 | -------------------------------------------------------------------------------- /gemfiles/rails-5-2-stable.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'rails', '~>5.2' 4 | 5 | gemspec :path => "../" 6 | -------------------------------------------------------------------------------- /gemfiles/rails-master.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "rails", github: "rails/rails", branch: "master" 4 | 5 | gemspec :path => "../" 6 | -------------------------------------------------------------------------------- /hello-rails.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | 3 | # Maintain your gem's version: 4 | require "hello/version" 5 | 6 | # Describe your gem and declare its dependencies: 7 | Gem::Specification.new do |s| 8 | s.name = "hello-rails" 9 | s.version = Hello::VERSION 10 | s.authors = ["James Pinto", "Michal Papis"] 11 | s.email = ["thejamespinto@gmail.com", "mpapis@gmail.com"] 12 | s.homepage = "http://github.com/hello-gem/hello" 13 | s.summary = "A Configurable Rails Authentication Engine" 14 | s.description = "Provides a set of valuable features for Registration, Authentication, Management and Internationalization." 15 | s.license = 'MIT' 16 | 17 | s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] 18 | 19 | s.test_files = Dir["spec/**/*"] 20 | 21 | s.add_runtime_dependency "before_actions", '~> 2.0' 22 | s.add_runtime_dependency "colorize", '~> 0.7' 23 | s.add_runtime_dependency "user_agent_parser", '~> 2.3' 24 | s.add_runtime_dependency "http_accept_language", '~> 2.0' 25 | s.add_runtime_dependency "rails-i18n", '~> 5.0' 26 | 27 | s.add_development_dependency "sqlite3", '~> 1.3' 28 | s.add_development_dependency 'rspec-rails', '~> 3.4' 29 | s.add_development_dependency 'capybara', '~> 2.6' 30 | s.add_development_dependency 'email_spec', '~> 2.0' 31 | s.add_development_dependency 'factory_bot_rails', '~> 4.11' 32 | s.add_development_dependency 'faker', '~> 1.6' 33 | s.add_development_dependency 'codeclimate-test-reporter', '~> 0' 34 | s.add_development_dependency 'bdd', '~> 0.1' 35 | s.add_development_dependency 'bcrypt', '~> 3.1' 36 | s.add_development_dependency 'rubocop', '~> 0' 37 | s.add_development_dependency 'rspec_junit_formatter', '~> 0.4' 38 | 39 | # save_and_open_page 40 | s.add_development_dependency 'launchy', '~> 2.4' 41 | end 42 | -------------------------------------------------------------------------------- /lib/generators/hello/concerns/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Explain the generator 3 | 4 | Example: 5 | rails generate hello:concerns 6 | 7 | This will create: 8 | what/will/it/create 9 | -------------------------------------------------------------------------------- /lib/generators/hello/concerns/concerns_generator.rb: -------------------------------------------------------------------------------- 1 | class Hello::ConcernsGenerator < Rails::Generators::Base 2 | # source_root File.expand_path('../templates', __FILE__) 3 | source_root File.expand_path('../../../../../', __FILE__) 4 | 5 | def copy_the_controller_concerns 6 | directory 'app/controllers/hello/concerns' 7 | end 8 | 9 | protected 10 | end 11 | -------------------------------------------------------------------------------- /lib/generators/hello/install/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Explain the generator 3 | 4 | Example: 5 | rails generate hello:install 6 | 7 | This will create: 8 | what/will/it/create 9 | -------------------------------------------------------------------------------- /lib/generators/hello/install/templates/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hello 5 | <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> 6 | <%= javascript_include_tag "application", "data-turbolinks-track" => true %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 | <%= render "/hello/shared/nav_pills" %> 21 | 22 |

Project name

23 |

Hello, <%= current_user && current_user.name || "Guest" %>!

24 | 25 | <%= render "/hello/shared/session_expiration" %> 26 |
27 | 28 | <%= render "/hello/shared/flash" %> 29 | 30 | <%= yield %> 31 | 32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /lib/generators/hello/install/templates/hello_helper.rb: -------------------------------------------------------------------------------- 1 | module HelloHelper 2 | 3 | # Please take one of two actions 4 | # 1. Add gem 'nav_lynx' to your Gemfile and remove this file 5 | # 2. edit partial 'views/hello/shared/_settings' and replace `nav_link_to` for `link_to` 6 | def nav_link_to(*args) 7 | if defined?(NavLYNX) 8 | super 9 | else 10 | puts "#{__FILE__}" 11 | args.pop 12 | link_to(*args) 13 | end 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /lib/generators/hello/install/templates/initializer.rb: -------------------------------------------------------------------------------- 1 | Hello.configure do |config| 2 | config.mailer_sender = 'hello@example.com' 3 | 4 | config.email_presence = true 5 | config.email_regex = /\A[A-Z0-9._-]+@[A-Z0-9.-]+\.[A-Z0-9.-]+\z/i 6 | config.email_length = 4..250 7 | 8 | config.username_presence = true 9 | config.username_regex = /\A[a-z0-9_-]+\z/i 10 | config.username_length = 4..32 11 | 12 | config.password_presence = true 13 | config.password_regex = /\A[a-z0-9]+\z/i 14 | config.password_length = 4..250 15 | 16 | config.sign_up_disabled = false # {reason: "standard maintenance", until: "3PM"} 17 | config.sign_up_fields = %w(username email password time_zone locale name) 18 | 19 | config.locales = %w(en es fr pl pt-BR zh-CN) 20 | config.time_zones = Hello::TimeZones.all 21 | 22 | config.sudo_expires_in = 10.minutes 23 | 24 | end 25 | -------------------------------------------------------------------------------- /lib/generators/hello/install/templates/models/concerns/user/authorization.rb: -------------------------------------------------------------------------------- 1 | module User::Authorization 2 | 3 | def role_is?(string) 4 | case string.to_s 5 | when 'guest' 6 | %w(guest).include?(role) 7 | when 'onboarding' 8 | %w(onboarding).include?(role) 9 | when 'user' 10 | %w(user webmaster).include?(role) 11 | when 'webmaster' 12 | %w(webmaster).include?(role) 13 | end 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /lib/generators/hello/install/templates/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | include Hello::RailsActiveRecord::User 3 | include Authorization 4 | 5 | def to_param 6 | username 7 | end 8 | 9 | validates_presence_of :name 10 | end 11 | -------------------------------------------------------------------------------- /lib/generators/hello/install/templates/onboarding/index.html.erb: -------------------------------------------------------------------------------- 1 |

Complete Onboarding Process

2 | 3 | <%= button_to "Continue As a User", onboarding_path(role: 'user') %> 4 | 5 | <%= button_to "Continue As a Webmaster", onboarding_path(role: 'webmaster') %> 6 | -------------------------------------------------------------------------------- /lib/generators/hello/install/templates/onboarding/onboarding_controller.rb: -------------------------------------------------------------------------------- 1 | class OnboardingController < ApplicationController 2 | dont_kick :onboarding 3 | 4 | def index 5 | end 6 | 7 | def continue 8 | respond_to do |format| 9 | if update(params[:role]) 10 | format.html { redirect_to root_path, notice: 'Welcome!' } 11 | format.json { render json: { user: current_user.as_json_web_api }, status: :ok } 12 | else 13 | format.html { render action: 'index' } 14 | format.json { render json: { errors: 'invalid role supplied' }, status: :unprocessable_entity } 15 | end 16 | end 17 | end 18 | 19 | private 20 | 21 | def update(role) 22 | case role 23 | when 'user' 24 | current_user.update! role: 'user' 25 | return true 26 | when 'webmaster' 27 | current_user.update! role: 'webmaster' 28 | return true 29 | else 30 | return false 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/generators/hello/install/templates/root/index.html.erb: -------------------------------------------------------------------------------- 1 |

Home Page

2 | 3 | <% if signed_in? %> 4 | 5 | <% else %> 6 | 7 | <% end %> 8 | -------------------------------------------------------------------------------- /lib/generators/hello/install/templates/root/root_controller.rb: -------------------------------------------------------------------------------- 1 | class RootController < ApplicationController 2 | kick :onboarding 3 | 4 | def index 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/generators/hello/locales/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | This generators copy available locales. 3 | 4 | Example: 5 | rails generate hello:locales en 6 | 7 | This will create: 8 | config/locales/hello.en.yml 9 | 10 | rails generate hello:locales 11 | 12 | Shows detailed help 13 | -------------------------------------------------------------------------------- /lib/generators/hello/locales/locales_generator.rb: -------------------------------------------------------------------------------- 1 | require 'hello' 2 | 3 | class Hello::LocalesGenerator < Rails::Generators::Base 4 | source_root Hello.root 5 | 6 | argument :selected_locales, type: :array, default: [], banner: 'lang1 [lang2] [lang3]' 7 | 8 | def copy_locales 9 | case 10 | when selected_locales == [] 11 | puts_usage 12 | puts 13 | when selected_locales == ['all'] 14 | copy_them(available_locales) 15 | when missing_locales.any? 16 | puts_usage 17 | puts_matching 18 | puts_missing 19 | puts 20 | else 21 | copy_them(matching_locales) 22 | end 23 | end 24 | 25 | 26 | protected 27 | 28 | def copy_them(locales) 29 | locales.sort.each do |l| 30 | copy_file "config/locales/hello.#{l}.yml" 31 | end 32 | end 33 | 34 | def puts_usage 35 | puts 36 | puts "Usage:".light_yellow 37 | puts " rails generate hello:locales all" 38 | puts " rails generate hello:locales #{available_locales.sort.join(' ')}" 39 | end 40 | 41 | def puts_missing 42 | puts 43 | puts "Missing:".light_red 44 | puts " rails generate hello:locales #{missing_locales.sort.join(' ')}" 45 | end 46 | 47 | def puts_matching 48 | puts 49 | puts "Matching:".light_green 50 | puts " rails generate hello:locales #{matching_locales.sort.join(' ')}" 51 | end 52 | 53 | def matching_locales 54 | selected_locales & available_locales 55 | end 56 | 57 | def missing_locales 58 | selected_locales - available_locales 59 | end 60 | 61 | def available_locales 62 | Dir[Hello.root.join('config', 'locales', '**', '*.yml')].map { |s| s.split('.')[-2] } 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/generators/hello/users/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Suggested scaffold for users 3 | 4 | Example: 5 | rails generate hello:users 6 | 7 | This will create: 8 | app/controller/users_controller.rb 9 | app/views/users/[...] 10 | 11 | -------------------------------------------------------------------------------- /lib/generators/hello/users/templates/app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | # 3 | # 4 | # 5 | # any role 6 | # 7 | 8 | before_action :find_user, only: [:show, :impersonate] 9 | 10 | # GET /users 11 | def index 12 | @users = User.order(:id) 13 | @count = User.count 14 | end 15 | 16 | # GET /users/username 17 | # GET /users/id -> redirects to /users/username 18 | def show 19 | end 20 | 21 | # 22 | # 23 | # 24 | # webmaster role 25 | # 26 | 27 | dont_kick :webmaster, only: [:list, :new, :create, :impersonate] 28 | sudo_mode only: [:list, :new, :create, :impersonate] 29 | before_action :init_user, only: [:new, :create] 30 | 31 | # GET /users/list 32 | def list 33 | @users = User.order(:id) 34 | @count = User.count 35 | end 36 | 37 | # GET /users/new 38 | def new 39 | end 40 | 41 | # POST /users 42 | def create 43 | create_user_params = params.require(:user).permit! 44 | if @user.register(create_user_params) 45 | redirect_to new_user_path, notice: t('hello.business.registration.sign_up.success') 46 | else 47 | render action: :new 48 | end 49 | end 50 | 51 | # POST /users/1/impersonate 52 | def impersonate 53 | sign_in!(@user, 60.minutes.from_now, 60.minutes.from_now) 54 | 55 | redirect_to root_path, notice: t('hello.business.authentication.sign_in.success') 56 | end 57 | 58 | private 59 | 60 | def find_user 61 | @user = User.find_by_username!(params[:id]) 62 | rescue ActiveRecord::RecordNotFound 63 | redirect_to User.find_by_id!(params[:id]) # forces redirect to path with username if used id on URL 64 | end 65 | 66 | def init_user 67 | @user = Hello::ClassicSignUpEntity.new 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/generators/hello/users/templates/app/views/users/index.html.erb: -------------------------------------------------------------------------------- 1 |

Listing Users (<%= @count %>)

2 | 3 | <% if current_user && current_user.role_is?(:webmaster) %> 4 |

<%= link_to "View User List as a Webmaster", list_users_path %>

5 | <% end %> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <% @users.each do |user| %> 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | <% end %> 28 | 29 |
UsernameNameRoleLocaleTime zone
<%= link_to user.username, user %><%= user.name %><%= user.role %><%= user.locale %><%= user.time_zone %>
30 | -------------------------------------------------------------------------------- /lib/generators/hello/users/templates/app/views/users/list.html.erb: -------------------------------------------------------------------------------- 1 |

Listing Users (<%= @count %>)

2 | 3 | <%= link_to('New User as a Webmaster', new_user_path) %> 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | <% @users.each do |user| %> 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | <% end %> 36 | 37 |
IDUsernameNameRoleLocaleTime zoneCredentialsAccesses
#<%= user.id %><%= link_to user.username, user %><%= user.name %><%= user.role %><%= user.locale %><%= user.time_zone %><%= user.credentials_count %><%= user.accesses_count %><%= button_to "Impersonate!", impersonate_user_path(user) if current_user != user %>
38 | -------------------------------------------------------------------------------- /lib/generators/hello/users/templates/app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @user, as: :user, url: users_path, html: {class: "form-horizontal", role: "form"} do |f| %> 2 | 3 | 4 | <% if @user.errors.any? %> 5 | <%= render '/hello/shared/errors', business: @user %> 6 | <% end %> 7 | 8 |
9 | <%= f.label :role, class: "col-sm-4 control-label" %> 10 |
11 | <%= f.text_field :role, class: "form-control", placeholder: "Role" %> 12 |
13 |
14 | 15 |
16 | <%= f.label :name, class: "col-sm-4 control-label" %> 17 |
18 | <%= f.text_field :name, class: "form-control", placeholder: "Name" %> 19 |
20 |
21 | 22 |
23 | <%= f.label :email, class: "col-sm-4 control-label" %> 24 |
25 | <%= f.text_field :email, class: "form-control", placeholder: "Email" %> 26 |
27 |
28 |
29 | <%= f.label :username, class: "col-sm-4 control-label" %> 30 |
31 | <%= f.text_field :username, class: "form-control", placeholder: "Username" %> 32 |
33 |
34 |
35 | <%= f.label :password, class: "col-sm-4 control-label" %> 36 |
37 | <%= f.password_field :password, class: "form-control", placeholder: "Password" %> 38 |
39 |
40 | 41 |
42 | <%= f.label :locale, class: "col-sm-4 control-label" %> 43 |
44 | <%= f.select :locale, hello_locale_select_options, {}, class: "form-control" %> 45 |
46 |
47 | 48 |
49 | <%= f.label :time_zone, class: "col-sm-4 control-label" %> 50 |
51 | <%= f.time_zone_select :time_zone, nil, {}, class: "form-control" %> 52 |
53 |
54 | 55 |
56 |
57 | <%= f.submit "Create User", class: "btn btn-primary" %> 58 |
59 |
60 | <% end %> 61 | -------------------------------------------------------------------------------- /lib/generators/hello/users/users_generator.rb: -------------------------------------------------------------------------------- 1 | class Hello::UsersGenerator < Rails::Generators::Base 2 | source_root File.expand_path('../templates', __FILE__) 3 | 4 | def copy_the_files 5 | directory 'app' 6 | end 7 | 8 | def append_the_routes 9 | route %( 10 | resources :users, only: [:index, :show, :new, :create] do 11 | collection do 12 | get 'list' 13 | end 14 | member do 15 | post 'impersonate' 16 | end 17 | end 18 | ) 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /lib/generators/hello/views/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Explain the generator 3 | 4 | Example: 5 | rails generate hello:views 6 | 7 | This will create: 8 | what/will/it/create 9 | -------------------------------------------------------------------------------- /lib/generators/hello/views/views_generator.rb: -------------------------------------------------------------------------------- 1 | require 'hello' 2 | 3 | class Hello::ViewsGenerator < Rails::Generators::Base 4 | source_root Hello.root 5 | 6 | def copy_the_views 7 | directory 'app/views/hello' 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/hello-rails.rb: -------------------------------------------------------------------------------- 1 | require 'hello' 2 | -------------------------------------------------------------------------------- /lib/hello.rb: -------------------------------------------------------------------------------- 1 | require 'colorize' 2 | 3 | require_relative 'hello/engine' 4 | require_relative 'hello/configuration' 5 | 6 | module Hello 7 | def self.root 8 | @root ||= Pathname(File.dirname(__FILE__)).join('..') 9 | end 10 | 11 | def self.warning(s2) 12 | s1 = 'HELLO DEV WARNING:'.black.on_yellow.bold 13 | puts "#{s1} #{s2.yellow}" 14 | end 15 | 16 | autoload :Business, 'hello/business' 17 | autoload :TimeZones, 'hello/time_zones' 18 | autoload :Encryptors, 'hello/encryptors' 19 | autoload :RailsActiveRecord, 'hello/rails_active_record' 20 | autoload :RailsController, 'hello/rails_controller' 21 | autoload :RailsHelper, 'hello/rails_helper' 22 | autoload :Errors, 'hello/errors' 23 | autoload :Utils, 'hello/utils' 24 | autoload :Middleware, 'hello/middleware' 25 | autoload :Locales, 'hello/locales' 26 | autoload :RequestManager, 'hello/request_manager' 27 | end 28 | 29 | if defined? Rails 30 | require 'before_actions' 31 | require 'http_accept_language' 32 | require 'rails-i18n' 33 | 34 | ActionView::Base.include Hello::RailsHelper 35 | ActionController::Base.include Hello::RailsController 36 | end 37 | -------------------------------------------------------------------------------- /lib/hello/CHANGES.md: -------------------------------------------------------------------------------- 1 | v 0.1.2 In Progress 2 | ----------- 3 | 4 | 5 | 6 | v 0.1.1 7 | ----------- 8 | 9 | - Admin Impersonation 10 | - Forgot Password and Welcome mailers 11 | - Confirm Email 12 | - I18n 13 | - JSON responses for sign_up, sign_in and sign_out 14 | - Reviewed configuration pattern 15 | 16 | v 0.1.0 17 | ----------- 18 | 19 | - BCrypt as suggested cryptography strategy 20 | - generator "hello:from_devise" 21 | - generator "hello:views" 22 | - multiple devices 23 | - sudo mode 24 | - configurable classic forgot password 25 | - configurable classic sign in 26 | - configurable classic sign up 27 | -------------------------------------------------------------------------------- /lib/hello/business.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | extend ActiveSupport::Autoload 4 | 5 | # Business are used to encapsulate behaviors and translations 6 | 7 | # please keep alphabetical order 8 | 9 | autoload :Base 10 | 11 | module Authentication 12 | extend ActiveSupport::Autoload 13 | 14 | autoload :SignIn 15 | autoload :SignOut 16 | autoload :SudoModeAuthentication 17 | autoload :SudoModeExpiration 18 | end 19 | 20 | module Internationalization 21 | extend ActiveSupport::Autoload 22 | 23 | autoload :UpdateLocale 24 | end 25 | 26 | module Management 27 | extend ActiveSupport::Autoload 28 | 29 | autoload :AddEmail 30 | autoload :CancelAccount 31 | autoload :ConfirmEmail 32 | autoload :ForgotPassword 33 | autoload :RemoveEmail 34 | autoload :ResetPassword 35 | autoload :SendConfirmationEmail 36 | autoload :UnlinkAccess 37 | autoload :UpdateProfile 38 | end 39 | 40 | module Registration 41 | extend ActiveSupport::Autoload 42 | 43 | autoload :SignUp 44 | end 45 | 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/hello/business/authentication/sign_out.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Authentication 4 | class SignOut < Base 5 | end 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/hello/business/authentication/sudo_mode_authentication.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Authentication 4 | class SudoModeAuthentication < Base 5 | attr_reader :access 6 | 7 | def initialize(access) 8 | @access = access 9 | end 10 | 11 | def authenticate!(password) 12 | if access.user.password_is?(password) 13 | access.update!(sudo_expires_at: sudo_expires_at) 14 | end 15 | end 16 | 17 | private 18 | 19 | def sudo_expires_at 20 | Hello.configuration.sudo_expires_in.from_now 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/hello/business/authentication/sudo_mode_expiration.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Authentication 4 | class SudoModeExpiration < Base 5 | attr_reader :access 6 | 7 | def initialize(access) 8 | @access = access 9 | end 10 | 11 | def expire! 12 | access.sudo_expire! 13 | end 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/hello/business/base.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | class Base 4 | include ActiveModel::Model 5 | 6 | def errors 7 | @errors ||= ActiveModel::Errors.new(self) 8 | end 9 | 10 | def error_message(extra = {}) 11 | t('error', { count: errors.count }.merge(extra)) 12 | end 13 | 14 | def alert_message(extra = {}) 15 | t('alert', extra) 16 | end 17 | 18 | def success_message(extra = {}) 19 | t('success', extra) 20 | end 21 | 22 | def t(key, extra = {}) 23 | I18n.t("#{i18n_scope}.#{key}", extra) 24 | end 25 | 26 | protected 27 | 28 | def i18n_scope 29 | self.class.name.underscore.tr('/', '.') 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/hello/business/internationalization/update_locale.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Internationalization 4 | class UpdateLocale < Base 5 | def initialize(locale) 6 | @locale = locale 7 | end 8 | 9 | def locale 10 | locale_if_available || I18n.default_locale 11 | end 12 | 13 | def success_message(_extra = {}) 14 | super(locale_name: current_locale_name) 15 | end 16 | 17 | private 18 | 19 | def locale_if_available 20 | ([@locale] & locales).first 21 | end 22 | 23 | def locales 24 | Hello.configuration.locales 25 | end 26 | 27 | def current_locale_name 28 | I18n.t('hello.locale_name') 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/hello/business/management/add_email.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Management 4 | class AddEmail < Base 5 | def initialize(email_credential) 6 | @email_credential = email_credential 7 | end 8 | 9 | def success_message 10 | super(email: @email_credential.email) 11 | end 12 | 13 | def error_message 14 | @email_credential.errors.full_messages.first 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/hello/business/management/cancel_account.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Management 4 | class CancelAccount < Base 5 | 6 | def initialize(user) 7 | @user = user 8 | end 9 | 10 | def cancel_account 11 | @user.destroy! 12 | rescue ActiveRecord::RecordNotDestroyed => invalid 13 | false 14 | end 15 | 16 | def info_message 17 | t('info') 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/hello/business/management/confirm_email.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Management 4 | class ConfirmEmail < Base 5 | attr_reader :credential 6 | 7 | def initialize(credential) 8 | @credential = credential 9 | end 10 | 11 | def validate_token(unencrypted_token) 12 | # puts "validate_token('#{unencrypted_token}')".blue 13 | return false unless found_credential? 14 | return true if credential.verifying_token_is?(unencrypted_token) 15 | @credential = nil 16 | end 17 | 18 | def confirm_with_token(token) 19 | validate_token(token) && confirm_email! 20 | end 21 | 22 | def confirm_email! 23 | credential.confirm_email! 24 | end 25 | 26 | def found_credential? 27 | !!credential 28 | end 29 | 30 | def success_message 31 | super(email: credential.email) 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/hello/business/management/forgot_password.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Management 4 | class ForgotPassword < Base 5 | attr_accessor :login 6 | attr_reader :user 7 | 8 | def initialize(attrs = nil) 9 | if attrs 10 | @login = attrs[:login] 11 | @user = find_user 12 | end 13 | end 14 | 15 | def reset 16 | if user.present? 17 | true 18 | else 19 | errors.add(:login, 'was not found') 20 | false 21 | end 22 | end 23 | 24 | def email? 25 | login.to_s.include? '@' 26 | end 27 | 28 | def success_message(_extra = {}) 29 | super(login: @login) 30 | end 31 | 32 | private 33 | 34 | # initialize helpers 35 | 36 | def find_user 37 | if email? 38 | credential = ::EmailCredential.find_by_email(login) 39 | credential.user 40 | else 41 | ::User.where(username: login).first 42 | end 43 | end 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/hello/business/management/remove_email.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Management 4 | class RemoveEmail < Base 5 | def initialize(email_credential) 6 | @email_credential = email_credential 7 | end 8 | 9 | def success_message 10 | super(email: @email_credential.email) 11 | end 12 | 13 | def error_message 14 | @email_credential.errors.full_messages.first 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/hello/business/management/reset_password.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Management 4 | class ResetPassword < Base 5 | attr_reader :password_credential 6 | 7 | def initialize(password_credential) 8 | @password_credential = password_credential 9 | end 10 | 11 | def update_password(plain_text_password) 12 | if @password_credential.update(password: plain_text_password) 13 | @password_credential.reset_verifying_token! 14 | return true 15 | else 16 | merge_errors_to_self 17 | return false 18 | end 19 | end 20 | 21 | def user 22 | password_credential.user 23 | end 24 | 25 | private 26 | 27 | def merge_errors_to_self 28 | hash = @password_credential.errors.to_hash 29 | hash.each { |k, v| v.each { |v1| errors.add(k, v1) } } 30 | end 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/hello/business/management/send_confirmation_email.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Management 4 | class SendConfirmationEmail < Base 5 | attr_reader :controller, :email_credential 6 | 7 | def initialize(controller, email_credential) 8 | @controller = controller 9 | @email_credential = email_credential 10 | end 11 | 12 | def deliver 13 | token = email_credential.reset_verifying_token! 14 | check_token!(token) 15 | url = controller.confirm_email_url(email_credential, token) 16 | mail = Hello::Mailer.confirm_email(email, user, url) 17 | mail.deliver 18 | end 19 | 20 | def success_message(_extra = {}) 21 | super(email: email_credential.email) 22 | end 23 | 24 | private 25 | 26 | def email 27 | email_credential.email 28 | end 29 | 30 | def user 31 | email_credential.user 32 | end 33 | 34 | def check_token!(unencrypted_token) 35 | fail 'no match' unless email_credential.verifying_token_is?(unencrypted_token) 36 | end 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/hello/business/management/unlink_access.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Management 4 | class UnlinkAccess < Base 5 | end 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/hello/business/management/update_profile.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Management 4 | class UpdateProfile < Base 5 | def initialize(user) 6 | @user = user 7 | self.class.send :attr_accessor, *permitted_column_names 8 | end 9 | 10 | def update(attrs) 11 | # puts "update(#{attrs})".blue 12 | clear_attrs(attrs).each do |k, v| 13 | # puts "@user.send('#{k}=', '#{v}')".blue 14 | @user.send("#{k}=", v) 15 | end 16 | @user.save 17 | end 18 | 19 | # def update(attrs) 20 | # @user.update(clear_attrs(attrs)) 21 | # end 22 | 23 | def errors 24 | @user.errors 25 | end 26 | 27 | private 28 | 29 | def clear_attrs(attrs) 30 | attrs.slice(*permitted_column_names) 31 | end 32 | 33 | def permitted_column_names 34 | ignore_columns = %w(id created_at updated_at role) 35 | the_columns = ::User.column_names 36 | the_columns -= ignore_columns 37 | the_columns.reject! { |column| column.ends_with? '_count' } 38 | the_columns.reject! { |column| column.starts_with? 'password_' } 39 | the_columns 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/hello/business/registration/sign_up.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Business 3 | module Registration 4 | class SignUp < Base 5 | attr_reader :user 6 | 7 | def initialize 8 | init_user 9 | end 10 | 11 | def register(attrs) 12 | init_user(attrs) 13 | 14 | user.save.tap do 15 | merge_errors_for(@email_credential) 16 | merge_errors_for(@password_credential) 17 | errors.delete(:email_credentials) 18 | errors.delete(:password_credentials) 19 | end 20 | end 21 | 22 | # errors.added? DOES NOT WORK when the validation was given a custom message :) 23 | def email_taken? 24 | @email_credential && @email_credential.errors.added?(:email, :taken) 25 | end 26 | 27 | # errors.added? DOES NOT WORK when the validation was given a custom message :) 28 | def username_taken? 29 | errors.added?(:username, :taken) 30 | end 31 | 32 | def method_missing(method_name, *args, &block) 33 | user.send(method_name) 34 | end 35 | 36 | private 37 | 38 | def init_user(attrs={}) 39 | attrs.reverse_merge!(defaults) 40 | @user = User.new(attrs) 41 | @email_credential = user.email_credentials.first 42 | @password_credential = user.password_credentials.first 43 | @errors = user.errors 44 | end 45 | 46 | def defaults 47 | { 48 | locale: I18n.locale.to_s, 49 | time_zone: Time.zone.name, 50 | email: '', 51 | password: '', 52 | } 53 | end 54 | 55 | def merge_errors_for(model) 56 | if model 57 | model.valid? 58 | model.errors.each do |k, v| 59 | errors.add(k, v) 60 | end 61 | end 62 | end 63 | 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/hello/configuration.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | # invoked from config/initializers/hello.rb 3 | def self.configure 4 | yield(configuration) 5 | end 6 | 7 | # invoked internally 8 | def self.configuration 9 | Rails.configuration.hello ||= ActiveSupport::OrderedOptions.new 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /lib/hello/encryptors.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Encryptors 3 | autoload :Simple, 'hello/encryptors/simple' 4 | autoload :Complex, 'hello/encryptors/complex' 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/hello/encryptors/complex.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Encryptors 3 | class Complex < Simple 4 | def initialize(cost=nil) 5 | require 'bcrypt' 6 | cost ||= Rails.env.test? ? 1 : 10 7 | BCrypt::Engine.cost = cost 8 | rescue LoadError 9 | s = "your Gemfile needs: gem 'bcrypt'" 10 | puts [s.red, s.yellow, s.green] 11 | raise 12 | end 13 | 14 | def encrypt(string) 15 | BCrypt::Password.create(string).to_s 16 | end 17 | 18 | def match(string, digest) 19 | BCrypt::Password.new(digest) == string 20 | rescue BCrypt::Errors::InvalidHash 21 | false 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/hello/encryptors/simple.rb: -------------------------------------------------------------------------------- 1 | require 'singleton' 2 | 3 | module Hello 4 | module Encryptors 5 | class Simple 6 | include Singleton 7 | 8 | # probability = 1 / ((8*2) ** (8*2)) 9 | def single(complexity = 8) 10 | SecureRandom.hex(complexity) 11 | end 12 | 13 | def pair(complexity = 8) 14 | s = single(complexity) 15 | [s, encrypt(s)] 16 | end 17 | 18 | def encrypt(s) 19 | Digest::MD5.hexdigest(s) 20 | end 21 | 22 | def match(string, token) 23 | encrypt(string) == token 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/hello/engine.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | class Engine < Rails::Engine 3 | isolate_namespace Hello 4 | 5 | config.hello = nil 6 | 7 | initializer 'hello.filter' do |app| 8 | app.config.filter_parameters += [:password, :token] 9 | end 10 | 11 | initializer 'hello.add_middleware' do |app| 12 | app.config.app_middleware.use Hello::Middleware 13 | end 14 | 15 | config.generators do |g| 16 | g.test_framework :rspec, view_specs: false, 17 | controller_specs: false 18 | # g.fixture_replacement :factory_girl, :dir => 'spec/factories' 19 | g.assets false 20 | g.helper false 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/hello/errors.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Errors 3 | class Error < StandardError 4 | end 5 | 6 | class JsonNotSupported < Error 7 | def message 8 | "add your locale as a 'param' or 'header' instead" 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/hello/locales.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Locales 3 | def self.all 4 | @all ||= YAML.load(File.read(Hello.root.join('lib/data/locales.yml'))) 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/hello/middleware.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | class Middleware 3 | def initialize(app) 4 | @app = app 5 | end 6 | 7 | def call(env) 8 | request = ActionDispatch::Request.new(env) 9 | env['hello'] = Hello::RequestManager.create(request) 10 | response = @app.call(env) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/hello/rails_active_record.rb: -------------------------------------------------------------------------------- 1 | # Cannot be named ActiveRecord or will compete with ::ActiveRecord 2 | module Hello 3 | module RailsActiveRecord 4 | autoload :User, 'hello/rails_active_record/user' 5 | autoload :Credential, 'hello/rails_active_record/credential' 6 | autoload :EmailCredential, 'hello/rails_active_record/email_credential' 7 | autoload :PasswordCredential, 'hello/rails_active_record/password_credential' 8 | autoload :Access, 'hello/rails_active_record/access' 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/hello/rails_active_record/access.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RailsActiveRecord 3 | module Access 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | # ASSOCIATIONS 8 | belongs_to :user, counter_cache: true 9 | 10 | # VALIDATIONS 11 | validates_presence_of :user, :expires_at, :user_agent_string, :token 12 | validates_uniqueness_of :token 13 | 14 | before_validation on: :create do 15 | self.token = "#{user_id}-#{Hello::Encryptors::Simple.instance.single(16)}" 16 | end 17 | end 18 | 19 | # CUSTOM METHODS 20 | 21 | def keep_alive! 22 | renew! if expiring? 23 | end 24 | 25 | def full_device_name 26 | Hello::Utils::DeviceName.instance.parse(user_agent_string) 27 | end 28 | 29 | def active_token_or_destroy 30 | if expires_at.future? 31 | token 32 | else 33 | destroy && (return nil) 34 | end 35 | end 36 | 37 | def as_json_web_api 38 | as_json.slice(*%w(expires_at token user_id)).merge({ user: user.as_json_web_api }) 39 | end 40 | 41 | def sudo_expire! 42 | update! sudo_expires_at: 1.second.ago 43 | end 44 | 45 | private 46 | 47 | def expiring? 48 | expires_at < 20.minutes.from_now 49 | end 50 | 51 | def renew! 52 | update_attributes!(expires_at: 30.minutes.from_now) 53 | end 54 | 55 | module ClassMethods 56 | def destroy_all_expired 57 | where('expires_at < ?', Time.now).destroy_all 58 | true 59 | end 60 | 61 | def cached_destroy_all_expired 62 | @@destroy_all_expired ||= destroy_all_expired 63 | end 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/hello/rails_active_record/credential.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RailsActiveRecord 3 | module Credential 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | 8 | # ASSOCIATIONS 9 | belongs_to :user, counter_cache: true 10 | 11 | # VALIDATIONS 12 | validates_presence_of :user, on: :update 13 | 14 | after_destroy do 15 | if destroyed_by_association.nil? 16 | if user.invalid? 17 | user.errors.each { |k, v| errors.add(k, v) if k.to_s.include?('credentials')} 18 | fail ActiveRecord::Rollback 19 | end 20 | end 21 | end 22 | end 23 | 24 | # CUSTOM METHODS 25 | 26 | def first_error_message 27 | errors.messages.values.flatten.first if errors.any? 28 | end 29 | 30 | # verifying token 31 | 32 | def verifying_token_is?(unencrypted_token) 33 | digest = simple_encryptor.encrypt(unencrypted_token) 34 | verifying_token_digest == digest 35 | end 36 | 37 | def reset_verifying_token! 38 | uuid, digest = simple_encryptor.pair 39 | update!(verifying_token_digest: digest, verifying_token_digested_at: 1.second.ago) 40 | uuid 41 | end 42 | 43 | protected 44 | 45 | def complex_encryptor 46 | Hello::Encryptors::Complex.instance 47 | end 48 | 49 | def simple_encryptor 50 | Hello::Encryptors::Simple.instance 51 | end 52 | 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/hello/rails_active_record/email_credential.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RailsActiveRecord 3 | module EmailCredential 4 | 5 | extend ActiveSupport::Concern 6 | 7 | included do 8 | include Credential 9 | 10 | # VALIDATIONS 11 | validates_uniqueness_of :email 12 | validates_presence_of :email 13 | validate :hello_validations, if: :email? 14 | 15 | end 16 | 17 | # SETTERS 18 | def email=(v) 19 | super(v.to_s.downcase.delete(' ')) 20 | end 21 | 22 | # CUSTOM METHODS 23 | 24 | def email_confirmed? 25 | !!confirmed_at 26 | end 27 | 28 | def email_delivered? 29 | !!email_delivered_at 30 | end 31 | 32 | def email_delivered_at 33 | verifying_token_digested_at 34 | end 35 | 36 | def confirm_email! 37 | update! verifying_token_digest: nil, verifying_token_digested_at: nil, confirmed_at: 1.second.ago 38 | end 39 | 40 | private 41 | 42 | def hello_validations 43 | return if errors.has_key?(:email) 44 | 45 | c = Hello.configuration 46 | validates_length_of :email, in: c.email_length 47 | validates_format_of :email, with: c.email_regex 48 | end 49 | 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/hello/rails_active_record/password_credential.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RailsActiveRecord 3 | module PasswordCredential 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | include Credential 8 | 9 | # VALIDATIONS 10 | validates_presence_of :password, on: :create 11 | validate :hello_validations, if: :digest_changed? 12 | 13 | end 14 | 15 | # SETTERS 16 | 17 | attr_reader :password 18 | 19 | def password=(value) 20 | # puts "password=('#{value}')".blue 21 | self.digest = @password = nil if value.blank? 22 | @password = value 23 | 24 | self.digest = complex_encryptor.encrypt(value) 25 | end 26 | 27 | # CUSTOM METHODS 28 | 29 | def password_is?(plain_text_password) 30 | complex_encryptor.match(plain_text_password, digest) 31 | end 32 | 33 | def set_generated_password 34 | self.password = simple_encryptor.single(4) # 8 chars 35 | end 36 | 37 | private 38 | 39 | def hello_validations 40 | return if errors.has_key?(:password) 41 | c = Hello.configuration 42 | validates_length_of :password, in: c.password_length 43 | return if errors.has_key?(:password) 44 | validates_format_of :password, with: c.password_regex 45 | end 46 | 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/hello/rails_helper.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RailsHelper 3 | def method_missing(method, *args, &block) 4 | # # http://candland.net/2012/04/17/rails-routes-used-in-an-isolated-engine/ 5 | # puts "LOOKING FOR ROUTES #{method}" 6 | return super unless method.to_s.end_with?('_path', '_url') 7 | return super unless main_app.respond_to?(method) 8 | main_app.send(method, *args) 9 | end 10 | 11 | def respond_to?(method) 12 | return super unless method.to_s.end_with?('_path', '_url') 13 | return super unless main_app.respond_to?(method) 14 | true 15 | end 16 | 17 | # [['English', 'en']] 18 | def hello_locale_select_options 19 | available_locales_with_names.map { |k, v| [v, k] } 20 | end 21 | 22 | def human_current_locale 23 | t('hello.locale_name') 24 | end 25 | 26 | def current_locale 27 | session['locale'] 28 | end 29 | 30 | def available_locales_with_names 31 | Hello::Locales.all.select { |k, _v| Hello.configuration.locales.include? k } 32 | end 33 | 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/hello/request_manager.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RequestManager 3 | 4 | def self.create(request) 5 | Factory.new(request).create 6 | end 7 | 8 | autoload :Factory, 'hello/request_manager/factory' 9 | autoload :Abstract, 'hello/request_manager/abstract' 10 | autoload :Stateless, 'hello/request_manager/stateless' 11 | autoload :Stateful, 'hello/request_manager/stateful' 12 | 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/hello/request_manager/abstract.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RequestManager 3 | class Abstract 4 | def initialize(request) 5 | @request = request 6 | end 7 | 8 | def clear_cache 9 | @current_access = @current_accesses = nil 10 | end 11 | 12 | def signed_in? 13 | !!current_user 14 | end 15 | 16 | def is_current_user?(user) 17 | current_user == user 18 | end 19 | 20 | def is_current_access?(access) 21 | current_access == access 22 | end 23 | 24 | def current_user 25 | current_access && current_access.user 26 | end 27 | 28 | def current_accesses 29 | fail NotImplementedError 30 | end 31 | 32 | def current_access 33 | fail NotImplementedError 34 | end 35 | 36 | def stateful? 37 | fail NotImplementedError 38 | end 39 | 40 | def sign_in!(user, expires_at = nil, sudo_expires_at = nil) 41 | expires_at ||= 30.minutes.from_now 42 | 43 | attrs = { 44 | user: user, 45 | user_agent_string: user_agent, 46 | expires_at: expires_at, 47 | ip: remote_ip 48 | } 49 | attrs[:sudo_expires_at] = sudo_expires_at if sudo_expires_at 50 | ::Access.create!(attrs) 51 | end 52 | 53 | def sign_out!(access = current_access) 54 | access && access.destroy! 55 | clear_cache 56 | end 57 | 58 | # protected 59 | 60 | def user_agent 61 | request.user_agent || 'blank_user_agent' 62 | end 63 | 64 | def remote_ip 65 | request.remote_ip 66 | end 67 | 68 | def request 69 | @request 70 | end 71 | 72 | def env 73 | request.env 74 | end 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /lib/hello/request_manager/factory.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RequestManager 3 | class Factory 4 | 5 | def initialize(request) 6 | @request = request 7 | end 8 | 9 | def create 10 | klass.new(@request) 11 | end 12 | 13 | private 14 | 15 | def klass 16 | is_stateless? ? Stateless : Stateful 17 | end 18 | 19 | def is_stateless? 20 | has_host_api? || has_url_api? 21 | end 22 | 23 | def has_host_api? 24 | @request.host.starts_with?('api.') 25 | end 26 | 27 | def has_url_api? 28 | @request.fullpath.starts_with?('/api/') 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/hello/request_manager/stateful.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RequestManager 3 | class Stateful < Abstract 4 | 5 | autoload :Finder, 'hello/request_manager/stateful/finder' 6 | autoload :SessionWrapper, 'hello/request_manager/stateful/session_wrapper' 7 | 8 | def initialize(*args) 9 | super(*args) 10 | @finder = Finder.new(self) 11 | @session_wrapper = SessionWrapper.new(self) 12 | end 13 | 14 | delegate :session_token, :session_token=, 15 | :session_tokens, :session_tokens=, 16 | :refresh_session_tokens, 17 | to: :@session_wrapper 18 | 19 | def stateful? 20 | true 21 | end 22 | 23 | # read 24 | 25 | delegate :current_accesses, to: :@finder 26 | 27 | def current_access 28 | if session_token.presence 29 | @current_access ||= current_accesses.find { |a| a.token == session_token } 30 | end 31 | end 32 | 33 | # write 34 | 35 | def sign_in!(*args) 36 | super(*args).tap do |access| 37 | self.session_token = access.token 38 | session_tokens << access.token 39 | end 40 | end 41 | 42 | # delete 43 | 44 | def sign_out!(access = current_access) 45 | self.session_token = session_tokens.first if is_current_access?(access) 46 | 47 | super(access) 48 | 49 | refresh_session_tokens 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/hello/request_manager/stateful/finder.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RequestManager 3 | class Stateful < Abstract 4 | class Finder 5 | def initialize(manager) 6 | @manager = manager 7 | end 8 | 9 | def current_accesses 10 | @models || models 11 | end 12 | 13 | def models 14 | gather_wanted_strings 15 | gather_wanted_models 16 | 17 | gather_valid_strings 18 | ensure_consistency_accross_models_and_session 19 | 20 | @models 21 | end 22 | 23 | private 24 | 25 | def gather_wanted_strings 26 | @wanted_strings = @manager.session_tokens 27 | end 28 | 29 | def gather_wanted_models 30 | strings = @wanted_strings 31 | 32 | # a small attempt to avoid a database call unless needed 33 | case strings.size 34 | when 0 then return @models = [] 35 | when 1 then strings = strings.first 36 | end 37 | 38 | # TODO: 39 | # optimize this process since each string starts with the user_id, 40 | # check StatelessRequestManager for example 41 | 42 | @models = ::Access.where(token: strings) 43 | end 44 | 45 | def gather_valid_strings 46 | @valid_strings = @models.map(&:active_token_or_destroy).map(&:presence).compact 47 | end 48 | 49 | def ensure_consistency_accross_models_and_session 50 | if @wanted_strings != @valid_strings 51 | @manager.session_tokens = @valid_strings 52 | @models = @models.select { |a| @valid_strings.include?(a.token) } 53 | end 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/hello/request_manager/stateful/session_wrapper.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RequestManager 3 | class Stateful < Abstract 4 | class SessionWrapper 5 | def initialize(manager) 6 | @manager = manager 7 | end 8 | 9 | def session_tokens 10 | session['tokens'] ||= [] 11 | end 12 | 13 | def session_tokens=(v) 14 | session['tokens'] = v 15 | @manager.clear_cache 16 | end 17 | 18 | def session_token 19 | session['token'] 20 | end 21 | 22 | def session_token=(v) 23 | session['token'] = v 24 | @manager.clear_cache 25 | end 26 | 27 | def refresh_session_tokens 28 | self.session_tokens = ::Access.where(token: session_tokens).pluck(:token) 29 | end 30 | 31 | def session 32 | @manager.request.session 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/hello/request_manager/stateless.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module RequestManager 3 | class Stateless < Abstract 4 | def current_accesses 5 | [] 6 | end 7 | 8 | def current_access 9 | @current_access ||= begin 10 | return nil unless string = param || header 11 | return nil unless user_id = string.split('-').first 12 | return nil unless user = ::User.find_by_id(user_id) 13 | return nil unless model = user.accesses.find_by_token(string) 14 | return nil unless model.active_token_or_destroy 15 | 16 | model 17 | end 18 | end 19 | 20 | def stateful? 21 | false 22 | end 23 | 24 | private 25 | 26 | def param 27 | request.parameters['access_token'] 28 | end 29 | 30 | def header 31 | request.headers['HTTP_ACCESS_TOKEN'] 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/hello/time_zones.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module TimeZones 3 | def self.all 4 | ActiveSupport::TimeZone.send(:zones_map).values.map(&:name) 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/hello/utils.rb: -------------------------------------------------------------------------------- 1 | module Hello 2 | module Utils 3 | 4 | autoload :DeviceName, 'hello/utils/device_name' 5 | 6 | def self.trailing_options(args) 7 | options = args.last.is_a?(Hash) ? args.pop : {} 8 | [options, args] 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/hello/utils/device_name.rb: -------------------------------------------------------------------------------- 1 | require 'user_agent_parser' 2 | 3 | module Hello 4 | module Utils 5 | class DeviceName 6 | # https://github.com/toolmantim/user_agent_parser 7 | # Instantiate the parser on load as it's quite expensive 8 | include Singleton 9 | 10 | def parse(user_agent_string) 11 | obj = user_agent_parser.parse(user_agent_string) 12 | a_browser = obj.to_s 13 | a_os = obj.os.to_s 14 | a_browser = "#{obj.name} #{obj.version && obj.version.major}".strip 15 | a_os = "#{obj.os.name} #{obj.os.version && obj.os.version.major}".strip 16 | a_device = obj.device.name 17 | 18 | a_browser = a_browser.gsub('IE', 'Internet Explorer') if a_browser.start_with? 'IE' 19 | 20 | if a_device == 'Other' 21 | "#{a_os} - #{a_browser}" 22 | elsif a_device == 'Spider' 23 | "Spider: #{a_browser}" 24 | else 25 | "#{a_os} (#{a_device}) - #{a_browser}" 26 | end.strip 27 | end 28 | 29 | def user_agent_parser 30 | @uap = UserAgentParser::Parser.new 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/hello/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Hello 3 | VERSION = '0.5.0'.freeze 4 | end 5 | -------------------------------------------------------------------------------- /lib/tasks/hello_tasks.rake: -------------------------------------------------------------------------------- 1 | # desc "Explaining what the task does" 2 | # task :hello do 3 | # # Task goes here 4 | # end 5 | -------------------------------------------------------------------------------- /spec/bdd/hello/authentication/authorization/authorization_router_constraints_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "Router Constraints" do 4 | 5 | story "For Users" do 6 | scenario "As a Guest" do 7 | Given "I am a Guest" do 8 | # :) 9 | end 10 | 11 | When "I visit a route constrained to users" do 12 | # :) 13 | end 14 | 15 | Then "I should see a 404 error" do 16 | expect { 17 | visit "/middleware/bad_kitty" 18 | }.to raise_error ActionController::RoutingError 19 | end 20 | end 21 | 22 | 23 | 24 | scenario "As a User" do 25 | Given "I am a User" do 26 | given_I_have_signed_in 27 | end 28 | 29 | When "I visit a route constrained to users" do 30 | # :) 31 | end 32 | 33 | Then "I should not see a 404 error" do 34 | expect { 35 | visit "/middleware/bad_kitty" 36 | }.not_to raise_error 37 | end 38 | end 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /spec/bdd/hello/authentication/authorization/authorization_sensitive_restriction_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "Sensitive Restriction" do 4 | 5 | story "Enable Sudo Mode" do 6 | before do 7 | Given "I see the Sudo Mode form" do 8 | given_I_have_signed_in 9 | 10 | click_link "Settings" 11 | click_link "Devices" 12 | 13 | expect(page).to have_content "Confirm Password to Continue" 14 | expect(current_path).to eq hello.accesses_path 15 | expect(Access.last.sudo_expires_at).to be < Time.now 16 | end 17 | end 18 | 19 | 20 | 21 | scenario "Success" do 22 | When "I submit the correct password" do 23 | when_I_confirm_my_user_password 24 | end 25 | 26 | Then "and I should see a confirmation message" do 27 | expect_flash_notice "Now we know it's really you. We won't be asking your password again for 60 minutes" 28 | end 29 | 30 | then_I_expect_to_be_on_sudo_mode 31 | end 32 | 33 | 34 | 35 | scenario "Blank Password" do 36 | When "I submit an empty form" do 37 | when_I_confirm_my_user_password('', false) 38 | end 39 | 40 | _then_failed_to_enable_sudo_mode 41 | end 42 | 43 | 44 | 45 | scenario "Wrong Password" do 46 | When "I submit an incorrect password" do 47 | when_I_confirm_my_user_password('wrong', false) 48 | end 49 | 50 | _then_failed_to_enable_sudo_mode 51 | end 52 | end 53 | 54 | 55 | 56 | story "Disable Sudo Mode" do 57 | scenario "Success" do 58 | given_I_have_signed_in_with_sudo_mode 59 | 60 | When "I disable sudo mode" do 61 | click_link "expire" 62 | end 63 | 64 | Then "I should see a confirmation message" do 65 | expect_flash_notice "We will now ask your password for sensitive access" 66 | end 67 | 68 | then_I_expect_not_to_be_on_sudo_mode 69 | end 70 | end 71 | 72 | 73 | 74 | def _then_failed_to_enable_sudo_mode 75 | Then "I expect to see an error message" do 76 | expect_flash_alert "Incorrect Password" 77 | end 78 | 79 | then_I_expect_not_to_be_on_sudo_mode 80 | end 81 | 82 | 83 | 84 | end 85 | -------------------------------------------------------------------------------- /spec/bdd/hello/authentication/authorization/bdd.yml: -------------------------------------------------------------------------------- 1 | capability: Authorization 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/authentication/bdd.yml: -------------------------------------------------------------------------------- 1 | goal: Authentication 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/bdd.yml: -------------------------------------------------------------------------------- 1 | vision: Hello Gem 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/internalionalization/anyone_can_change_their_locale/bdd.yml: -------------------------------------------------------------------------------- 1 | capability: Anyone Can Change Their Locale 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/internalionalization/anyone_can_change_their_locale/change_locale_on_the_profile_page_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "On The Profile Page" do 4 | 5 | # ACCEPTANCE CRITERIA 6 | # - 7 | 8 | story "As a User" do 9 | scenario "Success" do 10 | given_I_have_signed_in 11 | 12 | _then_the_database_locale_should_be('en') 13 | 14 | 15 | 16 | _when_I_update_my_locale_to('Portuguese (Brazil)') 17 | 18 | Then "I expect to a confirmation message in 'pt-BR'" do 19 | expect_flash_notice "Você atualizou seu perfil com sucesso" 20 | end 21 | 22 | _then_the_browser_locale_should_be('pt-BR') 23 | 24 | _then_the_database_locale_should_be('pt-BR') 25 | 26 | 27 | 28 | _when_I_update_my_locale_to('Spanish') 29 | 30 | Then "I expect to a confirmation message in 'es'" do 31 | expect_flash_notice "Su perfil ha sido actualizado exitosamente" 32 | end 33 | 34 | _then_the_browser_locale_should_be('es') 35 | 36 | _then_the_database_locale_should_be('es') 37 | end 38 | end 39 | 40 | 41 | 42 | def _when_I_update_my_locale_to(string) 43 | When "I update my locale to '#{string}'" do 44 | visit hello.profile_path 45 | 46 | within("form") do 47 | find("#user_locale").select(string) 48 | click_button "Update" 49 | end 50 | end 51 | end 52 | 53 | def _then_the_browser_locale_should_be(string) 54 | Then "the Browser's locale should be #{string}" do 55 | expect_to_see "dummy-locale: #{string}" 56 | end 57 | end 58 | 59 | def _then_the_database_locale_should_be(string) 60 | Then "the Database's locale should be '#{string}'" do 61 | expect(User.last.locale).to eq string 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/bdd/hello/internalionalization/anyone_can_change_their_locale/change_locale_on_the_sign_in_form_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "On The Sign In Form" do 4 | 5 | # ACCEPTANCE CRITERIA 6 | # - My browser learns my account’s locale 7 | 8 | story "As a Guest" do 9 | scenario "Success" do 10 | Given "The browser's locale is set to 'English'" do 11 | visit '/' 12 | expect_to_see "dummy-locale: en" 13 | end 14 | 15 | When "I sign in to a 'pt-BR' user" do 16 | user = given_I_have_a_user 17 | user.update! locale: 'pt-BR' 18 | when_sign_in_with_standard_data(skip_expire: true) 19 | end 20 | 21 | Then "I expect to a confirmation message in 'en'" do 22 | expect_flash_notice "You have signed in successfully" 23 | end 24 | 25 | Then "the Browser's locale should be 'pt-BR'" do 26 | expect_to_see "dummy-locale: pt-BR" 27 | end 28 | end 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /spec/bdd/hello/internalionalization/anyone_can_change_their_locale/change_locale_on_the_sign_up_form_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "On The Sign Up Form" do 4 | 5 | # ACCEPTANCE CRITERIA 6 | # - My account learns my browser’s locale 7 | 8 | story "As a Guest" do 9 | scenario "Success" do 10 | Given "I set the browser's locale to 'Portuguese (Brazil)'" do 11 | visit hello.locale_path 12 | click_button 'Portuguese (Brazil)' 13 | expect_flash_notice "Seu idioma foi atualizado com sucesso. 'Português (Brasil)'" 14 | end 15 | 16 | When "I sign up" do 17 | when_sign_up_as_an_onboarding(skip_expire: true, expect_welcome_mailer: true) 18 | end 19 | 20 | Then "I expect to a confirmation message in 'pt-BR'" do 21 | expect_flash_notice "Você se cadastrou com sucesso" 22 | end 23 | 24 | Then "the Browser's locale should be 'pt-BR'" do 25 | expect_to_see "dummy-locale: pt-BR" 26 | end 27 | 28 | Then "the Database's locale should be 'pt-BR'" do 29 | expect(User.last.locale).to eq('pt-BR') 30 | end 31 | end 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /spec/bdd/hello/internalionalization/anyone_can_change_their_timezone/bdd.yml: -------------------------------------------------------------------------------- 1 | capability: Anyone Can Change Their Timezone 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/internalionalization/anyone_can_change_their_timezone/change_timezone_on_the_profile_page_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "On The Profile Page" do 4 | 5 | # ACCEPTANCE CRITERIA 6 | # - 7 | 8 | story "As a User" do 9 | scenario "Success" do 10 | skip 11 | end 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /spec/bdd/hello/internalionalization/anyone_can_change_their_timezone/change_timezone_on_the_sign_in_form_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "On The Sign In Form" do 4 | 5 | # ACCEPTANCE CRITERIA 6 | # - My browser learns my account’s timezone 7 | 8 | story "As a Guest" do 9 | scenario "Success" do 10 | skip 11 | end 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /spec/bdd/hello/internalionalization/anyone_can_change_their_timezone/change_timezone_on_the_sign_up_form_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "On The Sign Up Form" do 4 | 5 | # ACCEPTANCE CRITERIA 6 | # - My account learns my browser’s timezone 7 | 8 | story "As a Guest" do 9 | scenario "Success" do 10 | skip 11 | end 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /spec/bdd/hello/internalionalization/bdd.yml: -------------------------------------------------------------------------------- 1 | goal: Internationalization 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/bdd.yml: -------------------------------------------------------------------------------- 1 | goal: Management 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/manage_email_credentials/bdd.yml: -------------------------------------------------------------------------------- 1 | capability: Manage Email Credentials 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/manage_email_credentials/manage_email_credentials_emails_api_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.api "Emails API" do 4 | 5 | pending "TODO: JSON suppport" 6 | 7 | end 8 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/manage_password_credentials/bdd.yml: -------------------------------------------------------------------------------- 1 | capability: Manage Password 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/manage_password_credentials/manage_password_forgot_password_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "Forgot Password" do 4 | 5 | story "-" do 6 | 7 | before do 8 | given_I_have_a_user 9 | end 10 | 11 | 12 | 13 | context "Credentials Found" do 14 | 15 | scenario 'Username' do 16 | When "I submit a valid username" do 17 | when_I_ask_to_reset_my_password 18 | end 19 | 20 | Then "I should see a confirmation message" do 21 | expect_to_see "To get back into your account, follow the instructions we've sent to your \"foobar\" email address." 22 | expect(current_path).to eq hello.forgot_passwords_path 23 | end 24 | end 25 | 26 | scenario 'Email' do 27 | When "I submit a valid email" do 28 | when_I_ask_to_reset_my_password('foo@bar.com') 29 | end 30 | 31 | Then "I should see a confirmation message" do 32 | expect_to_see "To get back into your account, follow the instructions we've sent to your \"foo@bar.com\" email address." 33 | expect(current_path).to eq hello.forgot_passwords_path 34 | end 35 | end 36 | 37 | 38 | 39 | Then "and I should receive an email with a password reset URL" do 40 | regexp = Regexp.new(/example.com\/hello\/passwords\/(\d*)\/reset\/(\d*)\/\w*/) 41 | expect(open_last_email.to_s).to match regexp 42 | # TODO: test this for a valid route 43 | end 44 | end 45 | 46 | 47 | 48 | scenario "Credentials Not Found" do 49 | When "I submit an invalid email" do 50 | when_I_ask_to_reset_my_password('wrong') 51 | end 52 | 53 | 54 | 55 | Then "I should see an alert message" do 56 | expect_error_message "1 error was found while locating your credentials" 57 | expect(current_path).to eq hello.forgot_passwords_path 58 | end 59 | end 60 | end 61 | 62 | 63 | 64 | after do 65 | then_I_expect_to_be_signed_out 66 | end 67 | 68 | end 69 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/manage_password_credentials/manage_password_page_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "Password Page" do 4 | 5 | 6 | 7 | def self._before__given_I_am_on_the_password_page 8 | before do 9 | Given "I am on the Password Management Page" do 10 | given_I_have_signed_in_with_sudo_mode 11 | click_link "Settings" 12 | click_link "Password" 13 | expect(current_path).to eq hello.password_path(PasswordCredential.last) 14 | end 15 | end 16 | end 17 | 18 | 19 | 20 | story "Update Password" do 21 | _before__given_I_am_on_the_password_page 22 | 23 | 24 | 25 | scenario "Valid" do 26 | When "I submit a new valid password" do 27 | fill_in 'password_credential_password', with: (@new_password = 'newpassword') 28 | click_button 'Update' 29 | end 30 | 31 | Then "I should see a confirmation message" do 32 | expect_flash_notice "You have updated your profile successfully" 33 | end 34 | 35 | then_I_expect_to_be_signed_in 36 | 37 | Then "and I should be able to sign in with the new password" do 38 | click_link "Sign Out" 39 | then_I_expect_to_be_signed_out 40 | 41 | when_sign_in_with_standard_data(password: @new_password) 42 | then_I_expect_to_be_signed_in 43 | end 44 | end 45 | 46 | 47 | 48 | scenario "Invalid" do 49 | When "I submit an invalid password" do 50 | fill_in 'password_credential_password', with: '' 51 | click_button 'Update' 52 | end 53 | 54 | Then "I should see an alert message" do 55 | expect_error_message "1 error was found while updating your profile" 56 | end 57 | end 58 | end 59 | 60 | end 61 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/manage_profile/bdd.yml: -------------------------------------------------------------------------------- 1 | capability: Manage Profile 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/manage_profile/manage_profile_api_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.api "API" do 4 | 5 | pending "TODO: JSON suppport" 6 | 7 | end 8 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/manage_profile/manage_profile_page_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "Profile Page" do 4 | 5 | 6 | 7 | def self._before__given_I_am_on_the_profile_page 8 | before do 9 | Given "I am on the Profile Management Page" do 10 | given_I_have_signed_in 11 | click_link "Settings" 12 | expect(current_path).to eq hello_path 13 | end 14 | end 15 | end 16 | 17 | 18 | 19 | story "Update Fields" do 20 | _before__given_I_am_on_the_profile_page 21 | 22 | 23 | 24 | scenario "Valid" do 25 | When "I submit new valid values in the form" do 26 | fill_in 'user_name', with: (@new_name = 'James Pinto') 27 | fill_in 'user_username', with: (@new_username = 'new_username') 28 | click_button 'Update' 29 | end 30 | 31 | 32 | 33 | Then "I should see a confirmation message" do 34 | expect_flash_notice "You have updated your profile successfully" 35 | end 36 | 37 | 38 | 39 | Then "and the new values should reflect on the database" do 40 | user = User.last 41 | expect(user.name).to eq(@new_name) 42 | expect(user.username).to eq(@new_username) 43 | end 44 | end 45 | 46 | 47 | 48 | scenario "Invalid" do 49 | When "I submit new invalid values in the form" do 50 | fill_in 'user_name', with: '' 51 | click_button 'Update' 52 | end 53 | 54 | 55 | 56 | Then "I should see an alert message" do 57 | expect_error_message "1 error was found while updating your profile" 58 | end 59 | end 60 | end 61 | 62 | 63 | end 64 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/manage_social_credentials/bdd.yml: -------------------------------------------------------------------------------- 1 | capability: Manage Social Credentials 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/manage_social_credentials/manage_social_credentials_pending_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.uic "-" do 4 | 5 | pending 6 | 7 | end 8 | -------------------------------------------------------------------------------- /spec/bdd/hello/management/unlink_sessions_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.capability 'I can Unlink Sessions' do 4 | 5 | role 'User' do 6 | Given 'I am a User' do 7 | sign_in_as_a('user') 8 | expect(Access.count).to eq(1) 9 | end 10 | 11 | uic 'Unlink Button', type: :feature do 12 | 13 | scenario 'Two Accesses' do 14 | 15 | Given 'a second device has logged into my account' do 16 | create(:valid_access, user: current_user) 17 | end 18 | 19 | Given 'I visit the Accesses Page' do 20 | visit '/' 21 | click_link 'Settings' 22 | click_link 'Devices' 23 | end 24 | 25 | Given 'I go through sudo mode' do 26 | fill_in 'user_password', with: '1234' 27 | click_button 'Confirm' 28 | end 29 | 30 | Given 'I should have 2 accesses in the database but only see 1 unlink button' do 31 | expect(Access.where(user_id: current_user.id).count).to eq(2) 32 | expect(page).to have_button('Unlink', count: 1) 33 | end 34 | 35 | When 'I attempt to unlink the second device' do 36 | click_button 'Unlink' 37 | end 38 | 39 | Then 'I should see a confirmation message' do 40 | expect_flash_notice('Device has been unlinked from your account') 41 | end 42 | 43 | Then 'Database now has 1 Access' do 44 | expect(Access.count).to eq(1) 45 | end 46 | 47 | end # scenario 48 | 49 | end # uic 50 | 51 | api 'API', type: :request do 52 | 53 | skip 'TODO: write API features here too' 54 | 55 | end # api 56 | 57 | end # role 58 | 59 | end # capability 60 | -------------------------------------------------------------------------------- /spec/bdd/hello/other/bdd.yml: -------------------------------------------------------------------------------- 1 | goal: Other 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/other/create_user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.capability "I can Create Users" do 4 | 5 | role "Webmaster" do 6 | context "Components", type: :feature do 7 | 8 | Given "I am a Webmaster" do 9 | sign_in_as_a('webmaster') 10 | expect_to_see "dummy-accounts-1" 11 | end 12 | 13 | uic "New User Webmaster Page" do 14 | 15 | Given "a user James exists" do 16 | create(:user, id: 1234, username: 'james') 17 | end 18 | 19 | Given 'I visit New User Webmaster Page with Sudo Mode' do 20 | visit '/users' 21 | click_link "View User List as a Webmaster" 22 | fill_in 'user_password', with: '1234' 23 | click_button 'Confirm' 24 | click_link "New User as a Webmaster" 25 | end 26 | 27 | scenario "Success" do 28 | 29 | When 'I submit a new user John' do 30 | fill_in 'user_name', with: 'john' 31 | fill_in 'user_username', with: 'john' 32 | fill_in 'user_email', with: 'john@test.com' 33 | fill_in 'user_password', with: '1234' 34 | click_button 'Create' 35 | end 36 | 37 | Then "I should see a confirmation message" do 38 | expect_flash_notice 'You have signed up successfully' 39 | end 40 | 41 | Then "There should be 3 users in the database" do 42 | expect(User.count).to eq(3) 43 | end 44 | end # scenario 45 | 46 | 47 | scenario "Failure" do 48 | 49 | When 'I submit a new user James' do 50 | # fill_in 'user_password', with: '1234' 51 | click_button 'Create' 52 | end 53 | 54 | Then "I should see an error message" do 55 | expect_to_see "errors were" 56 | end 57 | 58 | Then "There should be 2 users in the database" do 59 | expect(User.count).to eq(2) 60 | end 61 | end # scenario 62 | 63 | end # uic 64 | end # context 65 | 66 | end # role 67 | 68 | end # capability 69 | -------------------------------------------------------------------------------- /spec/bdd/hello/other/impersonate_user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.bdd.capability "I can Impersonate Users" do 4 | 5 | role "Webmaster" do 6 | context "Components", type: :feature do 7 | 8 | Given "I am a Webmaster" do 9 | sign_in_as_a('webmaster') 10 | expect_to_see "dummy-accounts-1" 11 | end 12 | 13 | uic "Users List Webmaster Page" do 14 | 15 | Given "a user James exists" do 16 | create(:user, id: 1234, username: 'james') 17 | end 18 | 19 | scenario "Success" do 20 | Given 'I visit Users List Webmaster Page with Sudo Mode' do 21 | visit '/users' 22 | click_link "View User List as a Webmaster" 23 | end 24 | 25 | Given 'I go through sudo mode' do 26 | fill_in 'user_password', with: '1234' 27 | click_button 'Confirm' 28 | end 29 | 30 | When "I attempt to impersonate James" do 31 | click_button 'Impersonate!' 32 | end 33 | 34 | Then "I should see a confirmation message" do 35 | expect_flash_notice "You have signed in successfully" 36 | end 37 | 38 | Then "I should be signed in as a User" do 39 | then_I_should_see "dummy-logged-in-role-user" 40 | end 41 | 42 | Then "I should be signed in with Sudo Mode" do 43 | then_I_should_see "dummy-logged-in-with-sudo-mode" 44 | end 45 | 46 | Then "I should be signed in with 2 accounts" do 47 | expect_to_see "dummy-accounts-2" 48 | end 49 | end # scenario 50 | 51 | 52 | end # uic 53 | end # context 54 | 55 | end # role 56 | 57 | end # capability 58 | -------------------------------------------------------------------------------- /spec/bdd/hello/registration/bdd.yml: -------------------------------------------------------------------------------- 1 | goal: Registration 2 | -------------------------------------------------------------------------------- /spec/bdd/hello/support.rb: -------------------------------------------------------------------------------- 1 | 2 | def fill_in_registration_form 3 | within("form#new_sign_up") do 4 | fill_in 'sign_up_name', with: 'James Pinto' 5 | fill_in 'sign_up_email', with: 'foo@bar.com' 6 | fill_in 'sign_up_username', with: 'foobar' 7 | fill_in 'sign_up_password', with: '1234' 8 | end 9 | end 10 | 11 | def fill_in_login_form(username, password='1234') 12 | within("form#new_sign_in") do 13 | fill_in 'sign_in_login', with: username 14 | fill_in 'sign_in_password', with: password 15 | end 16 | end 17 | 18 | def sign_in_with(login, password='1234') 19 | visit '/hello/sign_in' 20 | fill_in_login_form(login, password) 21 | click_button 'Sign In' 22 | click_link 'expire' 23 | __fetch_current_access 24 | end 25 | 26 | def sign_in_as_an_onboarding 27 | u = create(:user_onboarding) 28 | sign_in_with(u.username) 29 | expect(current_user.role).to eq('onboarding') 30 | end 31 | 32 | def sign_in_as_a_user 33 | u = create(:user_user) 34 | sign_in_with(u.username) 35 | expect(current_user.role).to eq('user') 36 | end 37 | 38 | def sign_in_as_a_webmaster 39 | u = create(:user_webmaster) 40 | sign_in_with(u.username) 41 | expect(current_user.role).to eq('webmaster') 42 | end 43 | 44 | def sign_in_as_a(role) 45 | case role.to_sym 46 | when :guest # nothing to do 47 | when :onboarding then sign_in_as_an_onboarding 48 | when :user then sign_in_as_a_user 49 | when :webmaster then sign_in_as_a_webmaster 50 | else raise("Role #{role} is unknown") 51 | end 52 | end 53 | 54 | def visit_path_after(path, delay) 55 | current_access.update expires_at: (current_access.expires_at - delay + 1.second) 56 | visit path 57 | end 58 | 59 | -------------------------------------------------------------------------------- /spec/fixtures/hello/password_mailer/confirmation: -------------------------------------------------------------------------------- 1 | PasswordMailer#confirmation 2 | 3 | Hi, find me in app/views/hello/password_mailer/confirmation 4 | -------------------------------------------------------------------------------- /spec/fixtures/hello/password_mailer/forgot: -------------------------------------------------------------------------------- 1 | PasswordMailer#forgot 2 | 3 | Hi, find me in app/views/hello/password_mailer/forgot 4 | -------------------------------------------------------------------------------- /spec/fixtures/hello/password_mailer/sign_up: -------------------------------------------------------------------------------- 1 | PasswordMailer#sign_up 2 | 3 | Hi, find me in app/views/hello/password_mailer/sign_up 4 | -------------------------------------------------------------------------------- /spec/mailers/hello/mailer_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Hello 4 | describe Mailer do 5 | let(:email_credential) { create(:email_credential, user: create(:user, name: "John O'Cornel")) } 6 | let(:name) { 'John O'Cornel' } 7 | 8 | describe 'welcome' do 9 | let(:mail) { Mailer.welcome(email_credential.email, email_credential.user, 'THE_PASSWORD') } 10 | 11 | it 'renders the headers' do 12 | expect(mail.subject).to eq('Welcome to our website') 13 | expect(mail.to).to eq([email_credential.email]) 14 | expect(mail.from).to eq(['hello@example.com']) 15 | end 16 | 17 | it 'renders the body' do 18 | expect(mail.body.to_s).to match("Hello #{name},") 19 | expect(mail.body.to_s).to match('Welcome') 20 | end 21 | end 22 | 23 | describe 'confirm_email' do 24 | let(:mail) { Mailer.confirm_email(email_credential.email, email_credential.user, 'THE_URL') } 25 | 26 | it 'renders the headers' do 27 | expect(mail.subject).to eq('Confirm This Email') 28 | expect(mail.to).to eq([email_credential.email]) 29 | expect(mail.from).to eq(['hello@example.com']) 30 | end 31 | 32 | it 'renders the body' do 33 | expect(mail.body.to_s).to match("Hello #{name},") 34 | expect(mail.body.to_s).to match('>THE_URL') 35 | end 36 | end 37 | 38 | describe 'forgot_password' do 39 | let(:mail) { Mailer.forgot_password(email_credential.email, email_credential.user, 'THE_URL') } 40 | 41 | it 'renders the headers' do 42 | expect(mail.subject).to eq('Reset Password Instructions') 43 | expect(mail.to).to eq([email_credential.email]) 44 | expect(mail.from).to eq(['hello@example.com']) 45 | end 46 | 47 | it 'renders the body' do 48 | expect(mail.body.to_s).to match("Hello #{name},") 49 | expect(mail.body.to_s).to match('>THE_URL') 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/models/access_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Access do 4 | it 'validations' do 5 | subject.valid? 6 | 7 | expect(subject.errors.messages).to eq(user: ["can't be blank"], 8 | user_agent_string: ["can't be blank"]) 9 | end 10 | 11 | describe 'methods' do 12 | it '#full_device_name' do 13 | # Mock 14 | expect(Hello::Utils::DeviceName.instance).to receive(:parse) 15 | # When 16 | subject.full_device_name 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/models/create_and_destroy/password_credential_create_and_destroy_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe PasswordCredential do 4 | 5 | describe 'ClassMethods' do 6 | describe '#new' do 7 | it "is not valid" do 8 | expect(subject).to be_invalid 9 | expect(subject.errors.messages).to eq({:password => ["can't be blank"]}) 10 | end 11 | end 12 | 13 | describe ".create" do 14 | subject { PasswordCredential.new(password: '1234') } 15 | 16 | describe "valid examples" do 17 | describe "does save without a user" do 18 | it "does save without a user" do 19 | expect(subject.user).to eq(nil) 20 | expect(subject.save).to eq(true) 21 | expect(Credential.count).to eq(1) 22 | end 23 | 24 | it "does save with an invalid user" do 25 | subject.user = User.new 26 | expect(subject.save).to eq(true) 27 | expect(subject.errors.messages).to eq({}) 28 | expect(Credential.count).to eq(1) 29 | end 30 | end 31 | 32 | it "saves with a valid user" do 33 | user = create(:user) 34 | expect(Credential.count).to eq(2) 35 | subject.user = user 36 | expect(subject.save).to eq(true) 37 | expect(Credential.count).to eq(3) 38 | end 39 | 40 | end 41 | end 42 | 43 | describe ".destroy!" do 44 | describe "valid examples" do 45 | describe "with 1 email and 1 password" do 46 | subject { create(:user) } 47 | 48 | it "works" do 49 | subject # touch 50 | expect(User.count).to eq(1) 51 | expect(Credential.count).to eq(2) 52 | subject.destroy! 53 | expect(User.count).to eq(0) 54 | expect(Credential.count).to eq(0) 55 | end 56 | end 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/models/credential_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Credential do 4 | example 'Validations' do 5 | expect(subject.valid?).to eq(true) 6 | expect(subject.errors.messages).to eq({}) 7 | end 8 | 9 | example 'Saving Validations' do 10 | # can save without a user 11 | expect(subject.save).to eq(true) 12 | expect(subject.errors.messages).to eq({}) 13 | 14 | # cannot update without a user 15 | expect(subject.save).to eq(false) 16 | expect(subject.errors.messages).to eq({:user => ["can't be blank"]}) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/others/configuration_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Hello 4 | describe 'Configuration' do 5 | let(:hello_config) { Hello.configuration } 6 | let(:rails_config) { Rails.configuration.hello } 7 | 8 | describe 'Works With Rails' do 9 | it 'is the same instance' do 10 | expect(hello_config).to eq(rails_config) 11 | expect(hello_config.object_id).to eq(rails_config.object_id) 12 | end 13 | 14 | it 'Sets in Hello, Gets in Rails' do 15 | hello_config.mailer_sender = 'foo' 16 | expect(rails_config.mailer_sender).to eq('foo') 17 | end 18 | 19 | it 'Sets in Rails, Gets in Hello' do 20 | rails_config.mailer_sender = 'bar' 21 | expect(hello_config.mailer_sender).to eq('bar') 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/others/encryptors/complex_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Hello::Encryptors::Complex do 4 | subject { described_class.instance } 5 | 6 | describe '#encrypt' do 7 | def use_bcrypt 8 | start_with('$2a$') 9 | end 10 | 11 | it('works with nil') { expect(subject.encrypt(nil)).to use_bcrypt } 12 | it('works with ""') { expect(subject.encrypt('')).to use_bcrypt } 13 | it('works with "abc"') { expect(subject.encrypt('abc')).to use_bcrypt } 14 | end 15 | 16 | describe '#match' do 17 | def enc(s) 18 | subject.encrypt(s) 19 | end 20 | 21 | it('works with nil') { digest = enc(nil); expect(subject.match(nil, digest)) } 22 | it('works with ""') { digest = enc(''); expect(subject.match('', digest)) } 23 | it('works with "abc"') { digest = enc('abc'); expect(subject.match('abc', digest)) } 24 | end 25 | 26 | end # describe 27 | -------------------------------------------------------------------------------- /spec/others/encryptors/simple_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Hello::Encryptors::Simple do 4 | subject { described_class.instance } 5 | 6 | describe '#encrypt' do 7 | it('does not work with nil') { expect { subject.encrypt(nil).length }.to raise_error(TypeError) } 8 | it('works with ""') { expect(subject.encrypt('').length ).to eq(32) } 9 | it('works with "abc"') { expect(subject.encrypt('abc').length).to eq(32) } 10 | end 11 | 12 | describe '#match' do 13 | def enc(s) 14 | subject.encrypt(s) 15 | end 16 | 17 | it('works with ""') { digest = enc(''); expect(subject.match('', digest)) } 18 | it('works with "abc"') { digest = enc('abc'); expect(subject.match('abc', digest)) } 19 | end 20 | 21 | end # describe 22 | -------------------------------------------------------------------------------- /spec/others/helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Helper' do 4 | it 'hello_locale_select_options' do 5 | obj = Object.new 6 | obj.extend Hello::RailsHelper 7 | expect(obj.hello_locale_select_options).to be_an Array 8 | expect(obj.hello_locale_select_options).to include %w(English en) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/requests/forgot_password_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe 'Forgot Password', type: :request do 4 | describe 'POST /password/forgot.json' do 5 | describe 'Error' do 6 | it 'missing' do 7 | post '/hello/passwords/forgot.json' 8 | 9 | expect(response.status).to eq(400) 10 | expect(response.status_message).to eq('Bad Request') 11 | expect(json_response['exception']).to eq('class' => 'ActionController::ParameterMissing', 12 | 'message' => 'param is missing or the value is empty: forgot_password') 13 | end 14 | 15 | it 'blank' do 16 | forgot_password_params = { login: '' } 17 | post '/hello/passwords/forgot.json', params: { forgot_password: forgot_password_params } 18 | 19 | expect(response.status).to eq(422) 20 | expect(response.status_message).to eq('Unprocessable Entity') 21 | expect(json_response).to eq('login' => ['was not found']) 22 | end 23 | end 24 | 25 | it 'Success' do 26 | given_I_have_a_user 27 | 28 | forgot_password_params = { login: 'foobar', password: '1234' } 29 | post '/hello/passwords/forgot.json', params: { forgot_password: forgot_password_params } 30 | 31 | expect(response.status).to eq(201) 32 | expect(response.status_message).to eq('Created') 33 | expect(json_response.keys).to match_array %w(sent) 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/requests/reset_password_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe 'Reset Password', type: :request do 4 | describe 'POST /password/reset.json' do 5 | # TODO: this feature should not be tested not to accept JSON 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/requests/security/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe 'Security', type: :request do 4 | context 'PATCH /profile.json' do 5 | before(:each) do 6 | @auth_headers = { 'HTTP_ACCESS_TOKEN' => given_I_have_a_classic_access_token.token } 7 | mock_stateless! 8 | end 9 | 10 | it 'Role' do 11 | expect do 12 | patch '/hello/profile.json', params: { user: { role: 'webmaster' } }, headers: @auth_headers 13 | 14 | expect(response.status).to eq(200) 15 | end.not_to change { User.last.role }.from('user') 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/routing/hello/accesses_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Hello 4 | describe Management::AccessesController do 5 | describe 'routing' do 6 | routes { Hello::Engine.routes } 7 | 8 | it 'routes to #index' do 9 | expect(get: '/accesses').to route_to('hello/management/accesses#index') 10 | end 11 | 12 | it 'routes to #destroy' do 13 | expect(delete: '/accesses/1').to route_to('hello/management/accesses#destroy', id: '1') 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/routing/hello/emails_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Hello 4 | describe Management::EmailsController do 5 | describe 'routing' do 6 | routes { Hello::Engine.routes } 7 | 8 | it 'routes to #index' do 9 | expect(get: '/emails').to route_to('hello/management/emails#index') 10 | end 11 | 12 | it 'routes to #create' do 13 | expect(post: '/emails').to route_to('hello/management/emails#create') 14 | end 15 | 16 | it 'routes to #destroy' do 17 | expect(delete: '/emails/1').to route_to('hello/management/emails#destroy', id: '1') 18 | end 19 | 20 | it 'routes to #deliver' do 21 | expect(post('/emails/1/deliver')).to route_to('hello/management/emails#deliver', id: '1') 22 | end 23 | 24 | # 25 | # CONFIRM EMAIL 26 | # 27 | 28 | it 'routes to #confirm' do 29 | expect(get('/emails/1/confirm/123')).to route_to('hello/management/confirm_emails#confirm', id: '1', token: '123') 30 | end 31 | 32 | it 'routes to #expired_token' do 33 | expect(get: '/emails/expired_confirmation_token').to route_to('hello/management/confirm_emails#expired_confirmation_token') 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/routing/hello/locale_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Hello 4 | describe Internationalization::LocaleController do 5 | describe 'routing' do 6 | routes { Hello::Engine.routes } 7 | 8 | it 'routes to #index' do 9 | expect(get('/locale')).to route_to('hello/internationalization/locale#index') 10 | end 11 | 12 | it 'routes to #update' do 13 | expect(post('/locale')).to route_to('hello/internationalization/locale#update') 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/routing/hello/profile_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Hello 4 | describe Management::ProfilesController do 5 | describe 'routing' do 6 | routes { Hello::Engine.routes } 7 | 8 | it 'routes to #show too' do 9 | expect(get('/')).to route_to('hello/management/profiles#show') 10 | end 11 | 12 | it 'routes to #show' do 13 | expect(get('/profile')).to route_to('hello/management/profiles#show') 14 | end 15 | 16 | it 'routes to #update' do 17 | expect(patch('/profile')).to route_to('hello/management/profiles#update') 18 | end 19 | 20 | it 'routes to #cancel' do 21 | expect(get('/profile/cancel')).to route_to('hello/management/profiles#cancel') 22 | end 23 | 24 | it 'routes to #destroy' do 25 | expect(delete('/profile')).to route_to('hello/management/profiles#destroy') 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/routing/hello/registration_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Hello 4 | describe 'routing' do 5 | routes { Hello::Engine.routes } 6 | 7 | it 'routes to #index' do 8 | expect(get('/sign_up')).to route_to('hello/registration/sign_up#index') 9 | end 10 | 11 | it 'routes to #create' do 12 | expect(post('/sign_up')).to route_to('hello/registration/sign_up#create') 13 | end 14 | 15 | it 'routes to #index' do 16 | expect(get('/sign_in')).to route_to('hello/authentication/sign_in#index') 17 | end 18 | 19 | it 'routes to #authenticate' do 20 | expect(post('/sign_in')).to route_to('hello/authentication/sign_in#authenticate') 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/routing/hello/sign_out_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Hello 4 | describe Authentication::SessionsController do 5 | describe 'routing' do 6 | it 'routes to #sign_out' do 7 | expect(get: '/hello/sign_out').to route_to('hello/authentication/sessions#sign_out') 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/routing/hello/sudo_mode_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Hello 4 | module Authentication 5 | describe SudoModeController do 6 | describe 'routing' do 7 | routes { Hello::Engine.routes } 8 | 9 | it 'routes to #form' do 10 | expect(get: '/sudo_mode').to route_to('hello/authentication/sudo_mode#form') 11 | end 12 | 13 | it 'routes to #authenticate' do 14 | expect(patch: '/sudo_mode').to route_to('hello/authentication/sudo_mode#authenticate') 15 | end 16 | 17 | it 'routes to #expire' do 18 | expect(get: '/sudo_mode/expire').to route_to('hello/authentication/sudo_mode#expire') 19 | end 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/routing/hello/users_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Hello 4 | describe UsersController, type: :routing do 5 | describe 'routing' do 6 | it 'routes to #index' do 7 | expect(get: '/users').to route_to('users#index') 8 | end 9 | 10 | it 'routes to #show' do 11 | expect(get: '/users/1').to route_to('users#show', id: '1') 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | SPEC_ROOT = Pathname(File.dirname(__FILE__)) 3 | DUMMY_ROOT = SPEC_ROOT.join('../dummy') 4 | 5 | require 'spec_helper/codeclimate' # this should be atop all 6 | require 'spec_helper/dummy_and_test_dependencies' 7 | require 'spec_helper/support' 8 | require 'spec_helper/create_database' 9 | require 'spec_helper/configure_rspec' 10 | -------------------------------------------------------------------------------- /spec/spec_helper/codeclimate.rb: -------------------------------------------------------------------------------- 1 | require 'codeclimate-test-reporter' 2 | CodeClimate::TestReporter.start 3 | -------------------------------------------------------------------------------- /spec/spec_helper/configure_rspec.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.color = true 3 | config.default_formatter = Bdd::RSpec::Formatter 4 | 5 | if ENV['GITLAB_CI'] 6 | config.formatters << 'RspecJunitFormatter' 7 | config.formatters << Bdd::RSpec::Formatter 8 | end 9 | 10 | config.mock_with :rspec 11 | config.use_transactional_fixtures = true 12 | config.infer_base_class_for_anonymous_controllers = false 13 | # config.order = "random" 14 | 15 | config.infer_spec_type_from_file_location! 16 | 17 | config.include Hello::FeatureSupportGiven, type: :feature 18 | config.include Hello::RequestSupport, type: :request 19 | 20 | config.include FactoryBot::Syntax::Methods 21 | 22 | config.include(EmailSpec::Helpers) 23 | config.include(EmailSpec::Matchers) 24 | 25 | config.before(:each) { I18n.locale = :en } 26 | 27 | config.before(:each, type: :request) { host! 'api.example.com' } 28 | end 29 | -------------------------------------------------------------------------------- /spec/spec_helper/create_database.rb: -------------------------------------------------------------------------------- 1 | puts "RUBY #{RUBY_VERSION}.#{RUBY_PATCHLEVEL} #{RUBY_RELEASE_DATE} (#{RUBY_PLATFORM})".magenta 2 | puts "RAILS #{Rails::VERSION::STRING}".magenta 3 | # database: ":memory:" 4 | puts 'loading sqlite schema in memory' 5 | load "#{Rails.root}/db/schema.rb" 6 | -------------------------------------------------------------------------------- /spec/spec_helper/dummy_and_test_dependencies.rb: -------------------------------------------------------------------------------- 1 | require DUMMY_ROOT.join('config/environment.rb') 2 | 3 | require 'factory_bot_rails' 4 | require 'faker' 5 | require 'rspec/rails' 6 | require 'capybara/rails' 7 | require 'email_spec' 8 | require 'bdd/rspec' 9 | 10 | # silencers 11 | Rails.backtrace_cleaner.remove_silencers! 12 | 13 | # deprecation 14 | ActiveSupport::Deprecation.silenced = true 15 | 16 | # quiet migrations 17 | ActiveRecord::Schema.verbose = false 18 | -------------------------------------------------------------------------------- /spec/spec_helper/support.rb: -------------------------------------------------------------------------------- 1 | Dir[SPEC_ROOT.join('support/**/*.rb')].each { |f| require f } 2 | -------------------------------------------------------------------------------- /spec/support/factories.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_bot 2 | # https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md 3 | 4 | # https://github.com/stympy/faker#usage 5 | 6 | FactoryBot.define do 7 | 8 | factory :user do 9 | name { Faker::Name.name } 10 | locale 'en' 11 | time_zone Time.zone.name 12 | role 'user' 13 | username { Faker::Internet.user_name(name, %w(-_)) } 14 | email { "#{username}@provider.com" } 15 | password '1234' 16 | 17 | trait :without_credentials do 18 | email nil 19 | password nil 20 | end 21 | 22 | factory :user_webmaster do 23 | name 'Webmaster' 24 | role 'webmaster' 25 | end 26 | 27 | factory :user_onboarding do 28 | name 'Onboarding' 29 | role 'onboarding' 30 | end 31 | 32 | factory :user_user do 33 | name 'User' 34 | role 'user' 35 | end 36 | end 37 | 38 | factory :email_credential do 39 | user 40 | email { Faker::Internet.email } 41 | end 42 | 43 | factory :password_credential do 44 | user 45 | after(:build) { |pc| pc.password = '1234' } 46 | end 47 | 48 | factory :access do 49 | user 50 | expires_at nil 51 | user_agent_string 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36' 52 | factory :valid_access do 53 | expires_at { 30.minutes.from_now } 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/support/features/feature_support_given.rb: -------------------------------------------------------------------------------- 1 | module Hello::FeatureSupportGiven 2 | def given_I_have_not_signed_in 3 | Given('I have not signed in') {} 4 | end 5 | 6 | def given_I_have_signed_in 7 | Given 'I have signed in' do 8 | given_I_am_logged_in 9 | # @current_user = User.last 10 | # @current_credential = Credential.last 11 | # @current_access = Access.last 12 | then_I_expect_to_be_signed_in_with_role('user') 13 | end 14 | end 15 | 16 | def given_I_have_signed_in_with_sudo_mode 17 | Given 'I have signed in with sudo mode' do 18 | given_I_have_signed_in 19 | then_I_expect_not_to_be_on_sudo_mode 20 | visit '/hello/emails' 21 | when_I_confirm_sudo_mode 22 | then_I_expect_to_be_on_sudo_mode 23 | end 24 | end 25 | 26 | def given_I_have_signed_in_as_an_onboarding 27 | Given 'I have signed in as an onboarding' do 28 | sign_up_as_an_onboarding 29 | then_I_expect_to_be_signed_in_with_role('onboarding') 30 | end 31 | end 32 | 33 | def given_I_have_signed_in_as_a_user 34 | Given 'I have signed in as a user' do 35 | given_I_have_signed_in 36 | end 37 | end 38 | 39 | def given_I_have_signed_in_as_a_webmaster 40 | Given 'I have signed in as a webmaster' do 41 | given_I_have_a_webmaster_password_credential 42 | when_sign_in_with_webmaster_data 43 | then_I_expect_to_be_signed_in_with_role('webmaster') 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/support/features/feature_support_then.rb: -------------------------------------------------------------------------------- 1 | module Hello::FeatureSupportGiven 2 | def then_I_expect_to_be_signed_out 3 | Then 'I should be signed out' do 4 | then_I_should_see 'dummy-logged-out' 5 | end 6 | end 7 | 8 | def then_I_expect_to_be_signed_in 9 | Then 'I should be signed in' do 10 | then_I_should_see 'dummy-logged-in' 11 | end 12 | end 13 | 14 | def then_I_expect_to_be_signed_in_with_role(role = 'user') 15 | Then "I should be signed in as a #{role.capitalize}" do 16 | then_I_should_see "dummy-logged-in-role-#{role}" 17 | end 18 | end 19 | 20 | def then_I_expect_to_be_signed_in_with_id(user_id) 21 | Then 'I should be signed in as a specific user' do 22 | then_I_should_see "dummy-logged-in-User##{user_id}" 23 | end 24 | end 25 | 26 | def then_I_expect_to_be_signed_in_with_sudo_mode 27 | Then 'I should be signed in with Sudo Mode' do 28 | then_I_should_see 'dummy-logged-in-with-sudo-mode' 29 | end 30 | end 31 | 32 | def then_I_expect_to_be_on_sudo_mode 33 | Then 'I should be on Sudo Mode' do 34 | then_I_should_see 'dummy-logged-in-with-sudo-mode' 35 | end 36 | end 37 | 38 | def then_I_expect_not_to_be_on_sudo_mode 39 | Then 'I should not be on Sudo Mode' do 40 | then_I_should_see 'dummy-logged-in-without-sudo-mode' 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/support/helpers/aliases.rb: -------------------------------------------------------------------------------- 1 | 2 | # STORY CARD 3 | 4 | def story_card(options = {}) 5 | before(:all) do 6 | array = [] 7 | array << ['|', ' Who'.bold, options[:who].white].join(' ') 8 | array << ['|', ' What'.bold, options[:what].white].join(' ') 9 | array << ['|', ' Why'.bold, options[:why].white].join(' ') 10 | 11 | x = array.max(&:length).uncolorize.length 12 | s = '+' + ('-' * x) 13 | t = 'STORY CARD:'.light_blue 14 | puts nil, t, s, array, s, nil 15 | end 16 | end 17 | 18 | # FEATURE TERMS 19 | 20 | def feature_set(*args, &b) 21 | describe(*args, &b) 22 | end 23 | 24 | # AGILE TERMS 25 | 26 | # def epic(*args, &b) 27 | # describe(*args, &b) 28 | # end 29 | # 30 | # def story(*args, &b) 31 | # describe(*args, &b) 32 | # end 33 | 34 | # WWW 35 | 36 | def feature_www(s, options = {}, &b) 37 | s = ['Feature:', s, www(options)] 38 | feature(s, &b) 39 | end 40 | 41 | def www(options = {}) 42 | r = [] 43 | r << "\n Who....: #{options[:who].light_black}" if options[:who] 44 | r << "\n What...: #{options[:what].light_black}" if options[:what] 45 | r << "\n Why....: #{options[:why].light_black}" if options[:why] 46 | r << "\n Where..: #{options[:where].light_black}" if options[:where] 47 | r.join('') 48 | end 49 | 50 | # WWW GROUPING 51 | 52 | def who(s, &b) 53 | describe('Who:', s.light_black, &b) 54 | end 55 | 56 | def what(s, &b) 57 | describe('What:', s.light_black, &b) 58 | end 59 | 60 | # def why(s, &b) 61 | # describe("Why:", s.light_black, &b) 62 | # end 63 | 64 | # WWW SCENARIOS 65 | 66 | # def who_scenario(s, &b) 67 | # scenario(s, &b) 68 | # end 69 | 70 | # def what_scenario(s, &b) 71 | # scenario(s, &b) 72 | # end 73 | 74 | # def why_scenario(s, &b) 75 | # scenario(s, &b) 76 | # end 77 | -------------------------------------------------------------------------------- /spec/support/helpers/configuration.rb: -------------------------------------------------------------------------------- 1 | 2 | # CONFIGURATION 3 | 4 | def reload_initializer! 5 | load "#{Rails.root}/config/initializers/hello.rb" 6 | end 7 | -------------------------------------------------------------------------------- /spec/support/helpers/current.rb: -------------------------------------------------------------------------------- 1 | 2 | def __fetch_current_access 3 | @current_access = Access.last 4 | end 5 | 6 | def current_access 7 | @current_access 8 | end 9 | 10 | def last_credential 11 | Credential.last 12 | end 13 | 14 | def current_user 15 | current_access.user 16 | end 17 | -------------------------------------------------------------------------------- /spec/support/helpers/expect.rb: -------------------------------------------------------------------------------- 1 | 2 | def expect_flash_notice(text) 3 | expect(page).to have_selector '.alert-success', text: text 4 | end 5 | 6 | def expect_flash_alert(text) 7 | expect(page).to have_selector '.alert-warning', text: text 8 | end 9 | 10 | def expect_flash_info(text) 11 | expect(page).to have_selector '.alert-info', text: text 12 | end 13 | 14 | def expect_flash_info_blank 15 | expect(page).not_to have_selector '.alert-info' 16 | end 17 | 18 | def expect_flash_alert_blank 19 | expect(page).not_to have_selector '.alert-warning' 20 | end 21 | 22 | def expect_error_message(text) 23 | expect(page).to have_selector 'form h2.errors', text: text 24 | end 25 | 26 | def expect_to_see(text) 27 | expect(page.text).to include text 28 | end 29 | 30 | def expect_not_to_see(text) 31 | expect(page.text).not_to include text 32 | end 33 | 34 | def expect_to_have_a_layout 35 | expect_to_see('dummy') 36 | end 37 | 38 | def expect_not_to_have_a_layout 39 | expect_not_to_see('dummy') 40 | end 41 | 42 | def expect_to_be_on(path) 43 | expect(current_path).to eq path 44 | end 45 | 46 | def expect_flash_notice_signed_in 47 | expect_flash_notice 'You have signed in successfully' 48 | end 49 | 50 | def expect_flash_auth(situation) 51 | case situation 52 | when nil then expect_flash_alert_blank 53 | when :must_be_authenticated then expect_flash_alert 'You must sign in to continue.' 54 | when :cannot_be_a_authenticated then expect_flash_alert 'You have already signed in.' 55 | when :must_be_an_onboarding then expect_flash_alert 'You have already completed your registration.' 56 | when :cannot_be_an_onboarding then expect_flash_alert 'Please complete your registration.' 57 | when :must_be_a_master then expect_flash_alert 'This section of website is restricted to admins.' 58 | else fail("unknown auth_situation '#{situation}'") 59 | 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/support/helpers/given.rb: -------------------------------------------------------------------------------- 1 | 2 | # frozen_string_literal: true 3 | USER_TEST_EMAIL = 'foo@bar.com'.freeze 4 | USER_TEST_USERNAME = 'foobar'.freeze 5 | 6 | def given_I_have_a_classic_access_token 7 | user = create(:user, name: 'James Pinto', username: USER_TEST_USERNAME, email: USER_TEST_EMAIL) 8 | Access.create!(user: user, user_agent_string: 'testing', expires_at: 24.hours.from_now) 9 | end 10 | 11 | def given_I_have_a_user 12 | create(:user, name: 'James Pinto', email: USER_TEST_EMAIL, username: USER_TEST_USERNAME) 13 | end 14 | 15 | def given_I_have_a_webmaster_password_credential 16 | create(:user_webmaster) 17 | end 18 | 19 | def given_I_have_a_user_and_forgot_my_password 20 | user = given_I_have_a_user 21 | unencrypted_token = user.password_credential.reset_verifying_token! 22 | end 23 | 24 | def given_I_am_logged_in_with_a_classic_credential 25 | # when_sign_up_with_standard_data(expect_welcome_mailer: true) 26 | given_I_have_a_user 27 | when_sign_in_with_standard_data 28 | then_I_should_be_logged_in 29 | end 30 | 31 | def given_I_am_logged_in 32 | given_I_am_logged_in_with_a_classic_credential 33 | end 34 | 35 | def given_I_am_logged_in_with_two_accounts 36 | given_I_have_signed_in 37 | given_I_have_a_webmaster_password_credential 38 | when_sign_in_with_webmaster_data 39 | then_I_expect_to_be_signed_in_with_role('webmaster') 40 | expect_to_see 'dummy-accounts-2' 41 | end 42 | -------------------------------------------------------------------------------- /spec/support/helpers/shortcuts.rb: -------------------------------------------------------------------------------- 1 | 2 | def json_response 3 | JSON(response.body) 4 | end 5 | 6 | def response_status 7 | [response.status, response.status_message] 8 | end 9 | 10 | def show_me 11 | save_and_open_page 12 | end 13 | 14 | def page_reload 15 | visit current_url 16 | end 17 | 18 | def click_nth_button(string, i) 19 | page.all(:button, string)[i].click 20 | end 21 | 22 | def mock_stateless! 23 | allow_any_instance_of(Hello::RequestManager::Factory).to receive(:is_stateless?).and_return(true) 24 | end 25 | -------------------------------------------------------------------------------- /spec/support/helpers/then.rb: -------------------------------------------------------------------------------- 1 | 2 | def then_I_should_be_logged_in 3 | then_I_should_be_logged_in_as_a_user 4 | end 5 | 6 | def then_I_should_be_logged_in_as_a_user(expected_accesses_count = 1) 7 | then_I_should_see 'Hello, James Pinto!' 8 | then_I_should_see 'Sign Out' 9 | expect(Access.count).to eq(expected_accesses_count) 10 | end 11 | 12 | def then_I_should_be_logged_in_as_a_webmaster 13 | then_I_should_see 'Hello, Admin!' 14 | then_I_should_see 'Sign Out' 15 | expect(Access.count).to eq(1) 16 | end 17 | 18 | def then_I_should_be_logged_out(_expected_accesses_count = 0) 19 | then_I_should_see 'Hello, Guest!' 20 | # expect(Access.count).to eq(expected_accesses_count) 21 | end 22 | 23 | def then_I_should_see(text) 24 | expect(page).to have_content text 25 | end 26 | 27 | def then_I_should_not_see(text) 28 | expect(page).not_to have_content text 29 | end 30 | -------------------------------------------------------------------------------- /spec/support/requests/request_support.rb: -------------------------------------------------------------------------------- 1 | module Hello::RequestSupport 2 | def then_I_should_get_a_response(i, s) 3 | Then "I should get a #{i} '#{s}' response" do 4 | expect(response.status).to eq(i) 5 | expect(response.status_message).to eq(s) 6 | end 7 | end 8 | end 9 | --------------------------------------------------------------------------------