├── .env.sample ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .ruby-style.yml ├── .ruby-version ├── .travis.yml ├── CONTRIBUTING.md ├── DEPLOYMENT.md ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── LICENSE.md ├── Procfile ├── README.md ├── Rakefile ├── app.json ├── app ├── assets │ ├── images │ │ └── favicon.ico │ ├── javascripts │ │ ├── application.js │ │ ├── application │ │ │ ├── home.js │ │ │ └── projects.js │ │ ├── dasherize.js │ │ └── shared │ │ │ └── .keep │ └── stylesheets │ │ ├── application.scss │ │ ├── application │ │ ├── base.scss │ │ ├── home.scss │ │ └── projects.scss │ │ └── shared │ │ └── base.scss ├── controllers │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ ├── errors_controller.rb │ ├── home_controller.rb │ ├── omniauth_callbacks_controller.rb │ └── projects_controller.rb ├── helpers │ └── application_helper.rb ├── inputs │ └── string_input.rb ├── jobs │ └── .keep ├── mailers │ └── .keep ├── models │ ├── ability.rb │ ├── concerns │ │ └── .keep │ ├── oauth_account.rb │ ├── project.rb │ ├── project_decorator.rb │ ├── status │ │ ├── base.rb │ │ ├── circleci.rb │ │ ├── codeship.rb │ │ ├── null.rb │ │ └── travis.rb │ └── user.rb ├── services │ └── .keep └── views │ ├── devise │ ├── confirmations │ │ └── new.html.slim │ ├── mailer │ │ ├── confirmation_instructions.html.slim │ │ ├── reset_password_instructions.html.slim │ │ └── unlock_instructions.html.slim │ ├── passwords │ │ ├── edit.html.slim │ │ └── new.html.slim │ ├── registrations │ │ ├── edit.html.slim │ │ └── new.html.slim │ ├── sessions │ │ └── new.html.slim │ ├── shared │ │ └── _links.html.slim │ └── unlocks │ │ └── new.html.slim │ ├── errors │ ├── 404.html.slim │ ├── 422.html.slim │ └── 500.html.slim │ ├── home │ └── index.html.slim │ ├── layouts │ └── application.html.slim │ ├── projects │ ├── _form.html.slim │ ├── _project.html.slim │ ├── edit.html.slim │ ├── index.html.slim │ ├── new.html.slim │ └── show.html.slim │ └── shared │ ├── _google_analytics.html.slim │ └── actions │ ├── _form_actions.html.slim │ └── _show_edit_back_destroy_links.html.slim ├── bin ├── bundle ├── rails ├── rake ├── rspec ├── setup └── spring ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ ├── staging.rb │ └── test.rb ├── initializers │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── devise.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── new_relic.rb │ ├── octokit.rb │ ├── redis.rb │ ├── session_store.rb │ ├── simple_form.rb │ ├── simple_form_materializecss.rb │ └── wrap_parameters.rb ├── locales │ ├── datetime_picker.en.yml │ ├── devise.en.yml │ ├── en.yml │ └── simple_form.en.yml ├── newrelic.yml ├── puma.rb ├── routes.rb └── secrets.yml ├── db ├── migrate │ ├── 00000000000001_add_devise_to_users.rb │ ├── 00000000000002_create_oauth_accounts.rb │ ├── 00000000000003_add_rolify_to_users.rb │ ├── 20150721075541_create_projects.rb │ ├── 20150918095957_add_ci_type_to_projects.rb │ ├── 20150918100022_add_travis_token_to_projects.rb │ ├── 20151006134112_add_circleci_token_to_projects.rb │ ├── 20151007091120_remove_roles.rb │ └── 20151009034510_remove_columns_from_oauth_accounts_and_users.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep ├── cache_store_initializer.rb ├── tasks │ ├── dev.rake │ ├── maintain.rake │ └── run.rake └── templates │ ├── active_record │ └── model │ │ └── model.rb │ ├── rails │ └── scaffold_controller │ │ └── controller.rb │ ├── rspec │ ├── model │ │ └── model_spec.rb │ └── scaffold │ │ └── controller_spec.rb │ └── slim │ └── scaffold │ ├── _form.html.slim │ ├── edit.html.slim │ ├── index.html.slim │ ├── new.html.slim │ └── show.html.slim ├── log └── .keep ├── public ├── favicon.ico └── robots.txt ├── spec ├── controllers │ ├── application_controller_spec.rb │ ├── errors_controller_spec.rb │ ├── home_controller_spec.rb │ ├── omniauth_callbacks_controller_spec.rb │ └── projects_controller_spec.rb ├── factories │ ├── oauth_accounts.rb │ ├── projects.rb │ └── users.rb ├── feature_helper.rb ├── features │ ├── add_a_new_project_spec.rb │ ├── authentication_spec.rb │ └── presentation_spec.rb ├── fixtures │ ├── github │ │ ├── google_visualr.json │ │ ├── sinatra.json │ │ └── twemoji.json │ ├── omniauth.json │ └── travis │ │ ├── google_visualr.json │ │ ├── sinatra.json │ │ └── twemoji.json ├── helpers │ └── application_helper_spec.rb ├── models │ ├── ability_spec.rb │ ├── oauth_account_spec.rb │ ├── project_decorator_spec.rb │ ├── project_spec.rb │ ├── status │ │ ├── base_spec.rb │ │ ├── circleci_spec.rb │ │ ├── codeship_spec.rb │ │ └── travis_spec.rb │ └── user_spec.rb ├── rails_helper.rb ├── services │ └── .keep ├── spec_helper.rb └── support │ ├── api_helper.rb │ └── oauth_helper.rb └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.env.sample: -------------------------------------------------------------------------------- 1 | WWW_HOSTNAME=localhost:3000 2 | GITHUB_CLIENT_ID= 3 | GITHUB_CLIENT_SECRET= 4 | REDISCLOUD_URL=redis://localhost:6379 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/*.log 16 | /tmp 17 | 18 | # Ignore all bundled gems 19 | /vendor/bundle 20 | 21 | .env 22 | /coverage/ 23 | .todo 24 | 25 | spec/examples.txt 26 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: 2 | - .ruby-style.yml 3 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.3.0 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | sudo: false 3 | rvm: 4 | - 2.3.0 5 | cache: 6 | bundler: true 7 | bundler_args: --jobs=3 --retry=3 --without development 8 | before_script: 9 | - psql -c 'create database dasherize_test;' -U postgres 10 | - bin/setup 11 | script: 12 | - bin/rspec 13 | services: 14 | - redis-server 15 | notifications: 16 | email: false 17 | slack: 18 | secure: gTDbTpguWJc40Nh++6wC8PqXgKZ4opBvadPCWs7i4JVR2cn6E7aPrVQCOXpJK67glrUQfCYbj8uP2+EcbyZikVsRwoBM1ACRkpycPpzhNrirU3V8UBAMn3yHa1/xE0/gEmlA9D6MDiRxtj77i38ncaevTUHC/WW9A7aZtbx/k+t+NQnLnkShUdk2T1M8EJ7RYz9yOLC5Bs2Gcu+r176DoeO03y5viYZW4BR/ZbR1jJ194oCN4qA3RHMrnjj7hnY2nq0MzGLw+Gemck9/yrl1acJ1eNuBNMgpUyOJHwCv9X0yv0r3ONZfZT8H7ontB0gkIMN3pGJD7b9uFs5vbXiJajdraSzQRPSqk7BoNtfq1PLkDRru+gupa0EsFAvt366m0dPmM1cv020XhbJ2kJz+8LNqJGhWUw97iJAHesqYkmPnAt6z8WU5WedpE4qYjPHVi3v39k1W7OdrlFFfwY+1vSSDwJ+9YLY9UyeyVwqb6zKvxhxBtCOYwJ74voL8X0oANNrxALuMZp4XfPOGxK5vGXTVSYy3xPQYkhrjWNmwT5P8LhdEYSmlFyjPonoIo47uH4yyYMhf0nMB4yMf9QyGGdiCEqfLZdY1lKw+6l8wZH4KznTrV58yn60erAy1+jSn8Gd7/6aZfWrqdcVHBTrzLixJdAyIwbiBMQVPS/jym5A= 19 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for contributing! We :heart: pull requests from everyone. 4 | 5 | We have a few technical guidelines to follow: 6 | 7 | 1. Open an issue to discuss a new feature. 8 | 2. Write tests. 9 | 3. Make sure the entire test suite passes locally and on CI. 10 | 4. Update your code according to `rubocop` preferences. 11 | 5. Open a pull request. 12 | 6. Participate in the reviews on the pull request. 13 | 14 | For pure documentation changes, please add `[ci skip]` to your commit message, 15 | to prevent CI server from needless :runner:. 16 | 17 | ## Configure Your Local Development Environment 18 | 19 | After cloning this repository, run the setup script: 20 | 21 | `bin/setup` 22 | 23 | Prepare your `ENV` variables accordingly with `.env.sample`. 24 | 25 | ## Testing 26 | 27 | Set up your development environment as per previous section. 28 | 29 | Run `bin/rspec` to execute the full test suite. 30 | 31 | ## Style Checks 32 | 33 | ### Ruby 34 | 35 | Run style checks with `bundle exec rubocop -DR` 36 | 37 | ## Contributor License Agreement 38 | 39 | If you submit a contribution to this application's source code, you hereby grant 40 | it under MIT LICENSE. See [LICENSE](/LICENSE.md). 41 | -------------------------------------------------------------------------------- /DEPLOYMENT.md: -------------------------------------------------------------------------------- 1 | # Deployment 2 | 3 | Host on Heroku. [Integrated with GitHub](https://devcenter.heroku.com/articles/github-integration). 4 | 5 | ## Deploy to Staging 6 | 7 | git push staging master 8 | 9 | ## Deploy a Feature Branch to Staging 10 | 11 | Suppose feature branch name: `feature/awesome`: 12 | 13 | git push staging feature/awesome:master 14 | 15 | ## Deploy to Production 16 | 17 | git push production master 18 | 19 | For more information, please refer to [Heroku Docs](https://devcenter.heroku.com/articles/git). 20 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | ruby "2.3.0" 4 | 5 | gem "rails", "~> 4.2.5.2" 6 | 7 | # Server and Database 8 | gem "puma" 9 | gem "pg" 10 | 11 | # HTML, CSS and JavaScript Stack 12 | gem "slim-rails" 13 | gem "sass-rails" 14 | gem "uglifier" 15 | gem "coffee-rails" 16 | gem "jquery-rails" 17 | gem "turbolinks", git: "https://github.com/turbolinks/turbolinks-classic.git" 18 | gem "jquery-turbolinks" 19 | 20 | gem "materialize-sass" 21 | gem "font-awesome-sass" 22 | 23 | gem "http" 24 | gem "rack-cors", require: "rack/cors" # CORS for fonts through Cloudfront 25 | 26 | # Cache 27 | gem "dalli" 28 | gem "redis-rails" 29 | gem "redis", require: ["redis", "redis/connection/hiredis"] 30 | gem "hiredis" 31 | 32 | # App Specific 33 | gem "devise", ">= 3.5.4" 34 | gem "rolify" 35 | gem "cancancan" 36 | 37 | gem "simple_form" 38 | 39 | gem "rails_utils" 40 | gem "local_time" 41 | 42 | gem "octokit" 43 | gem "faraday-http-cache" 44 | gem "codeship" 45 | gem "parallel" 46 | 47 | # Third party logins 48 | gem "omniauth" 49 | gem "omniauth-github" 50 | 51 | group :staging, :production do 52 | gem "rails_12factor" 53 | gem "heroku-deflater" 54 | gem "newrelic_rpm" 55 | end 56 | 57 | group :staging do 58 | gem "recipient_interceptor" 59 | end 60 | 61 | group :development, :test do 62 | gem "dotenv-rails" 63 | gem "pry-rails" 64 | gem "pry-byebug" 65 | gem "rspec-rails" 66 | gem "rubocop", require: false 67 | gem "shoulda", require: false 68 | gem "factory_girl_rails" 69 | gem "faker" 70 | gem "capybara" 71 | gem "selenium-webdriver" 72 | gem "launchy" 73 | gem "email_spec" 74 | end 75 | 76 | group :development do 77 | gem "spring" 78 | gem "spring-commands-rspec" 79 | gem "quiet_assets" 80 | gem "better_errors" 81 | gem "binding_of_caller" 82 | gem "letter_opener" 83 | gem "bullet" 84 | gem "benchmark-ips" 85 | 86 | # Guards 87 | gem "guard-rspec", require: false 88 | gem "guard-livereload" 89 | gem "rack-livereload" 90 | end 91 | 92 | group :test do 93 | gem "database_rewinder" 94 | gem "webmock" 95 | end 96 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: https://github.com/turbolinks/turbolinks-classic.git 3 | revision: 37a7c296232d20a61bd1946f600da7f2009189db 4 | specs: 5 | turbolinks (3.0.0) 6 | coffee-rails 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | actionmailer (4.2.5.2) 12 | actionpack (= 4.2.5.2) 13 | actionview (= 4.2.5.2) 14 | activejob (= 4.2.5.2) 15 | mail (~> 2.5, >= 2.5.4) 16 | rails-dom-testing (~> 1.0, >= 1.0.5) 17 | actionpack (4.2.5.2) 18 | actionview (= 4.2.5.2) 19 | activesupport (= 4.2.5.2) 20 | rack (~> 1.6) 21 | rack-test (~> 0.6.2) 22 | rails-dom-testing (~> 1.0, >= 1.0.5) 23 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 24 | actionview (4.2.5.2) 25 | activesupport (= 4.2.5.2) 26 | builder (~> 3.1) 27 | erubis (~> 2.7.0) 28 | rails-dom-testing (~> 1.0, >= 1.0.5) 29 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 30 | activejob (4.2.5.2) 31 | activesupport (= 4.2.5.2) 32 | globalid (>= 0.3.0) 33 | activemodel (4.2.5.2) 34 | activesupport (= 4.2.5.2) 35 | builder (~> 3.1) 36 | activerecord (4.2.5.2) 37 | activemodel (= 4.2.5.2) 38 | activesupport (= 4.2.5.2) 39 | arel (~> 6.0) 40 | activesupport (4.2.5.2) 41 | i18n (~> 0.7) 42 | json (~> 1.7, >= 1.7.7) 43 | minitest (~> 5.1) 44 | thread_safe (~> 0.3, >= 0.3.4) 45 | tzinfo (~> 1.1) 46 | addressable (2.4.0) 47 | arel (6.0.3) 48 | ast (2.2.0) 49 | bcrypt (3.1.11) 50 | benchmark-ips (2.5.0) 51 | better_errors (2.1.1) 52 | coderay (>= 1.0.0) 53 | erubis (>= 2.6.6) 54 | rack (>= 0.9.0) 55 | binding_of_caller (0.7.2) 56 | debug_inspector (>= 0.0.1) 57 | builder (3.2.2) 58 | bullet (5.0.0) 59 | activesupport (>= 3.0.0) 60 | uniform_notifier (~> 1.9.0) 61 | byebug (8.2.2) 62 | cancancan (1.13.1) 63 | capybara (2.6.2) 64 | addressable 65 | mime-types (>= 1.16) 66 | nokogiri (>= 1.3.3) 67 | rack (>= 1.0.0) 68 | rack-test (>= 0.5.4) 69 | xpath (~> 2.0) 70 | childprocess (0.5.9) 71 | ffi (~> 1.0, >= 1.0.11) 72 | coderay (1.1.1) 73 | codeship (0.1.3) 74 | json 75 | coffee-rails (4.1.1) 76 | coffee-script (>= 2.2.0) 77 | railties (>= 4.0.0, < 5.1.x) 78 | coffee-script (2.4.1) 79 | coffee-script-source 80 | execjs 81 | coffee-script-source (1.10.0) 82 | concurrent-ruby (1.0.1) 83 | crack (0.4.3) 84 | safe_yaml (~> 1.0.0) 85 | dalli (2.7.6) 86 | database_rewinder (0.5.3) 87 | debug_inspector (0.0.2) 88 | devise (3.5.6) 89 | bcrypt (~> 3.0) 90 | orm_adapter (~> 0.1) 91 | railties (>= 3.2.6, < 5) 92 | responders 93 | thread_safe (~> 0.1) 94 | warden (~> 1.2.3) 95 | diff-lcs (1.2.5) 96 | domain_name (0.5.20160309) 97 | unf (>= 0.0.5, < 1.0.0) 98 | dotenv (2.1.0) 99 | dotenv-rails (2.1.0) 100 | dotenv (= 2.1.0) 101 | railties (>= 4.0, < 5.1) 102 | em-websocket (0.5.1) 103 | eventmachine (>= 0.12.9) 104 | http_parser.rb (~> 0.6.0) 105 | email_spec (2.0.0) 106 | htmlentities (~> 4.3.3) 107 | launchy (~> 2.1) 108 | mail (~> 2.6.3) 109 | erubis (2.7.0) 110 | eventmachine (1.2.0.1) 111 | execjs (2.6.0) 112 | factory_girl (4.5.0) 113 | activesupport (>= 3.0.0) 114 | factory_girl_rails (4.6.0) 115 | factory_girl (~> 4.5.0) 116 | railties (>= 3.0.0) 117 | faker (1.6.3) 118 | i18n (~> 0.5) 119 | faraday (0.9.2) 120 | multipart-post (>= 1.2, < 3) 121 | faraday-http-cache (1.2.2) 122 | faraday (~> 0.8) 123 | ffi (1.9.10) 124 | font-awesome-sass (4.5.0) 125 | sass (>= 3.2) 126 | formatador (0.2.5) 127 | globalid (0.3.6) 128 | activesupport (>= 4.1.0) 129 | guard (2.13.0) 130 | formatador (>= 0.2.4) 131 | listen (>= 2.7, <= 4.0) 132 | lumberjack (~> 1.0) 133 | nenv (~> 0.1) 134 | notiffany (~> 0.0) 135 | pry (>= 0.9.12) 136 | shellany (~> 0.0) 137 | thor (>= 0.18.1) 138 | guard-compat (1.2.1) 139 | guard-livereload (2.5.2) 140 | em-websocket (~> 0.5) 141 | guard (~> 2.8) 142 | guard-compat (~> 1.0) 143 | multi_json (~> 1.8) 144 | guard-rspec (4.6.4) 145 | guard (~> 2.1) 146 | guard-compat (~> 1.1) 147 | rspec (>= 2.99.0, < 4.0) 148 | hashdiff (0.3.0) 149 | hashie (3.4.3) 150 | heroku-deflater (0.6.2) 151 | rack (>= 1.4.5) 152 | hiredis (0.6.1) 153 | htmlentities (4.3.4) 154 | http (1.0.3) 155 | addressable (~> 2.3) 156 | http-cookie (~> 1.0) 157 | http-form_data (~> 1.0.1) 158 | http_parser.rb (~> 0.6.0) 159 | http-cookie (1.0.2) 160 | domain_name (~> 0.5) 161 | http-form_data (1.0.1) 162 | http_parser.rb (0.6.0) 163 | i18n (0.7.0) 164 | jquery-rails (4.1.1) 165 | rails-dom-testing (>= 1, < 3) 166 | railties (>= 4.2.0) 167 | thor (>= 0.14, < 2.0) 168 | jquery-turbolinks (2.1.0) 169 | railties (>= 3.1.0) 170 | turbolinks 171 | json (1.8.3) 172 | jwt (1.5.1) 173 | launchy (2.4.3) 174 | addressable (~> 2.3) 175 | letter_opener (1.4.1) 176 | launchy (~> 2.2) 177 | listen (3.0.6) 178 | rb-fsevent (>= 0.9.3) 179 | rb-inotify (>= 0.9.7) 180 | local_time (1.0.3) 181 | coffee-rails 182 | loofah (2.0.3) 183 | nokogiri (>= 1.5.9) 184 | lumberjack (1.0.10) 185 | mail (2.6.3) 186 | mime-types (>= 1.16, < 3) 187 | materialize-sass (0.97.5) 188 | sass (~> 3.3) 189 | method_source (0.8.2) 190 | mime-types (2.99.1) 191 | mini_portile2 (2.0.0) 192 | minitest (5.8.4) 193 | multi_json (1.11.2) 194 | multi_xml (0.5.5) 195 | multipart-post (2.0.0) 196 | nenv (0.3.0) 197 | newrelic_rpm (3.15.0.314) 198 | nokogiri (1.6.7.2) 199 | mini_portile2 (~> 2.0.0.rc2) 200 | notiffany (0.0.8) 201 | nenv (~> 0.1) 202 | shellany (~> 0.0) 203 | oauth2 (1.1.0) 204 | faraday (>= 0.8, < 0.10) 205 | jwt (~> 1.0, < 1.5.2) 206 | multi_json (~> 1.3) 207 | multi_xml (~> 0.5) 208 | rack (>= 1.2, < 3) 209 | octokit (4.3.0) 210 | sawyer (~> 0.7.0, >= 0.5.3) 211 | omniauth (1.3.1) 212 | hashie (>= 1.2, < 4) 213 | rack (>= 1.0, < 3) 214 | omniauth-github (1.1.2) 215 | omniauth (~> 1.0) 216 | omniauth-oauth2 (~> 1.1) 217 | omniauth-oauth2 (1.4.0) 218 | oauth2 (~> 1.0) 219 | omniauth (~> 1.2) 220 | orm_adapter (0.5.0) 221 | parallel (1.6.2) 222 | parser (2.3.0.6) 223 | ast (~> 2.2) 224 | pg (0.18.4) 225 | powerpack (0.1.1) 226 | pry (0.10.3) 227 | coderay (~> 1.1.0) 228 | method_source (~> 0.8.1) 229 | slop (~> 3.4) 230 | pry-byebug (3.3.0) 231 | byebug (~> 8.0) 232 | pry (~> 0.10) 233 | pry-rails (0.3.4) 234 | pry (>= 0.9.10) 235 | puma (3.1.0) 236 | quiet_assets (1.1.0) 237 | railties (>= 3.1, < 5.0) 238 | rack (1.6.4) 239 | rack-cors (0.4.0) 240 | rack-livereload (0.3.16) 241 | rack 242 | rack-test (0.6.3) 243 | rack (>= 1.0) 244 | rails (4.2.5.2) 245 | actionmailer (= 4.2.5.2) 246 | actionpack (= 4.2.5.2) 247 | actionview (= 4.2.5.2) 248 | activejob (= 4.2.5.2) 249 | activemodel (= 4.2.5.2) 250 | activerecord (= 4.2.5.2) 251 | activesupport (= 4.2.5.2) 252 | bundler (>= 1.3.0, < 2.0) 253 | railties (= 4.2.5.2) 254 | sprockets-rails 255 | rails-deprecated_sanitizer (1.0.3) 256 | activesupport (>= 4.2.0.alpha) 257 | rails-dom-testing (1.0.7) 258 | activesupport (>= 4.2.0.beta, < 5.0) 259 | nokogiri (~> 1.6.0) 260 | rails-deprecated_sanitizer (>= 1.0.1) 261 | rails-html-sanitizer (1.0.3) 262 | loofah (~> 2.0) 263 | rails_12factor (0.0.3) 264 | rails_serve_static_assets 265 | rails_stdout_logging 266 | rails_serve_static_assets (0.0.5) 267 | rails_stdout_logging (0.0.4) 268 | rails_utils (3.3.5) 269 | rails (>= 3.2) 270 | railties (4.2.5.2) 271 | actionpack (= 4.2.5.2) 272 | activesupport (= 4.2.5.2) 273 | rake (>= 0.8.7) 274 | thor (>= 0.18.1, < 2.0) 275 | rainbow (2.1.0) 276 | rake (11.1.1) 277 | rb-fsevent (0.9.7) 278 | rb-inotify (0.9.7) 279 | ffi (>= 0.5.0) 280 | recipient_interceptor (0.1.2) 281 | mail 282 | redis (3.2.2) 283 | redis-actionpack (4.0.1) 284 | actionpack (~> 4) 285 | redis-rack (~> 1.5.0) 286 | redis-store (~> 1.1.0) 287 | redis-activesupport (4.1.5) 288 | activesupport (>= 3, < 5) 289 | redis-store (~> 1.1.0) 290 | redis-rack (1.5.0) 291 | rack (~> 1.5) 292 | redis-store (~> 1.1.0) 293 | redis-rails (4.0.0) 294 | redis-actionpack (~> 4) 295 | redis-activesupport (~> 4) 296 | redis-store (~> 1.1.0) 297 | redis-store (1.1.7) 298 | redis (>= 2.2) 299 | responders (2.1.1) 300 | railties (>= 4.2.0, < 5.1) 301 | rolify (5.0.0) 302 | rspec (3.4.0) 303 | rspec-core (~> 3.4.0) 304 | rspec-expectations (~> 3.4.0) 305 | rspec-mocks (~> 3.4.0) 306 | rspec-core (3.4.4) 307 | rspec-support (~> 3.4.0) 308 | rspec-expectations (3.4.0) 309 | diff-lcs (>= 1.2.0, < 2.0) 310 | rspec-support (~> 3.4.0) 311 | rspec-mocks (3.4.1) 312 | diff-lcs (>= 1.2.0, < 2.0) 313 | rspec-support (~> 3.4.0) 314 | rspec-rails (3.4.2) 315 | actionpack (>= 3.0, < 4.3) 316 | activesupport (>= 3.0, < 4.3) 317 | railties (>= 3.0, < 4.3) 318 | rspec-core (~> 3.4.0) 319 | rspec-expectations (~> 3.4.0) 320 | rspec-mocks (~> 3.4.0) 321 | rspec-support (~> 3.4.0) 322 | rspec-support (3.4.1) 323 | rubocop (0.38.0) 324 | parser (>= 2.3.0.6, < 3.0) 325 | powerpack (~> 0.1) 326 | rainbow (>= 1.99.1, < 3.0) 327 | ruby-progressbar (~> 1.7) 328 | unicode-display_width (~> 1.0, >= 1.0.1) 329 | ruby-progressbar (1.7.5) 330 | rubyzip (1.2.0) 331 | safe_yaml (1.0.4) 332 | sass (3.4.21) 333 | sass-rails (5.0.4) 334 | railties (>= 4.0.0, < 5.0) 335 | sass (~> 3.1) 336 | sprockets (>= 2.8, < 4.0) 337 | sprockets-rails (>= 2.0, < 4.0) 338 | tilt (>= 1.1, < 3) 339 | sawyer (0.7.0) 340 | addressable (>= 2.3.5, < 2.5) 341 | faraday (~> 0.8, < 0.10) 342 | selenium-webdriver (2.53.0) 343 | childprocess (~> 0.5) 344 | rubyzip (~> 1.0) 345 | websocket (~> 1.0) 346 | shellany (0.0.1) 347 | shoulda (3.5.0) 348 | shoulda-context (~> 1.0, >= 1.0.1) 349 | shoulda-matchers (>= 1.4.1, < 3.0) 350 | shoulda-context (1.2.1) 351 | shoulda-matchers (2.8.0) 352 | activesupport (>= 3.0.0) 353 | simple_form (3.2.1) 354 | actionpack (> 4, < 5.1) 355 | activemodel (> 4, < 5.1) 356 | slim (3.0.6) 357 | temple (~> 0.7.3) 358 | tilt (>= 1.3.3, < 2.1) 359 | slim-rails (3.0.1) 360 | actionmailer (>= 3.1, < 5.0) 361 | actionpack (>= 3.1, < 5.0) 362 | activesupport (>= 3.1, < 5.0) 363 | railties (>= 3.1, < 5.0) 364 | slim (~> 3.0) 365 | slop (3.6.0) 366 | spring (1.6.4) 367 | spring-commands-rspec (1.0.4) 368 | spring (>= 0.9.1) 369 | sprockets (3.5.2) 370 | concurrent-ruby (~> 1.0) 371 | rack (> 1, < 3) 372 | sprockets-rails (3.0.4) 373 | actionpack (>= 4.0) 374 | activesupport (>= 4.0) 375 | sprockets (>= 3.0.0) 376 | temple (0.7.6) 377 | thor (0.19.1) 378 | thread_safe (0.3.5) 379 | tilt (2.0.2) 380 | tzinfo (1.2.2) 381 | thread_safe (~> 0.1) 382 | uglifier (2.7.2) 383 | execjs (>= 0.3.0) 384 | json (>= 1.8.0) 385 | unf (0.1.4) 386 | unf_ext 387 | unf_ext (0.0.7.2) 388 | unicode-display_width (1.0.2) 389 | uniform_notifier (1.9.0) 390 | warden (1.2.6) 391 | rack (>= 1.0) 392 | webmock (1.24.2) 393 | addressable (>= 2.3.6) 394 | crack (>= 0.3.2) 395 | hashdiff 396 | websocket (1.2.2) 397 | xpath (2.0.0) 398 | nokogiri (~> 1.3) 399 | 400 | PLATFORMS 401 | ruby 402 | 403 | DEPENDENCIES 404 | benchmark-ips 405 | better_errors 406 | binding_of_caller 407 | bullet 408 | cancancan 409 | capybara 410 | codeship 411 | coffee-rails 412 | dalli 413 | database_rewinder 414 | devise (>= 3.5.4) 415 | dotenv-rails 416 | email_spec 417 | factory_girl_rails 418 | faker 419 | faraday-http-cache 420 | font-awesome-sass 421 | guard-livereload 422 | guard-rspec 423 | heroku-deflater 424 | hiredis 425 | http 426 | jquery-rails 427 | jquery-turbolinks 428 | launchy 429 | letter_opener 430 | local_time 431 | materialize-sass 432 | newrelic_rpm 433 | octokit 434 | omniauth 435 | omniauth-github 436 | parallel 437 | pg 438 | pry-byebug 439 | pry-rails 440 | puma 441 | quiet_assets 442 | rack-cors 443 | rack-livereload 444 | rails (~> 4.2.5.2) 445 | rails_12factor 446 | rails_utils 447 | recipient_interceptor 448 | redis 449 | redis-rails 450 | rolify 451 | rspec-rails 452 | rubocop 453 | sass-rails 454 | selenium-webdriver 455 | shoulda 456 | simple_form 457 | slim-rails 458 | spring 459 | spring-commands-rspec 460 | turbolinks! 461 | uglifier 462 | webmock 463 | 464 | BUNDLED WITH 465 | 1.11.2 466 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # Note: The cmd option is now required due to the increasing number of ways 2 | # rspec may be run, below are examples of the most common uses. 3 | # * bundler: 'bundle exec rspec' 4 | # * bundler binstubs: 'bin/rspec' 5 | # * spring: 'bin/rspec' (This will use spring if running and you have 6 | # installed the spring binstubs per the docs) 7 | # * zeus: 'zeus rspec' (requires the server to be started separately) 8 | # * 'just' rspec: 'rspec' 9 | guard :rspec, cmd: 'bin/rspec' do 10 | watch(%r{^spec/.+_spec\.rb$}) 11 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } 12 | watch('spec/spec_helper.rb') { "spec" } 13 | 14 | # Rails example 15 | watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } 16 | watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } 17 | watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } 18 | watch(%r{^spec/support/(.+)\.rb$}) { "spec" } 19 | watch('config/routes.rb') { "spec/routing" } 20 | watch('app/controllers/application_controller.rb') { "spec/controllers" } 21 | watch('spec/rails_helper.rb') { "spec" } 22 | 23 | # Capybara features specs 24 | watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" } 25 | 26 | # Turnip features and steps 27 | watch(%r{^spec/acceptance/(.+)\.feature$}) 28 | watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } 29 | end 30 | 31 | guard 'livereload' do 32 | watch(%r{app/views/.+\.(slim)$}) 33 | watch(%r{app/helpers/.+\.rb}) 34 | watch(%r{public/.+\.(css|js|html)}) 35 | watch(%r{config/locales/.+\.yml}) 36 | # Rails Assets Pipeline 37 | watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|js|scss|coffee|html|png|jpg|gif))).*}) { |m| "/assets/#{m[3]}" } 38 | end 39 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jolly Good Code 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb -p $PORT 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dasherize 2 | 3 | [![Build Status](https://travis-ci.org/jollygoodcode/dasherize.svg?branch=master)](https://travis-ci.org/jollygoodcode/dasherize) 4 | [![Code Climate](https://codeclimate.com/github/jollygoodcode/dasherize/badges/gpa.svg)](https://codeclimate.com/github/jollygoodcode/dasherize) 5 | 6 | Dasherize is a beautiful material-based dashboard for your projects which provides you with an overview of open Pull Requests and Issues, and statuses of your CI, all on a single web page. 7 | 8 | Currently supports GitHub, Travis, CodeShip and CircleCI. 9 | 10 | Take it for a spin on [http://dasherize.com](http://dasherize.com). See [our blog](https://github.com/jollygoodcode/jollygoodcode.github.io/issues/5) for some technical details. 11 | 12 | ![screen shot 2015-09-30 at 11 12 13 pm](https://cloud.githubusercontent.com/assets/2112/10197301/712d75ce-67c9-11e5-8dca-563266c1a6ad.png) 13 | 14 | ## Origin 15 | 16 | Dasherize was created as an alternative to [ProjectMonitor](https://github.com/pivotal/projectmonitor) and [Dashing](https://github.com/Shopify/dashing) because [@winstonyw](https://www.twitter.com/winstonyw) wanted a dashboard that displays GitHub Pull Requests and Issues count, and Continuous Integration status out of the box. 17 | 18 | [@winstonyw](https://www.twitter.com/winstonyw) reviews Dasherize every morning over a cup of tea, to check on the CI status of projects and clear the backlog of PRs and Issues. 19 | 20 | At the same time, a Presentation mode is made available for putting up Dasherize as a dashboard on a big screen monitor or TV, so that the projects' statuses are visible for all to see. 21 | 22 | Please feel free to: 23 | 24 | - Use Dasherize as a service on [http://dasherize.com](http://dasherize.com) 25 | - Deploy a copy of Dasherize on Heroku 26 | 27 | ## Deploy on Heroku 28 | 29 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) 30 | 31 | Once deployed on Heroku, you'll have to set up some ENV variables. 32 | 33 | ### ENV 34 | 35 | `WWW_HOSTNAME` is your heroku app url (without scheme) for `config/application.rb`: 36 | 37 | ``` 38 | WWW_HOSTNAME=.herokuapp.com 39 | ``` 40 | 41 | You will also need to register a GitHub OAuth application. 42 | 43 | Go to [Applications](https://github.com/settings/applications/new) and 44 | fill in the details: 45 | 46 | - Application Name: Dasherize 47 | - Homepage URL: `http://.herokuapp.com` 48 | - Authorization Callback URL: `http://.herokuapp.com` 49 | 50 | On the confirmation screen, copy the Client ID and Client Secret and set 51 | `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET`: 52 | 53 | ``` 54 | GITHUB_CLIENT_ID= 55 | GITHUB_CLIENT_SECRET= 56 | ``` 57 | 58 | For performance, these are settings which you can use: 59 | 60 | ``` 61 | WEB_CONCURRENCY=2 62 | WEB_MAX_THREADS=15 63 | DB_POOL=15 64 | DB_REAPING_FREQUENCY=10 65 | RUBY_GC_HEAP_INIT_SLOTS=500000 66 | RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=0.9 67 | ``` 68 | 69 | ## Wish List 70 | 71 | - Production/Staging/Master commit SHA and how far apart are they? 72 | - CI status of Pull Requests 73 | - Health of PivotalTracker Project 74 | - Bugs Count (when not using [RuntimeError.net](http://runtimeerror.net/)) 75 | 76 | ## Contributing 77 | 78 | Please see the [CONTRIBUTING.md](/CONTRIBUTING.md) file. 79 | 80 | ## Deployment 81 | 82 | Please see the [DEPLOYMENT.md](/DEPLOYMENT.md) file. 83 | 84 | ## Credits 85 | 86 | A huge THANK YOU to all our [contributors](https://github.com/jollygoodcode/dasherize/graphs/contributors)! :heart: 87 | 88 | ## License 89 | 90 | Please see the [LICENSE.md](/LICENSE.md) file. 91 | 92 | ## Maintained by Jolly Good Code 93 | 94 | [![Jolly Good Code](https://cloud.githubusercontent.com/assets/1000669/9362336/72f9c406-46d2-11e5-94de-5060e83fcf83.jpg)](http://www.jollygoodcode.com) 95 | 96 | We specialise in rapid development of high quality MVPs. [Hire us](http://www.jollygoodcode.com/#get-in-touch) to turn your product idea into reality. 97 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path("../config/application", __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dasherize", 3 | "description": "Dasherize is a simple and beautiful material-based dashboard for your projects.", 4 | "repository": "https://github.com/jollygoodcode/dasherize", 5 | "logo": "https://cloud.githubusercontent.com/assets/1000669/10200839/41b802ac-67db-11e5-8427-3da4b1264fe4.jpg", 6 | "keywords": ["ruby", "rails", "dashboard", "github", "travis", "codeship", "ci"], 7 | "env": { 8 | "WWW_HOSTNAME": { 9 | "description": "Your Heroku App url without scheme (**app-name.herokuapp.com**)", 10 | "required": true 11 | }, 12 | "GITHUB_CLIENT_ID": { 13 | "description": "OAuth application's unique Client ID: https://developer.github.com/v3/oauth", 14 | "required": true 15 | }, 16 | "GITHUB_CLIENT_SECRET": { 17 | "description": "OAuth application's unique Client Secret: https://developer.github.com/v3/oauth", 18 | "required": true 19 | } 20 | }, 21 | "image": "heroku/ruby", 22 | "addons": [ 23 | "rediscloud:30" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /app/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/app/assets/images/favicon.ico -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | //= require jquery 2 | //= require jquery.turbolinks 3 | //= require jquery_ujs 4 | //= require turbolinks 5 | //= require materialize-sprockets 6 | //= require local_time 7 | //= require dasherize 8 | //= require_directory ./shared 9 | //= require_directory ./application 10 | -------------------------------------------------------------------------------- /app/assets/javascripts/application/home.js: -------------------------------------------------------------------------------- 1 | window.Dasherize.home = { 2 | init: function() { 3 | } 4 | }; 5 | -------------------------------------------------------------------------------- /app/assets/javascripts/application/projects.js: -------------------------------------------------------------------------------- 1 | window.Dasherize.projects = { 2 | init_index: function() { 3 | $(".js-project").not('.in-progress').addClass('in-progress').click(); 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /app/assets/javascripts/dasherize.js: -------------------------------------------------------------------------------- 1 | window.Dasherize = { 2 | init: function() { 3 | Turbolinks.ProgressBar.enable(); 4 | 5 | // Material Forms 6 | $('select').material_select(); 7 | 8 | // Material SideNav for Mobile View 9 | $(".button-collapse").sideNav(); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /app/assets/javascripts/shared/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/app/assets/javascripts/shared/.keep -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | @import "materialize"; 2 | 3 | @import "font-awesome-sprockets"; 4 | @import "font-awesome"; 5 | 6 | @import "shared/base"; 7 | 8 | @import "application/base"; 9 | @import "application/home"; 10 | @import "application/projects"; 11 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application/base.scss: -------------------------------------------------------------------------------- 1 | nav { 2 | background-color: color('light-blue', 'lighten-2'); 3 | } 4 | 5 | footer.page-footer { 6 | background-color: color('light-blue', 'lighten-2'); 7 | } 8 | 9 | .input-field { 10 | @extend .col; 11 | @extend .s12; 12 | } 13 | 14 | .error-block { 15 | margin: -10px 0 0 0.75rem; 16 | 17 | font-size: 0.8rem; 18 | font-weight: 400; 19 | 20 | color: color('red', 'base'); 21 | } 22 | 23 | .help-block { 24 | margin: -10px 0 0 0; 25 | 26 | font-size: 0.8rem; 27 | font-style: italic; 28 | font-weight: 300; 29 | color: color('grey', 'base'); 30 | } 31 | 32 | .error-block + .help-block { 33 | margin-top: 2px; 34 | } 35 | 36 | 37 | .presentation { 38 | nav, 39 | footer, 40 | .card-content, 41 | .gear 42 | { 43 | display: none; 44 | } 45 | 46 | .mar-lg-top { 47 | margin: 0; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application/home.scss: -------------------------------------------------------------------------------- 1 | .home.index { 2 | .call-to-actions { 3 | margin: 40px 0 60px 0; 4 | } 5 | 6 | .btn-try a { 7 | background-color: color("amber", "darken-2") 8 | } 9 | 10 | .btn-code a { 11 | background-color: color("amber", "darken-2") 12 | } 13 | 14 | @media #{$small-and-down} { 15 | .btn-try, 16 | .btn-code { 17 | text-align: center; 18 | } 19 | 20 | .btn-try { 21 | margin-bottom: 10px; 22 | } 23 | } 24 | 25 | @media #{$medium-and-up} { 26 | .btn-try { 27 | text-align: right; 28 | } 29 | .btn-code { 30 | text-align: left; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application/projects.scss: -------------------------------------------------------------------------------- 1 | .project { 2 | .card-panel { 3 | font-weight: 300; 4 | } 5 | 6 | .card-heading { 7 | padding: 20px; 8 | color: white; 9 | 10 | a { 11 | color: white; 12 | } 13 | 14 | a:hover { 15 | text-decoration: underline; 16 | } 17 | } 18 | 19 | .card-title { 20 | overflow: hidden; 21 | 22 | .name { 23 | font-size: 2.5rem; 24 | line-height: 3rem; 25 | } 26 | 27 | .org_user { 28 | font-size: 1.2rem; 29 | font-weight: 200; 30 | } 31 | 32 | .right a { color: color("grey", "lighten-1") } 33 | } 34 | 35 | .card-overview { 36 | display: block; 37 | overflow: hidden; 38 | 39 | padding: 40px 0 0 0; 40 | } 41 | 42 | .number { 43 | font-size: 2.75rem; 44 | font-weight: 200; 45 | line-height: 1.25em; 46 | } 47 | 48 | .number-title { 49 | font-size: 1.25rem; 50 | font-weight: 200; 51 | } 52 | 53 | .card-status { 54 | padding: 5px 20px; 55 | color: white; 56 | 57 | font-size: 0.65rem; 58 | font-style: italic; 59 | 60 | a, 61 | a:visited { 62 | color: white; 63 | 64 | &:hover { 65 | text-decoration: underline; 66 | } 67 | } 68 | } 69 | 70 | .card-content { 71 | padding: 20px; 72 | 73 | font-size: 0.9rem; 74 | } 75 | 76 | .collection { 77 | min-height: 161px; 78 | border: none; 79 | 80 | margin-bottom: 0; 81 | 82 | .collection-item { 83 | line-height: 1.1rem; 84 | padding: 10px 20px; 85 | 86 | // Avatar Collection 87 | &.avatar { 88 | min-height: 0; 89 | padding-left: 45px; 90 | 91 | .circle { 92 | width: 32px; 93 | height: 32px; 94 | left: 0; 95 | } 96 | 97 | .title, p { 98 | font-size: 0.85rem 99 | } 100 | } 101 | } 102 | } 103 | } 104 | 105 | .card-heading { 106 | &.card-default { 107 | background: color("blue", "darken-1"); 108 | } 109 | &.card-success { 110 | background: color("green", "darken-1"); 111 | } 112 | &.card-in-progress { 113 | background: color("yellow", "darken-1"); 114 | } 115 | &.card-failure { 116 | background: color("red", "darken-1"); 117 | } 118 | } 119 | 120 | .card-status { 121 | height: 25px; 122 | 123 | &.card-default { 124 | background: color("blue", "darken-3"); 125 | } 126 | &.card-success { 127 | background: color("green", "darken-3"); 128 | } 129 | &.card-in-progress { 130 | @extend .progress; 131 | height: 25px; 132 | margin: 0; 133 | 134 | background-color: lighten(color("yellow", "darken-3"), 40%); 135 | 136 | .indeterminate { 137 | background-color: color("yellow", "darken-3"); 138 | } 139 | } 140 | &.card-failure { 141 | background: color("red", "darken-3"); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /app/assets/stylesheets/shared/base.scss: -------------------------------------------------------------------------------- 1 | // vars for padding and margin 2 | $sizes : ((sm, 10px), (md, 20px), (lg, 40px)); 3 | $corners: (top, right, bottom, left); 4 | 5 | // margin 6 | // .mar-sm-top { margin-top: 10px } 7 | // .mar-md-top { margin-top: 20px } 8 | // .mar-lg-top { margin-top: 40px } 9 | // .mar-sm-right { margin-right: 10px } 10 | // .mar-md-right { margin-right: 20px } 11 | // .mar-lg-right { margin-right: 40px } 12 | // .mar-sm-bottom { margin-bottom: 10px } 13 | // .mar-md-bottom { margin-bottom: 20px } 14 | // .mar-lg-bottom { margin-bottom: 40px } 15 | // .mar-sm-left { margin-left: 10px } 16 | // .mar-md-left { margin-left: 20px } 17 | // .mar-lg-left { margin-left: 40px } 18 | @each $size in $sizes { 19 | @each $corner in $corners { 20 | .mar-#{nth($size, 1)}-#{$corner} { 21 | margin-#{$corner}: #{nth($size, 2)}; 22 | } 23 | } 24 | } 25 | 26 | // padding 27 | // .pad-sm-top { padding-top: 10px } 28 | // .pad-md-top { padding-top: 20px } 29 | // .pad-lg-top { padding-top: 40px } 30 | // .pad-sm-right { padding-right: 10px } 31 | // .pad-md-right { padding-right: 20px } 32 | // .pad-lg-right { padding-right: 40px } 33 | // .pad-sm-bottom { padding-bottom: 10px } 34 | // .pad-md-bottom { padding-bottom: 20px } 35 | // .pad-lg-bottom { padding-bottom: 40px } 36 | // .pad-sm-left { padding-left: 10px } 37 | // .pad-md-left { padding-left: 20px } 38 | // .pad-lg-left { padding-left: 40px } 39 | @each $size in $sizes { 40 | @each $corner in $corners { 41 | .pad-#{nth($size, 1)}-#{$corner} { 42 | padding-#{$corner}: #{nth($size, 2)}; 43 | } 44 | } 45 | } 46 | 47 | .none { 48 | display: none; 49 | } 50 | 51 | /* Overrides */ 52 | .table { 53 | margin-bottom: 0; 54 | 55 | tbody tr:first-child td { 56 | border: none; 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /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 | 6 | before_action :authenticate_user! 7 | before_action :configure_permitted_parameters, if: :devise_controller? 8 | 9 | # omniauth-* 10 | # rescue_from OAuth::Unauthorized, with: :oauth_failure 11 | 12 | rescue_from CanCan::AccessDenied do |exception| 13 | store_user_return_to_location 14 | respond_to_access_denied(exception) 15 | end 16 | 17 | protected 18 | 19 | def configure_permitted_parameters 20 | devise_parameter_sanitizer.for(:sign_up) << [:name] 21 | devise_parameter_sanitizer.for(:account_update) << [:name] 22 | end 23 | 24 | def oauth_failure 25 | flash.keep 26 | redirect_to new_user_session_path, flash: { error: "Sign in failed" } 27 | end 28 | 29 | # would love to use `store_location_for(:user, location)` 30 | # but devise opt to strip anchor tag i.e. `/path?x=y#abc` will be stored as `/path?x=y` 31 | # so we're manually performing `session['user_return_to'] = ...` 32 | def store_user_return_to_location 33 | session["user_return_to"] = [ 34 | params[:user_return_to], 35 | request.get? ? request.url : request.referrer, 36 | session["user_return_to"], 37 | ].find(&:present?) 38 | end 39 | 40 | def respond_to_access_denied(_exception) 41 | if request.format.html? && (!request.xhr?) 42 | if current_user 43 | redirect_to root_path, flash: { error: "Sorry, you're not authorized to do that." } 44 | else 45 | redirect_to new_user_session_path 46 | end 47 | else 48 | head :unauthorized 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/errors_controller.rb: -------------------------------------------------------------------------------- 1 | class ErrorsController < ApplicationController 2 | skip_before_action :authenticate_user! 3 | 4 | def show 5 | render view, status: status_code 6 | end 7 | 8 | private 9 | 10 | def view 11 | status_code 12 | end 13 | 14 | def status_code 15 | params[:code] || 500 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | skip_before_action :authenticate_user! 3 | before_action :redirect_to_dashboard, if: :user_signed_in? 4 | 5 | def index 6 | @projects = 7 | Rails.cache.fetch("home-index", expires_in: 24.hours) do 8 | [ 9 | Project.new(repo_name: "sinatra/sinatra", ci_type: :travis), 10 | Project.new(repo_name: "jollygoodcode/twemoji", ci_type: :travis), 11 | Project.new(repo_name: "winston/google_visualr", ci_type: :travis) 12 | ].map! { |project| ProjectDecorator.new(project) }.each(&:process_with) 13 | end 14 | end 15 | 16 | private 17 | 18 | def redirect_to_dashboard 19 | redirect_to projects_path 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/omniauth_callbacks_controller.rb: -------------------------------------------------------------------------------- 1 | class OmniauthCallbacksController < Devise::OmniauthCallbacksController 2 | def callback 3 | @user = OauthAccount.from_omniauth(request.env["omniauth.auth"]).user 4 | 5 | if @user.present? 6 | set_flash_message(:notice, :success, kind: params[:action].try(:titleize)) if is_navigational_format? 7 | sign_in_and_redirect(@user, event: :authentication) 8 | else 9 | set_flash_message(:alert, :failure, kind: params[:action].try(:titleize), reason: "something went wrong") if is_navigational_format? 10 | redirect_to root_path 11 | end 12 | end 13 | 14 | alias_method :github, :callback 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/projects_controller.rb: -------------------------------------------------------------------------------- 1 | class ProjectsController < ApplicationController 2 | load_and_authorize_resource param_method: :model_params 3 | 4 | def index 5 | @projects = @projects.order(:repo_name) 6 | end 7 | 8 | def new 9 | end 10 | 11 | def create 12 | if @project.save 13 | redirect_to projects_path, notice: "#{Project.model_name.human} was successfully created.", change: "projects:#{@project.id}" 14 | else 15 | render :new 16 | end 17 | end 18 | 19 | def show 20 | @project = ProjectDecorator.new(@project) 21 | @project.process_with(current_user.oauth_account.oauth_token) 22 | 23 | render change: "project:#{@project.id}" 24 | end 25 | 26 | def edit 27 | end 28 | 29 | def update 30 | if @project.update(model_params) 31 | redirect_to @project, notice: "#{Project.model_name.human} was successfully updated." 32 | else 33 | render :edit 34 | end 35 | end 36 | 37 | def destroy 38 | @project.destroy 39 | redirect_to projects_path, notice: "#{Project.model_name.human} was successfully destroyed." 40 | end 41 | 42 | private 43 | 44 | def model_params 45 | params.require(:project).permit(:repo_name, :ci_type, :travis_token, :codeship_uuid, :circleci_token) 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def status_class(status) 3 | case status 4 | when :unavailable 5 | "card-default" 6 | when :passed 7 | "card-success" 8 | when :waiting 9 | "card-in-progress" 10 | else # :failed 11 | "card-failure" 12 | end 13 | end 14 | 15 | def status_text(status) 16 | case status 17 | when :unavailable 18 | "Project does not have a CI status" 19 | when :passed 20 | "All specs passing on Master branch" 21 | when :waiting 22 | "" 23 | else # :failed 24 | "A few specs failing on Master branch" 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/inputs/string_input.rb: -------------------------------------------------------------------------------- 1 | class StringInput < SimpleForm::Inputs::StringInput 2 | enable :placeholder, :maxlength, :pattern 3 | 4 | def input(wrapper_options = nil) 5 | unless string? 6 | input_html_classes.unshift("string") 7 | input_html_options[:type] ||= input_type if html5? 8 | end 9 | 10 | # For materializecss's errors to work 11 | input_html_classes << wrapper_options[:error_class] if has_errors? 12 | 13 | merged_input_options = merge_wrapper_options(input_html_options, wrapper_options) 14 | 15 | @builder.text_field(attribute_name, merged_input_options) 16 | end 17 | 18 | private 19 | 20 | def string? 21 | input_type == :string 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/jobs/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/app/jobs/.keep -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/app/mailers/.keep -------------------------------------------------------------------------------- /app/models/ability.rb: -------------------------------------------------------------------------------- 1 | class Ability 2 | include CanCan::Ability 3 | 4 | def initialize(user) 5 | # `manage` == EVERYTHING, so we should use it with care! 6 | # Prefer to use `crud` that is more specific to the 7 RESTful actions 7 | alias_action :create, :read, :update, :destroy, to: :crud 8 | 9 | user ||= User.new # Guest User 10 | 11 | # Anyone 12 | 13 | # Normal 14 | if user.persisted? 15 | can :crud, Project, user_id: user.id 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/oauth_account.rb: -------------------------------------------------------------------------------- 1 | class OauthAccount < ActiveRecord::Base 2 | belongs_to :user 3 | 4 | def self.from_omniauth(auth) 5 | where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |account| 6 | account.provider = auth.provider 7 | account.uid = auth.uid 8 | account.name = auth.info.name 9 | account.oauth_token = auth.credentials.token 10 | 11 | (account.user || account.build_user).tap do |user| 12 | user.attributes = { 13 | name: auth.info.name || auth.info.nickname || auth.extra.raw_info.name, 14 | email: auth.info.email || auth.extra.email 15 | } 16 | user.confirm 17 | user.save(validate: false) 18 | end 19 | 20 | account.save(validate: false) 21 | end 22 | rescue StandardError 23 | logger.error auth.inspect 24 | raise 25 | end 26 | 27 | def oauth_token=(token) 28 | self[:oauth_token] = cryptor.encrypt_and_sign(token) 29 | end 30 | 31 | def oauth_token 32 | encrypted_token = self[:oauth_token] 33 | 34 | if encrypted_token.present? 35 | cryptor.decrypt_and_verify(encrypted_token) 36 | end 37 | end 38 | 39 | private 40 | 41 | def cryptor 42 | ActiveSupport::MessageEncryptor.new(Rails.application.secrets.secret_key_base) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/models/project.rb: -------------------------------------------------------------------------------- 1 | class Project < ActiveRecord::Base 2 | belongs_to :user 3 | 4 | validates_presence_of :repo_name 5 | end 6 | -------------------------------------------------------------------------------- /app/models/project_decorator.rb: -------------------------------------------------------------------------------- 1 | class ProjectDecorator < SimpleDelegator 2 | def process_with(oauth_token=nil) 3 | @oauth_token = oauth_token 4 | 5 | call_apis 6 | end 7 | 8 | def org_user 9 | @_org_user ||= repo_name.split("/").first 10 | end 11 | 12 | def name 13 | @_name ||= repo_name.split("/").last 14 | end 15 | 16 | def url 17 | "https://github.com/#{repo_name}" 18 | end 19 | 20 | def pull_requests_url 21 | "https://github.com/#{repo_name}/pulls" 22 | end 23 | 24 | def issues_url 25 | "https://github.com/#{repo_name}/issues" 26 | end 27 | 28 | def pull_requests 29 | @_prs ||= @_issues.select(&:pull_request) 30 | end 31 | 32 | def issues 33 | @_iss ||= @_issues.reject(&:pull_request) 34 | end 35 | 36 | def status 37 | @_ci.status 38 | end 39 | 40 | def status_url 41 | @_ci.url 42 | end 43 | 44 | private 45 | 46 | def call_apis 47 | Parallel.each(api_functions, in_threads: api_functions.size) { |func| func.call } 48 | end 49 | 50 | def api_functions 51 | [ 52 | method(:init_repos), 53 | method(:init_ci) 54 | ] 55 | end 56 | 57 | def init_repos 58 | client = Octokit::Client.new(access_token: @oauth_token) 59 | @_issues = client.issues(repo_name) 60 | end 61 | 62 | def init_ci 63 | @_ci = 64 | case ci_type 65 | when "travis" 66 | Status::Travis.new(repo_name, travis_token).run! 67 | when "codeship" 68 | Status::Codeship.new(repo_name, codeship_uuid).run! 69 | when "circleci" 70 | Status::Circleci.new(repo_name, circleci_token).run! 71 | else 72 | Status::Null.new 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /app/models/status/base.rb: -------------------------------------------------------------------------------- 1 | module Status 2 | class Base 3 | attr_reader :repo_name, :auth_token, :response 4 | 5 | def initialize(repo_name, auth_token=nil) 6 | @repo_name = repo_name 7 | @auth_token = auth_token 8 | end 9 | 10 | def branch 11 | "master" 12 | end 13 | 14 | def run! 15 | @response ||= api_result 16 | self 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/models/status/circleci.rb: -------------------------------------------------------------------------------- 1 | class Status::Circleci < Status::Base 2 | ENDPOINT = "https://circleci.com/api/v1/project".freeze 3 | 4 | def status 5 | case build_state 6 | when "fixed", "success" 7 | :passed 8 | when "retried", "canceled", "infrastructure_fail", "timedout", "failed", "no_tests" 9 | :failed 10 | else # "not_run", "running", "queued", "scheduled", "not_running" 11 | :waiting 12 | end 13 | end 14 | 15 | def url 16 | build_url 17 | end 18 | 19 | private 20 | 21 | def api_result 22 | JSON.parse(HTTP.headers(headers).get(api_endpoint)).first 23 | end 24 | 25 | def build_state 26 | response["status"] 27 | end 28 | 29 | def build_url 30 | response["build_url"] 31 | end 32 | 33 | def api_endpoint 34 | if auth_token 35 | "#{ENDPOINT}/#{repo_name}/tree/#{branch}?circle-token=#{auth_token}" 36 | else 37 | "#{ENDPOINT}/#{repo_name}/tree/#{branch}" 38 | end 39 | end 40 | 41 | def headers 42 | { accept: "application/json" } 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/models/status/codeship.rb: -------------------------------------------------------------------------------- 1 | class Status::Codeship < Status::Base 2 | def status 3 | case build_state 4 | when :success 5 | :passed 6 | when :error, :projectnotfound, :branchnotfound, :ignored, :stopped, :infrastructure_failure 7 | :failed 8 | else # :testing, :waiting 9 | :waiting 10 | end 11 | end 12 | 13 | def url 14 | "https://codeship.com/projects" 15 | end 16 | 17 | private 18 | 19 | def api_result 20 | Codeship::Status.new(auth_token, branch: branch) 21 | end 22 | 23 | def build_state 24 | response.status 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/models/status/null.rb: -------------------------------------------------------------------------------- 1 | class Status::Null 2 | def status 3 | :unavailable 4 | end 5 | 6 | def url 7 | "" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/models/status/travis.rb: -------------------------------------------------------------------------------- 1 | class Status::Travis < Status::Base 2 | ENDPOINT = "https://api.travis-ci.org/repos".freeze 3 | 4 | def status 5 | case build_state 6 | when "passed" 7 | :passed 8 | when "failed" 9 | :failed 10 | else 11 | :waiting 12 | end 13 | end 14 | 15 | def url 16 | "https://travis-ci.org/#{repo_name}/builds/#{build_id}" 17 | end 18 | 19 | private 20 | 21 | def api_result 22 | JSON.parse(HTTP.headers(headers).get(api_endpoint)) 23 | end 24 | 25 | def build_state 26 | response["branch"]["state"] 27 | end 28 | 29 | def build_id 30 | response["branch"]["id"] 31 | end 32 | 33 | def api_endpoint 34 | if auth_token.present? 35 | "#{ENDPOINT}/#{repo_name}/branches/#{branch}.json&token=#{auth_token}" 36 | else 37 | "#{ENDPOINT}/#{repo_name}/branches/#{branch}.json" 38 | end 39 | end 40 | 41 | def headers 42 | { "User-Agent" => "Dasherize/1.0.0", accept: "application/vnd.travis-ci.2+json" } 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | has_one :oauth_account, dependent: :delete 3 | 4 | delegate :image_url, to: :oauth_account, allow_nil: true 5 | 6 | validates_presence_of :name 7 | 8 | devise :registerable, :database_authenticatable, :validatable, 9 | :confirmable, :rememberable, 10 | :recoverable, :trackable, 11 | :omniauthable 12 | 13 | devise :omniauthable, omniauth_providers: [:github] 14 | end 15 | -------------------------------------------------------------------------------- /app/services/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/app/services/.keep -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.slim: -------------------------------------------------------------------------------- 1 | h1.page-header Send Confirmation Instructions 2 | 3 | .row 4 | .col-sm-6.col-sm-offset-3 5 | 6 | p Please enter your email below and we'll send you an email for confirming your account. 7 | 8 | = simple_form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }, defaults: { wrapper: :vertical_form, label: false }) do |f| 9 | = f.full_error :confirmation_token, class: 'alert alert-danger' 10 | 11 | = f.input :email, autofocus: true 12 | 13 | .form-action.form-group 14 | = f.button :submit, "Send Confirmation Instructions", class: 'btn btn-primary', data: { disable_with: t('helpers.disable_with') } 15 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.slim: -------------------------------------------------------------------------------- 1 | p 2 | | Welcome 3 | = @email 4 | | ! 5 | p 6 | | You can confirm your account email through the link below: 7 | p 8 | = link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) 9 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.slim: -------------------------------------------------------------------------------- 1 | p 2 | | Hello 3 | = @resource.email 4 | | ! 5 | p 6 | | Someone has requested a link to change your password. You can do this through the link below. 7 | p 8 | = link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) 9 | p 10 | | If you didn't request this, please ignore this email. 11 | p 12 | | Your password won't change until you access the link above and create a new one. 13 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.slim: -------------------------------------------------------------------------------- 1 | p 2 | | Hello 3 | = @resource.email 4 | | ! 5 | p 6 | | Your account has been locked due to an excessive number of unsuccessful sign in attempts. 7 | p 8 | | Click the link below to unlock your account: 9 | p 10 | = link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) 11 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.slim: -------------------------------------------------------------------------------- 1 | h1.page-header Change Your Password 2 | 3 | .row 4 | .col-sm-6.col-sm-offset-3 5 | 6 | = simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }, defaults: { wrapper: :vertical_form, label: false }) do |f| 7 | = f.full_error :reset_password_token, class: 'alert alert-danger' 8 | = f.input :reset_password_token, as: :hidden 9 | 10 | = f.input :password, autofocus: true 11 | = f.input :password_confirmation 12 | 13 | .form-action.form-group 14 | = f.button :submit, 'Change My Password', class: 'btn btn-primary', data: { disable_with: t('helpers.disable_with') } 15 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.slim: -------------------------------------------------------------------------------- 1 | h1.page-header Forgot Your Password? 2 | 3 | .row 4 | .col-sm-6.col-sm-offset-3 5 | 6 | p Please enter your email below and we'll send you an email for resetting your password. 7 | 8 | = simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }, defaults: { wrapper: :vertical_form, label: false }) do |f| 9 | = f.input :email, autofocus: true 10 | 11 | .form-action.form-group 12 | = f.button :submit, 'Reset Password', class: 'btn btn-primary', data: { disable_with: t('helpers.disable_with') } 13 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.slim: -------------------------------------------------------------------------------- 1 | .container 2 | h2.mar-lg-bottom Edit Profile 3 | 4 | = simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| 5 | .row 6 | = f.input :name , disabled: true 7 | .row 8 | = f.input :email, disabled: true 9 | 10 | - if devise_mapping.confirmable? && resource.pending_reconfirmation? 11 | p Currently waiting confirmation for: #{resource.unconfirmed_email} 12 | 13 | /= f.input :password, autocomplete: 'off', hint: %(Leave this blank if you don't want to change your password), required: false 14 | /= f.input :password_confirmation, required: false 15 | / 16 | /= f.input :current_password, hint: 'For security, we need your current password to confirm your changes', required: true 17 | 18 | .mar-lg-top.mar-lg-bottom 19 | = f.button :submit, 'Update Profile', class: 'btn btn-primary', disabled: true, data: { disable_with: t('helpers.disable_with') } 20 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.slim: -------------------------------------------------------------------------------- 1 | h1.page-header Sign Up 2 | 3 | .row 4 | .col-sm-4.col-sm-offset-4 5 | 6 | = simple_form_for(resource, as: resource_name, url: registration_path(resource_name), defaults: { wrapper: :vertical_form, label: false }) do |f| 7 | = f.input :name, autofocus: true 8 | = f.input :email 9 | = f.input :password, placeholder: 'Password (Minimum 8 characters)' 10 | = f.input :password_confirmation 11 | 12 | .form-action.form-group 13 | = f.button :submit, 'Sign Up', class: 'btn btn-block btn-primary', data: { disable_with: t('helpers.disable_with') } 14 | p.pad-sm-top.text-center 15 | | Have an Account? #{link_to 'Sign In', new_user_session_path} 16 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.slim: -------------------------------------------------------------------------------- 1 | h1.page-header Sign In 2 | 3 | .row 4 | .col-sm-4.col-sm-offset-4 5 | 6 | = simple_form_for(resource, as: resource_name, url: session_path(resource_name), defaults: { wrapper: :vertical_form, label: false }) do |f| 7 | = f.input :email, autofocus: true 8 | = f.input :password 9 | = f.input :remember_me, as: :boolean, inline_label: 'Remember me', wrapper: :vertical_boolean if devise_mapping.rememberable? 10 | 11 | .form-action.form-group 12 | = f.button :submit, 'Sign In', class: 'btn btn-block btn-primary', data: { disable_with: t('helpers.disable_with') } 13 | p.pad-sm-top.text-center 14 | | #{link_to 'Create Account', new_user_registration_path} or #{link_to 'Forgot Password', new_password_path(resource_name)} 15 | -------------------------------------------------------------------------------- /app/views/devise/shared/_links.html.slim: -------------------------------------------------------------------------------- 1 | - if controller_name != 'sessions' 2 | = link_to "Log in", new_session_path(resource_name) 3 | br 4 | - if devise_mapping.registerable? && controller_name != 'registrations' 5 | = link_to "Sign up", new_registration_path(resource_name) 6 | br 7 | - if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' 8 | = link_to "Forgot your password?", new_password_path(resource_name) 9 | br 10 | - if devise_mapping.confirmable? && controller_name != 'confirmations' 11 | = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) 12 | br 13 | - if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' 14 | = link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) 15 | br 16 | - if devise_mapping.omniauthable? 17 | - resource_class.omniauth_providers.each do |provider| 18 | = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) 19 | br 20 | -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.slim: -------------------------------------------------------------------------------- 1 | h1.page-header Send Unlock Instructions 2 | 3 | .row 4 | .col-sm-6.col-sm-offset-3 5 | 6 | p Please enter your email below and we'll send you an email for unlocking your account. 7 | 8 | = simple_form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }, defaults: { wrapper: :vertical_form, label: false }) do |f| 9 | = f.full_error :unlock_token, class: 'alert alert-danger' 10 | 11 | = f.input :email, autofocus: true 12 | 13 | .form-action.form-group 14 | = f.button :submit, 'Send Unlock Instructions', class: 'btn btn-primary', data: { disable_with: t('helpers.disable_with') } 15 | -------------------------------------------------------------------------------- /app/views/errors/404.html.slim: -------------------------------------------------------------------------------- 1 | .container 2 | h2 The page you were looking for doesn't exist. 3 | 4 | p You may have mistyped the address or the page may have moved. 5 | -------------------------------------------------------------------------------- /app/views/errors/422.html.slim: -------------------------------------------------------------------------------- 1 | .container 2 | h2 The change you wanted was rejected. 3 | 4 | p Maybe you tried to change something you didn't have access to. 5 | -------------------------------------------------------------------------------- /app/views/errors/500.html.slim: -------------------------------------------------------------------------------- 1 | .container 2 | h2 We're sorry, but something went wrong. 3 | p 4 | ' Feel free to 5 | => link_to "open an issue", "https://github.com/jollygoodcode/dasherize/issues/new" 6 | | on GitHub. 7 | -------------------------------------------------------------------------------- /app/views/home/index.html.slim: -------------------------------------------------------------------------------- 1 | .section.mar-lg-top.mar-lg-bottom 2 | .container 3 | .center 4 | h1 5 | strong Dasherize 6 | h5.thin.grey-text A simple and beautiful material-based dashboard for your projects 7 | 8 | .row.call-to-actions 9 | .col.s12.m6.btn-try 10 | = link_to "/users/auth/github", class: "btn btn-large waves-effect waves-light" do 11 | = icon("television", "Try Dasherize", class: "left") 12 | .col.s12.m6.btn-code 13 | = link_to "https://www.github.com/jollygoodcode/dasherize", class: "btn btn-large waves-effect waves-light" do 14 | = icon("code", "Source Code", class: "left") 15 | 16 | .divider 17 | 18 | .section 19 | .header.mar-md-bottom 20 | h4.center Cards View 21 | h6.center.thin.grey-text Great as an overview of all your projects, and goes really well with your morning coffee. 22 | 23 | .row 24 | - @projects.each do |project| 25 | .col.s12.m4 26 | = render 'projects/project', project: project 27 | 28 | .section 29 | .container 30 | .divider 31 | 32 | .section 33 | .header.mar-md-bottom 34 | h4.center Presentation View 35 | h6.center.thin.grey-text Essential as a big screen display in the office. 36 | 37 | .row.presentation 38 | - @projects.each do |project| 39 | .col.s12.m4 40 | = render 'projects/project', project: project 41 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.slim: -------------------------------------------------------------------------------- 1 | doctype 5 2 | html(lang="en") 3 | head 4 | title= content_for?(:title) ? yield(:title) : "Dasherize" 5 | 6 | meta(charset="utf-8") 7 | meta(http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1") 8 | meta(name="description" content="Dasherize is a beautiful material-based dashboard for your projects which provides you with an overview of open Pull Requests and Issues, and statuses of your CI, all on a single web page.") 9 | meta(name="keywords" content="ruby, rails, dashboard, github, travis, codeship, ci, open source") 10 | meta(name="viewport" content="width=device-width, initial-scale=1.0") 11 | link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" 12 | 13 | /![if lt IE 9] 14 | = javascript_include_tag "//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js" 15 | = javascript_include_tag "application", "data-turbolinks-track" => true 16 | 17 | = stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true 18 | = favicon_link_tag "favicon.ico", rel: "shortcut icon" 19 | 20 | = csrf_meta_tags 21 | 22 | javascript: 23 | $(document).on("page:change", function() { 24 | // only for `/projects` and `/projects/123` 25 | if (window.location.pathname.match(/projects[\/\d+]*$/)) { 26 | var handler = 27 | setTimeout(function() { 28 | console.log("reloading"); 29 | window.location.reload(true); 30 | }, 300000); 31 | 32 | $(document).on("page:fetch", function() { clearTimeout(handler); }); 33 | } 34 | }); 35 | 36 | body class=(page_class + (params[:mode] == "presentation" ? " presentation" : "" )) 37 | = render "shared/google_analytics" 38 | 39 | nav 40 | .container 41 | .nav-wrapper 42 | a.brand-logo.mar-sm-left(href=root_path) Dasherize 43 | = link_to "#", class: "button-collapse", data: { activates: "nav-mobile" } do 44 | i.material-icons menu 45 | 46 | ul.right.hide-on-med-and-down.thin 47 | - if user_signed_in? 48 | li= link_to icon("user", current_user.name, class: "left") 49 | li data-spec="new-project" 50 | = link_to icon("plus", "New Project", class: "left"), new_project_path 51 | li data-spec="presentation" 52 | = link_to icon("laptop", "Presentation", class: "left"), projects_path(mode: :presentation) 53 | li data-spec="sign-out" 54 | = link_to icon("sign-out" , "Sign Out", class: "left"), destroy_user_session_path, method: :delete 55 | - else 56 | li data-spec="sign-in" 57 | = link_to icon("sign-in", "Sign In with GitHub", class: "left"), "/users/auth/github" 58 | 59 | ul#nav-mobile.side-nav 60 | - if user_signed_in? 61 | li= link_to "Hi #{current_user.name}!", "" 62 | li= link_to "New Project" , new_project_path 63 | li= link_to "Presentation", projects_path(mode: :presentation) 64 | li= link_to "Sign Out" , destroy_user_session_path, method: :delete 65 | - else 66 | li= link_to "Sign In with GitHub", "/users/auth/github" 67 | 68 | .content 69 | .row 70 | .col.s12 71 | = yield 72 | 73 | footer.page-footer.thin 74 | .container.white-text 75 | .row 76 | .col.s12.l6 77 | h5 About Dasherize 78 | p 79 | ' Dasherize is a beautiful material-based dashboard for your projects 80 | ' which provides you with an overview of open Pull Requests and Issues, and statuses of your CI, all on a single web page. 81 | p 82 | ' Currently supports GitHub, Travis, CodeShip and CircleCI. 83 | 84 | .col.s12.l4.offset-l2 85 | h5 Contact Us 86 | ul 87 | li 88 | => icon("laptop") 89 | '   90 | => link_to "Jolly Good Code", "http://www.jollygoodcode.com" , class: "grey-text text-lighten-4" 91 | li 92 | => icon("envelope-o") 93 | '   94 | => link_to "hello@jollygoodcode.com", "mailto:hello@jollygoodcode.com", class: "grey-text text-lighten-4" 95 | 96 | .footer-copyright 97 | .container.center 98 | ' Built with #{icon("heart")} by Jolly Good Code 99 | 100 | = javascript_initialization 101 | -------------------------------------------------------------------------------- /app/views/projects/_form.html.slim: -------------------------------------------------------------------------------- 1 | = simple_form_for @project do |f| 2 | .row 3 | = f.input :repo_name, autofocus: true 4 | .row 5 | = f.input :ci_type, collection: [:travis, :codeship, :circleci], prompt: "Please select a Provider" 6 | .row 7 | .col.s12 8 | p.grey-text Only private repository needs to fill in token or uuid. 9 | .row 10 | = f.input :travis_token 11 | .row 12 | = f.input :codeship_uuid 13 | .row 14 | = f.input :circleci_token 15 | 16 | .mar-lg-top.mar-lg-bottom 17 | = render "shared/actions/form_actions", 18 | f: f, 19 | cancel_new_path: projects_path, 20 | cancel_edit_path: projects_path, 21 | destroy_path: can?(:destroy, @project) && @project 22 | -------------------------------------------------------------------------------- /app/views/projects/_project.html.slim: -------------------------------------------------------------------------------- 1 | .project id="project:#{project.id}" 2 | .card-panel.no-padding 3 | .card-heading class=status_class(project.status) 4 | .card-title.clearfix 5 | .left 6 | .name 7 | = link_to project.name, project.url 8 | .org_user 9 | em> in 10 | = project.org_user 11 | 12 | - if project.persisted? 13 | .right 14 | = link_to icon("gear"), edit_project_path(project), class: 'gear' 15 | .card-overview.center 16 | .col.s6.no-padding 17 | .number= link_to project.pull_requests.count, project.pull_requests_url 18 | .number-title Pull Requests 19 | .col.s6.no-padding 20 | .number= link_to project.issues.count, project.issues_url 21 | .number-title Issues 22 | .card-status.center class=status_class(project.status) 23 | .indeterminate 24 | = link_to status_text(project.status), project.status_url, target: "_blank" 25 | 26 | .card-content 27 | h6 Pull Requests 28 | - recent = project.pull_requests[0...3] 29 | - if recent.present? 30 | ul.collection 31 | - recent.each do |pr| 32 | li.collection-item.avatar 33 | = image_tag pr.user.avatar_url, class: "circle" 34 | span.title.truncate 35 | = link_to pr.title, pr.html_url, title: pr.title, target: "_blank" 36 | p 37 | = local_time_ago(pr.created_at) 38 | - else 39 | ul.collection 40 | li 41 | p No Pull Requests! Yea! 42 | 43 | .divider 44 | 45 | .card-content 46 | h6 Issues 47 | - recent = project.issues[0...3] 48 | - if recent.present? 49 | ul.collection 50 | - recent.each do |is| 51 | li.collection-item.avatar 52 | = image_tag is.user.avatar_url, class: "circle" 53 | span.title.truncate 54 | = link_to is.title, is.html_url, title: is.title, target: "_blank" 55 | p 56 | = local_time_ago(is.created_at) 57 | - else 58 | ul.collection 59 | li 60 | p No Issues! Yea! 61 | -------------------------------------------------------------------------------- /app/views/projects/edit.html.slim: -------------------------------------------------------------------------------- 1 | .container 2 | h2.mar-lg-bottom= page_title(model: Project.model_name.human) 3 | 4 | = render "form" 5 | -------------------------------------------------------------------------------- /app/views/projects/index.html.slim: -------------------------------------------------------------------------------- 1 | - if @projects.present? 2 | .row.mar-lg-top 3 | - @projects.each do |project| 4 | .col.s12.m4 5 | .project id="project:#{project.id}" 6 | = link_to project_path(project), project_path(project), remote: true, class: 'hide js-project' 7 | 8 | .card-panel.no-padding.grey.darken-1 9 | .card-heading 10 | .card-title 11 | = link_to project.repo_name 12 | .right 13 | = link_to icon("gear"), edit_project_path(project), class: "gear" 14 | .card-status.center.progress 15 | .indeterminate 16 | 17 | - else 18 | .container.center 19 | p 20 | em I am feeling empty. 21 | p 22 | = link_to icon("plus", "Add a Project now"), new_project_path, class: "btn btn-primary" 23 | -------------------------------------------------------------------------------- /app/views/projects/new.html.slim: -------------------------------------------------------------------------------- 1 | .container 2 | h2.mar-lg-bottom= page_title(model: Project.model_name.human) 3 | 4 | = render "form" 5 | -------------------------------------------------------------------------------- /app/views/projects/show.html.slim: -------------------------------------------------------------------------------- 1 | = render 'project', project: @project 2 | -------------------------------------------------------------------------------- /app/views/shared/_google_analytics.html.slim: -------------------------------------------------------------------------------- 1 | javascript: 2 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 3 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 4 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 5 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 6 | 7 | ga('create', 'UA-53661970-3', 'auto'); 8 | ga('send', 'pageview'); 9 | -------------------------------------------------------------------------------- /app/views/shared/actions/_form_actions.html.slim: -------------------------------------------------------------------------------- 1 | .form-action.form-group 2 | .form-left-offset.form-right 3 | - if f.object.new_record? 4 | => f.button :button, class: 'btn', data: { disable_with: t('helpers.disable_with') } do 5 | = icon 'check-square-o', t('helpers.submit.create', model: f.object.class.model_name.human.titleize) 6 | = ' or ' 7 | = link_to t('.cancel', default: t('helpers.links.cancel')), cancel_new_path 8 | - else 9 | => f.button :button, class: 'btn', data: { disable_with: t('helpers.disable_with') } do 10 | = icon 'check-square-o', t('helpers.submit.update', model: f.object.class.model_name.human.titleize) 11 | = ' or ' 12 | = link_to t('.cancel', default: t('helpers.links.cancel')), cancel_edit_path 13 | - if destroy_path 14 | .pull-right 15 | = link_to destroy_path, class: 'btn red', method: :delete, data: { confirm: t('helpers.confirm'), disable_with: t('helpers.disable_with') } do 16 | = icon 'trash', t('.destroy', default: t('helpers.links.destroy')) 17 | -------------------------------------------------------------------------------- /app/views/shared/actions/_show_edit_back_destroy_links.html.slim: -------------------------------------------------------------------------------- 1 | .row 2 | .col-lg-12.col-md-12.col-sm-12 3 | => link_to edit_path, class: 'btn btn-primary' do 4 | = icon 'edit', t('helpers.links.edit', model: klass.model_name.human) 5 | = ' or ' 6 | = link_to t('.back', default: t('helpers.links.back')), back_path 7 | - if destroy_path 8 | .pull-right 9 | = link_to destroy_path, class: 'btn btn-danger', method: :delete, data: { confirm: t('helpers.confirm'), disable_with: t('helpers.disable_with') } do 10 | = icon 'trash', t('.destroy', default: t('helpers.links.destroy')) 11 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError 5 | end 6 | APP_PATH = File.expand_path('../../config/application', __FILE__) 7 | require_relative '../config/boot' 8 | require 'rails/commands' 9 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError 5 | end 6 | require_relative '../config/boot' 7 | require 'rake' 8 | Rake.application.run 9 | -------------------------------------------------------------------------------- /bin/rspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | # 8 | # This file was generated by Bundler. 9 | # 10 | # The application 'rspec' is installed as part of a gem, and 11 | # this file is here to facilitate running it. 12 | # 13 | 14 | require "pathname" 15 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 16 | Pathname.new(__FILE__).realpath) 17 | 18 | require "rubygems" 19 | require "bundler/setup" 20 | 21 | load Gem.bin_path("rspec-core", "rspec") 22 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 5 | 6 | Dir.chdir APP_ROOT do 7 | puts "== Installing dependencies ==" 8 | system "bundle check || bundle install" 9 | 10 | puts "\n== Copying .env ==" 11 | if !File.exist?('.env') && File.exist?('.env.sample') 12 | `cp .env.sample .env` 13 | end 14 | 15 | puts "\n== Preparing database ==" 16 | system "bin/rake db:create db:setup db:test:prepare" 17 | 18 | puts "\n== Removing old logs and tempfiles ==" 19 | system "rm -f log/*" 20 | system "rm -rf tmp/cache" 21 | end 22 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)) 11 | Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) } 12 | gem 'spring', match[1] 13 | require 'spring/binstub' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require "active_model/railtie" 4 | require "active_record/railtie" 5 | require "action_controller/railtie" 6 | require "action_mailer/railtie" 7 | require "action_view/railtie" 8 | require "sprockets/railtie" 9 | # require "rails/test_unit/railtie" 10 | require File.expand_path('../../lib/cache_store_initializer', __FILE__) 11 | 12 | # Require the gems listed in Gemfile, including any gems 13 | # you've limited to :test, :development, or :production. 14 | Bundler.require(*Rails.groups) 15 | 16 | module Dasherize 17 | class Application < Rails::Application 18 | # Settings in config/environments/* take precedence over those specified here. 19 | # Application configuration should go into files in config/initializers 20 | # -- all .rb files in that directory are automatically loaded. 21 | 22 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 23 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 24 | # config.time_zone = 'Central Time (US & Canada)' 25 | 26 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 27 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 28 | # config.i18n.default_locale = :de 29 | 30 | # Do not swallow errors in after_commit/after_rollback callbacks. 31 | config.active_record.raise_in_transactional_callbacks = true 32 | 33 | # Dynamic Error Pages 34 | # http://wearestac.com/blog/dynamic-error-pages-in-rails 35 | config.exceptions_app = self.routes 36 | 37 | # Sending Mail 38 | config.action_mailer.default_url_options = { host: ENV['WWW_HOSTNAME'] } 39 | 40 | # https://github.com/cyu/rack-cors 41 | # Note that this might still not work as static files are usually served from the web server 42 | config.middleware.insert_before ActionDispatch::Static, Rack::Cors do 43 | allow do 44 | origins ENV['WWW_HOSTNAME'] 45 | resource '/assets/*', headers: :any, methods: [:head, :get, :options] 46 | end 47 | end 48 | 49 | CacheStoreInitializer.setup(config) 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 8.2 and up are supported. 2 | # 3 | # Install the pg driver: 4 | # gem install pg 5 | # On OS X with Homebrew: 6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config 7 | # On OS X with MacPorts: 8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config 9 | # On Windows: 10 | # gem install pg 11 | # Choose the win32 build. 12 | # Install PostgreSQL and put its /bin directory on your path. 13 | # 14 | # Configure Using Gemfile 15 | # gem 'pg' 16 | # 17 | default: &default 18 | adapter: postgresql 19 | encoding: unicode 20 | # For details on connection pooling, see rails configuration guide 21 | # http://guides.rubyonrails.org/configuring.html#database-pooling 22 | pool: <%= [ENV.fetch('WEB_MAX_THREADS', 5), ENV.fetch('DB_POOL', 5)].max %> 23 | reaping_frequency: <%= ENV.fetch('DB_REAPING_FREQUENCY', 10) %> 24 | 25 | development: 26 | <<: *default 27 | database: dasherize_development 28 | host: localhost 29 | 30 | # The specified database role being used to connect to postgres. 31 | # To create additional roles in postgres see `$ createuser --help`. 32 | # When left blank, postgres will use the default role. This is 33 | # the same name as the operating system user that initialized the database. 34 | #username: dasherize 35 | 36 | # The password associated with the postgres role (username). 37 | #password: 38 | 39 | # Connect on a TCP socket. Omitted by default since the client uses a 40 | # domain socket that doesn't need configuration. Windows does not have 41 | # domain sockets, so uncomment these lines. 42 | #host: localhost 43 | 44 | # The TCP port the server listens on. Defaults to 5432. 45 | # If your server runs on a different port number, change accordingly. 46 | #port: 5432 47 | 48 | # Schema search path. The server defaults to $user,public 49 | #schema_search_path: myapp,sharedapp,public 50 | 51 | # Minimum log levels, in increasing order: 52 | # debug5, debug4, debug3, debug2, debug1, 53 | # log, notice, warning, error, fatal, and panic 54 | # Defaults to warning. 55 | #min_messages: notice 56 | 57 | # Warning: The database defined as "test" will be erased and 58 | # re-generated from your development database when you run "rake". 59 | # Do not set this db to the same as development or production. 60 | test: 61 | <<: *default 62 | database: dasherize_test 63 | host: localhost 64 | 65 | # As with config/secrets.yml, you never want to store sensitive information, 66 | # like your database password, in your source code. If your source code is 67 | # ever seen by anyone, they now have access to your database. 68 | # 69 | # Instead, provide the password as a unix environment variable when you boot 70 | # the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database 71 | # for a full rundown on how to provide these environment variables in a 72 | # production deployment. 73 | # 74 | # On Heroku and other platform providers, you may have a full connection URL 75 | # available as an environment variable. For example: 76 | # 77 | # DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" 78 | # 79 | # You can use this database configuration with: 80 | # 81 | # production: 82 | # url: <%= ENV['DATABASE_URL'] %> 83 | # 84 | production: 85 | <<: *default 86 | database: dasherize_production 87 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.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 | 30 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 31 | # yet still be able to expire them through the digest params. 32 | config.assets.digest = true 33 | 34 | # Adds additional error checking when serving assets at runtime. 35 | # Checks for improperly declared sprockets dependencies. 36 | # Raises helpful error messages. 37 | config.assets.raise_runtime_errors = true 38 | 39 | # Raises error for missing translations 40 | # config.action_view.raise_on_missing_translations = true 41 | 42 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 43 | config.action_controller.asset_host = "http://#{ENV['WWW_HOSTNAME']}" 44 | 45 | # Custom Generators 46 | config.generators do |g| 47 | g.template_engine :slim 48 | 49 | g.test_framework :rspec, fixture: true 50 | g.fixture_replacement :factory_girl, dir: 'spec/factories' 51 | g.integration_tool :rspec 52 | 53 | g.view_specs false 54 | g.helper_specs false 55 | g.request_specs false 56 | g.routing_specs false 57 | 58 | g.stylesheets false 59 | g.javascripts false 60 | g.assets false 61 | end 62 | 63 | # Scaffold 64 | config.app_generators.scaffold_controller = :scaffold_controller 65 | 66 | # Rack Live Reload 67 | config.middleware.use Rack::LiveReload 68 | 69 | # Bullet Gem 70 | config.after_initialize do 71 | Bullet.enable = false 72 | Bullet.alert = false 73 | Bullet.bullet_logger = false 74 | Bullet.console = false 75 | Bullet.rails_logger = false 76 | Bullet.add_footer = false 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like 20 | # NGINX, varnish or squid. 21 | # config.action_dispatch.rack_cache = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | # config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 35 | # yet still be able to expire them through the digest params. 36 | config.assets.digest = true 37 | 38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 39 | 40 | # Specifies the header that your server uses for sending files. 41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX: 43 | 44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 45 | config.force_ssl = true 46 | 47 | # Use the lowest log level to ensure availability of diagnostic information 48 | # when problems arise. 49 | config.log_level = :info 50 | 51 | # Prepend all log lines with the following tags. 52 | # config.log_tags = [ :subdomain, :uuid ] 53 | 54 | # Use a different logger for distributed setups. 55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 61 | config.action_controller.asset_host = "https://#{ENV['WWW_HOSTNAME']}" 62 | 63 | # Ignore bad email addresses and do not raise email delivery errors. 64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 65 | # config.action_mailer.raise_delivery_errors = false 66 | 67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 68 | # the I18n.default_locale when a translation cannot be found). 69 | config.i18n.fallbacks = true 70 | 71 | # Send deprecation notices to registered listeners. 72 | config.active_support.deprecation = :notify 73 | 74 | # Disable automatic flushing of the log to improve performance. 75 | # config.autoflush_log = false 76 | 77 | # Use default logging formatter so that PID and timestamp are not suppressed. 78 | config.log_formatter = ::Logger::Formatter.new 79 | 80 | # Do not dump schema after migrations. 81 | config.active_record.dump_schema_after_migration = false 82 | end 83 | -------------------------------------------------------------------------------- /config/environments/staging.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like 20 | # NGINX, varnish or squid. 21 | # config.action_dispatch.rack_cache = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | # config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 35 | # yet still be able to expire them through the digest params. 36 | config.assets.digest = true 37 | 38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 39 | 40 | # Specifies the header that your server uses for sending files. 41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX: 43 | 44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 45 | # config.force_ssl = true 46 | 47 | # Use the lowest log level to ensure availability of diagnostic information 48 | # when problems arise. 49 | config.log_level = :info 50 | 51 | # Prepend all log lines with the following tags. 52 | # config.log_tags = [ :subdomain, :uuid ] 53 | 54 | # Use a different logger for distributed setups. 55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 61 | config.action_controller.asset_host = "http://#{ENV['WWW_HOSTNAME']}" 62 | 63 | # Ignore bad email addresses and do not raise email delivery errors. 64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 65 | # config.action_mailer.raise_delivery_errors = false 66 | 67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 68 | # the I18n.default_locale when a translation cannot be found). 69 | config.i18n.fallbacks = true 70 | 71 | # Send deprecation notices to registered listeners. 72 | config.active_support.deprecation = :notify 73 | 74 | # Disable automatic flushing of the log to improve performance. 75 | # config.autoflush_log = false 76 | 77 | # Use default logging formatter so that PID and timestamp are not suppressed. 78 | config.log_formatter = ::Logger::Formatter.new 79 | 80 | # Do not dump schema after migrations. 81 | config.active_record.dump_schema_after_migration = false 82 | end 83 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.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.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Randomize the order test cases are executed. 35 | config.active_support.test_order = :random 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | 43 | # Rails 4 no longer sets default config values for Sprockets in test.rb, 44 | # so test.rb now requires Sprockets configuration. 45 | # http://edgeguides.rubyonrails.org/asset_pipeline.html 46 | config.assets.compile = true 47 | config.assets.compress = false 48 | config.assets.debug = false 49 | config.assets.digest = false 50 | end 51 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.1' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json -------------------------------------------------------------------------------- /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] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/new_relic.rb: -------------------------------------------------------------------------------- 1 | # https://docs.newrelic.com/docs/agents/ruby-agent/features/garbage-collection#gc_setup 2 | GC::Profiler.enable 3 | -------------------------------------------------------------------------------- /config/initializers/octokit.rb: -------------------------------------------------------------------------------- 1 | unless Rails.env.test? 2 | stack = Faraday::RackBuilder.new do |builder| 3 | builder.use Faraday::HttpCache, shared_cache: false, store: Rails.cache, serializer: Marshal 4 | builder.use Octokit::Response::RaiseError 5 | builder.adapter Faraday.default_adapter 6 | builder.response :logger 7 | end 8 | Octokit.middleware = stack 9 | end 10 | 11 | Octokit.auto_paginate = true 12 | -------------------------------------------------------------------------------- /config/initializers/redis.rb: -------------------------------------------------------------------------------- 1 | if ENV["REDISCLOUD_URL"] 2 | $redis = Redis.new(url: ENV["REDISCLOUD_URL"], driver: :hiredis) 3 | end 4 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_untitledapp_session' 4 | -------------------------------------------------------------------------------- /config/initializers/simple_form.rb: -------------------------------------------------------------------------------- 1 | # Use this setup block to configure all options available in SimpleForm. 2 | SimpleForm.setup do |config| 3 | # Wrappers are used by the form builder to generate a 4 | # complete input. You can remove any component from the 5 | # wrapper, change the order or even add your own to the 6 | # stack. The options given below are used to wrap the 7 | # whole input. 8 | config.wrappers :default, class: :input, 9 | hint_class: :field_with_hint, error_class: :field_with_errors do |b| 10 | ## Extensions enabled by default 11 | # Any of these extensions can be disabled for a 12 | # given input by passing: `f.input EXTENSION_NAME => false`. 13 | # You can make any of these extensions optional by 14 | # renaming `b.use` to `b.optional`. 15 | 16 | # Determines whether to use HTML5 (:email, :url, ...) 17 | # and required attributes 18 | b.use :html5 19 | 20 | # Calculates placeholders automatically from I18n 21 | # You can also pass a string as f.input placeholder: "Placeholder" 22 | b.use :placeholder 23 | 24 | ## Optional extensions 25 | # They are disabled unless you pass `f.input EXTENSION_NAME => true` 26 | # to the input. If so, they will retrieve the values from the model 27 | # if any exists. If you want to enable any of those 28 | # extensions by default, you can change `b.optional` to `b.use`. 29 | 30 | # Calculates maxlength from length validations for string inputs 31 | b.optional :maxlength 32 | 33 | # Calculates pattern from format validations for string inputs 34 | b.optional :pattern 35 | 36 | # Calculates min and max from length validations for numeric inputs 37 | b.optional :min_max 38 | 39 | # Calculates readonly automatically from readonly attributes 40 | b.optional :readonly 41 | 42 | ## Inputs 43 | b.use :label_input 44 | b.use :hint, wrap_with: { tag: :span, class: :hint } 45 | b.use :error, wrap_with: { tag: :span, class: :error } 46 | 47 | ## full_messages_for 48 | # If you want to display the full error message for the attribute, you can 49 | # use the component :full_error, like: 50 | # 51 | # b.use :full_error, wrap_with: { tag: :span, class: :error } 52 | end 53 | 54 | # The default wrapper to be used by the FormBuilder. 55 | config.default_wrapper = :default 56 | 57 | # Define the way to render check boxes / radio buttons with labels. 58 | # Defaults to :nested for bootstrap config. 59 | # inline: input + label 60 | # nested: label > input 61 | config.boolean_style = :nested 62 | 63 | # Default class for buttons 64 | config.button_class = 'btn' 65 | 66 | # Method used to tidy up errors. Specify any Rails Array method. 67 | # :first lists the first message for each field. 68 | # Use :to_sentence to list all errors for each field. 69 | # config.error_method = :first 70 | 71 | # Default tag used for error notification helper. 72 | config.error_notification_tag = :div 73 | 74 | # CSS class to add for error notification helper. 75 | config.error_notification_class = 'error_notification' 76 | 77 | # ID to add for error notification helper. 78 | # config.error_notification_id = nil 79 | 80 | # Series of attempts to detect a default label method for collection. 81 | # config.collection_label_methods = [ :to_label, :name, :title, :to_s ] 82 | 83 | # Series of attempts to detect a default value method for collection. 84 | # config.collection_value_methods = [ :id, :to_s ] 85 | 86 | # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none. 87 | # config.collection_wrapper_tag = nil 88 | 89 | # You can define the class to use on all collection wrappers. Defaulting to none. 90 | # config.collection_wrapper_class = nil 91 | 92 | # You can wrap each item in a collection of radio/check boxes with a tag, 93 | # defaulting to :span. Please note that when using :boolean_style = :nested, 94 | # SimpleForm will force this option to be a label. 95 | # config.item_wrapper_tag = :span 96 | 97 | # You can define a class to use in all item wrappers. Defaulting to none. 98 | # config.item_wrapper_class = nil 99 | 100 | # How the label text should be generated altogether with the required text. 101 | # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" } 102 | 103 | # You can define the class to use on all labels. Default is nil. 104 | # config.label_class = nil 105 | 106 | # You can define the class to use on all forms. Default is simple_form. 107 | # config.form_class = :simple_form 108 | 109 | # You can define which elements should obtain additional classes 110 | # config.generate_additional_classes_for = [:wrapper, :label, :input] 111 | 112 | # Whether attributes are required by default (or not). Default is true. 113 | # config.required_by_default = true 114 | 115 | # Tell browsers whether to use the native HTML5 validations (novalidate form option). 116 | # These validations are enabled in SimpleForm's internal config but disabled by default 117 | # in this configuration, which is recommended due to some quirks from different browsers. 118 | # To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations, 119 | # change this configuration to true. 120 | config.browser_validations = false 121 | 122 | # Collection of methods to detect if a file type was given. 123 | # config.file_methods = [ :mounted_as, :file?, :public_filename ] 124 | 125 | # Custom mappings for input types. This should be a hash containing a regexp 126 | # to match as key, and the input type that will be used when the field name 127 | # matches the regexp as value. 128 | # config.input_mappings = { /count/ => :integer } 129 | 130 | # Custom wrappers for input types. This should be a hash containing an input 131 | # type as key and the wrapper that will be used for all inputs with specified type. 132 | # config.wrapper_mappings = { string: :prepend } 133 | 134 | # Namespaces where SimpleForm should look for custom input classes that 135 | # override default inputs. 136 | # config.custom_inputs_namespaces << "CustomInputs" 137 | 138 | # Default priority for time_zone inputs. 139 | # config.time_zone_priority = nil 140 | 141 | # Default priority for country inputs. 142 | # config.country_priority = nil 143 | 144 | # When false, do not use translations for labels. 145 | # config.translate_labels = true 146 | 147 | # Automatically discover new inputs in Rails' autoload path. 148 | # config.inputs_discovery = true 149 | 150 | # Cache SimpleForm inputs discovery 151 | # config.cache_discovery = !Rails.env.development? 152 | 153 | # Default class for inputs 154 | # config.input_class = nil 155 | 156 | # Define the default class of the input wrapper of the boolean input. 157 | config.boolean_label_class = 'checkbox' 158 | 159 | # Defines if the default input wrapper class should be included in radio 160 | # collection wrappers. 161 | # config.include_default_input_wrapper_class = true 162 | 163 | # Defines which i18n scope will be used in Simple Form. 164 | # config.i18n_scope = 'simple_form' 165 | end 166 | -------------------------------------------------------------------------------- /config/initializers/simple_form_materializecss.rb: -------------------------------------------------------------------------------- 1 | # Use this setup block to configure all options available in SimpleForm. 2 | SimpleForm.setup do |config| 3 | config.wrappers :vertical_form, tag: "div", class: "input-field", error_class: "has-error" do |b| 4 | b.use :html5 5 | b.use :placeholder 6 | b.optional :maxlength 7 | b.optional :pattern 8 | b.optional :min_max 9 | b.optional :readonly 10 | 11 | b.use :input, class: "validate", error_class: "invalid" 12 | b.use :label 13 | 14 | b.use :error, wrap_with: { tag: "p", class: "error-block" } 15 | b.use :hint, wrap_with: { tag: "p", class: "help-block" } 16 | end 17 | 18 | # Wrappers for forms and inputs using the Bootstrap toolkit. 19 | # Check the Bootstrap docs (http://getbootstrap.com) 20 | # to learn about the different styles for forms and inputs, 21 | # buttons and other elements. 22 | config.default_wrapper = :vertical_form 23 | 24 | # Old Bootstrap styling 25 | config.wrappers :vertical_file_input, tag: "div", class: "form-group", error_class: "has-error" do |b| 26 | b.use :html5 27 | b.use :placeholder 28 | b.optional :maxlength 29 | b.optional :readonly 30 | b.use :label, class: "control-label" 31 | 32 | b.use :input 33 | b.use :error, wrap_with: { tag: "span", class: "help-block" } 34 | b.use :hint, wrap_with: { tag: "p", class: "help-block" } 35 | end 36 | 37 | config.wrappers :vertical_boolean, tag: "div", class: "form-group", error_class: "has-error" do |b| 38 | b.use :html5 39 | b.optional :readonly 40 | 41 | b.wrapper tag: "div", class: "checkbox" do |ba| 42 | ba.use :label_input 43 | end 44 | 45 | b.use :error, wrap_with: { tag: "span", class: "help-block" } 46 | b.use :hint, wrap_with: { tag: "p", class: "help-block" } 47 | end 48 | 49 | config.wrappers :vertical_radio_and_checkboxes, tag: "div", class: "form-group", error_class: "has-error" do |b| 50 | b.use :html5 51 | b.optional :readonly 52 | b.use :label_input 53 | b.use :error, wrap_with: { tag: "span", class: "help-block" } 54 | b.use :hint, wrap_with: { tag: "p", class: "help-block" } 55 | end 56 | 57 | config.wrappers :horizontal_form, tag: "div", class: "form-group", error_class: "has-error" do |b| 58 | b.use :html5 59 | b.use :placeholder 60 | b.optional :maxlength 61 | b.optional :pattern 62 | b.optional :min_max 63 | b.optional :readonly 64 | b.use :label, class: "form-left control-label" 65 | 66 | b.wrapper tag: "div", class: "form-right" do |ba| 67 | ba.use :input, class: "form-control" 68 | ba.use :error, wrap_with: { tag: "span", class: "help-block" } 69 | ba.use :hint, wrap_with: { tag: "p", class: "help-block" } 70 | end 71 | end 72 | 73 | config.wrappers :horizontal_file_input, tag: "div", class: "form-group", error_class: "has-error" do |b| 74 | b.use :html5 75 | b.use :placeholder 76 | b.optional :maxlength 77 | b.optional :readonly 78 | b.use :label, class: "form-left control-label" 79 | 80 | b.wrapper tag: "div", class: "form-right" do |ba| 81 | ba.use :input 82 | ba.use :error, wrap_with: { tag: "span", class: "help-block" } 83 | ba.use :hint, wrap_with: { tag: "p", class: "help-block" } 84 | end 85 | end 86 | 87 | config.wrappers :horizontal_boolean, tag: "div", class: "form-group", error_class: "has-error" do |b| 88 | b.use :html5 89 | b.optional :readonly 90 | 91 | b.wrapper tag: "div", class: "form-left-offset form-right" do |wr| 92 | wr.wrapper tag: "div", class: "checkbox" do |ba| 93 | ba.use :label_input, class: "form-right" 94 | end 95 | 96 | wr.use :error, wrap_with: { tag: "span", class: "help-block" } 97 | wr.use :hint, wrap_with: { tag: "p", class: "help-block" } 98 | end 99 | end 100 | 101 | config.wrappers :horizontal_radio_and_checkboxes, tag: "div", class: "form-group", error_class: "has-error" do |b| 102 | b.use :html5 103 | b.optional :readonly 104 | 105 | b.use :label, class: "form-left control-label" 106 | 107 | b.wrapper tag: "div", class: "form-right" do |ba| 108 | ba.use :input 109 | ba.use :error, wrap_with: { tag: "span", class: "help-block" } 110 | ba.use :hint, wrap_with: { tag: "p", class: "help-block" } 111 | end 112 | end 113 | 114 | config.wrappers :inline_form, tag: "div", class: "form-group", error_class: "has-error" do |b| 115 | b.use :html5 116 | b.use :placeholder 117 | b.optional :maxlength 118 | b.optional :pattern 119 | b.optional :min_max 120 | b.optional :readonly 121 | b.use :label, class: "sr-only" 122 | 123 | b.use :input, class: "form-control" 124 | b.use :error, wrap_with: { tag: "span", class: "help-block" } 125 | b.use :hint, wrap_with: { tag: "p", class: "help-block" } 126 | end 127 | end 128 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/locales/datetime_picker.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | datepicker: 3 | dformat: '%d/%m/%Y' 4 | pformat: 'DD/MM/YYYY' 5 | timepicker: 6 | dformat: '%R' 7 | pformat: 'HH:mm' 8 | -------------------------------------------------------------------------------- /config/locales/devise.en.yml: -------------------------------------------------------------------------------- 1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n 2 | 3 | en: 4 | devise: 5 | confirmations: 6 | confirmed: "Your email address has been successfully confirmed." 7 | send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." 8 | send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." 9 | failure: 10 | already_authenticated: "You are already signed in." 11 | inactive: "Your account is not activated yet." 12 | invalid: "Invalid email or password." 13 | locked: "Your account is locked." 14 | last_attempt: "You have one more attempt before your account is locked." 15 | not_found_in_database: "Invalid email address or password." 16 | timeout: "Your session expired. Please sign in again to continue." 17 | unauthenticated: "You need to sign in or sign up before continuing." 18 | unconfirmed: "You have to confirm your email address before continuing." 19 | mailer: 20 | confirmation_instructions: 21 | subject: "Confirmation instructions" 22 | reset_password_instructions: 23 | subject: "Reset password instructions" 24 | unlock_instructions: 25 | subject: "Unlock instructions" 26 | omniauth_callbacks: 27 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"." 28 | success: "Successfully authenticated from %{kind} account." 29 | passwords: 30 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." 31 | send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." 32 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." 33 | updated: "Your password has been changed successfully. You are now signed in." 34 | updated_not_active: "Your password has been changed successfully." 35 | registrations: 36 | destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." 37 | signed_up: "Welcome! You have signed up successfully." 38 | signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." 39 | signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." 40 | signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." 41 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." 42 | updated: "Your account has been updated successfully." 43 | sessions: 44 | signed_in: "Signed in successfully." 45 | signed_out: "Signed out successfully." 46 | already_signed_out: "Signed out successfully." 47 | unlocks: 48 | send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." 49 | send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." 50 | unlocked: "Your account has been unlocked successfully. Please sign in to continue." 51 | errors: 52 | messages: 53 | already_confirmed: "was already confirmed, please try signing in" 54 | confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" 55 | expired: "has expired, please request a new one" 56 | not_found: "not found" 57 | not_locked: "was not locked" 58 | not_saved: 59 | one: "1 error prohibited this %{resource} from being saved:" 60 | other: "%{count} errors prohibited this %{resource} from being saved:" 61 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | projects: 24 | new: 25 | title: "New %{model}" 26 | edit: 27 | title: "Edit %{model}" 28 | index: 29 | title: "%{model}" 30 | show: 31 | title: "%{model}" 32 | 33 | helpers: 34 | actions: 'Actions' 35 | disable_with: 'Processing..' 36 | confirm: 'Are you sure you want to delete this?' 37 | links: 38 | back: 'Back' 39 | cancel: 'Cancel' 40 | new: 'New %{model}' 41 | show: 'View' 42 | edit: 'Edit' 43 | destroy: 'Delete' 44 | confirm: 'Are you sure?' 45 | titles: 46 | new: 'New %{model}' 47 | edit: 'Edit %{model}' 48 | save: 'Save %{model}' 49 | delete: 'Delete %{model}' 50 | submit: 51 | create: 'Create %{model}' 52 | update: 'Update %{model}' 53 | submit: 'Submit your %{model}' 54 | -------------------------------------------------------------------------------- /config/locales/simple_form.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | simple_form: 3 | "yes": Yes 4 | "no": No 5 | required: 6 | text: required 7 | mark: '*' 8 | # You can uncomment the line below if you need to overwrite the whole required html. 9 | # When using html, text and mark won't be used. 10 | # html: '*' 11 | error_notification: 12 | default_message: "Please review the problems below:" 13 | # Examples 14 | # labels: 15 | # defaults: 16 | # password: 'Password' 17 | # user: 18 | # new: 19 | # email: 'E-mail to sign in.' 20 | # edit: 21 | # email: 'E-mail.' 22 | # hints: 23 | # defaults: 24 | # username: 'User name to sign in.' 25 | # password: 'No special characters, please.' 26 | # include_blanks: 27 | # defaults: 28 | # age: 'Rather not say' 29 | # prompts: 30 | # defaults: 31 | # age: 'Select your age' 32 | user_defaults: &user_defaults 33 | user: 34 | name: Name 35 | email: Email 36 | new: 37 | password: Password 38 | password_confirmation: Password Confirmation 39 | edit: 40 | password: New Password 41 | password_confirmation: New Password Confirmation 42 | current_password: Current Password 43 | 44 | labels: 45 | <<: *user_defaults 46 | user: 47 | password_confirmation: Confirm password 48 | registration: 49 | password_confirmation: Confirm password 50 | project: 51 | repo_name: Full Repository Name 52 | ci_type: CI Provider 53 | travis_token: Travis Token 54 | codeship_uuid: Codeship API Key 55 | circleci_token: CircleCI API Token 56 | 57 | placeholders: 58 | <<: *user_defaults 59 | 60 | hints: 61 | project: 62 | repo_name: E.g. rails/rails, jollygoodcode/twemoji 63 | travis_token: Retrieve Travis Token from the Profile tab on your Accounts page 64 | codeship_uuid: Retrieve Project UUID from https://codeship.com/projects/[PROJECTID]/configure 65 | circleci_token: "Add a CircleCI Token from your account dashboard. https://circleci.com/docs/api#authentication" 66 | 67 | -------------------------------------------------------------------------------- /config/newrelic.yml: -------------------------------------------------------------------------------- 1 | # 2 | # This file configures the New Relic Agent. New Relic monitors 3 | # Ruby, Java, .NET, PHP, and Python applications with deep visibility and low overhead. 4 | # For more information, visit www.newrelic.com. 5 | # 6 | # Generated June 03, 2013 7 | # 8 | # This configuration file is custom generated for Barsoom 9 | 10 | 11 | # Here are the settings that are common to all environments 12 | common: &default_settings 13 | # ============================== LICENSE KEY =============================== 14 | 15 | # You must specify the license key associated with your New Relic 16 | # account. This key binds your Agent's data to your account in the 17 | # New Relic service. 18 | license_key: '<%= ENV["NEW_RELIC_LICENSE_KEY"] %>' 19 | 20 | # Agent Enabled (Ruby/Rails Only) 21 | # Use this setting to force the agent to run or not run. 22 | # Default is 'auto' which means the agent will install and run only 23 | # if a valid dispatcher such as Mongrel is running. This prevents 24 | # it from running with Rake or the console. Set to false to 25 | # completely turn the agent off regardless of the other settings. 26 | # Valid values are true, false and auto. 27 | # 28 | # agent_enabled: auto 29 | 30 | # Application Name Set this to be the name of your application as 31 | # you'd like it show up in New Relic. The service will then auto-map 32 | # instances of your application into an "application" on your 33 | # dashboard page. If you want to map this instance into multiple 34 | # apps, like "AJAX Requests" and "All UI" then specify a semicolon 35 | # separated list of up to three distinct names, or a yaml list. 36 | # Defaults to the capitalized RAILS_ENV or RACK_ENV (i.e., 37 | # Production, Staging, etc) 38 | # 39 | # Example: 40 | # 41 | # app_name: 42 | # - Ajax Service 43 | # - All Services 44 | # 45 | app_name: <%= ENV["NEW_RELIC_APP_NAME"] %> 46 | 47 | # When "true", the agent collects performance data about your 48 | # application and reports this data to the New Relic service at 49 | # newrelic.com. This global switch is normally overridden for each 50 | # environment below. (formerly called 'enabled') 51 | monitor_mode: true 52 | 53 | # Developer mode should be off in every environment but 54 | # development as it has very high overhead in memory. 55 | developer_mode: false 56 | 57 | # The newrelic agent generates its own log file to keep its logging 58 | # information separate from that of your application. Specify its 59 | # log level here. 60 | log_level: info 61 | 62 | # Optionally set the path to the log file This is expanded from the 63 | # root directory (may be relative or absolute, e.g. 'log/' or 64 | # '/var/log/') The agent will attempt to create this directory if it 65 | # does not exist. 66 | # log_file_path: 'log' 67 | 68 | # Optionally set the name of the log file, defaults to 'newrelic_agent.log' 69 | # log_file_name: 'newrelic_agent.log' 70 | 71 | # The newrelic agent communicates with the service via https by default. This 72 | # prevents eavesdropping on the performance metrics transmitted by the agent. 73 | # The encryption required by SSL introduces a nominal amount of CPU overhead, 74 | # which is performed asynchronously in a background thread. If you'd prefer 75 | # to send your metrics over http uncomment the following line. 76 | # ssl: false 77 | 78 | #============================== Browser Monitoring =============================== 79 | # New Relic Real User Monitoring gives you insight into the performance real users are 80 | # experiencing with your website. This is accomplished by measuring the time it takes for 81 | # your users' browsers to download and render your web pages by injecting a small amount 82 | # of JavaScript code into the header and footer of each page. 83 | browser_monitoring: 84 | # By default the agent automatically injects the monitoring JavaScript 85 | # into web pages. Set this attribute to false to turn off this behavior. 86 | auto_instrument: true 87 | 88 | # Proxy settings for connecting to the New Relic server. 89 | # 90 | # If a proxy is used, the host setting is required. Other settings 91 | # are optional. Default port is 8080. 92 | # 93 | # proxy_host: hostname 94 | # proxy_port: 8080 95 | # proxy_user: 96 | # proxy_pass: 97 | 98 | # The agent can optionally log all data it sends to New Relic servers to a 99 | # separate log file for human inspection and auditing purposes. To enable this 100 | # feature, change 'enabled' below to true. 101 | # See: https://newrelic.com/docs/ruby/audit-log 102 | audit_log: 103 | enabled: false 104 | 105 | # Tells transaction tracer and error collector (when enabled) 106 | # whether or not to capture HTTP params. When true, frameworks can 107 | # exclude HTTP parameters from being captured. 108 | # Rails: the RoR filter_parameter_logging excludes parameters 109 | # Java: create a config setting called "ignored_params" and set it to 110 | # a comma separated list of HTTP parameter names. 111 | # ex: ignored_params: credit_card, ssn, password 112 | capture_params: false 113 | 114 | # Transaction tracer captures deep information about slow 115 | # transactions and sends this to the New Relic service once a 116 | # minute. Included in the transaction is the exact call sequence of 117 | # the transactions including any SQL statements issued. 118 | transaction_tracer: 119 | 120 | # Transaction tracer is enabled by default. Set this to false to 121 | # turn it off. This feature is only available at the Professional 122 | # and above product levels. 123 | enabled: true 124 | 125 | # Threshold in seconds for when to collect a transaction 126 | # trace. When the response time of a controller action exceeds 127 | # this threshold, a transaction trace will be recorded and sent to 128 | # New Relic. Valid values are any float value, or (default) "apdex_f", 129 | # which will use the threshold for an dissatisfying Apdex 130 | # controller action - four times the Apdex T value. 131 | transaction_threshold: apdex_f 132 | 133 | # When transaction tracer is on, SQL statements can optionally be 134 | # recorded. The recorder has three modes, "off" which sends no 135 | # SQL, "raw" which sends the SQL statement in its original form, 136 | # and "obfuscated", which strips out numeric and string literals. 137 | record_sql: obfuscated 138 | 139 | # Threshold in seconds for when to collect stack trace for a SQL 140 | # call. In other words, when SQL statements exceed this threshold, 141 | # then capture and send to New Relic the current stack trace. This is 142 | # helpful for pinpointing where long SQL calls originate from. 143 | stack_trace_threshold: 0.500 144 | 145 | # Determines whether the agent will capture query plans for slow 146 | # SQL queries. Only supported in mysql and postgres. Should be 147 | # set to false when using other adapters. 148 | explain_enabled: false 149 | 150 | # Threshold for query execution time below which query plans will 151 | # not be captured. Relevant only when `explain_enabled` is true. 152 | # explain_threshold: 0.5 153 | 154 | # Error collector captures information about uncaught exceptions and 155 | # sends them to New Relic for viewing 156 | error_collector: 157 | 158 | # Error collector is enabled by default. Set this to false to turn 159 | # it off. This feature is only available at the Professional and above 160 | # product levels. 161 | enabled: true 162 | 163 | # Rails Only - tells error collector whether or not to capture a 164 | # source snippet around the place of the error when errors are View 165 | # related. 166 | capture_source: true 167 | 168 | # To stop specific errors from reporting to New Relic, set this property 169 | # to comma-separated values. Default is to ignore routing errors, 170 | # which are how 404's get triggered. 171 | ignore_errors: "ActionController::RoutingError,Sinatra::NotFound" 172 | 173 | # If you're interested in capturing memcache keys as though they 174 | # were SQL uncomment this flag. Note that this does increase 175 | # overhead slightly on every memcached call, and can have security 176 | # implications if your memcached keys are sensitive 177 | # capture_memcache_keys: true 178 | 179 | # Application Environments 180 | # ------------------------------------------ 181 | # Environment-specific settings are in this section. 182 | # For Rails applications, RAILS_ENV is used to determine the environment. 183 | # For Java applications, pass -Dnewrelic.environment to set 184 | # the environment. 185 | 186 | # NOTE if your application has other named environments, you should 187 | # provide newrelic configuration settings for these environments here. 188 | 189 | development: 190 | <<: *default_settings 191 | # Turn off communication to New Relic service in development mode (also 192 | # 'enabled'). 193 | # NOTE: for initial evaluation purposes, you may want to temporarily 194 | # turn the agent on in development mode. 195 | monitor_mode: false 196 | 197 | # Rails Only - when running in Developer Mode, the New Relic Agent will 198 | # present performance information on the last 100 transactions you have 199 | # executed since starting the mongrel. 200 | # NOTE: There is substantial overhead when running in developer mode. 201 | # Do not use for production or load testing. 202 | developer_mode: true 203 | 204 | # Enable textmate links 205 | # textmate: true 206 | 207 | test: 208 | <<: *default_settings 209 | # It almost never makes sense to turn on the agent when running 210 | # unit, functional or integration tests or the like. 211 | monitor_mode: false 212 | 213 | # Turn on the agent in production for 24x7 monitoring. NewRelic 214 | # testing shows an average performance impact of < 5 ms per 215 | # transaction, you can leave this on all the time without 216 | # incurring any user-visible performance degradation. 217 | production: 218 | <<: *default_settings 219 | monitor_mode: true 220 | 221 | # Many applications have a staging environment which behaves 222 | # identically to production. Support for that environment is provided 223 | # here. By default, the staging environment has the agent turned on. 224 | staging: 225 | <<: *default_settings 226 | monitor_mode: true 227 | app_name: <%= ENV["NEW_RELIC_APP_NAME"] %> (Staging) 228 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | workers Integer(ENV["WEB_CONCURRENCY"]|| 2) 2 | threads Integer(ENV.fetch("WEB_MAX_THREADS", 5)), Integer(ENV.fetch("WEB_MAX_THREADS", 5)) 3 | 4 | preload_app! 5 | 6 | rackup DefaultRackup 7 | port ENV.fetch("PORT", 3000) 8 | environment ENV.fetch("RACK_ENV", "development") 9 | 10 | on_worker_boot do 11 | # Worker specific setup for Rails 4.1+ 12 | # See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot 13 | 14 | ActiveRecord::Base.establish_connection 15 | 16 | Rails.cache.reconnect if Rails.cache.respond_to?(:reconnect) 17 | end 18 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "home#index" 3 | 4 | devise_for :users, controllers: { omniauth_callbacks: "omniauth_callbacks" } 5 | 6 | resources :projects 7 | 8 | %w(404 422 500).each do |status_code| 9 | get status_code, to: "errors#show", code: status_code 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 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 the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 56dcfe4483387cd807e847e71d434ea1902dd1a8c1c669574eb73580d88da5c7e9dc4f2408ca8343eefc7071172a3722222ace66c8e56b8009655f077f21bf8a 15 | 16 | test: 17 | secret_key_base: 7408a1244204eed7a5bbfddfeb7c41c4b3692512e26a687178acf2ca5dd98fe1e46503955cd392d731ef19c4e9c108022c03e61079bafa68c44efd113c76b308 18 | 19 | # Do not keep production & staging secrets in the repository, 20 | # instead read values from the environment. 21 | 22 | staging: 23 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 24 | 25 | production: 26 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 27 | -------------------------------------------------------------------------------- /db/migrate/00000000000001_add_devise_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddDeviseToUsers < ActiveRecord::Migration 2 | def change 3 | create_table(:users) do |t| 4 | t.string :name 5 | 6 | ## Database authenticatable 7 | t.string :email, null: false, default: "" 8 | t.string :encrypted_password, null: false, default: "" 9 | 10 | ## Recoverable 11 | t.string :reset_password_token 12 | t.datetime :reset_password_sent_at 13 | 14 | ## Rememberable 15 | t.datetime :remember_created_at 16 | 17 | ## Trackable 18 | t.integer :sign_in_count, default: 0, null: false 19 | t.datetime :current_sign_in_at 20 | t.datetime :last_sign_in_at 21 | t.inet :current_sign_in_ip 22 | t.inet :last_sign_in_ip 23 | 24 | ## Confirmable 25 | t.string :confirmation_token 26 | t.datetime :confirmed_at 27 | t.datetime :confirmation_sent_at 28 | t.string :unconfirmed_email # Only if using reconfirmable 29 | 30 | ## Lockable 31 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 32 | # t.string :unlock_token # Only if unlock strategy is :email or :both 33 | # t.datetime :locked_at 34 | 35 | # Uncomment below if timestamps were not included in your original model. 36 | t.timestamps 37 | end 38 | 39 | add_index :users, :email, unique: true 40 | add_index :users, :reset_password_token, unique: true 41 | add_index :users, :confirmation_token, unique: true 42 | # add_index :users, :unlock_token, unique: true 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /db/migrate/00000000000002_create_oauth_accounts.rb: -------------------------------------------------------------------------------- 1 | class CreateOauthAccounts < ActiveRecord::Migration 2 | def change 3 | create_table :oauth_accounts do |t| 4 | t.string :provider 5 | t.string :uid 6 | t.string :name 7 | t.string :email 8 | t.string :image_url 9 | t.string :oauth_token 10 | t.string :oauth_secret 11 | t.string :oauth_expires_at 12 | 13 | t.references :user, index: true 14 | 15 | t.timestamps 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/00000000000003_add_rolify_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddRolifyToUsers < ActiveRecord::Migration 2 | def change 3 | create_table(:roles) do |t| 4 | t.string :name 5 | t.references :resource, :polymorphic => true 6 | 7 | t.timestamps 8 | end 9 | 10 | create_table(:users_roles, :id => false) do |t| 11 | t.references :user 12 | t.references :role 13 | end 14 | 15 | add_index(:roles, :name) 16 | add_index(:roles, [ :name, :resource_type, :resource_id ]) 17 | add_index(:users_roles, [ :user_id, :role_id ]) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /db/migrate/20150721075541_create_projects.rb: -------------------------------------------------------------------------------- 1 | class CreateProjects < ActiveRecord::Migration 2 | def change 3 | create_table :projects do |t| 4 | t.references :user 5 | t.string :repo_name 6 | t.string :codeship_uuid 7 | 8 | t.timestamps null: false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20150918095957_add_ci_type_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddCiTypeToProjects < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :ci_type, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150918100022_add_travis_token_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddTravisTokenToProjects < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :travis_token, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151006134112_add_circleci_token_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddCircleciTokenToProjects < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :circleci_token, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151007091120_remove_roles.rb: -------------------------------------------------------------------------------- 1 | class RemoveRoles < ActiveRecord::Migration 2 | def change 3 | drop_table :roles 4 | drop_table :users_roles 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20151009034510_remove_columns_from_oauth_accounts_and_users.rb: -------------------------------------------------------------------------------- 1 | class RemoveColumnsFromOauthAccountsAndUsers < ActiveRecord::Migration 2 | def change 3 | remove_column :oauth_accounts, :email, :string 4 | remove_column :oauth_accounts, :image_url, :string 5 | remove_column :oauth_accounts, :oauth_secret, :string 6 | remove_column :oauth_accounts, :oauth_expires_at, :string 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20151009034510) do 15 | 16 | # These are extensions that must be enabled in order to support this database 17 | enable_extension "plpgsql" 18 | 19 | create_table "oauth_accounts", force: :cascade do |t| 20 | t.string "provider" 21 | t.string "uid" 22 | t.string "name" 23 | t.string "oauth_token" 24 | t.integer "user_id" 25 | t.datetime "created_at" 26 | t.datetime "updated_at" 27 | end 28 | 29 | add_index "oauth_accounts", ["user_id"], name: "index_oauth_accounts_on_user_id", using: :btree 30 | 31 | create_table "projects", force: :cascade do |t| 32 | t.integer "user_id" 33 | t.string "repo_name" 34 | t.string "codeship_uuid" 35 | t.datetime "created_at", null: false 36 | t.datetime "updated_at", null: false 37 | t.string "ci_type" 38 | t.string "travis_token" 39 | t.string "circleci_token" 40 | end 41 | 42 | create_table "users", force: :cascade do |t| 43 | t.string "name" 44 | t.string "email", default: "", null: false 45 | t.string "encrypted_password", default: "", null: false 46 | t.string "reset_password_token" 47 | t.datetime "reset_password_sent_at" 48 | t.datetime "remember_created_at" 49 | t.integer "sign_in_count", default: 0, null: false 50 | t.datetime "current_sign_in_at" 51 | t.datetime "last_sign_in_at" 52 | t.inet "current_sign_in_ip" 53 | t.inet "last_sign_in_ip" 54 | t.string "confirmation_token" 55 | t.datetime "confirmed_at" 56 | t.datetime "confirmation_sent_at" 57 | t.string "unconfirmed_email" 58 | t.datetime "created_at" 59 | t.datetime "updated_at" 60 | end 61 | 62 | add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree 63 | add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree 64 | add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree 65 | 66 | end 67 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/lib/assets/.keep -------------------------------------------------------------------------------- /lib/cache_store_initializer.rb: -------------------------------------------------------------------------------- 1 | class CacheStoreInitializer 2 | class << self 3 | def setup(config) 4 | # Used in config/application.rb 5 | options = { failover: true, socket_timeout: 1.5, socket_failure_delay: 0.2 } 6 | 7 | if ENV["REDISCLOUD_URL"].present? 8 | config.cache_store = :redis_store, ENV["REDISCLOUD_URL"].split(","), options 9 | config.assets.cache_store = :redis_store, ENV["REDISCLOUD_URL"].split(","), options.merge(expires_in: 1.hour, race_condition_ttl: 5.second) 10 | else 11 | # Rails defaults to ActiveSupport::Cache::FileStore 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/tasks/dev.rake: -------------------------------------------------------------------------------- 1 | namespace :dev do 2 | desc "Rebuild development system" 3 | task rebuild: ["tmp:clear", "log:clear", "db:reset", "db:seed"] 4 | 5 | desc "Create development data" 6 | task data: :environment do 7 | admin = 8 | User.where(email: "admin@example.com") 9 | .first_or_create!( 10 | name: "Admin", 11 | password: "password", 12 | password_confirmation: "password", 13 | confirmed_at: Time.now, 14 | ) 15 | admin.add_role(:admin) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/tasks/maintain.rake: -------------------------------------------------------------------------------- 1 | namespace :maintain do 2 | desc "update gems" 3 | task :update do 4 | today = Time.current.strftime("%F") 5 | 6 | abort("Please clean your local file changes first.") if `git status --porcelain` != "" 7 | 8 | abort("Please run this task against master branch") if `git rev-parse --abbrev-ref HEAD`.chomp! != "master" 9 | 10 | output_warning "\n== Pulling latest changes from remote origin ==" 11 | `git pull origin master` 12 | 13 | output_warning "\n== Bundle update & Running specs ==" 14 | `bundle update && bin/rspec` 15 | 16 | if $CHILD_STATUS.success? 17 | `git checkout -b bundle-update-#{today}` 18 | `git add Gemfile.lock` 19 | `git commit -m "Bundle update on #{today}\nAuto-pushed by 'rake maintain:update task'."` 20 | `git push origin bundle-update-#{today}` 21 | else 22 | `git checkout Gemfile.lock` 23 | output_warning "Something went wrong while performing `bundle update` or updated gems break tests." 24 | end 25 | end 26 | end 27 | 28 | def output_warning(info) 29 | if Rails.env.development? 30 | puts info 31 | else 32 | logger.warn info 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/tasks/run.rake: -------------------------------------------------------------------------------- 1 | namespace :run do 2 | if Rails.env.development? || Rails.env.test? 3 | require "rubocop/rake_task" 4 | require "rspec/core/rake_task" 5 | 6 | desc "Run rubocop -DR" 7 | RuboCop::RakeTask.new(:rubocop) do |task| 8 | task.options = ["-DR"] # Rails, display cop name 9 | task.fail_on_error = false # don't abort rake on failure 10 | end 11 | 12 | desc "Run Rspec" 13 | RSpec::Core::RakeTask.new(spec: "spec:prepare") 14 | 15 | desc "Run all that matters" 16 | task all: %i(spec rubocop) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/templates/active_record/model/model.rb: -------------------------------------------------------------------------------- 1 | <% module_namespacing do -%> 2 | class <%= class_name %> < <%= parent_class_name.classify %> 3 | # Extends 4 | 5 | # Includes 6 | 7 | # Associations 8 | <% attributes.select(&:reference?).each do |attribute| -%> 9 | belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %> 10 | <% end -%> 11 | 12 | # accepts_nested_attributes 13 | 14 | # Delegates 15 | 16 | # Validations 17 | 18 | # Scopes 19 | 20 | # Callbacks 21 | 22 | # Custom 23 | # friendly_id .. 24 | # has_attached_file 25 | # enumerize .. 26 | 27 | # Methods (class methods before instance methods) 28 | end 29 | <% end -%> 30 | -------------------------------------------------------------------------------- /lib/templates/rails/scaffold_controller/controller.rb: -------------------------------------------------------------------------------- 1 | <% module_namespacing do -%> 2 | class <%= controller_class_name %>Controller < ApplicationController 3 | load_and_authorize_resource param_method: :model_params 4 | before_action :set_<%= plural_table_name %>_breadcrumbs 5 | before_action :set_action_breadcrumbs 6 | 7 | def index 8 | @<%= plural_table_name %> = @<%= plural_table_name %>.order(created_at: :desc).page(params[:page]) 9 | end 10 | 11 | def new 12 | end 13 | 14 | def create 15 | if @<%= orm_instance.save %> 16 | redirect_to @<%= singular_table_name %>, notice: "#{<%= class_name %>.model_name.human} was successfully created." 17 | else 18 | render :new 19 | end 20 | end 21 | 22 | def show 23 | end 24 | 25 | def edit 26 | end 27 | 28 | def update 29 | if @<%= singular_table_name %>.update(model_params) 30 | redirect_to @<%= singular_table_name %>, notice: "#{<%= class_name %>.model_name.human} was successfully updated." 31 | else 32 | render :edit 33 | end 34 | end 35 | 36 | def destroy 37 | @<%= orm_instance.destroy %> 38 | redirect_to <%= index_helper %>_path, notice: "#{<%= class_name %>.model_name.human} was successfully destroyed." 39 | end 40 | 41 | private 42 | 43 | def model_params 44 | <%- if attributes_names.empty? -%> 45 | params.require(<%= ":#{singular_table_name}" %>) 46 | <%- else -%> 47 | params.require(<%= ":#{singular_table_name}" %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) 48 | <%- end -%> 49 | end 50 | 51 | def set_<%= plural_table_name %>_breadcrumbs 52 | add_crumb 'Home', root_path 53 | add_crumb <%= class_name %>.model_name.human.pluralize, <%= plural_table_name %>_path 54 | add_crumb @<%= singular_table_name %>.name_was, <%= singular_table_name %>_path(@<%= singular_table_name %>) if @<%= singular_table_name %>.try(:persisted?) 55 | end 56 | 57 | def set_action_breadcrumbs 58 | case params[:action] 59 | when 'new', 'create' 60 | add_crumb 'New' 61 | when 'edit', 'update' 62 | add_crumb 'Edit' 63 | end 64 | end 65 | 66 | end 67 | <% end -%> 68 | -------------------------------------------------------------------------------- /lib/templates/rspec/model/model_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | <% module_namespacing do -%> 4 | describe <%= class_name %> do 5 | context 'associations' do 6 | 7 | end 8 | 9 | context 'validations' do 10 | 11 | end 12 | 13 | # context 'scope' do 14 | 15 | # end 16 | 17 | # context 'callbacks' do 18 | 19 | # end 20 | end 21 | <% end -%> 22 | -------------------------------------------------------------------------------- /lib/templates/rspec/scaffold/controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | <% module_namespacing do -%> 4 | describe <%= controller_class_name %>Controller do 5 | let(:user) { create(:user) } 6 | 7 | before { sign_in user } 8 | 9 | <% unless options[:singleton] -%> 10 | describe '#index' do 11 | def do_request 12 | get :index 13 | end 14 | 15 | before { do_request } 16 | 17 | xit { expect(response).to be_success } 18 | end 19 | 20 | <% end -%> 21 | describe '#new' do 22 | def do_request 23 | get :new 24 | end 25 | 26 | before { do_request } 27 | 28 | xit { expect(assigns[:<%= ns_file_name %>]).to be_instance_of(<%= class_name %>) } 29 | xit { expect(response).to be_success } 30 | end 31 | 32 | describe '#create' do 33 | def do_request 34 | post :create, <%= ns_file_name %>: params 35 | end 36 | 37 | context 'success' do 38 | let(:params) { Hash(name: 'foo') } 39 | 40 | xit 'creates new <%= ns_file_name %>' do 41 | expect { do_request }.to change(<%= class_name %>, :count).by(1) 42 | end 43 | 44 | xit 'redirects' do 45 | do_request 46 | expect(response).to redirect_to <%= ns_file_name %>_path(assigns(:<%= ns_file_name %>)) 47 | end 48 | end 49 | 50 | context 'failure' do 51 | let(:params) { Hash(name: nil) } 52 | 53 | xit 'does not create <%= ns_file_name %>' do 54 | expect { do_request }.not_to change(<%= class_name %>, :count) 55 | end 56 | 57 | xit 'renders new' do 58 | do_request 59 | expect(response).to render_template(:new) 60 | end 61 | end 62 | end 63 | 64 | describe '#show' do 65 | let(:<%= ns_file_name %>) { create(:<%= ns_file_name %>, user: user) } 66 | 67 | def do_request 68 | get :show, id: <%= ns_file_name %>.id 69 | end 70 | 71 | before { do_request } 72 | 73 | xit { expect(assigns(:<%= ns_file_name %>)).to eq <%= ns_file_name %> } 74 | xit { expect(response).to be_success } 75 | end 76 | 77 | describe '#edit' do 78 | let(:<%= ns_file_name %>) { create(:<%= ns_file_name %>, user: user) } 79 | 80 | def do_request 81 | get :edit, id: <%= ns_file_name %>.id 82 | end 83 | 84 | before { do_request } 85 | 86 | xit { expect(assigns(:<%= ns_file_name %>)).to eq <%= ns_file_name %> } 87 | xit { expect(response).to be_success } 88 | end 89 | 90 | describe '#update' do 91 | let(:<%= ns_file_name %>) { create(:<%= ns_file_name %>, user: user) } 92 | 93 | def do_request 94 | patch :update, id: <%= ns_file_name %>.id, <%= ns_file_name %>: params 95 | end 96 | 97 | before { do_request } 98 | 99 | context 'success' do 100 | let(:params) { Hash(id: <%= ns_file_name %>.id, name: <%= ns_file_name %>.name + '-modified') } 101 | 102 | before { do_request } 103 | 104 | xit { expect(assigns(:<%= ns_file_name %>).name).to match '-modified' } 105 | xit { expect(response).to redirect_to <%= ns_file_name %>_path(assigns(:<%= ns_file_name %>)) } 106 | end 107 | 108 | context 'failure' do 109 | let(:params) { Hash(name: '') } 110 | 111 | xit { expect(response).to render_template :edit } 112 | end 113 | end 114 | 115 | describe '#destroy' do 116 | let!(:<%= ns_file_name %>) { create(:<%= ns_file_name %>, user: user) } 117 | 118 | def do_request 119 | delete :destroy, id: <%= ns_file_name %>.id 120 | end 121 | 122 | xit 'deletes a <%= ns_file_name %>' do 123 | expect { do_request }.to change(<%= class_name %>, :count).by(-1) 124 | end 125 | 126 | xit 'redirects' do 127 | do_request 128 | expect(response).to redirect_to <%= index_helper %>_path 129 | end 130 | end 131 | end 132 | <% end -%> 133 | -------------------------------------------------------------------------------- /lib/templates/slim/scaffold/_form.html.slim: -------------------------------------------------------------------------------- 1 | = simple_form_for @<%= singular_table_name %>, html: { class: 'form-horizontal' } do |f| 2 | <%- attributes.each do |attribute| -%> 3 | = f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> 4 | <%- end -%> 5 | 6 | = render 'shared/actions/form_actions', 7 | f: f, 8 | cancel_new_path: <%= plural_table_name %>_path, 9 | cancel_edit_path: <%= plural_table_name %>_path, 10 | destroy_path: can?(:destroy, @<%= singular_table_name %>) && @<%= singular_table_name %> 11 | -------------------------------------------------------------------------------- /lib/templates/slim/scaffold/edit.html.slim: -------------------------------------------------------------------------------- 1 | .page-header 2 | h1= page_title(model: <%= class_name %>.model_name.human, default: crumbs.last.first) 3 | 4 | = render 'form' 5 | -------------------------------------------------------------------------------- /lib/templates/slim/scaffold/index.html.slim: -------------------------------------------------------------------------------- 1 | .page-header 2 | .pull-right 3 | = link_to icon('plus', t('helpers.links.new', model: <%= class_name %>.model_name.human)), new_<%= singular_table_name %>_path, class: 'btn btn-primary' 4 | h1= page_title(model: <%= class_name %>.model_name.human.pluralize, default: crumbs.last.first) 5 | 6 | table.table.table-hover 7 | thead 8 | tr 9 | th id 10 | th 11 | tbody 12 | - @<%= plural_table_name %>.each do |<%= singular_table_name %>| 13 | tr 14 | td= link_to <%= singular_table_name %>.to_param, <%= singular_table_name %> 15 | td.text-right 16 | = link_to <%= singular_table_name %>_path(<%= singular_table_name %>), class: 'btn btn-success btn-sm' do 17 | = icon 'eye' , t('helpers.links.show', model: <%= class_name %>.model_name.human) 18 | '   19 | = link_to edit_<%= singular_table_name %>_path(<%= singular_table_name %>), class: 'btn btn-warning btn-sm' do 20 | = icon 'edit' , t('helpers.links.edit', model: <%= class_name %>.model_name.human) 21 | '   22 | = link_to <%= singular_table_name %>_path(<%= singular_table_name %>), class: 'btn btn-danger btn-sm', method: :delete, data: { confirm: 'Are you sure?' } do 23 | = icon 'trash', t('helpers.links.destroy', model: <%= class_name %>.model_name.human) 24 | 25 | hr 26 | 27 | .row 28 | .col-sm-6 29 | = page_entries_info @<%= plural_table_name %>, entry_name: <%= class_name %>.model_name.human 30 | .col-sm-6 31 | = paginate(@<%= plural_table_name %>) 32 | -------------------------------------------------------------------------------- /lib/templates/slim/scaffold/new.html.slim: -------------------------------------------------------------------------------- 1 | .page-header 2 | h1= page_title(model: <%= class_name %>.model_name.human, default: crumbs.last.first) 3 | 4 | = render 'form' 5 | -------------------------------------------------------------------------------- /lib/templates/slim/scaffold/show.html.slim: -------------------------------------------------------------------------------- 1 | .page-header 2 | h1= page_title(model: <%= class_name %>.model_name.human, default: crumbs.last.first) 3 | 4 | .row 5 | .col-lg-12.col-md-12.col-sm-12 6 | .panel.panel-default 7 | table.table.table-striped 8 | tbody 9 | - @<%= singular_table_name %>.attributes.each_pair do |attr, value| 10 | = render_attr_value attr, value 11 | 12 | = render 'shared/actions/show_edit_back_destroy_links', 13 | klass: <%= class_name %>, 14 | edit_path: edit_<%= singular_table_name %>_path(@<%= singular_table_name %>), 15 | back_path: <%= plural_table_name %>_path, 16 | destroy_path: can?(:destroy, @<%= singular_table_name %>) && @<%= singular_table_name %> 17 | -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/log/.keep -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /spec/controllers/application_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe ApplicationController do 4 | controller do 5 | skip_before_action :authenticate_user! 6 | 7 | def index 8 | raise CanCan::AccessDenied 9 | end 10 | end 11 | 12 | describe "respond_to_access_denied" do 13 | let(:referer) { "http://test.host/?123=456" } 14 | 15 | before { request.env["HTTP_REFERER"] = referer } 16 | 17 | describe "GET" do 18 | it "respond forbidden for xhr" do 19 | xhr :get, :index 20 | expect(response.status).to eq(401) 21 | end 22 | 23 | it "redirects to root_path when signed in" do 24 | sign_in create(:user) 25 | get :index 26 | expect(response).to redirect_to(root_path) 27 | end 28 | 29 | it "redirects to new_user_session_path when not signed in" do 30 | get :index 31 | expect(response).to redirect_to(new_user_session_path) 32 | end 33 | 34 | it "stores request.url as session[user_return_to]" do 35 | expect { 36 | get :index, abc: 123 37 | }.to change { 38 | session["user_return_to"] 39 | }.to eq("http://test.host/anonymous?abc=123") 40 | end 41 | 42 | it "stores param[user_return_to] as session[user_return_to]" do 43 | expect { 44 | get :index, abc: 123, user_return_to: root_path(hello: "world") 45 | }.to change { 46 | session["user_return_to"] 47 | }.to eq(root_path(hello: "world")) 48 | end 49 | end 50 | 51 | describe "POST" do 52 | it "respond forbidden for xhr" do 53 | xhr :post, :index 54 | expect(response.status).to eq(401) 55 | end 56 | 57 | it "redirects to new_user_session_path" do 58 | post :index 59 | expect(response).to redirect_to(new_user_session_path) 60 | end 61 | 62 | it "stores request.referer as session[user_return_to]" do 63 | expect { 64 | post :index, abc: 123 65 | }.to change { 66 | session["user_return_to"] 67 | }.to eq(referer) 68 | end 69 | 70 | it "stores param[user_return_to] as session[user_return_to]" do 71 | expect { 72 | post :index, abc: 123, user_return_to: root_path(hello: "world") 73 | }.to change { 74 | session["user_return_to"] 75 | }.to eq(root_path(hello: "world")) 76 | end 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /spec/controllers/errors_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe ErrorsController do 4 | describe "#show" do 5 | def do_request 6 | get :show, params 7 | end 8 | 9 | context "404" do 10 | let(:params) { Hash(code: 404) } 11 | 12 | it "renders and status" do 13 | do_request 14 | 15 | expect(response).to render_template("404") 16 | expect(response).to have_http_status(404) 17 | end 18 | end 19 | 20 | context "422" do 21 | let(:params) { Hash(code: 422) } 22 | 23 | it "renders and status" do 24 | do_request 25 | 26 | expect(response).to render_template("422") 27 | expect(response).to have_http_status(422) 28 | end 29 | end 30 | 31 | context "500" do 32 | let(:params) { Hash(code: 500) } 33 | 34 | it "renders and status" do 35 | do_request 36 | 37 | expect(response).to render_template("500") 38 | expect(response).to have_http_status(500) 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/controllers/home_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe HomeController do 4 | let(:user) { create(:user, :with_oauth_account) } 5 | 6 | describe "#index" do 7 | def do_request 8 | get :index 9 | end 10 | 11 | context "signed in" do 12 | before { sign_in(user) } 13 | 14 | it "redirects" do 15 | do_request 16 | 17 | expect(response).to redirect_to projects_path 18 | end 19 | end 20 | 21 | context "not signed in" do 22 | before { Rails.cache.clear } 23 | 24 | it "cached" do 25 | stub_ci_requests_for_home_page 26 | expect(ProjectDecorator).to receive(:new).exactly(3).times.and_call_original 27 | 28 | do_request 29 | do_request 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/controllers/omniauth_callbacks_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe OmniauthCallbacksController do 4 | let(:user) { create(:user, :with_oauth_account) } 5 | 6 | def do_request 7 | get :github 8 | end 9 | 10 | before { request.env["devise.mapping"] = Devise.mappings[:user] } 11 | 12 | describe "#callback" do 13 | context "existing user" do 14 | before do 15 | expect(OauthAccount).to receive(:from_omniauth) { double(user: user) } 16 | end 17 | 18 | it "works" do 19 | do_request 20 | is_expected.to set_flash[:notice].to "Successfully authenticated from Github account." 21 | expect(response).to redirect_to root_path 22 | end 23 | end 24 | 25 | context "new user" do 26 | let(:user_info) do 27 | double( 28 | :user, 29 | name: "Audrey", 30 | email: "audrey@example.com", 31 | oauth_account: double(oauth_token: "token") 32 | ) 33 | end 34 | 35 | it "creates new user" do 36 | request.env["omniauth.auth"] = stub_oauth(user_info) 37 | 38 | do_request 39 | 40 | expect(User.count).to eq 1 41 | expect(User.last).to have_attributes(name: "Audrey", email: "audrey@example.com") 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/controllers/projects_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe ProjectsController do 4 | let(:user) { create(:user, :with_oauth_account) } 5 | 6 | before { sign_in user } 7 | 8 | describe '#index' do 9 | def do_request 10 | get :index 11 | end 12 | 13 | before { do_request } 14 | 15 | it { expect(response).to be_success } 16 | end 17 | 18 | describe '#new' do 19 | def do_request 20 | get :new 21 | end 22 | 23 | before { do_request } 24 | 25 | it { expect(assigns[:project]).to be_instance_of(Project) } 26 | it { expect(response).to be_success } 27 | end 28 | 29 | describe '#create' do 30 | def do_request 31 | post :create, project: params 32 | end 33 | 34 | context "success" do 35 | let(:params) { attributes_for(:project) } 36 | 37 | it "creates new project" do 38 | expect { do_request }.to change(Project, :count).by(1) 39 | 40 | project = Project.last 41 | expect(project.user).to eq user 42 | params.each_pair do |attr, value| 43 | expect(project.send(attr)).to eq value 44 | end 45 | end 46 | 47 | it "redirects" do 48 | do_request 49 | expect(response).to redirect_to projects_path 50 | end 51 | end 52 | 53 | context "failure" do 54 | let(:params) { Hash(repo_name: nil) } 55 | 56 | it "does not create project" do 57 | expect { do_request }.not_to change(Project, :count) 58 | end 59 | 60 | it "renders new" do 61 | do_request 62 | expect(response).to render_template(:new) 63 | end 64 | end 65 | end 66 | 67 | describe '#show' do 68 | let(:project) { create(:project, user: user) } 69 | 70 | def do_request 71 | get :show, id: project.id 72 | end 73 | 74 | before do 75 | allow_any_instance_of(ProjectDecorator).to receive(:call_apis) 76 | do_request 77 | end 78 | 79 | it { expect(assigns(:project)).to eq project } 80 | it { expect(response).to be_success } 81 | end 82 | 83 | describe '#edit' do 84 | let(:project) { create(:project, user: user) } 85 | 86 | def do_request 87 | get :edit, id: project.id 88 | end 89 | 90 | before { do_request } 91 | 92 | it { expect(assigns(:project)).to eq project } 93 | it { expect(response).to be_success } 94 | end 95 | 96 | describe '#update' do 97 | let(:project) { create(:project, user: user) } 98 | 99 | def do_request 100 | patch :update, id: project.id, project: params 101 | end 102 | 103 | before { do_request } 104 | 105 | context "success" do 106 | let(:params) { Hash(id: project.id, repo_name: project.repo_name + "-modified") } 107 | 108 | before { do_request } 109 | 110 | it { expect(assigns(:project).repo_name).to match "-modified" } 111 | it { expect(response).to redirect_to project_path(assigns(:project)) } 112 | end 113 | 114 | context "failure" do 115 | let(:params) { Hash(repo_name: "") } 116 | 117 | it { expect(response).to render_template :edit } 118 | end 119 | end 120 | 121 | describe '#destroy' do 122 | let!(:project) { create(:project, user: user) } 123 | 124 | def do_request 125 | delete :destroy, id: project.id 126 | end 127 | 128 | it "deletes a project" do 129 | expect { do_request }.to change(Project, :count).by(-1) 130 | end 131 | 132 | it "redirects" do 133 | do_request 134 | expect(response).to redirect_to projects_path 135 | end 136 | end 137 | end 138 | -------------------------------------------------------------------------------- /spec/factories/oauth_accounts.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :oauth_account do 3 | user nil 4 | provider "github" 5 | uid "1234" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/projects.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :project do 3 | repo_name { Faker::Code.isbn } 4 | codeship_uuid { Faker::Code.isbn } 5 | end 6 | 7 | trait :with_user do 8 | user 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :user do 3 | name { Faker::Name.name } 4 | sequence(:email) { |n| "user#{n}@example.com" } 5 | password "topsecret" 6 | password_confirmation "topsecret" 7 | 8 | confirmed_at { Time.current } 9 | 10 | trait :with_oauth_account do 11 | oauth_account 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/feature_helper.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.configure do |config| 4 | config.use_transactional_fixtures = false 5 | 6 | config.before(:suite) do 7 | DatabaseRewinder.strategy = :truncation 8 | DatabaseRewinder.clean_with(:truncation) 9 | end 10 | 11 | config.before(:each) do 12 | DatabaseRewinder.start 13 | end 14 | 15 | config.after(:each) do 16 | DatabaseRewinder.clean 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/features/add_a_new_project_spec.rb: -------------------------------------------------------------------------------- 1 | require "feature_helper" 2 | 3 | RSpec.feature "Adds a New Project" do 4 | scenario "works" do 5 | stub_ci_requests_for_home_page 6 | user = create(:user, :with_oauth_account) 7 | 8 | sign_in_as(user) 9 | visit root_path 10 | 11 | expect(page).not_to have_content "twemoji" 12 | 13 | within "[data-spec=new-project]" do 14 | click_on "New Project" 15 | end 16 | 17 | fill_in "Full Repository Name", with: "jollygoodcode/twemoji" 18 | select "travis", from: "project[ci_type]" 19 | click_on "Create Project" 20 | 21 | expect(page).to have_content "twemoji" 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/features/authentication_spec.rb: -------------------------------------------------------------------------------- 1 | require "feature_helper" 2 | 3 | RSpec.feature "Authentication" do 4 | scenario "sign in" do 5 | stub_ci_requests_for_home_page 6 | user = create(:user, :with_oauth_account) 7 | 8 | sign_in_as(user) 9 | 10 | expect(page).to have_content(user.name) 11 | end 12 | 13 | scenario "sign out" do 14 | stub_ci_requests_for_home_page 15 | user = create(:user, :with_oauth_account) 16 | sign_in_as(user) 17 | 18 | sign_out_user 19 | 20 | expect(page).not_to have_content(user.name) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/features/presentation_spec.rb: -------------------------------------------------------------------------------- 1 | require "feature_helper" 2 | 3 | RSpec.feature "Presentation" do 4 | scenario "works" do 5 | stub_ci_requests_for_home_page 6 | user = create(:user, :with_oauth_account) 7 | create(:project, repo_name: "jollygoodcode/twemoji", user: user) 8 | create(:project, repo_name: "jollygoodcode/dasherize", user: user) 9 | sign_in_as(user) 10 | 11 | visit root_path 12 | within "[data-spec=presentation]" do 13 | click_on "Presentation" 14 | end 15 | 16 | expect(page).to have_content("jollygoodcode/twemoji") 17 | expect(page).to have_content("jollygoodcode/dasherize") 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/fixtures/github/google_visualr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "https://api.github.com/repos/winston/google_visualr/issues/86", 4 | "labels_url": "https://api.github.com/repos/winston/google_visualr/issues/86/labels{/name}", 5 | "comments_url": "https://api.github.com/repos/winston/google_visualr/issues/86/comments", 6 | "events_url": "https://api.github.com/repos/winston/google_visualr/issues/86/events", 7 | "html_url": "https://github.com/winston/google_visualr/issues/86", 8 | "id": 55636885, 9 | "number": 86, 10 | "title": "Support to add packages", 11 | "user": { 12 | "login": "dtelaroli", 13 | "id": 536500, 14 | "avatar_url": "https://avatars.githubusercontent.com/u/536500?v=3", 15 | "gravatar_id": "", 16 | "url": "https://api.github.com/users/dtelaroli", 17 | "html_url": "https://github.com/dtelaroli", 18 | "followers_url": "https://api.github.com/users/dtelaroli/followers", 19 | "following_url": "https://api.github.com/users/dtelaroli/following{/other_user}", 20 | "gists_url": "https://api.github.com/users/dtelaroli/gists{/gist_id}", 21 | "starred_url": "https://api.github.com/users/dtelaroli/starred{/owner}{/repo}", 22 | "subscriptions_url": "https://api.github.com/users/dtelaroli/subscriptions", 23 | "organizations_url": "https://api.github.com/users/dtelaroli/orgs", 24 | "repos_url": "https://api.github.com/users/dtelaroli/repos", 25 | "events_url": "https://api.github.com/users/dtelaroli/events{/privacy}", 26 | "received_events_url": "https://api.github.com/users/dtelaroli/received_events", 27 | "type": "User", 28 | "site_admin": false 29 | }, 30 | "labels": [ 31 | 32 | ], 33 | "state": "open", 34 | "locked": false, 35 | "assignee": null, 36 | "milestone": null, 37 | "comments": 5, 38 | "created_at": "2015-01-27T16:05:45Z", 39 | "updated_at": "2015-09-03T01:38:58Z", 40 | "closed_at": null, 41 | "body": "I need to create a detailed chart to the select event.\r\n\r\nEg.:\r\n```\r\nchart = GoogleVisualr::Interactive::ColumnChart.new\r\nchart.add_package('table')\r\n```" 42 | }, 43 | { 44 | "url": "https://api.github.com/repos/winston/google_visualr/issues/74", 45 | "labels_url": "https://api.github.com/repos/winston/google_visualr/issues/74/labels{/name}", 46 | "comments_url": "https://api.github.com/repos/winston/google_visualr/issues/74/comments", 47 | "events_url": "https://api.github.com/repos/winston/google_visualr/issues/74/events", 48 | "html_url": "https://github.com/winston/google_visualr/pull/74", 49 | "id": 35670688, 50 | "number": 74, 51 | "title": "adding support for embedded functions inside the google visualization", 52 | "user": { 53 | "login": "golsombe", 54 | "id": 15931, 55 | "avatar_url": "https://avatars.githubusercontent.com/u/15931?v=3", 56 | "gravatar_id": "", 57 | "url": "https://api.github.com/users/golsombe", 58 | "html_url": "https://github.com/golsombe", 59 | "followers_url": "https://api.github.com/users/golsombe/followers", 60 | "following_url": "https://api.github.com/users/golsombe/following{/other_user}", 61 | "gists_url": "https://api.github.com/users/golsombe/gists{/gist_id}", 62 | "starred_url": "https://api.github.com/users/golsombe/starred{/owner}{/repo}", 63 | "subscriptions_url": "https://api.github.com/users/golsombe/subscriptions", 64 | "organizations_url": "https://api.github.com/users/golsombe/orgs", 65 | "repos_url": "https://api.github.com/users/golsombe/repos", 66 | "events_url": "https://api.github.com/users/golsombe/events{/privacy}", 67 | "received_events_url": "https://api.github.com/users/golsombe/received_events", 68 | "type": "User", 69 | "site_admin": false 70 | }, 71 | "labels": [ 72 | 73 | ], 74 | "state": "open", 75 | "locked": false, 76 | "assignee": null, 77 | "milestone": null, 78 | "comments": 3, 79 | "created_at": "2014-06-13T13:22:15Z", 80 | "updated_at": "2015-05-15T09:03:20Z", 81 | "closed_at": null, 82 | "pull_request": { 83 | "url": "https://api.github.com/repos/winston/google_visualr/pulls/74", 84 | "html_url": "https://github.com/winston/google_visualr/pull/74", 85 | "diff_url": "https://github.com/winston/google_visualr/pull/74.diff", 86 | "patch_url": "https://github.com/winston/google_visualr/pull/74.patch" 87 | }, 88 | "body": "I needed a new function inside the draw_chart() function to support custom tooltips for the TreeMap chart. Added a new add_function method along with additional spec for testing. Please advise?\r\n\r\nEx:\r\n\r\n```javascript\r\n@chart.add_function(\" function showTooltip(row, size, value) {\r\n return '
' +\r\n '' + data_table.getValue(row, 1) + '<\\b>
'; }\")\r\n```" 89 | }, 90 | { 91 | "url": "https://api.github.com/repos/winston/google_visualr/issues/43", 92 | "labels_url": "https://api.github.com/repos/winston/google_visualr/issues/43/labels{/name}", 93 | "comments_url": "https://api.github.com/repos/winston/google_visualr/issues/43/comments", 94 | "events_url": "https://api.github.com/repos/winston/google_visualr/issues/43/events", 95 | "html_url": "https://github.com/winston/google_visualr/issues/43", 96 | "id": 7616028, 97 | "number": 43, 98 | "title": "Remove image charts as they have been officially deprecated by Google", 99 | "user": { 100 | "login": "winston", 101 | "id": 2112, 102 | "avatar_url": "https://avatars.githubusercontent.com/u/2112?v=3", 103 | "gravatar_id": "", 104 | "url": "https://api.github.com/users/winston", 105 | "html_url": "https://github.com/winston", 106 | "followers_url": "https://api.github.com/users/winston/followers", 107 | "following_url": "https://api.github.com/users/winston/following{/other_user}", 108 | "gists_url": "https://api.github.com/users/winston/gists{/gist_id}", 109 | "starred_url": "https://api.github.com/users/winston/starred{/owner}{/repo}", 110 | "subscriptions_url": "https://api.github.com/users/winston/subscriptions", 111 | "organizations_url": "https://api.github.com/users/winston/orgs", 112 | "repos_url": "https://api.github.com/users/winston/repos", 113 | "events_url": "https://api.github.com/users/winston/events{/privacy}", 114 | "received_events_url": "https://api.github.com/users/winston/received_events", 115 | "type": "User", 116 | "site_admin": false 117 | }, 118 | "labels": [ 119 | { 120 | "url": "https://api.github.com/repos/winston/google_visualr/labels/cleanup", 121 | "name": "cleanup", 122 | "color": "0b02e1" 123 | } 124 | ], 125 | "state": "open", 126 | "locked": false, 127 | "assignee": null, 128 | "milestone": null, 129 | "comments": 2, 130 | "created_at": "2012-10-16T07:32:13Z", 131 | "updated_at": "2012-11-12T06:13:33Z", 132 | "closed_at": null, 133 | "body": "The Image Charts portion of Google Chart Tools has been officially deprecated as of April 20, 2012. \r\nIt will continue to work as per our deprecation policy.\r\n\r\nhttps://developers.google.com/chart/interactive/docs/gallery/genericimagechart\r\nhttps://developers.google.com/chart/interactive/docs/gallery/imagepiechart\r\nhttps://developers.google.com/chart/interactive/docs/gallery/imagelinechart\r\nhttps://developers.google.com/chart/interactive/docs/gallery/imagebarchart\r\nhttps://developers.google.com/chart/interactive/docs/gallery/imagesparkline" 134 | }, 135 | { 136 | "url": "https://api.github.com/repos/winston/google_visualr/issues/41", 137 | "labels_url": "https://api.github.com/repos/winston/google_visualr/issues/41/labels{/name}", 138 | "comments_url": "https://api.github.com/repos/winston/google_visualr/issues/41/comments", 139 | "events_url": "https://api.github.com/repos/winston/google_visualr/issues/41/events", 140 | "html_url": "https://github.com/winston/google_visualr/issues/41", 141 | "id": 6548275, 142 | "number": 41, 143 | "title": "Support different data filters", 144 | "user": { 145 | "login": "AmitPatel-BoTreeConsulting", 146 | "id": 1289894, 147 | "avatar_url": "https://avatars.githubusercontent.com/u/1289894?v=3", 148 | "gravatar_id": "", 149 | "url": "https://api.github.com/users/AmitPatel-BoTreeConsulting", 150 | "html_url": "https://github.com/AmitPatel-BoTreeConsulting", 151 | "followers_url": "https://api.github.com/users/AmitPatel-BoTreeConsulting/followers", 152 | "following_url": "https://api.github.com/users/AmitPatel-BoTreeConsulting/following{/other_user}", 153 | "gists_url": "https://api.github.com/users/AmitPatel-BoTreeConsulting/gists{/gist_id}", 154 | "starred_url": "https://api.github.com/users/AmitPatel-BoTreeConsulting/starred{/owner}{/repo}", 155 | "subscriptions_url": "https://api.github.com/users/AmitPatel-BoTreeConsulting/subscriptions", 156 | "organizations_url": "https://api.github.com/users/AmitPatel-BoTreeConsulting/orgs", 157 | "repos_url": "https://api.github.com/users/AmitPatel-BoTreeConsulting/repos", 158 | "events_url": "https://api.github.com/users/AmitPatel-BoTreeConsulting/events{/privacy}", 159 | "received_events_url": "https://api.github.com/users/AmitPatel-BoTreeConsulting/received_events", 160 | "type": "User", 161 | "site_admin": false 162 | }, 163 | "labels": [ 164 | { 165 | "url": "https://api.github.com/repos/winston/google_visualr/labels/feature", 166 | "name": "feature", 167 | "color": "02e10c" 168 | } 169 | ], 170 | "state": "open", 171 | "locked": false, 172 | "assignee": null, 173 | "milestone": null, 174 | "comments": 1, 175 | "created_at": "2012-08-30T08:53:20Z", 176 | "updated_at": "2012-10-15T09:15:25Z", 177 | "closed_at": null, 178 | "body": "How can we configure [data filters](https://developers.google.com/chart/interactive/docs/gallery/controls#gallery)?" 179 | }, 180 | { 181 | "url": "https://api.github.com/repos/winston/google_visualr/issues/35", 182 | "labels_url": "https://api.github.com/repos/winston/google_visualr/issues/35/labels{/name}", 183 | "comments_url": "https://api.github.com/repos/winston/google_visualr/issues/35/comments", 184 | "events_url": "https://api.github.com/repos/winston/google_visualr/issues/35/events", 185 | "html_url": "https://github.com/winston/google_visualr/issues/35", 186 | "id": 4935471, 187 | "number": 35, 188 | "title": "Add toolbars?", 189 | "user": { 190 | "login": "novito", 191 | "id": 851553, 192 | "avatar_url": "https://avatars.githubusercontent.com/u/851553?v=3", 193 | "gravatar_id": "", 194 | "url": "https://api.github.com/users/novito", 195 | "html_url": "https://github.com/novito", 196 | "followers_url": "https://api.github.com/users/novito/followers", 197 | "following_url": "https://api.github.com/users/novito/following{/other_user}", 198 | "gists_url": "https://api.github.com/users/novito/gists{/gist_id}", 199 | "starred_url": "https://api.github.com/users/novito/starred{/owner}{/repo}", 200 | "subscriptions_url": "https://api.github.com/users/novito/subscriptions", 201 | "organizations_url": "https://api.github.com/users/novito/orgs", 202 | "repos_url": "https://api.github.com/users/novito/repos", 203 | "events_url": "https://api.github.com/users/novito/events{/privacy}", 204 | "received_events_url": "https://api.github.com/users/novito/received_events", 205 | "type": "User", 206 | "site_admin": false 207 | }, 208 | "labels": [ 209 | { 210 | "url": "https://api.github.com/repos/winston/google_visualr/labels/feature", 211 | "name": "feature", 212 | "color": "02e10c" 213 | } 214 | ], 215 | "state": "open", 216 | "locked": false, 217 | "assignee": null, 218 | "milestone": null, 219 | "comments": 1, 220 | "created_at": "2012-06-06T18:29:41Z", 221 | "updated_at": "2012-10-15T09:09:55Z", 222 | "closed_at": null, 223 | "body": "Congrats for this gem. It's easy to use and really well documented.\n\nI have a question: Would it be difficult to add the Toolbar (https://google-developers.appspot.com/chart/interactive/docs/gallery/toolbar)? As it's set in the draw method I can't add it once the chart has been rendered.\n\nAny thoughts on how we could do that? " 224 | } 225 | ] 226 | -------------------------------------------------------------------------------- /spec/fixtures/github/twemoji.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "https://api.github.com/repos/jollygoodcode/twemoji/issues/18", 4 | "labels_url": "https://api.github.com/repos/jollygoodcode/twemoji/issues/18/labels{/name}", 5 | "comments_url": "https://api.github.com/repos/jollygoodcode/twemoji/issues/18/comments", 6 | "events_url": "https://api.github.com/repos/jollygoodcode/twemoji/issues/18/events", 7 | "html_url": "https://github.com/jollygoodcode/twemoji/pull/18", 8 | "id": 95581907, 9 | "number": 18, 10 | "title": "Add support for output json", 11 | "user": { 12 | "login": "JuanitoFatas", 13 | "id": 1000669, 14 | "avatar_url": "https://avatars.githubusercontent.com/u/1000669?v=3", 15 | "gravatar_id": "", 16 | "url": "https://api.github.com/users/JuanitoFatas", 17 | "html_url": "https://github.com/JuanitoFatas", 18 | "followers_url": "https://api.github.com/users/JuanitoFatas/followers", 19 | "following_url": "https://api.github.com/users/JuanitoFatas/following{/other_user}", 20 | "gists_url": "https://api.github.com/users/JuanitoFatas/gists{/gist_id}", 21 | "starred_url": "https://api.github.com/users/JuanitoFatas/starred{/owner}{/repo}", 22 | "subscriptions_url": "https://api.github.com/users/JuanitoFatas/subscriptions", 23 | "organizations_url": "https://api.github.com/users/JuanitoFatas/orgs", 24 | "repos_url": "https://api.github.com/users/JuanitoFatas/repos", 25 | "events_url": "https://api.github.com/users/JuanitoFatas/events{/privacy}", 26 | "received_events_url": "https://api.github.com/users/JuanitoFatas/received_events", 27 | "type": "User", 28 | "site_admin": false 29 | }, 30 | "labels": [ 31 | 32 | ], 33 | "state": "open", 34 | "locked": false, 35 | "assignee": null, 36 | "milestone": null, 37 | "comments": 1, 38 | "created_at": "2015-07-17T04:14:28Z", 39 | "updated_at": "2015-07-17T09:46:06Z", 40 | "closed_at": null, 41 | "pull_request": { 42 | "url": "https://api.github.com/repos/jollygoodcode/twemoji/pulls/18", 43 | "html_url": "https://github.com/jollygoodcode/twemoji/pull/18", 44 | "diff_url": "https://github.com/jollygoodcode/twemoji/pull/18.diff", 45 | "patch_url": "https://github.com/jollygoodcode/twemoji/pull/18.patch" 46 | }, 47 | "body": "Can output json like GitHub Emojis API: https://api.github.com/emojis\r\n\r\n" 48 | }, 49 | { 50 | "url": "https://api.github.com/repos/jollygoodcode/twemoji/issues/12", 51 | "labels_url": "https://api.github.com/repos/jollygoodcode/twemoji/issues/12/labels{/name}", 52 | "comments_url": "https://api.github.com/repos/jollygoodcode/twemoji/issues/12/comments", 53 | "events_url": "https://api.github.com/repos/jollygoodcode/twemoji/issues/12/events", 54 | "html_url": "https://github.com/jollygoodcode/twemoji/pull/12", 55 | "id": 62135302, 56 | "number": 12, 57 | "title": "Promote Ruby 2.0+.", 58 | "user": { 59 | "login": "JuanitoFatas", 60 | "id": 1000669, 61 | "avatar_url": "https://avatars.githubusercontent.com/u/1000669?v=3", 62 | "gravatar_id": "", 63 | "url": "https://api.github.com/users/JuanitoFatas", 64 | "html_url": "https://github.com/JuanitoFatas", 65 | "followers_url": "https://api.github.com/users/JuanitoFatas/followers", 66 | "following_url": "https://api.github.com/users/JuanitoFatas/following{/other_user}", 67 | "gists_url": "https://api.github.com/users/JuanitoFatas/gists{/gist_id}", 68 | "starred_url": "https://api.github.com/users/JuanitoFatas/starred{/owner}{/repo}", 69 | "subscriptions_url": "https://api.github.com/users/JuanitoFatas/subscriptions", 70 | "organizations_url": "https://api.github.com/users/JuanitoFatas/orgs", 71 | "repos_url": "https://api.github.com/users/JuanitoFatas/repos", 72 | "events_url": "https://api.github.com/users/JuanitoFatas/events{/privacy}", 73 | "received_events_url": "https://api.github.com/users/JuanitoFatas/received_events", 74 | "type": "User", 75 | "site_admin": false 76 | }, 77 | "labels": [ 78 | 79 | ], 80 | "state": "open", 81 | "locked": false, 82 | "assignee": null, 83 | "milestone": null, 84 | "comments": 0, 85 | "created_at": "2015-03-16T16:33:32Z", 86 | "updated_at": "2015-03-16T16:33:32Z", 87 | "closed_at": null, 88 | "pull_request": { 89 | "url": "https://api.github.com/repos/jollygoodcode/twemoji/pulls/12", 90 | "html_url": "https://github.com/jollygoodcode/twemoji/pull/12", 91 | "diff_url": "https://github.com/jollygoodcode/twemoji/pull/12.diff", 92 | "patch_url": "https://github.com/jollygoodcode/twemoji/pull/12.patch" 93 | }, 94 | "body": "- [ ] Changelog\r\n- [ ] Release v1.2.0" 95 | } 96 | ] 97 | -------------------------------------------------------------------------------- /spec/fixtures/omniauth.json: -------------------------------------------------------------------------------- 1 | { 2 | "provider": "github", 3 | "uid": "1000669", 4 | "info": { 5 | "nickname": "JuanitoFatas", 6 | "email": "katehuang0320@gmail.com", 7 | "name": "Juanito Fatas", 8 | "image": "https://avatars.githubusercontent.com/u/1000669?v=3", 9 | "urls": { 10 | "GitHub": "https://github.com/JuanitoFatas", 11 | "Blog": "http://juanitofatas.com" 12 | } 13 | }, 14 | "credentials": { 15 | "token": "55b76a860cef54e7d6366115fdb6668e81f25f2e", 16 | "expires": false 17 | }, 18 | "extra": { 19 | "raw_info": { 20 | "login": "JuanitoFatas", 21 | "id": 1000669, 22 | "avatar_url": "https://avatars.githubusercontent.com/u/1000669?v=3", 23 | "gravatar_id": "", 24 | "url": "https://api.github.com/users/JuanitoFatas", 25 | "html_url": "https://github.com/JuanitoFatas", 26 | "followers_url": "https://api.github.com/users/JuanitoFatas/followers", 27 | "following_url": "https://api.github.com/users/JuanitoFatas/following{/other_user}", 28 | "gists_url": "https://api.github.com/users/JuanitoFatas/gists{/gist_id}", 29 | "starred_url": "https://api.github.com/users/JuanitoFatas/starred{/owner}{/repo}", 30 | "subscriptions_url": "https://api.github.com/users/JuanitoFatas/subscriptions", 31 | "organizations_url": "https://api.github.com/users/JuanitoFatas/orgs", 32 | "repos_url": "https://api.github.com/users/JuanitoFatas/repos", 33 | "events_url": "https://api.github.com/users/JuanitoFatas/events{/privacy}", 34 | "received_events_url": "https://api.github.com/users/JuanitoFatas/received_events", 35 | "type": "User", 36 | "site_admin": false, 37 | "name": "Juanito Fatas", 38 | "company": "Jolly Good Code", 39 | "blog": "http://juanitofatas.com", 40 | "location": "Taipei, Taiwan", 41 | "email": "juanitofatas@gmail.com", 42 | "hireable": null, 43 | "bio": null, 44 | "public_repos": 409, 45 | "public_gists": 174, 46 | "followers": 472, 47 | "following": 8, 48 | "created_at": "2011-08-24T04:08:54Z", 49 | "updated_at": "2015-10-14T12:49:43Z" 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /spec/fixtures/travis/google_visualr.json: -------------------------------------------------------------------------------- 1 | {"branch":{"id":77793465,"repository_id":9295,"commit_id":22185215,"number":"102","config":{"language":"ruby","bundler_args":"--retry=3 --jobs=3 --no-deployment","cache":"bundler","sudo":false,"rvm":["2.0.0","2.1.6","2.2.2"],"gemfile":["gemfiles/3.2.gemfile","gemfiles/4.0.gemfile","gemfiles/4.1.gemfile","Gemfile"],".result":"configured"},"state":"passed","started_at":"2015-08-29T07:54:06Z","finished_at":"2015-08-29T07:55:30Z","duration":315,"job_ids":[77793466,77793468,77793469,77793470,77793471,77793472,77793474,77793475,77793476,77793477,77793478,77793480],"pull_request":false},"commit":{"id":22185215,"sha":"e799d4c714756957de030febac372b9cbc58dbf5","branch":"master","message":"Bump to 2.5.1","committed_at":"2015-08-29T07:53:46Z","author_name":"Winston","author_email":"winston.yongwei@gmail.com","committer_name":"Winston","committer_email":"winston.yongwei@gmail.com","compare_url":"https://github.com/winston/google_visualr/compare/ffae1726bc15...e799d4c71475"}} 2 | -------------------------------------------------------------------------------- /spec/fixtures/travis/sinatra.json: -------------------------------------------------------------------------------- 1 | {"branch":{"id":81354955,"repository_id":82,"commit_id":23170031,"number":"1034","config":{"language":"ruby","rvm":["1.8.7","1.9.2","1.9.3","2.0.0",2.1,2.2,"rbx-2","jruby","jruby-head","ruby-head"],"sudo":false,"matrix":{"include":[{"rvm":"1.8.7","env":"tilt=master"},{"rvm":"1.9.3","env":"tilt=master"},{"rvm":2.2,"env":"rack=master"},{"rvm":2.2,"env":"tilt=master"}],"allow_failures":[{"env":"rack=master"},{"env":"tilt=master"},{"rvm":"rbx-2"},{"rvm":"ruby-head"},{"rvm":"jruby-head"}]},".result":"configured"},"state":"passed","started_at":"2015-09-21T09:26:17Z","finished_at":"2015-09-21T09:31:19Z","duration":1287,"job_ids":[81354956,81354957,81354958,81354959,81354960,81354961,81354962,81354963,81354964,81354965,81354966,81354967,81354968,81354969],"pull_request":false},"commit":{"id":23170031,"sha":"52b9a798a71ac3594be44c99f3fa786bbe6c8824","branch":"master","message":"Merge pull request #1037 from sinatrasapporo/rm-whitespace\n\nRemove unnecessary whitespaces","committed_at":"2015-09-21T09:26:03Z","author_name":"Kashyap","author_email":"kashyap.kmbc@gmail.com","committer_name":"Kashyap","committer_email":"kashyap.kmbc@gmail.com","compare_url":"https://github.com/sinatra/sinatra/compare/1d48ab292961...52b9a798a71a"}} 2 | -------------------------------------------------------------------------------- /spec/fixtures/travis/twemoji.json: -------------------------------------------------------------------------------- 1 | {"branch":{"id":83433474,"repository_id":3731380,"commit_id":23751070,"number":"91","config":{"language":"ruby","sudo":false,"bundler_args":"--retry=3 --jobs=3","rvm":[2.0,2.1,2.2,"ruby-head"],"matrix":{"allow_failures":[{"rvm":"ruby-head"}],"fast_finish":true},"notifications":{"slack":{"secure":"wZ1UPEcH/Zf/wSPvpR1q76vpUQvAEGuwR5500ML8C+shW7PedvuSZaiZYAOZxhyD17RXzXYroON0ArEk3NUcKD8supPzYjUWzCeWDIFaYxEb7dOm7tpRBf4GuQ/oy8HGsBi947ZTX3WytGWZU0q0aRu2EirPcoY05vzley/hYTI="}},".result":"configured"},"state":"passed","started_at":"2015-10-03T09:41:23Z","finished_at":"2015-10-03T09:42:24Z","duration":172,"job_ids":[83433475,83433476,83433477,83433478],"pull_request":false},"commit":{"id":23751070,"sha":"67b71b7b6238aca1dd692aa49c5a4c7cc1fe85fe","branch":"master","message":"Merge pull request #18 from jollygoodcode/feature/export-json\n\nAdd support for output json","committed_at":"2015-10-03T09:41:16Z","author_name":"Juanito Fatas","author_email":"katehuang0320@gmail.com","committer_name":"Juanito Fatas","committer_email":"katehuang0320@gmail.com","compare_url":"https://github.com/jollygoodcode/twemoji/compare/67f726daea33...67b71b7b6238"}} 2 | -------------------------------------------------------------------------------- /spec/helpers/application_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe ApplicationHelper do 4 | describe "#status_class" do 5 | subject(:status_class) { helper.status_class(status) } 6 | 7 | context "unavailable" do 8 | let(:status) { :unavailable } 9 | 10 | it { expect(status_class).to eq "card-default" } 11 | end 12 | 13 | context "passed" do 14 | let(:status) { :passed } 15 | 16 | it { expect(status_class).to eq "card-success" } 17 | end 18 | 19 | context "waiting" do 20 | let(:status) { :waiting } 21 | 22 | it { expect(status_class).to eq "card-in-progress" } 23 | end 24 | 25 | context "failed" do 26 | let(:status) { :anything } 27 | 28 | it { expect(status_class).to eq "card-failure" } 29 | end 30 | end 31 | 32 | describe "#status_text" do 33 | subject(:status_text) { helper.status_text(status) } 34 | 35 | context "unavailable" do 36 | let(:status) { :unavailable } 37 | 38 | it { expect(status_text).to eq "Project does not have a CI status" } 39 | end 40 | 41 | context "passed" do 42 | let(:status) { :passed } 43 | 44 | it { expect(status_text).to eq "All specs passing on Master branch" } 45 | end 46 | 47 | context "waiting" do 48 | let(:status) { :waiting } 49 | 50 | it { expect(status_text).to eq "" } 51 | end 52 | 53 | context "failed" do 54 | let(:status) { :anything } 55 | 56 | it { expect(status_text).to eq "A few specs failing on Master branch" } 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/models/ability_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | require "cancan/matchers" 3 | 4 | RSpec.describe Ability do 5 | subject(:ability) { Ability.new(user) } 6 | 7 | context "normal user" do 8 | let(:user) { create(:user) } 9 | 10 | it { is_expected.to be_able_to(:crud, Project.new(user_id: user.id)) } 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/models/oauth_account_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe OauthAccount do 4 | context "associations" do 5 | it { is_expected.to belong_to(:user) } 6 | end 7 | 8 | let(:omniauth_json) { OmniAuth::AuthHash.new JSON.parse(IO.read("spec/fixtures/omniauth.json")) } 9 | let(:oauth_account) { OauthAccount.from_omniauth(omniauth_json) } 10 | 11 | describe ".from_omniauth" do 12 | it "should create OauthAccount + User" do 13 | expect(oauth_account).to be_kind_of(OauthAccount) 14 | expect(oauth_account).to be_persisted 15 | end 16 | 17 | it "should be associated with a user" do 18 | user = oauth_account.user 19 | 20 | expect(user).to be_kind_of(User) 21 | expect(user).to be_persisted 22 | expect(user.email).to be_present 23 | end 24 | 25 | it "should retrieve existing OauthAccount and User" do 26 | oauth_account 27 | 28 | expect { 29 | oauth_account 30 | }.not_to change { 31 | OauthAccount.count 32 | User.count 33 | } 34 | end 35 | end 36 | 37 | describe "#oauth_token=" do 38 | it "encrypts" do 39 | cryptor = double 40 | expect(ActiveSupport::MessageEncryptor).to receive(:new).and_return(cryptor) 41 | 42 | expect(cryptor).to receive(:encrypt_and_sign) 43 | 44 | oauth_account 45 | end 46 | end 47 | 48 | describe "#oauth_token" do 49 | it "decrypts" do 50 | cryptor = double 51 | expect(ActiveSupport::MessageEncryptor).to receive(:new).and_return(cryptor) 52 | 53 | expect(cryptor).to receive(:encrypt_and_sign) 54 | 55 | oauth_account.oauth_token 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/models/project_decorator_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe ProjectDecorator do 2 | subject { ProjectDecorator.new(project) } 3 | 4 | describe "#process_with" do 5 | let(:project) { build(:project) } 6 | 7 | it "invokes Parallel" do 8 | allow(Parallel).to receive(:each) { spy } 9 | 10 | subject.process_with 11 | 12 | expect(Parallel).to have_received(:each) 13 | end 14 | end 15 | 16 | describe "#org_user" do 17 | let(:project) { build(:project, repo_name: "jollygoodcode/dasherize") } 18 | 19 | it "returns first of project's repo name" do 20 | expect(subject.org_user).to eq "jollygoodcode" 21 | end 22 | end 23 | 24 | describe "#name" do 25 | let(:project) { build(:project, repo_name: "jollygoodcode/dasherize") } 26 | 27 | it "returns last of project's repo name" do 28 | expect(subject.name).to eq "dasherize" 29 | end 30 | end 31 | 32 | describe "#url" do 33 | let(:project) { build(:project, repo_name: "jollygoodcode/dasherize") } 34 | 35 | it "returns url based on project's repo name" do 36 | expect(subject.url).to eq "https://github.com/jollygoodcode/dasherize" 37 | end 38 | end 39 | 40 | describe "#pull_requests_url" do 41 | let(:project) { build(:project, repo_name: "jollygoodcode/dasherize") } 42 | 43 | it "returns Pull Requests url based on project's repo name" do 44 | expect(subject.pull_requests_url).to eq "https://github.com/jollygoodcode/dasherize/pulls" 45 | end 46 | end 47 | 48 | describe "#issues_url" do 49 | let(:project) { build(:project, repo_name: "jollygoodcode/dasherize") } 50 | 51 | it "returns Issues url based on project's repo name" do 52 | expect(subject.issues_url).to eq "https://github.com/jollygoodcode/dasherize/issues" 53 | end 54 | end 55 | 56 | describe "#pull_requests" do 57 | let(:project) { build(:project) } 58 | let(:pull_request) { double("Pull Request", pull_request: true) } 59 | let(:issue) { double("Issue", pull_request: false) } 60 | 61 | it "returns issue responds to pull_request" do 62 | expect(Octokit::Client).to receive(:new) { double(issues: [pull_request, issue]) } 63 | 64 | subject.process_with 65 | 66 | expect(subject.pull_requests).to eq [pull_request] 67 | end 68 | end 69 | 70 | describe "#issues" do 71 | let(:project) { build(:project) } 72 | let(:pull_request) { double("Pull Request", pull_request: true) } 73 | let(:issue) { double("Issue", pull_request: false) } 74 | 75 | it "returns issue not responds to pull_request" do 76 | expect(Octokit::Client).to receive(:new) { double(issues: [pull_request, issue]) } 77 | 78 | subject.process_with 79 | 80 | expect(subject.issues).to eq [issue] 81 | end 82 | end 83 | 84 | describe "#status" do 85 | let(:project) { build(:project, ci_type: ci_type) } 86 | 87 | before { expect(Octokit::Client).to receive(:new).and_return(spy) } 88 | 89 | context "travis" do 90 | let(:ci_type) { "travis" } 91 | 92 | it "returns API result from Status::Travis" do 93 | expect(Status::Travis).to receive_message_chain(:new, :run!) { double(status: :passed) } 94 | 95 | subject.process_with 96 | 97 | expect(subject.status).to eq :passed 98 | end 99 | end 100 | 101 | context "codeship" do 102 | let(:ci_type) { "codeship" } 103 | 104 | it "returns API result from Status::Codeship" do 105 | expect(Status::Codeship).to receive_message_chain(:new, :run!) { double(status: :passed) } 106 | 107 | subject.process_with 108 | 109 | expect(subject.status).to eq :passed 110 | end 111 | end 112 | 113 | context "Circle CI" do 114 | let(:ci_type) { "circleci" } 115 | 116 | it "returns API result from Status::Circleci" do 117 | expect(Status::Circleci).to receive_message_chain(:new, :run!) { double(status: :passed) } 118 | 119 | subject.process_with 120 | 121 | expect(subject.status).to eq :passed 122 | end 123 | end 124 | 125 | context "other CIs" do 126 | let(:ci_type) { "sideci" } 127 | 128 | it "returns :unavailable for other CIs" do 129 | subject.process_with 130 | 131 | expect(subject.status).to eq :unavailable 132 | end 133 | end 134 | end 135 | end 136 | -------------------------------------------------------------------------------- /spec/models/project_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe Project do 4 | context "associations" do 5 | it { is_expected.to belong_to :user } 6 | end 7 | 8 | context "validations" do 9 | it { is_expected.to validate_presence_of :repo_name } 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/models/status/base_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe Status::Base do 2 | describe "#branch" do 3 | it "returns master" do 4 | base_status = Status::Base.new("jollygoodcode/dasherize") 5 | 6 | expect(base_status.branch).to eq "master" 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/models/status/circleci_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe Status::Circleci do 2 | describe "#status" do 3 | let(:circleci) { Status::Circleci.new("mtchavez/circleci").run! } 4 | 5 | let(:failure_messages) do 6 | %w(retried canceled infrastructure_fail timedout failed no_tests) 7 | end 8 | 9 | let(:waiting_messages) do 10 | %w(not_run running queued scheduled not_running) 11 | end 12 | 13 | before do 14 | allow(HTTP).to receive(:headers) { spy } 15 | allow(JSON).to receive(:parse) { [{ "branch" => "master", "status" => api_result }] } 16 | end 17 | 18 | context "API returns success" do 19 | let(:api_result) { "success" } 20 | 21 | it "passed" do 22 | expect(circleci.status).to eq :passed 23 | end 24 | end 25 | 26 | context "API returns one of the failure messages" do 27 | let(:api_result) { failure_messages.sample } 28 | 29 | it "failed" do 30 | expect(circleci.status).to eq :failed 31 | end 32 | end 33 | 34 | context "API returns anything else" do 35 | let(:api_result) { waiting_messages.sample } 36 | 37 | it "waiting" do 38 | expect(circleci.status).to eq :waiting 39 | end 40 | end 41 | end 42 | 43 | describe "#url" do 44 | let(:circleci) { Status::Circleci.new("mtchavez/circleci").run! } 45 | 46 | before do 47 | allow(HTTP).to receive(:headers) { spy } 48 | allow(JSON).to receive(:parse) { [{ "branch" => "master", "build_url" => "https://circleci.com" }] } 49 | end 50 | 51 | it { expect(circleci.url).to eq "https://circleci.com" } 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/models/status/codeship_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe Status::Codeship do 2 | describe "#status" do 3 | let(:codeship) { Status::Codeship.new("jollygoodcode/dasherize").run! } 4 | 5 | let(:failure_messages) do 6 | %i(error projectnotfound branchnotfound ignored stopped infrastructure_failure) 7 | end 8 | 9 | before { allow(Codeship::Status).to receive(:new) { double(status: api_result) } } 10 | 11 | context "API returns success" do 12 | let(:api_result) { :success } 13 | 14 | it "passed" do 15 | expect(codeship.status).to eq :passed 16 | end 17 | end 18 | 19 | context "API returns one of the failure messages" do 20 | let(:api_result) { failure_messages.sample } 21 | 22 | it "failed" do 23 | expect(codeship.status).to eq :failed 24 | end 25 | end 26 | 27 | context "API returns anything else" do 28 | let(:api_result) { :hacking } 29 | 30 | it "waiting" do 31 | expect(codeship.status).to eq :waiting 32 | end 33 | end 34 | end 35 | 36 | # Codeship doesn't have an api that could easily return the build url 37 | # If we have to implement this, we would first need to know the Codeship ID of the project 38 | # which means storing another field in the database -> not something we want.. 39 | # Hence best effort for now is at least to link to Codeship 40 | describe "#url" do 41 | let(:codeship) { Status::Codeship.new("jollygoodcode/dasherize").run! } 42 | 43 | it { expect(codeship.url).to eq "https://codeship.com/projects" } 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/models/status/travis_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe Status::Travis do 2 | describe "#status" do 3 | let(:travis) { Status::Travis.new("jollygoodcode/dasherize").run! } 4 | 5 | before do 6 | allow(HTTP).to receive(:headers) { spy } 7 | allow(JSON).to receive(:parse) { Hash("branch" => { "state" => api_result }) } 8 | end 9 | 10 | context "API returns 0" do 11 | let(:api_result) { "passed" } 12 | 13 | it "passed" do 14 | expect(travis.status).to eq :passed 15 | end 16 | end 17 | 18 | context "API returns 1" do 19 | let(:api_result) { "failed" } 20 | 21 | it "failed" do 22 | expect(travis.status).to eq :failed 23 | end 24 | end 25 | 26 | context "API returns anything else" do 27 | let(:api_result) { "anything" } 28 | 29 | it "waiting" do 30 | expect(travis.status).to eq :waiting 31 | end 32 | end 33 | end 34 | 35 | describe "#url" do 36 | let(:repo) { "jollygoodcode/dasherize" } 37 | let(:build_id) { "1234567890" } 38 | 39 | let(:travis) { Status::Travis.new(repo).run! } 40 | 41 | before do 42 | allow(HTTP).to receive(:headers) { spy } 43 | allow(JSON).to receive(:parse) { Hash("branch" => { "id" => build_id }) } 44 | end 45 | 46 | it { expect(travis.url).to eq "https://travis-ci.org/#{repo}/builds/#{build_id}"} 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe User do 4 | context "associations" do 5 | it { is_expected.to have_one(:oauth_account) } 6 | end 7 | 8 | context "validations" do 9 | it { is_expected.to validate_presence_of(:name) } 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV["RAILS_ENV"] ||= "test" 3 | 4 | require "spec_helper" 5 | require File.expand_path("../../config/environment", __FILE__) 6 | require "rspec/rails" 7 | require "shoulda" 8 | require "webmock/rspec" 9 | 10 | # Requires supporting ruby files with custom matchers and macros, etc, in 11 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 12 | # run as spec files by default. This means that files in spec/support that end 13 | # in _spec.rb will both be required and run as specs, causing the specs to be 14 | # run twice. It is recommended that you do not name files matching this glob to 15 | # end with _spec.rb. You can configure this pattern with the --pattern 16 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 17 | Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } 18 | 19 | # Checks for pending migrations before tests are run. 20 | # If you are not using ActiveRecord, you can remove this line. 21 | ActiveRecord::Migration.maintain_test_schema! 22 | 23 | RSpec.configure do |config| 24 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 25 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 26 | 27 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 28 | # examples within a transaction, remove the following line or assign false 29 | # instead of true. 30 | config.use_transactional_fixtures = true 31 | 32 | # RSpec Rails can automatically mix in different behaviours to your tests 33 | # based on their file location, for example enabling you to call `get` and 34 | # `post` in specs under `spec/controllers`. 35 | # 36 | # You can disable this behaviour by removing the line below, and instead 37 | # explicitly tag your specs with their type, e.g.: 38 | # 39 | # RSpec.describe UsersController, type: :controller do 40 | # # ... 41 | # end 42 | # 43 | # The different available types are documented in the features, such as in 44 | # https://relishapp.com/rspec/rspec-rails/docs 45 | config.infer_spec_type_from_file_location! 46 | 47 | # Devise, sign_in, sign_out helpers 48 | config.include Devise::TestHelpers, type: :controller 49 | config.include APIHelper 50 | config.include OAuthHelper 51 | 52 | WebMock.disable_net_connect!(allow_localhost: true) 53 | end 54 | 55 | OmniAuth.config.test_mode = true 56 | -------------------------------------------------------------------------------- /spec/services/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/spec/services/.keep -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | # 7 | # Given that it is always loaded, you are encouraged to keep this file as 8 | # light-weight as possible. Requiring heavyweight dependencies from this file 9 | # will add to the boot time of your test suite on EVERY test run, even for an 10 | # individual file that may not need all of that loaded. Instead, consider making 11 | # a separate helper file that requires the additional dependencies and performs 12 | # the additional setup, and require it from the spec files that actually need 13 | # it. 14 | # 15 | # The `.rspec` file also contains a few flags that are not defaults but that 16 | # users commonly want. 17 | # 18 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 19 | RSpec.configure do |config| 20 | # rspec-expectations config goes here. You can use an alternate 21 | # assertion/expectation library such as wrong or the stdlib/minitest 22 | # assertions if you prefer. 23 | config.expect_with :rspec do |expectations| 24 | # This option will default to `true` in RSpec 4. It makes the `description` 25 | # and `failure_message` of custom matchers include text for helper methods 26 | # defined using `chain`, e.g.: 27 | # be_bigger_than(2).and_smaller_than(4).description 28 | # # => "be bigger than 2 and smaller than 4" 29 | # ...rather than: 30 | # # => "be bigger than 2" 31 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 32 | end 33 | 34 | # rspec-mocks config goes here. You can use an alternate test double 35 | # library (such as bogus or mocha) by changing the `mock_with` option here. 36 | config.mock_with :rspec do |mocks| 37 | # Prevents you from mocking or stubbing a method that does not exist on 38 | # a real object. This is generally recommended, and will default to 39 | # `true` in RSpec 4. 40 | mocks.verify_partial_doubles = true 41 | end 42 | 43 | # The settings below are suggested to provide a good initial experience 44 | # with RSpec, but feel free to customize to your heart's content. 45 | 46 | # These two settings work together to allow you to limit a spec run 47 | # to individual examples or groups you care about by tagging them with 48 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 49 | # get run. 50 | config.filter_run :focus 51 | config.run_all_when_everything_filtered = true 52 | 53 | # Allows RSpec to persist some state between runs in order to support 54 | # the `--only-failures` and `--next-failure` CLI options. We recommend 55 | # you configure your source control system to ignore this file. 56 | config.example_status_persistence_file_path = "spec/examples.txt" 57 | 58 | # Limits the available syntax to the non-monkey patched syntax that is 59 | # recommended. For more details, see: 60 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 61 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 62 | # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching 63 | config.disable_monkey_patching! 64 | 65 | # Many RSpec users commonly either run the entire suite or an individual 66 | # file, and it's useful to allow more verbose output when running an 67 | # individual spec file. 68 | if config.files_to_run.one? 69 | # Use the documentation formatter for detailed output, 70 | # unless a formatter has already been configured 71 | # (e.g. via a command-line flag). 72 | config.default_formatter = "doc" 73 | end 74 | 75 | # Print the 10 slowest examples and example groups at the 76 | # end of the spec run, to help surface which specs are running 77 | # particularly slow. 78 | # config.profile_examples = 10 79 | 80 | # Run specs in random order to surface order dependencies. If you find an 81 | # order dependency and want to debug it, you can fix the order by providing 82 | # the seed, which is printed after each run. 83 | # --seed 1234 84 | config.order = :random 85 | 86 | # Seed global randomization in this process using the `--seed` CLI option. 87 | # Setting this allows you to use `--seed` to deterministically reproduce 88 | # test failures related to randomization by passing the same `--seed` value 89 | # as the one that triggered the failure. 90 | Kernel.srand config.seed 91 | 92 | # Factory Girl 93 | require 'factory_girl_rails' 94 | config.include FactoryGirl::Syntax::Methods 95 | end 96 | -------------------------------------------------------------------------------- /spec/support/api_helper.rb: -------------------------------------------------------------------------------- 1 | module APIHelper 2 | def stub_ci_requests_for_home_page 3 | stub_request( 4 | :get, "https://api.github.com/repos/sinatra/sinatra/issues?per_page=100" 5 | ).with( 6 | headers: { "Content-Type" => "application/json" } 7 | ).to_return( 8 | status: 200, body: IO.read("spec/fixtures/github/sinatra.json"), 9 | headers: { "Content-Type" => "application/json" } 10 | ) 11 | 12 | stub_request( 13 | :get, "https://api.github.com/repos/jollygoodcode/twemoji/issues?per_page=100" 14 | ).with( 15 | headers: { "User-Agent" => "Octokit Ruby Gem #{Octokit::VERSION}" } 16 | ).to_return( 17 | status: 200, body: IO.read("spec/fixtures/github/twemoji.json"), 18 | headers: { "Content-Type" => "application/json" } 19 | ) 20 | 21 | stub_request( 22 | :get, "https://api.github.com/repos/winston/google_visualr/issues?per_page=100" 23 | ).with( 24 | headers: { "User-Agent" => "Octokit Ruby Gem #{Octokit::VERSION}" } 25 | ).to_return( 26 | status: 200, body: IO.read("spec/fixtures/github/google_visualr.json"), 27 | headers: { "Content-Type" => "application/json" } 28 | ) 29 | 30 | stub_request( 31 | :get, "https://api.travis-ci.org/repos/sinatra/sinatra/branches/master.json" 32 | ).with( 33 | headers: { "User-Agent" => "Dasherize/1.0.0", "Accept" => "application/vnd.travis-ci.2+json" } 34 | ).to_return( 35 | status: 200, body: IO.read("spec/fixtures/travis/sinatra.json"), 36 | headers: { "Content-Type" => "application/json" } 37 | ) 38 | 39 | stub_request( 40 | :get, "https://api.travis-ci.org/repos/jollygoodcode/twemoji/branches/master.json" 41 | ).with( 42 | headers: { "User-Agent" => "Dasherize/1.0.0", "Accept"=>"application/vnd.travis-ci.2+json" } 43 | ).to_return( 44 | status: 200, body: IO.read("spec/fixtures/travis/twemoji.json"), 45 | headers: { "Content-Type" => "application/json" } 46 | ) 47 | 48 | stub_request( 49 | :get, "https://api.travis-ci.org/repos/winston/google_visualr/branches/master.json" 50 | ).with( 51 | headers: { "User-Agent" => "Dasherize/1.0.0", "Accept" => "application/vnd.travis-ci.2+json" } 52 | ).to_return( 53 | status: 200, body: IO.read("spec/fixtures/travis/google_visualr.json"), 54 | headers: { "Content-Type" => "application/json" } 55 | ) 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/support/oauth_helper.rb: -------------------------------------------------------------------------------- 1 | module OAuthHelper 2 | def sign_in_as(user) 3 | stub_oauth(user) 4 | visit root_path 5 | within "[data-spec=sign-in]" do 6 | click_link "Sign In with GitHub" 7 | end 8 | end 9 | 10 | def sign_out_user 11 | within "[data-spec=sign-out]" do 12 | find(%{a[href="/users/sign_out"]}).click 13 | end 14 | end 15 | 16 | def stub_oauth(user) 17 | OmniAuth.config.add_mock( 18 | :github, 19 | info: { name: user.name, email: user.email }, 20 | credentials: { token: user.oauth_account.oauth_token } 21 | ) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jollygoodcode/dasherize/da71516357dfe62fc58d68d86b7a1275f54f6ba5/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------