├── chapter_2
├── bacon
│ ├── lib
│ │ └── bacon.rb
│ └── spec
│ │ └── bacon_spec.rb
├── bacon_test.rb
└── example_test.rb
├── things_i_bought
├── .gitignore
├── Gemfile
├── Gemfile.lock
├── README.rdoc
├── Rakefile
├── app
│ ├── assets
│ │ ├── images
│ │ │ └── .keep
│ │ ├── javascripts
│ │ │ ├── application.js
│ │ │ └── purchases.coffee
│ │ └── stylesheets
│ │ │ ├── application.css
│ │ │ ├── purchases.scss
│ │ │ └── scaffolds.scss
│ ├── controllers
│ │ ├── application_controller.rb
│ │ ├── concerns
│ │ │ └── .keep
│ │ └── purchases_controller.rb
│ ├── helpers
│ │ ├── application_helper.rb
│ │ └── purchases_helper.rb
│ ├── mailers
│ │ └── .keep
│ ├── models
│ │ ├── .keep
│ │ ├── concerns
│ │ │ └── .keep
│ │ └── purchase.rb
│ └── views
│ │ ├── layouts
│ │ └── application.html.erb
│ │ └── purchases
│ │ ├── _form.html.erb
│ │ ├── edit.html.erb
│ │ ├── index.html.erb
│ │ ├── index.json.jbuilder
│ │ ├── new.html.erb
│ │ ├── show.html.erb
│ │ └── show.json.jbuilder
├── bin
│ ├── bundle
│ ├── rails
│ ├── rake
│ ├── setup
│ └── spring
├── config.ru
├── config
│ ├── application.rb
│ ├── boot.rb
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ │ ├── development.rb
│ │ ├── production.rb
│ │ └── test.rb
│ ├── initializers
│ │ ├── assets.rb
│ │ ├── backtrace_silencers.rb
│ │ ├── cookies_serializer.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── inflections.rb
│ │ ├── mime_types.rb
│ │ ├── session_store.rb
│ │ └── wrap_parameters.rb
│ ├── locales
│ │ └── en.yml
│ ├── routes.rb
│ └── secrets.yml
├── db
│ ├── migrate
│ │ └── 20150311101434_create_purchases.rb
│ ├── schema.rb
│ └── seeds.rb
├── lib
│ ├── assets
│ │ └── .keep
│ └── tasks
│ │ └── .keep
├── log
│ └── .keep
├── public
│ ├── 404.html
│ ├── 422.html
│ ├── 500.html
│ ├── favicon.ico
│ └── robots.txt
├── test
│ ├── controllers
│ │ ├── .keep
│ │ └── purchases_controller_test.rb
│ ├── fixtures
│ │ ├── .keep
│ │ └── purchases.yml
│ ├── helpers
│ │ └── .keep
│ ├── integration
│ │ └── .keep
│ ├── mailers
│ │ └── .keep
│ ├── models
│ │ ├── .keep
│ │ └── purchase_test.rb
│ └── test_helper.rb
└── vendor
│ └── assets
│ ├── javascripts
│ └── .keep
│ └── stylesheets
│ └── .keep
├── ticketee
├── .gitignore
├── .rspec
├── .travis.yml
├── Gemfile
├── Gemfile.lock
├── Procfile
├── README.rdoc
├── Rakefile
├── app
│ ├── assets
│ │ ├── images
│ │ │ └── .keep
│ │ ├── javascripts
│ │ │ ├── admin
│ │ │ │ ├── application.coffee
│ │ │ │ ├── projects.coffee
│ │ │ │ ├── states.coffee
│ │ │ │ └── users.coffee
│ │ │ ├── api
│ │ │ │ └── tickets.coffee
│ │ │ ├── application.js
│ │ │ ├── attachments.coffee
│ │ │ ├── comments.coffee
│ │ │ ├── projects.coffee
│ │ │ ├── tags.coffee
│ │ │ └── tickets.coffee
│ │ └── stylesheets
│ │ │ ├── admin
│ │ │ ├── application.scss
│ │ │ ├── projects.scss
│ │ │ ├── states.scss
│ │ │ └── users.scss
│ │ │ ├── api
│ │ │ └── tickets.scss
│ │ │ ├── application.css.scss
│ │ │ ├── attachments.scss
│ │ │ ├── comments.scss
│ │ │ ├── projects.scss
│ │ │ ├── responsive.scss
│ │ │ ├── tags.scss
│ │ │ └── tickets.scss
│ ├── controllers
│ │ ├── admin
│ │ │ ├── application_controller.rb
│ │ │ ├── projects_controller.rb
│ │ │ ├── states_controller.rb
│ │ │ └── users_controller.rb
│ │ ├── api
│ │ │ ├── application_controller.rb
│ │ │ ├── tickets_controller.rb
│ │ │ └── v2
│ │ │ │ └── tickets.rb
│ │ ├── application_controller.rb
│ │ ├── attachments_controller.rb
│ │ ├── comments_controller.rb
│ │ ├── concerns
│ │ │ └── .keep
│ │ ├── projects_controller.rb
│ │ ├── tags_controller.rb
│ │ └── tickets_controller.rb
│ ├── helpers
│ │ ├── admin
│ │ │ ├── application_helper.rb
│ │ │ ├── projects_helper.rb
│ │ │ ├── states_helper.rb
│ │ │ └── users_helper.rb
│ │ ├── api
│ │ │ └── tickets_helper.rb
│ │ ├── application_helper.rb
│ │ ├── attachments_helper.rb
│ │ ├── comments_helper.rb
│ │ ├── projects_helper.rb
│ │ ├── tags_helper.rb
│ │ └── tickets_helper.rb
│ ├── mailers
│ │ ├── .keep
│ │ ├── application_mailer.rb
│ │ └── comment_notifier.rb
│ ├── models
│ │ ├── .keep
│ │ ├── attachment.rb
│ │ ├── comment.rb
│ │ ├── concerns
│ │ │ └── .keep
│ │ ├── project.rb
│ │ ├── role.rb
│ │ ├── state.rb
│ │ ├── tag.rb
│ │ ├── ticket.rb
│ │ └── user.rb
│ ├── policies
│ │ ├── application_policy.rb
│ │ ├── attachment_policy.rb
│ │ ├── comment_policy.rb
│ │ ├── project_policy.rb
│ │ └── ticket_policy.rb
│ ├── serializers
│ │ └── ticket_serializer.rb
│ ├── services
│ │ └── comment_creator.rb
│ ├── uploaders
│ │ └── attachment_uploader.rb
│ └── views
│ │ ├── admin
│ │ ├── application
│ │ │ └── index.html.erb
│ │ ├── projects
│ │ │ ├── _form.html.erb
│ │ │ └── new.html.erb
│ │ ├── states
│ │ │ ├── _form.html.erb
│ │ │ ├── index.html.erb
│ │ │ └── new.html.erb
│ │ └── users
│ │ │ ├── _form.html.erb
│ │ │ ├── edit.html.erb
│ │ │ ├── index.html.erb
│ │ │ ├── new.html.erb
│ │ │ └── show.html.erb
│ │ ├── attachments
│ │ ├── _form.html.erb
│ │ └── new.html.erb
│ │ ├── comment_notifier
│ │ └── created.text.erb
│ │ ├── comments
│ │ ├── _comment.html.erb
│ │ └── _form.html.erb
│ │ ├── devise
│ │ ├── confirmations
│ │ │ └── new.html.erb
│ │ ├── mailer
│ │ │ ├── confirmation_instructions.html.erb
│ │ │ ├── reset_password_instructions.html.erb
│ │ │ └── unlock_instructions.html.erb
│ │ ├── passwords
│ │ │ ├── edit.html.erb
│ │ │ └── new.html.erb
│ │ ├── registrations
│ │ │ ├── edit.html.erb
│ │ │ └── new.html.erb
│ │ ├── sessions
│ │ │ └── new.html.erb
│ │ ├── shared
│ │ │ └── _links.html.erb
│ │ └── unlocks
│ │ │ └── new.html.erb
│ │ ├── layouts
│ │ ├── application.html.erb
│ │ ├── mailer.html.erb
│ │ └── mailer.text.erb
│ │ ├── projects
│ │ ├── _form.html.erb
│ │ ├── edit.html.erb
│ │ ├── index.html.erb
│ │ └── show.html.erb
│ │ ├── states
│ │ └── _state.html.erb
│ │ ├── tags
│ │ ├── _form.html.erb
│ │ └── _tag.html.erb
│ │ └── tickets
│ │ ├── _form.html.erb
│ │ ├── edit.html.erb
│ │ ├── new.html.erb
│ │ └── show.html.erb
├── bin
│ ├── bundle
│ ├── rails
│ ├── rake
│ ├── setup
│ └── spring
├── config.ru
├── config
│ ├── application.rb
│ ├── boot.rb
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ │ ├── development.rb
│ │ ├── production.rb
│ │ └── test.rb
│ ├── initializers
│ │ ├── assets.rb
│ │ ├── backtrace_silencers.rb
│ │ ├── carrierwave.rb
│ │ ├── cookies_serializer.rb
│ │ ├── devise.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── inflections.rb
│ │ ├── mime_types.rb
│ │ ├── session_store.rb
│ │ ├── simple_form.rb
│ │ ├── simple_form_bootstrap.rb
│ │ └── wrap_parameters.rb
│ ├── locales
│ │ ├── devise.en.yml
│ │ ├── en.yml
│ │ └── simple_form.en.yml
│ ├── routes.rb
│ └── secrets.yml
├── db
│ ├── migrate
│ │ ├── 20150314100956_create_projects.rb
│ │ ├── 20150318033648_create_tickets.rb
│ │ ├── 20150321110052_devise_create_users.rb
│ │ ├── 20150322065031_add_author_to_tickets.rb
│ │ ├── 20150322084252_add_admin_to_users.rb
│ │ ├── 20150323072600_add_archived_at_to_users.rb
│ │ ├── 20150325092856_create_roles.rb
│ │ ├── 20150401104001_add_attachment_to_tickets.rb
│ │ ├── 20150402090612_create_attachments.rb
│ │ ├── 20150402090619_remove_attachment_from_tickets.rb
│ │ ├── 20150403021520_create_comments.rb
│ │ ├── 20150403044120_create_states.rb
│ │ ├── 20150403070314_add_previous_state_to_comments.rb
│ │ ├── 20150403084623_add_default_to_states.rb
│ │ ├── 20150404113718_create_tags.rb
│ │ ├── 20150404113820_create_join_table_tags_tickets.rb
│ │ ├── 20150405052036_create_join_table_ticket_watchers.rb
│ │ └── 20150406055658_add_api_key_to_users.rb
│ ├── schema.rb
│ └── seeds.rb
├── lib
│ ├── assets
│ │ └── .keep
│ ├── heartbeat
│ │ ├── application.rb
│ │ ├── config.ru
│ │ └── test_application.rb
│ ├── link_jumbler.rb
│ ├── tasks
│ │ └── .keep
│ └── templates
│ │ └── erb
│ │ └── scaffold
│ │ └── _form.html.erb
├── log
│ └── .keep
├── public
│ ├── 404.html
│ ├── 422.html
│ ├── 500.html
│ ├── favicon.ico
│ └── robots.txt
├── spec
│ ├── controllers
│ │ ├── admin
│ │ │ ├── application_controller_spec.rb
│ │ │ ├── projects_controller_spec.rb
│ │ │ └── states_controller_spec.rb
│ │ ├── api
│ │ │ └── tickets_controller_spec.rb
│ │ ├── attachments_controller_spec.rb
│ │ ├── comments_controller_spec.rb
│ │ ├── projects_controller_spec.rb
│ │ ├── tags_controller_spec.rb
│ │ └── tickets_controller_spec.rb
│ ├── factories
│ │ ├── attachment_factory.rb
│ │ ├── comment_factory.rb
│ │ ├── project_factory.rb
│ │ ├── state_factory.rb
│ │ ├── ticket_factory.rb
│ │ └── user_factory.rb
│ ├── features
│ │ ├── admin
│ │ │ ├── archiving_users_spec.rb
│ │ │ ├── creating_projects_spec.rb
│ │ │ ├── creating_states_spec.rb
│ │ │ ├── creating_users_spec.rb
│ │ │ ├── deleting_projects_spec.rb
│ │ │ ├── editing_users_spec.rb
│ │ │ ├── managing_roles_spec.rb
│ │ │ └── managing_states_spec.rb
│ │ ├── creating_comments_spec.rb
│ │ ├── creating_tickets_spec.rb
│ │ ├── deleting_tags_spec.rb
│ │ ├── deleting_tickets_spec.rb
│ │ ├── editing_projects_spec.rb
│ │ ├── editing_tickets_spec.rb
│ │ ├── hidden_links_spec.rb
│ │ ├── searching_spec.rb
│ │ ├── signing_in_spec.rb
│ │ ├── signing_out_spec.rb
│ │ ├── signing_up_spec.rb
│ │ ├── ticket_notifications_spec.rb
│ │ ├── viewing_attachments_spec.rb
│ │ ├── viewing_projects_spec.rb
│ │ ├── viewing_tickets_spec.rb
│ │ └── watching_tickets_spec.rb
│ ├── fixtures
│ │ ├── gradient.txt
│ │ ├── speed.txt
│ │ └── spin.txt
│ ├── mailers
│ │ ├── comment_notifier_spec.rb
│ │ └── previews
│ │ │ └── comment_notifier_preview.rb
│ ├── policies
│ │ ├── attachment_policy_spec.rb
│ │ ├── comment_policy_spec.rb
│ │ ├── project_policy_spec.rb
│ │ └── ticket_policy_spec.rb
│ ├── rails_helper.rb
│ ├── requests
│ │ └── api
│ │ │ ├── tickets_spec.rb
│ │ │ └── v2
│ │ │ └── tickets_spec.rb
│ ├── spec_helper.rb
│ └── support
│ │ ├── authorization_helpers.rb
│ │ ├── capybara_finders.rb
│ │ ├── capybara_matchers.rb
│ │ ├── database_cleaning.rb
│ │ ├── email_spec.rb
│ │ └── pundit_matcher.rb
└── vendor
│ └── assets
│ ├── javascripts
│ └── .keep
│ └── stylesheets
│ └── .keep
└── ticketee_api
└── tickets.rb
/chapter_2/bacon/lib/bacon.rb:
--------------------------------------------------------------------------------
1 | class Bacon
2 | attr_accessor :expired
3 |
4 | def edible?
5 | !expired
6 | end
7 |
8 | def expired!
9 | self.expired = true
10 | end
11 | end
--------------------------------------------------------------------------------
/chapter_2/bacon/spec/bacon_spec.rb:
--------------------------------------------------------------------------------
1 | require "bacon"
2 |
3 | RSpec.describe Bacon do
4 | it "is edible" do
5 | expect(Bacon.new.edible?).to be(true)
6 | end
7 |
8 | it "can expire" do
9 | bacon = Bacon.new
10 | bacon.expired!
11 | expect(bacon).to_not be_edible
12 | end
13 | end
--------------------------------------------------------------------------------
/chapter_2/bacon_test.rb:
--------------------------------------------------------------------------------
1 | require "minitest/autorun"
2 |
3 | class Bacon
4 | def self.saved?
5 | false
6 | end
7 | end
8 |
9 | class BaconTest < Minitest::Test
10 | def test_saved
11 | assert Bacon.saved?, "Our bacon was not saved :("
12 | end
13 | end
--------------------------------------------------------------------------------
/chapter_2/example_test.rb:
--------------------------------------------------------------------------------
1 | require "minitest/autorun"
2 |
3 | class ExampleTest < Minitest::Test
4 | def truth
5 | assert true
6 | end
7 | end
--------------------------------------------------------------------------------
/things_i_bought/.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/*
16 | !/log/.keep
17 | /tmp
18 |
--------------------------------------------------------------------------------
/things_i_bought/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 |
4 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
5 | gem 'rails', '4.2.0'
6 | # Use sqlite3 as the database for Active Record
7 | gem 'sqlite3'
8 | # Use SCSS for stylesheets
9 | gem 'sass-rails', '~> 5.0'
10 | # Use Uglifier as compressor for JavaScript assets
11 | gem 'uglifier', '>= 1.3.0'
12 | # Use CoffeeScript for .coffee assets and views
13 | gem 'coffee-rails', '~> 4.1.0'
14 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes
15 | # gem 'therubyracer', platforms: :ruby
16 |
17 | # Use jquery as the JavaScript library
18 | gem 'jquery-rails'
19 | # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
20 | gem 'turbolinks'
21 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
22 | gem 'jbuilder', '~> 2.0'
23 | # bundle exec rake doc:rails generates the API under doc/api.
24 | gem 'sdoc', '~> 0.4.0', group: :doc
25 |
26 | # Use ActiveModel has_secure_password
27 | # gem 'bcrypt', '~> 3.1.7'
28 |
29 | # Use Unicorn as the app server
30 | # gem 'unicorn'
31 |
32 | # Use Capistrano for deployment
33 | # gem 'capistrano-rails', group: :development
34 |
35 | group :development, :test do
36 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
37 | gem 'byebug'
38 |
39 | # Access an IRB console on exception pages or by using <%= console %> in views
40 | gem 'web-console', '~> 2.0'
41 |
42 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
43 | gem 'spring'
44 | end
45 |
46 |
--------------------------------------------------------------------------------
/things_i_bought/README.rdoc:
--------------------------------------------------------------------------------
1 | == README
2 |
3 | This README would normally document whatever steps are necessary to get the
4 | application up and running.
5 |
6 | Things you may want to cover:
7 |
8 | * Ruby version
9 |
10 | * System dependencies
11 |
12 | * Configuration
13 |
14 | * Database creation
15 |
16 | * Database initialization
17 |
18 | * How to run the test suite
19 |
20 | * Services (job queues, cache servers, search engines, etc.)
21 |
22 | * Deployment instructions
23 |
24 | * ...
25 |
26 |
27 | Please feel free to use a different markup language if you do not plan to run
28 | rake doc:app .
29 |
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/app/assets/images/.keep
--------------------------------------------------------------------------------
/things_i_bought/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file.
9 | //
10 | // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require jquery
14 | //= require jquery_ujs
15 | //= require turbolinks
16 | //= require_tree .
17 |
--------------------------------------------------------------------------------
/things_i_bought/app/assets/javascripts/purchases.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/things_i_bought/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any styles
10 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11 | * file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/things_i_bought/app/assets/stylesheets/purchases.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the purchases controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/things_i_bought/app/assets/stylesheets/scaffolds.scss:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #fff;
3 | color: #333;
4 | font-family: verdana, arial, helvetica, sans-serif;
5 | font-size: 13px;
6 | line-height: 18px;
7 | }
8 |
9 | p, ol, ul, td {
10 | font-family: verdana, arial, helvetica, sans-serif;
11 | font-size: 13px;
12 | line-height: 18px;
13 | }
14 |
15 | pre {
16 | background-color: #eee;
17 | padding: 10px;
18 | font-size: 11px;
19 | }
20 |
21 | a {
22 | color: #000;
23 | &:visited {
24 | color: #666;
25 | }
26 | &:hover {
27 | color: #fff;
28 | background-color: #000;
29 | }
30 | }
31 |
32 | div {
33 | &.field, &.actions {
34 | margin-bottom: 10px;
35 | }
36 | }
37 |
38 | #notice {
39 | color: green;
40 | }
41 |
42 | .field_with_errors {
43 | padding: 2px;
44 | background-color: red;
45 | display: table;
46 | }
47 |
48 | #error_explanation {
49 | width: 450px;
50 | border: 2px solid red;
51 | padding: 7px;
52 | padding-bottom: 0;
53 | margin-bottom: 20px;
54 | background-color: #f0f0f0;
55 | h2 {
56 | text-align: left;
57 | font-weight: bold;
58 | padding: 5px 5px 5px 15px;
59 | font-size: 12px;
60 | margin: -7px;
61 | margin-bottom: 0px;
62 | background-color: #c00;
63 | color: #fff;
64 | }
65 | ul li {
66 | font-size: 12px;
67 | list-style: square;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/things_i_bought/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | # Prevent CSRF attacks by raising an exception.
3 | # For APIs, you may want to use :null_session instead.
4 | protect_from_forgery with: :exception
5 | end
6 |
--------------------------------------------------------------------------------
/things_i_bought/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/things_i_bought/app/controllers/purchases_controller.rb:
--------------------------------------------------------------------------------
1 | class PurchasesController < ApplicationController
2 | before_action :set_purchase, only: [:show, :edit, :update, :destroy]
3 |
4 | # GET /purchases
5 | # GET /purchases.json
6 | def index
7 | @purchases = Purchase.all
8 | end
9 |
10 | # GET /purchases/1
11 | # GET /purchases/1.json
12 | def show
13 | end
14 |
15 | # GET /purchases/new
16 | def new
17 | @purchase = Purchase.new
18 | end
19 |
20 | # GET /purchases/1/edit
21 | def edit
22 | end
23 |
24 | # POST /purchases
25 | # POST /purchases.json
26 | def create
27 | @purchase = Purchase.new(purchase_params)
28 |
29 | respond_to do |format|
30 | if @purchase.save
31 | format.html { redirect_to @purchase, notice: 'Purchase was successfully created.' }
32 | format.json { render :show, status: :created, location: @purchase }
33 | else
34 | format.html { render :new }
35 | format.json { render json: @purchase.errors, status: :unprocessable_entity }
36 | end
37 | end
38 | end
39 |
40 | # PATCH/PUT /purchases/1
41 | # PATCH/PUT /purchases/1.json
42 | def update
43 | respond_to do |format|
44 | if @purchase.update(purchase_params)
45 | format.html { redirect_to @purchase, notice: 'Purchase was successfully updated.' }
46 | format.json { render :show, status: :ok, location: @purchase }
47 | else
48 | format.html { render :edit }
49 | format.json { render json: @purchase.errors, status: :unprocessable_entity }
50 | end
51 | end
52 | end
53 |
54 | # DELETE /purchases/1
55 | # DELETE /purchases/1.json
56 | def destroy
57 | @purchase.destroy
58 | respond_to do |format|
59 | format.html { redirect_to purchases_url, notice: 'Purchase was successfully destroyed.' }
60 | format.json { head :no_content }
61 | end
62 | end
63 |
64 | private
65 | # Use callbacks to share common setup or constraints between actions.
66 | def set_purchase
67 | @purchase = Purchase.find(params[:id])
68 | end
69 |
70 | # Never trust parameters from the scary internet, only allow the white list through.
71 | def purchase_params
72 | params.require(:purchase).permit(:name, :cost)
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/things_i_bought/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/things_i_bought/app/helpers/purchases_helper.rb:
--------------------------------------------------------------------------------
1 | module PurchasesHelper
2 | end
3 |
--------------------------------------------------------------------------------
/things_i_bought/app/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/app/mailers/.keep
--------------------------------------------------------------------------------
/things_i_bought/app/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/app/models/.keep
--------------------------------------------------------------------------------
/things_i_bought/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/app/models/concerns/.keep
--------------------------------------------------------------------------------
/things_i_bought/app/models/purchase.rb:
--------------------------------------------------------------------------------
1 | class Purchase < ActiveRecord::Base
2 | validates :name, presence: true
3 | validates :cost, numericality: { greater_than: 0 }
4 | end
--------------------------------------------------------------------------------
/things_i_bought/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ThingsIBought
5 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
6 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
7 | <%= csrf_meta_tags %>
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/things_i_bought/app/views/purchases/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_for(@purchase) do |f| %>
2 | <% if @purchase.errors.any? %>
3 |
4 |
<%= pluralize(@purchase.errors.count, "error") %> prohibited this purchase from being saved:
5 |
6 |
7 | <% @purchase.errors.full_messages.each do |message| %>
8 | <%= message %>
9 | <% end %>
10 |
11 |
12 | <% end %>
13 |
14 |
15 | <%= f.label :name %>
16 | <%= f.text_field :name %>
17 |
18 |
19 | <%= f.label :cost %>
20 | <%= f.text_field :cost %>
21 |
22 |
23 | <%= f.submit %>
24 |
25 | <% end %>
26 |
--------------------------------------------------------------------------------
/things_i_bought/app/views/purchases/edit.html.erb:
--------------------------------------------------------------------------------
1 | Editing Purchase
2 |
3 | <%= render 'form' %>
4 |
5 | <%= link_to 'Show', @purchase %> |
6 | <%= link_to 'Back', purchases_path %>
7 |
--------------------------------------------------------------------------------
/things_i_bought/app/views/purchases/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 | Listing Purchases
4 |
5 |
6 |
7 |
8 | Name
9 | Cost
10 |
11 |
12 |
13 |
14 |
15 | <% @purchases.each do |purchase| %>
16 |
17 | <%= purchase.name %>
18 | <%= purchase.cost %>
19 | <%= link_to 'Show', purchase %>
20 | <%= link_to 'Edit', edit_purchase_path(purchase) %>
21 | <%= link_to 'Destroy', purchase, method: :delete, data: { confirm: 'Are you sure?' } %>
22 |
23 | <% end %>
24 |
25 |
26 |
27 |
28 |
29 | <%= link_to 'New Purchase', new_purchase_path %>
30 |
--------------------------------------------------------------------------------
/things_i_bought/app/views/purchases/index.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.array!(@purchases) do |purchase|
2 | json.extract! purchase, :id, :name, :cost
3 | json.url purchase_url(purchase, format: :json)
4 | end
5 |
--------------------------------------------------------------------------------
/things_i_bought/app/views/purchases/new.html.erb:
--------------------------------------------------------------------------------
1 | New Purchase
2 |
3 | <%= render 'form' %>
4 |
5 | <%= link_to 'Back', purchases_path %>
6 |
--------------------------------------------------------------------------------
/things_i_bought/app/views/purchases/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 |
4 | Name:
5 | <%= @purchase.name %>
6 |
7 |
8 |
9 | Cost:
10 | <%= @purchase.cost %>
11 |
12 |
13 | <%= link_to 'Edit', edit_purchase_path(@purchase) %> |
14 | <%= link_to 'Back', purchases_path %>
15 |
--------------------------------------------------------------------------------
/things_i_bought/app/views/purchases/show.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.extract! @purchase, :id, :name, :cost, :created_at, :updated_at
2 |
--------------------------------------------------------------------------------
/things_i_bought/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 |
4 | # path to your application root.
5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
6 |
7 | Dir.chdir APP_ROOT do
8 | # This script is a starting point to setup your application.
9 | # Add necessary setup steps to this file:
10 |
11 | puts "== Installing dependencies =="
12 | system "gem install bundler --conservative"
13 | system "bundle check || bundle install"
14 |
15 | # puts "\n== Copying sample files =="
16 | # unless File.exist?("config/database.yml")
17 | # system "cp config/database.yml.sample config/database.yml"
18 | # end
19 |
20 | puts "\n== Preparing database =="
21 | system "bin/rake db:setup"
22 |
23 | puts "\n== Removing old logs and tempfiles =="
24 | system "rm -f log/*"
25 | system "rm -rf tmp/cache"
26 |
27 | puts "\n== Restarting application server =="
28 | system "touch tmp/restart.txt"
29 | end
30 |
--------------------------------------------------------------------------------
/things_i_bought/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 }
12 | gem "spring", match[1]
13 | require "spring/binstub"
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'rails/all'
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module ThingsIBought
10 | class Application < Rails::Application
11 | # Settings in config/environments/* take precedence over those specified here.
12 | # Application configuration should go into files in config/initializers
13 | # -- all .rb files in that directory are automatically loaded.
14 |
15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
17 | # config.time_zone = 'Central Time (US & Canada)'
18 |
19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
21 | # config.i18n.default_locale = :de
22 |
23 | # Do not swallow errors in after_commit/after_rollback callbacks.
24 | config.active_record.raise_in_transactional_callbacks = true
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/things_i_bought/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
2 |
3 | require 'bundler/setup' # Set up gems listed in the Gemfile.
4 |
--------------------------------------------------------------------------------
/things_i_bought/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: 5
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/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 | end
42 |
--------------------------------------------------------------------------------
/things_i_bought/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 file 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 | end
43 |
--------------------------------------------------------------------------------
/things_i_bought/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
9 | # 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 |
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/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
4 |
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/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: '_things_i_bought_session'
4 |
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/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 | hello: "Hello world"
24 |
--------------------------------------------------------------------------------
/things_i_bought/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | resources :purchases
3 |
4 | # The priority is based upon order of creation: first created -> highest priority.
5 | # See how all your routes lay out with "rake routes".
6 |
7 | # You can have the root of your site routed with "root"
8 | # root 'welcome#index'
9 |
10 | # Example of regular route:
11 | # get 'products/:id' => 'catalog#view'
12 |
13 | # Example of named route that can be invoked with purchase_url(id: product.id)
14 | # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
15 |
16 | # Example resource route (maps HTTP verbs to controller actions automatically):
17 | # resources :products
18 |
19 | # Example resource route with options:
20 | # resources :products do
21 | # member do
22 | # get 'short'
23 | # post 'toggle'
24 | # end
25 | #
26 | # collection do
27 | # get 'sold'
28 | # end
29 | # end
30 |
31 | # Example resource route with sub-resources:
32 | # resources :products do
33 | # resources :comments, :sales
34 | # resource :seller
35 | # end
36 |
37 | # Example resource route with more complex sub-resources:
38 | # resources :products do
39 | # resources :comments
40 | # resources :sales do
41 | # get 'recent', on: :collection
42 | # end
43 | # end
44 |
45 | # Example resource route with concerns:
46 | # concern :toggleable do
47 | # post 'toggle'
48 | # end
49 | # resources :posts, concerns: :toggleable
50 | # resources :photos, concerns: :toggleable
51 |
52 | # Example resource route within a namespace:
53 | # namespace :admin do
54 | # # Directs /admin/products/* to Admin::ProductsController
55 | # # (app/controllers/admin/products_controller.rb)
56 | # resources :products
57 | # end
58 | end
59 |
--------------------------------------------------------------------------------
/things_i_bought/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: fe00d7ed0bb367cf0c12c5db8577e47f883b00f7987e9c6638661bfd7ffdf767eead8e2c4cb13122e9e594128088bc144d003dda1ca223c587902aebe423231b
15 |
16 | test:
17 | secret_key_base: 8565490b69526ad344a52239671cc127e067d80f6ceaf655cd4fb8fa39d1c9102d90b86d337d0dd13c58580ef0a58e9578da30babfda13726563b6c03e111437
18 |
19 | # Do not keep production secrets in the repository,
20 | # instead read values from the environment.
21 | production:
22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
23 |
--------------------------------------------------------------------------------
/things_i_bought/db/migrate/20150311101434_create_purchases.rb:
--------------------------------------------------------------------------------
1 | class CreatePurchases < ActiveRecord::Migration
2 | def change
3 | create_table :purchases do |t|
4 | t.string :name
5 | t.decimal :cost
6 |
7 | t.timestamps null: false
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/things_i_bought/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: 20150311101434) do
15 |
16 | create_table "purchases", force: :cascade do |t|
17 | t.string "name"
18 | t.decimal "cost"
19 | t.datetime "created_at", null: false
20 | t.datetime "updated_at", null: false
21 | end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/lib/assets/.keep
--------------------------------------------------------------------------------
/things_i_bought/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/lib/tasks/.keep
--------------------------------------------------------------------------------
/things_i_bought/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/log/.keep
--------------------------------------------------------------------------------
/things_i_bought/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/things_i_bought/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/things_i_bought/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/things_i_bought/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/public/favicon.ico
--------------------------------------------------------------------------------
/things_i_bought/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 |
--------------------------------------------------------------------------------
/things_i_bought/test/controllers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/test/controllers/.keep
--------------------------------------------------------------------------------
/things_i_bought/test/controllers/purchases_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class PurchasesControllerTest < ActionController::TestCase
4 | setup do
5 | @purchase = purchases(:one)
6 | end
7 |
8 | test "should get index" do
9 | get :index
10 | assert_response :success
11 | assert_not_nil assigns(:purchases)
12 | end
13 |
14 | test "should get new" do
15 | get :new
16 | assert_response :success
17 | end
18 |
19 | test "should create purchase" do
20 | assert_difference('Purchase.count') do
21 | post :create, purchase: { cost: @purchase.cost, name: @purchase.name }
22 | end
23 |
24 | assert_redirected_to purchase_path(assigns(:purchase))
25 | end
26 |
27 | test "should show purchase" do
28 | get :show, id: @purchase
29 | assert_response :success
30 | end
31 |
32 | test "should get edit" do
33 | get :edit, id: @purchase
34 | assert_response :success
35 | end
36 |
37 | test "should update purchase" do
38 | patch :update, id: @purchase, purchase: { cost: @purchase.cost, name: @purchase.name }
39 | assert_redirected_to purchase_path(assigns(:purchase))
40 | end
41 |
42 | test "should destroy purchase" do
43 | assert_difference('Purchase.count', -1) do
44 | delete :destroy, id: @purchase
45 | end
46 |
47 | assert_redirected_to purchases_path
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/things_i_bought/test/fixtures/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/test/fixtures/.keep
--------------------------------------------------------------------------------
/things_i_bought/test/fixtures/purchases.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | name: MyString
5 | cost: 9.99
6 |
7 | two:
8 | name: MyString
9 | cost: 9.99
10 |
--------------------------------------------------------------------------------
/things_i_bought/test/helpers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/test/helpers/.keep
--------------------------------------------------------------------------------
/things_i_bought/test/integration/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/test/integration/.keep
--------------------------------------------------------------------------------
/things_i_bought/test/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/test/mailers/.keep
--------------------------------------------------------------------------------
/things_i_bought/test/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/test/models/.keep
--------------------------------------------------------------------------------
/things_i_bought/test/models/purchase_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class PurchaseTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/things_i_bought/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV['RAILS_ENV'] ||= 'test'
2 | require File.expand_path('../../config/environment', __FILE__)
3 | require 'rails/test_help'
4 |
5 | class ActiveSupport::TestCase
6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
7 | fixtures :all
8 |
9 | # Add more helper methods to be used by all tests here...
10 | end
11 |
--------------------------------------------------------------------------------
/things_i_bought/vendor/assets/javascripts/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/vendor/assets/javascripts/.keep
--------------------------------------------------------------------------------
/things_i_bought/vendor/assets/stylesheets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/things_i_bought/vendor/assets/stylesheets/.keep
--------------------------------------------------------------------------------
/ticketee/.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/*
16 | !/log/.keep
17 | /tmp
18 |
19 | uploads
20 |
--------------------------------------------------------------------------------
/ticketee/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --require spec_helper
3 |
--------------------------------------------------------------------------------
/ticketee/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm: 2.2.1
3 | script: rspec
4 | before_install:
5 | - export DISPLAY=:99.0
6 | - sh -e /etc/init.d/xvfb start
7 | deploy:
8 | provider: heroku
9 | api_key:
10 | secure: Q5efxQBnGeFY1wDm0pTpZaQKwRofw4jwCj7C4U5/ylzToeVq06O2UOQdcJc6iyJT/KGQ48OgU8ksFls61+f8aDqB8iFwvaTcWqDIBtgCsS8sA+YH57ceWT7Z2P/4tW0Y0zsPOnII240TMB+hp1bnwwMXCpz80IHMQmXtgE0IhoM=
11 | app: r4ia-ticketee
12 | on:
13 | repo: rubysherpas/r4ia_examples
14 |
--------------------------------------------------------------------------------
/ticketee/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | ruby "2.2.1"
3 |
4 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
5 | gem 'rails', '4.2.1'
6 |
7 | gem "sqlite3", group: [:development, :test]
8 | gem "pg", group: :production
9 |
10 | # Use SCSS for stylesheets
11 | gem 'sass-rails', '~> 5.0'
12 | # Use Uglifier as compressor for JavaScript assets
13 | gem 'uglifier', '>= 1.3.0'
14 | # Use CoffeeScript for .coffee assets and views
15 | gem 'coffee-rails', '~> 4.1.0'
16 | # See https://github.com/rails/execjs#readme for more supported runtimes
17 | # gem 'therubyracer', platforms: :ruby
18 |
19 | # Use jquery as the JavaScript library
20 | gem 'jquery-rails'
21 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
22 | gem 'jbuilder', '~> 2.0'
23 | # bundle exec rake doc:rails generates the API under doc/api.
24 | gem 'sdoc', '~> 0.4.0', group: :doc
25 |
26 | # Use ActiveModel has_secure_password
27 | # gem 'bcrypt', '~> 3.1.7'
28 |
29 | # Use Unicorn as the app server
30 | # gem 'unicorn'
31 |
32 | # Use Capistrano for deployment
33 | # gem 'capistrano-rails', group: :development
34 |
35 | gem "bootstrap-sass", "~> 3.3"
36 | gem "font-awesome-rails", "~> 4.3"
37 | gem "simple_form", "~> 3.1.0"
38 | gem "devise", "~> 3.4.1"
39 | gem "pundit", "~> 0.3.0"
40 | gem "searcher", github: "radar/searcher"
41 | gem "active_model_serializers", "~> 0.9.3"
42 |
43 | gem "carrierwave", "~> 0.10.0"
44 | gem "fog", "~> 1.29.0"
45 | gem "rails_12factor", group: :production
46 | gem "puma", group: :production
47 |
48 | gem "sinatra"
49 |
50 | group :development, :test do
51 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
52 | gem 'byebug'
53 |
54 | # Access an IRB console on exception pages or by using <%= console %> in views
55 | gem 'web-console', '~> 2.0'
56 |
57 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
58 | gem 'spring'
59 |
60 | gem "rspec-rails", "~> 3.2.1"
61 | end
62 |
63 | group :test do
64 | gem "capybara", "~> 2.4"
65 | gem "factory_girl_rails", "~> 4.5"
66 | gem "selenium-webdriver", "~> 2.45"
67 | gem "database_cleaner", "~> 1.4"
68 | gem "email_spec", "~> 1.6.0"
69 | end
70 |
--------------------------------------------------------------------------------
/ticketee/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec puma -t 5:5 -p ${PORT:-3000} -e ${RACK_ENV:-development}
2 |
--------------------------------------------------------------------------------
/ticketee/README.rdoc:
--------------------------------------------------------------------------------
1 | == README
2 |
3 | This README would normally document whatever steps are necessary to get the
4 | application up and running.
5 |
6 | Things you may want to cover:
7 |
8 | * Ruby version
9 |
10 | * System dependencies
11 |
12 | * Configuration
13 |
14 | * Database creation
15 |
16 | * Database initialization
17 |
18 | * How to run the test suite
19 |
20 | * Services (job queues, cache servers, search engines, etc.)
21 |
22 | * Deployment instructions
23 |
24 | * ...
25 |
26 |
27 | Please feel free to use a different markup language if you do not plan to run
28 | rake doc:app .
29 |
--------------------------------------------------------------------------------
/ticketee/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 |
--------------------------------------------------------------------------------
/ticketee/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/ticketee/app/assets/images/.keep
--------------------------------------------------------------------------------
/ticketee/app/assets/javascripts/admin/application.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/javascripts/admin/projects.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/javascripts/admin/states.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/javascripts/admin/users.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/javascripts/api/tickets.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require jquery
14 | //= require jquery_ujs
15 | //= require bootstrap-sprockets
16 | //= require_tree .
17 |
--------------------------------------------------------------------------------
/ticketee/app/assets/javascripts/attachments.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/javascripts/comments.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/javascripts/projects.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/javascripts/tags.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
5 | $ ->
6 | $(".tag .remove").on "ajax:success", ->
7 | $(this).parent().fadeOut()
8 |
--------------------------------------------------------------------------------
/ticketee/app/assets/javascripts/tickets.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
5 | $ ->
6 | $("#add_file").on "ajax:success", (event, data) ->
7 | $("#attachments").append data
8 | $(this).data "params", { index: $("#attachments div.file").length }
9 |
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/admin/application.scss:
--------------------------------------------------------------------------------
1 | #admin .col-md-3 h2 {
2 | font-size: 13px;
3 | letter-spacing: 1px;
4 | text-transform: uppercase;
5 | font-weight: bold;
6 | color: #959595;
7 | padding-left: 15px;
8 | }
9 |
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/admin/projects.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the admin/projects controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/admin/states.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the admin/states controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/admin/users.scss:
--------------------------------------------------------------------------------
1 | .roles {
2 | @extend .table;
3 | @extend .form-horizontal;
4 |
5 | tr:first-child {
6 | td, th {
7 | border-top: 0px;
8 | }
9 | }
10 |
11 | label {
12 | @extend .control-label;
13 | }
14 |
15 | select {
16 | @extend .form-control;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/api/tickets.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the api/tickets controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/application.css.scss:
--------------------------------------------------------------------------------
1 | @import "bootstrap-sprockets";
2 | @import "bootstrap";
3 | @import "font-awesome";
4 | @import "projects";
5 | @import "tickets";
6 | @import "admin/application";
7 | @import "admin/users";
8 | @import "responsive";
9 |
10 | header {
11 | @extend .page-header;
12 | position: relative;
13 | padding-bottom: 0px;
14 |
15 | &:first-child {
16 | margin-top: 20px;
17 | }
18 | }
19 |
20 | ul.actions {
21 | @extend .list-unstyled;
22 | @extend .list-inline;
23 | }
24 |
25 | a.new, a.edit, a.delete {
26 | @extend .btn;
27 |
28 | &:before {
29 | font-family: "FontAwesome";
30 | padding-right: 0.5em;
31 | }
32 | }
33 |
34 | a.new {
35 | @extend .btn-success;
36 |
37 | &:before {
38 | @extend .fa-plus;
39 | }
40 | }
41 |
42 | a.edit {
43 | @extend .btn-primary;
44 |
45 | &:before {
46 | @extend .fa-pencil;
47 | }
48 | }
49 |
50 | a.delete {
51 | @extend .btn-danger;
52 |
53 | &:before {
54 | @extend .fa-trash;
55 | }
56 | }
57 |
58 | .alert-notice {
59 | @extend .alert-success;
60 | }
61 |
62 | .alert-alert {
63 | @extend .alert-danger;
64 | }
65 |
66 | form {
67 | max-width: 500px;
68 | }
69 |
70 | body {
71 | padding-top: 70px;
72 | }
73 |
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/attachments.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the attachments controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/comments.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the comments controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/projects.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the projects controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
5 | #projects h2 {
6 | font-size: 16px;
7 | font-weight: bold;
8 | margin: 20px 0px 0px;
9 | }
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/responsive.scss:
--------------------------------------------------------------------------------
1 | @media(min-width: $screen-sm-min) {
2 | header {
3 | h1, h2, h3, h4, h5, h6 {
4 | max-width: 55%;
5 | }
6 | }
7 |
8 | ul.actions {
9 | position: absolute;
10 | bottom: -2px;
11 | right: 2px;
12 | max-width: 45%;
13 | text-align: right;
14 | }
15 | }
16 |
17 | @media(max-width: $screen-xs-max) {
18 | input#search {
19 | display: inline-block;
20 | vertical-align: middle;
21 | width: auto;
22 | }
23 | }
24 |
25 | @media(max-width: $screen-sm-max) {
26 | ul.actions .form-inline {
27 | padding-bottom: 5px;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/tags.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the tags controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/ticketee/app/assets/stylesheets/tickets.scss:
--------------------------------------------------------------------------------
1 | #attributes {
2 | @extend .table;
3 | @extend .table-condensed;
4 | width: 65%;
5 |
6 | th {
7 | width: 35%;
8 | }
9 |
10 | tr:first-child {
11 | td, th {
12 | border-top: 0px;
13 | }
14 | }
15 |
16 | td, th {
17 | line-height: 24px !important;
18 | }
19 | }
20 |
21 | blockquote.comment p {
22 | font-size: 14px;
23 | }
24 |
25 | .state {
26 | @extend .label;
27 | font-size: 12px;
28 | padding: 0.3em 0.6em;
29 | }
30 |
31 | #tickets li, #states li {
32 | padding-bottom: 10px;
33 | }
34 |
35 | .tag {
36 | @extend .state;
37 | @extend .label-info;
38 | margin-right: 10px;
39 |
40 | a {
41 | color: white;
42 |
43 | &.remove {
44 | font-family: "FontAwesome";
45 | @extend .fa-close;
46 | margin-right: 0.5em;
47 | text-decoration: none;
48 | }
49 | }
50 | }
51 |
52 | .watch, .unwatch {
53 | @extend .btn;
54 | @extend .btn-xs;
55 | font-weight: bold;
56 |
57 | &:before {
58 | font-family: "FontAwesome";
59 | padding-right: 0.5em;
60 | }
61 | }
62 |
63 | .watch {
64 | @extend .btn-success;
65 |
66 | &:before {
67 | @extend .fa-eye;
68 | }
69 | }
70 |
71 | .unwatch {
72 | @extend .btn-danger;
73 |
74 | &:before {
75 | @extend .fa-eye-slash;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/admin/application_controller.rb:
--------------------------------------------------------------------------------
1 | class Admin::ApplicationController < ApplicationController
2 | skip_after_action :verify_authorized, :verify_policy_scoped
3 | before_action :authorize_admin!
4 |
5 | def index
6 | end
7 |
8 | private
9 |
10 | def authorize_admin!
11 | authenticate_user!
12 |
13 | unless current_user.admin?
14 | redirect_to root_path, alert: "You must be an admin to do that."
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/admin/projects_controller.rb:
--------------------------------------------------------------------------------
1 | class Admin::ProjectsController < Admin::ApplicationController
2 | def new
3 | @project = Project.new
4 | end
5 |
6 | def create
7 | @project = Project.new(project_params)
8 |
9 | if @project.save
10 | flash[:notice] = "Project has been created."
11 | redirect_to @project
12 | else
13 | flash.now[:alert] = "Project has not been created."
14 | render "new"
15 | end
16 | end
17 |
18 | def destroy
19 | @project = Project.find(params[:id])
20 | @project.destroy
21 |
22 | flash[:notice] = "Project has been deleted."
23 | redirect_to projects_path
24 | end
25 |
26 | private
27 |
28 | def project_params
29 | params.require(:project).permit(:name, :description)
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/admin/states_controller.rb:
--------------------------------------------------------------------------------
1 | class Admin::StatesController < Admin::ApplicationController
2 | def index
3 | @states = State.all
4 | end
5 |
6 | def new
7 | @state = State.new
8 | end
9 |
10 | def create
11 | @state = State.new(state_params)
12 | if @state.save
13 | flash[:notice] = "State has been created."
14 | redirect_to admin_states_path
15 | else
16 | flash.now[:alert] = "State has not been created."
17 | render "new"
18 | end
19 | end
20 |
21 | def make_default
22 | @state = State.find(params[:id])
23 | @state.make_default!
24 |
25 | flash[:notice] = "'#{@state.name}' is now the default state."
26 | redirect_to admin_states_path
27 | end
28 |
29 | private
30 |
31 | def state_params
32 | params.require(:state).permit(:name, :color)
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/admin/users_controller.rb:
--------------------------------------------------------------------------------
1 | class Admin::UsersController < Admin::ApplicationController
2 | before_action :set_projects, only: [:new, :create, :edit, :update]
3 | before_action :set_user, only: [:show, :edit, :update, :archive]
4 |
5 | def index
6 | @users = User.excluding_archived.order(:email)
7 | end
8 |
9 | def show
10 | end
11 |
12 | def new
13 | @user = User.new
14 | end
15 |
16 | def create
17 | @user = User.new(user_params)
18 | build_roles_for(@user)
19 |
20 | if @user.save
21 | flash[:notice] = "User has been created."
22 | redirect_to admin_users_path
23 | else
24 | flash.now[:alert] = "User has not been created."
25 | render "new"
26 | end
27 | end
28 |
29 | def edit
30 | end
31 |
32 | def update
33 | if params[:user][:password].blank?
34 | params[:user].delete(:password)
35 | end
36 |
37 | User.transaction do
38 | @user.roles.clear
39 | build_roles_for(@user)
40 |
41 | if @user.update(user_params)
42 | flash[:notice] = "User has been updated."
43 | redirect_to admin_users_path
44 | else
45 | flash.now[:alert] = "User has not been updated."
46 | render "edit"
47 | raise ActiveRecord::Rollback
48 | end
49 | end
50 | end
51 |
52 | def archive
53 | if @user == current_user
54 | flash[:alert] = "You cannot archive yourself!"
55 | else
56 | @user.archive
57 | flash[:notice] = "User has been archived."
58 | end
59 |
60 | redirect_to admin_users_path
61 | end
62 |
63 | private
64 |
65 | def set_projects
66 | @projects = Project.order(:name)
67 | end
68 |
69 | def build_roles_for(user)
70 | role_data = params.fetch(:roles, [])
71 | role_data.each do |project_id, role_name|
72 | if role_name.present?
73 | user.roles.build(project_id: project_id, role: role_name)
74 | end
75 | end
76 | end
77 |
78 | def user_params
79 | params.require(:user).permit(:email, :password, :admin)
80 | end
81 |
82 | def set_user
83 | @user = User.find(params[:id])
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/api/application_controller.rb:
--------------------------------------------------------------------------------
1 | class API::ApplicationController < ApplicationController
2 | attr_reader :current_user
3 | before_action :authenticate_user
4 |
5 | private
6 |
7 | def authenticate_user
8 | authenticate_with_http_token do |token|
9 | @current_user = User.find_by(api_key: token)
10 | end
11 |
12 | if @current_user.nil?
13 | render json: { error: "Unauthorized" }, status: 401
14 | return
15 | end
16 | end
17 |
18 | def not_authorized
19 | render json: { error: "Unauthorized" }, status: 403
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/api/tickets_controller.rb:
--------------------------------------------------------------------------------
1 | class API::TicketsController < API::ApplicationController
2 | attr_reader :current_user
3 |
4 | before_action :set_project
5 |
6 | def show
7 | @ticket = @project.tickets.find(params[:id])
8 | authorize @ticket, :show?
9 | render json: @ticket
10 | end
11 |
12 | def create
13 | @ticket = @project.tickets.build(ticket_params)
14 | authorize @ticket, :create?
15 | if @ticket.save
16 | render json: @ticket, status: 201
17 | else
18 | render json: { errors: @ticket.errors.full_messages }, status: 422
19 | end
20 | end
21 |
22 | private
23 |
24 | def ticket_params
25 | params.require(:ticket).permit(:name, :description)
26 | end
27 |
28 | def set_project
29 | @project = Project.find(params[:project_id])
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/api/v2/tickets.rb:
--------------------------------------------------------------------------------
1 | require "sinatra"
2 |
3 | module API
4 | module V2
5 | class Tickets < Sinatra::Base
6 | before do
7 | headers "Content-Type" => "text/json"
8 | set_user
9 | set_project
10 | end
11 |
12 | get "/:id" do
13 | ticket = @project.tickets.find(params[:id])
14 | unless TicketPolicy.new(@user, ticket).show?
15 | halt 404, "The ticket you were looking for could not be found."
16 | end
17 | TicketSerializer.new(ticket).to_json
18 | end
19 |
20 | private
21 |
22 | def set_project
23 | @project = Project.find(params[:project_id])
24 | end
25 |
26 | def set_user
27 | if env["HTTP_AUTHORIZATION"].present?
28 | if auth_token = /Token token=(.*)/.match(env["HTTP_AUTHORIZATION"])
29 | @user = User.find_by(api_key: auth_token[1])
30 | return @user if @user.present?
31 | end
32 | end
33 |
34 | unauthenticated!
35 | end
36 |
37 | def unauthenticated!
38 | halt 401, {error: "Unauthenticated"}.to_json
39 | end
40 |
41 | def params
42 | hash = env["action_dispatch.request.path_parameters"].merge!(super)
43 | HashWithIndifferentAccess.new(hash)
44 | end
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | include Pundit
3 |
4 | after_action :verify_authorized, except: [:index],
5 | unless: :devise_controller?
6 | after_action :verify_policy_scoped, only: [:index],
7 | unless: :devise_controller?
8 |
9 | # Prevent CSRF attacks by raising an exception.
10 | # For APIs, you may want to use :null_session instead.
11 | protect_from_forgery with: :exception
12 |
13 | rescue_from Pundit::NotAuthorizedError, with: :not_authorized
14 |
15 | private
16 |
17 | def not_authorized
18 | redirect_to root_path, alert: "You aren't allowed to do that."
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/attachments_controller.rb:
--------------------------------------------------------------------------------
1 | class AttachmentsController < ApplicationController
2 | skip_after_action :verify_authorized, only: [:new]
3 |
4 | def show
5 | attachment = Attachment.find(params[:id])
6 | authorize attachment, :show?
7 | send_file file_to_send(attachment), disposition: :inline
8 | end
9 |
10 | def new
11 | @index = params[:index].to_i
12 | @ticket = Ticket.new
13 | @ticket.attachments.build
14 | render layout: false
15 | end
16 |
17 | private
18 |
19 | def file_to_send(attachment)
20 | if URI.parse(attachment.file.url).scheme
21 | filename = "/tmp/#{attachment.attributes["file"]}"
22 | File.open(filename, "wb+") do |tf|
23 | tf.write open(attachment.file.url).read
24 | end
25 | filename
26 | else
27 | attachment.file.path
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/comments_controller.rb:
--------------------------------------------------------------------------------
1 | class CommentsController < ApplicationController
2 | before_action :set_ticket
3 |
4 | def create
5 | @creator = CommentCreator.build(@ticket.comments, current_user,
6 | sanitized_parameters)
7 | authorize @creator.comment, :create?
8 |
9 | if @creator.save
10 | flash[:notice] = "Comment has been created."
11 | redirect_to [@ticket.project, @ticket]
12 | else
13 | flash.now[:alert] = "Comment has not been created."
14 | @project = @ticket.project
15 | @comment = @creator.comment
16 | render "tickets/show"
17 | end
18 | end
19 |
20 | private
21 |
22 | def set_ticket
23 | @ticket = Ticket.find(params[:ticket_id])
24 | end
25 |
26 | def comment_params
27 | params.require(:comment).permit(:text, :state_id, :tag_names)
28 | end
29 |
30 | def sanitized_parameters
31 | whitelisted_params = comment_params
32 |
33 | unless policy(@ticket).change_state?
34 | whitelisted_params.delete(:state_id)
35 | end
36 |
37 | unless policy(@ticket).tag?
38 | whitelisted_params.delete(:tag_names)
39 | end
40 |
41 | whitelisted_params
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/ticketee/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/ticketee/app/controllers/projects_controller.rb:
--------------------------------------------------------------------------------
1 | class ProjectsController < ApplicationController
2 | before_action :set_project, only: [:show, :edit, :update]
3 |
4 | def index
5 | @projects = policy_scope(Project)
6 | end
7 |
8 | def show
9 | authorize @project, :show?
10 | @tickets = @project.tickets
11 | end
12 |
13 | def edit
14 | authorize @project, :update?
15 | end
16 |
17 | def update
18 | authorize @project, :update?
19 | if @project.update(project_params)
20 | flash[:notice] = "Project has been updated."
21 | redirect_to @project
22 | else
23 | flash.now[:alert] = "Project has not been updated."
24 | render "edit"
25 | end
26 | end
27 |
28 | private
29 |
30 | def set_project
31 | @project = Project.find(params[:id])
32 | rescue ActiveRecord::RecordNotFound
33 | flash[:alert] = "The project you were looking for could not be found."
34 | redirect_to projects_path
35 | end
36 |
37 | def project_params
38 | params.require(:project).permit(:name, :description)
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/tags_controller.rb:
--------------------------------------------------------------------------------
1 | class TagsController < ApplicationController
2 | def remove
3 | @ticket = Ticket.find(params[:ticket_id])
4 | @tag = Tag.find(params[:id])
5 | authorize @ticket, :tag?
6 |
7 | @ticket.tags.destroy(@tag)
8 | head :ok
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/ticketee/app/controllers/tickets_controller.rb:
--------------------------------------------------------------------------------
1 | class TicketsController < ApplicationController
2 | before_action :set_project
3 | before_action :set_ticket, only: [:show, :edit, :update, :destroy, :watch]
4 |
5 | def search
6 | authorize @project, :show?
7 | if params[:search].present?
8 | @tickets = @project.tickets.search(params[:search])
9 | else
10 | @tickets = @project.tickets
11 | end
12 | render "projects/show"
13 | end
14 |
15 | def new
16 | @ticket = @project.tickets.build
17 | authorize @ticket, :create?
18 | @ticket.attachments.build
19 | end
20 |
21 | def create
22 | @ticket = @project.tickets.new
23 |
24 | whitelisted_params = ticket_params
25 | unless policy(@ticket).tag?
26 | whitelisted_params.delete(:tag_names)
27 | end
28 |
29 | @ticket.attributes = whitelisted_params
30 | @ticket.author = current_user
31 | authorize @ticket, :create?
32 |
33 | if @ticket.save
34 | flash[:notice] = "Ticket has been created."
35 | redirect_to [@project, @ticket]
36 | else
37 | flash.now[:alert] = "Ticket has not been created."
38 | render "new"
39 | end
40 | end
41 |
42 | def show
43 | authorize @ticket, :show?
44 | @comment = @ticket.comments.build(state_id: @ticket.state_id)
45 | end
46 |
47 | def edit
48 | authorize @ticket, :update?
49 | end
50 |
51 | def update
52 | authorize @ticket, :update?
53 | if @ticket.update(ticket_params)
54 | flash[:notice] = "Ticket has been updated."
55 | redirect_to [@project, @ticket]
56 | else
57 | flash.now[:alert] = "Ticket has not been updated."
58 | render "edit"
59 | end
60 | end
61 |
62 | def destroy
63 | authorize @ticket, :destroy?
64 | @ticket.destroy
65 | flash[:notice] = "Ticket has been deleted."
66 |
67 | redirect_to @project
68 | end
69 |
70 | def watch
71 | authorize @ticket, :show?
72 | if @ticket.watchers.exists?(current_user.id)
73 | @ticket.watchers.destroy(current_user)
74 | flash[:notice] = "You are no longer watching this ticket."
75 | else
76 | @ticket.watchers << current_user
77 | flash[:notice] = "You are now watching this ticket."
78 | end
79 |
80 | redirect_to project_ticket_path(@ticket.project, @ticket)
81 | end
82 |
83 | private
84 |
85 | def ticket_params
86 | params.require(:ticket).permit(:name, :description, :tag_names,
87 | attachments_attributes: [:file, :file_cache])
88 | end
89 |
90 | def set_project
91 | @project = Project.find(params[:project_id])
92 | end
93 |
94 | def set_ticket
95 | @ticket = @project.tickets.find(params[:id])
96 | end
97 | end
98 |
--------------------------------------------------------------------------------
/ticketee/app/helpers/admin/application_helper.rb:
--------------------------------------------------------------------------------
1 | module Admin::ApplicationHelper
2 | def roles
3 | hash = {}
4 |
5 | Role.available_roles.each do |role|
6 | hash[role.titleize] = role
7 | end
8 |
9 | hash
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/ticketee/app/helpers/admin/projects_helper.rb:
--------------------------------------------------------------------------------
1 | module Admin::ProjectsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/ticketee/app/helpers/admin/states_helper.rb:
--------------------------------------------------------------------------------
1 | module Admin::StatesHelper
2 | end
3 |
--------------------------------------------------------------------------------
/ticketee/app/helpers/admin/users_helper.rb:
--------------------------------------------------------------------------------
1 | module Admin::UsersHelper
2 | end
3 |
--------------------------------------------------------------------------------
/ticketee/app/helpers/api/tickets_helper.rb:
--------------------------------------------------------------------------------
1 | module API::TicketsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/ticketee/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | def title(*parts)
3 | unless parts.empty?
4 | content_for :title do
5 | (parts << "Ticketee").join(" - ")
6 | end
7 | end
8 | end
9 |
10 | def admins_only(&block)
11 | block.call if current_user.try(:admin?)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/ticketee/app/helpers/attachments_helper.rb:
--------------------------------------------------------------------------------
1 | module AttachmentsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/ticketee/app/helpers/comments_helper.rb:
--------------------------------------------------------------------------------
1 | module CommentsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/ticketee/app/helpers/projects_helper.rb:
--------------------------------------------------------------------------------
1 | module ProjectsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/ticketee/app/helpers/tags_helper.rb:
--------------------------------------------------------------------------------
1 | module TagsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/ticketee/app/helpers/tickets_helper.rb:
--------------------------------------------------------------------------------
1 | module TicketsHelper
2 | def state_transition_for(comment)
3 | if comment.previous_state != comment.state
4 | content_tag(:p) do
5 | value = " state changed "
6 | if comment.previous_state.present?
7 | value += " from #{render comment.previous_state}"
8 | end
9 | value += " to #{render comment.state}"
10 | value.html_safe
11 | end
12 | end
13 | end
14 |
15 | def toggle_watching_button(ticket)
16 | text = if ticket.watchers.include?(current_user)
17 | "Unwatch"
18 | else
19 | "Watch"
20 | end
21 | link_to text, watch_project_ticket_path(ticket.project, ticket),
22 | class: text.parameterize, method: :post
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/ticketee/app/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/ticketee/app/mailers/.keep
--------------------------------------------------------------------------------
/ticketee/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: "from@example.com"
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/ticketee/app/mailers/comment_notifier.rb:
--------------------------------------------------------------------------------
1 | class CommentNotifier < ApplicationMailer
2 | def created(comment, user)
3 | @comment = comment
4 | @user = user
5 | @ticket = comment.ticket
6 | @project = @ticket.project
7 |
8 | subject = "[ticketee] #{@project.name} - #{@ticket.name}"
9 | mail(to: user.email, subject: subject)
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/ticketee/app/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/ticketee/app/models/.keep
--------------------------------------------------------------------------------
/ticketee/app/models/attachment.rb:
--------------------------------------------------------------------------------
1 | class Attachment < ActiveRecord::Base
2 | belongs_to :ticket
3 |
4 | mount_uploader :file, AttachmentUploader
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/app/models/comment.rb:
--------------------------------------------------------------------------------
1 | class Comment < ActiveRecord::Base
2 | belongs_to :previous_state, class_name: "State"
3 | belongs_to :state
4 | belongs_to :ticket
5 | belongs_to :author, class_name: "User"
6 |
7 | validates :text, presence: true
8 |
9 | delegate :project, to: :ticket
10 |
11 | scope :persisted, lambda { where.not(id: nil) }
12 |
13 | before_create :set_previous_state
14 | after_create :set_ticket_state
15 | after_create :associate_tags_with_ticket
16 | after_create :author_watches_ticket
17 |
18 | attr_accessor :tag_names
19 |
20 | private
21 |
22 | def set_previous_state
23 | self.previous_state = ticket.state
24 | end
25 |
26 | def set_ticket_state
27 | ticket.state = state
28 | ticket.save!
29 | end
30 |
31 | def associate_tags_with_ticket
32 | if tag_names
33 | tag_names.split.each do |name|
34 | ticket.tags << Tag.find_or_create_by(name: name)
35 | end
36 | end
37 | end
38 |
39 | def author_watches_ticket
40 | if author.present? && !ticket.watchers.include?(author)
41 | ticket.watchers << author
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/ticketee/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/ticketee/app/models/concerns/.keep
--------------------------------------------------------------------------------
/ticketee/app/models/project.rb:
--------------------------------------------------------------------------------
1 | class Project < ActiveRecord::Base
2 | has_many :tickets, dependent: :delete_all
3 | has_many :roles, dependent: :delete_all
4 |
5 | validates :name, presence: true
6 |
7 | def has_member?(user)
8 | roles.exists?(user_id: user)
9 | end
10 |
11 | [:manager, :editor, :viewer].each do |role|
12 | define_method "has_#{role}?" do |user|
13 | roles.exists?(user_id: user, role: role)
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/ticketee/app/models/role.rb:
--------------------------------------------------------------------------------
1 | class Role < ActiveRecord::Base
2 | belongs_to :user
3 | belongs_to :project
4 |
5 | def self.available_roles
6 | %w(manager editor viewer)
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/ticketee/app/models/state.rb:
--------------------------------------------------------------------------------
1 | class State < ActiveRecord::Base
2 | def self.default
3 | find_by(default: true)
4 | end
5 |
6 | def make_default!
7 | State.update_all(default: false)
8 | update!(default: true)
9 | end
10 |
11 | def to_s
12 | name
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/ticketee/app/models/tag.rb:
--------------------------------------------------------------------------------
1 | class Tag < ActiveRecord::Base
2 | end
3 |
--------------------------------------------------------------------------------
/ticketee/app/models/ticket.rb:
--------------------------------------------------------------------------------
1 | class Ticket < ActiveRecord::Base
2 | belongs_to :project
3 | belongs_to :author, class_name: "User"
4 | belongs_to :state
5 | has_many :attachments, dependent: :destroy
6 | has_many :comments, dependent: :destroy
7 | has_and_belongs_to_many :tags, uniq: true
8 | has_and_belongs_to_many :watchers, join_table: "ticket_watchers",
9 | class_name: "User", uniq: true
10 |
11 | attr_accessor :tag_names
12 |
13 | validates :name, presence: true
14 | validates :description, presence: true, length: { minimum: 10 }
15 |
16 | accepts_nested_attributes_for :attachments, reject_if: :all_blank
17 |
18 | before_create :assign_default_state
19 | after_create :author_watches_me
20 |
21 | searcher do
22 | label :tag, from: :tags, field: "name"
23 | label :state, from: :state, field: "name"
24 | end
25 |
26 | def tag_names=(names)
27 | @tag_names = names
28 | names.split.each do |name|
29 | self.tags << Tag.find_or_initialize_by(name: name)
30 | end
31 | end
32 |
33 | private
34 |
35 | def author_watches_me
36 | if author.present? && !self.watchers.include?(author)
37 | self.watchers << author
38 | end
39 | end
40 |
41 | def assign_default_state
42 | self.state ||= State.default
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/ticketee/app/models/user.rb:
--------------------------------------------------------------------------------
1 | class User < ActiveRecord::Base
2 | has_many :roles
3 |
4 | # Include default devise modules. Others available are:
5 | # :confirmable, :lockable, :timeoutable and :omniauthable
6 | devise :database_authenticatable, :registerable,
7 | :recoverable, :rememberable, :trackable, :validatable
8 |
9 | scope :excluding_archived, lambda { where(archived_at: nil) }
10 |
11 | def to_s
12 | "#{email} (#{admin? ? "Admin" : "User"})"
13 | end
14 |
15 | def archive
16 | self.update(archived_at: Time.now)
17 | end
18 |
19 | def generate_api_key
20 | self.update_column(:api_key, SecureRandom.hex(16))
21 | end
22 |
23 | def active_for_authentication?
24 | super && archived_at.nil?
25 | end
26 |
27 | def inactive_message
28 | archived_at.nil? ? super : :archived
29 | end
30 |
31 | def role_on(project)
32 | roles.find_by(project_id: project).try(:name)
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/ticketee/app/policies/application_policy.rb:
--------------------------------------------------------------------------------
1 | class ApplicationPolicy
2 | attr_reader :user, :record
3 |
4 | def initialize(user, record)
5 | @user = user
6 | @record = record
7 | end
8 |
9 | def index?
10 | false
11 | end
12 |
13 | def show?
14 | scope.where(:id => record.id).exists?
15 | end
16 |
17 | def create?
18 | false
19 | end
20 |
21 | def new?
22 | create?
23 | end
24 |
25 | def update?
26 | false
27 | end
28 |
29 | def edit?
30 | update?
31 | end
32 |
33 | def destroy?
34 | false
35 | end
36 |
37 | def scope
38 | Pundit.policy_scope!(user, record.class)
39 | end
40 |
41 | class Scope
42 | attr_reader :user, :scope
43 |
44 | def initialize(user, scope)
45 | @user = user
46 | @scope = scope
47 | end
48 |
49 | def resolve
50 | scope
51 | end
52 | end
53 | end
54 |
55 |
--------------------------------------------------------------------------------
/ticketee/app/policies/attachment_policy.rb:
--------------------------------------------------------------------------------
1 | class AttachmentPolicy < ApplicationPolicy
2 | class Scope < Scope
3 | def resolve
4 | scope
5 | end
6 | end
7 |
8 | def show?
9 | user.try(:admin?) || record.ticket.project.has_member?(user)
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/ticketee/app/policies/comment_policy.rb:
--------------------------------------------------------------------------------
1 | class CommentPolicy < TicketPolicy
2 | end
3 |
--------------------------------------------------------------------------------
/ticketee/app/policies/project_policy.rb:
--------------------------------------------------------------------------------
1 | class ProjectPolicy < ApplicationPolicy
2 | class Scope < Scope
3 | def resolve
4 | return scope.none if user.nil?
5 | return scope.all if user.admin?
6 |
7 | scope.joins(:roles).where(roles: {user_id: user})
8 | end
9 | end
10 |
11 | def show?
12 | user.try(:admin?) || record.has_member?(user)
13 | end
14 |
15 | def update?
16 | user.try(:admin?) || record.has_manager?(user)
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/ticketee/app/policies/ticket_policy.rb:
--------------------------------------------------------------------------------
1 | class TicketPolicy < ApplicationPolicy
2 | class Scope < Scope
3 | def resolve
4 | scope
5 | end
6 | end
7 |
8 | def show?
9 | user.try(:admin?) || record.project.has_member?(user)
10 | end
11 |
12 | def create?
13 | user.try(:admin?) || record.project.has_manager?(user) ||
14 | record.project.has_editor?(user)
15 | end
16 |
17 | def update?
18 | user.try(:admin?) || record.project.has_manager?(user) ||
19 | (record.project.has_editor?(user) && record.author == user)
20 | end
21 |
22 | def destroy?
23 | user.try(:admin?) || record.project.has_manager?(user)
24 | end
25 |
26 | def change_state?
27 | destroy?
28 | end
29 |
30 | def tag?
31 | destroy?
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/ticketee/app/serializers/ticket_serializer.rb:
--------------------------------------------------------------------------------
1 | class TicketSerializer < ActiveModel::Serializer
2 | attributes :id, :name, :description, :project_id, :created_at,
3 | :updated_at, :author_id
4 |
5 | has_one :state
6 | end
7 |
--------------------------------------------------------------------------------
/ticketee/app/services/comment_creator.rb:
--------------------------------------------------------------------------------
1 | class CommentCreator
2 | attr_reader :comment
3 |
4 | def self.build(scope, current_user, comment_params)
5 | comment = scope.build(comment_params)
6 | comment.author = current_user
7 |
8 | new(comment)
9 | end
10 |
11 | def initialize(comment)
12 | @comment = comment
13 | end
14 |
15 | def save
16 | if @comment.save
17 | notify_watchers
18 | end
19 | end
20 |
21 | def notify_watchers
22 | (@comment.ticket.watchers - [@comment.author]).each do |user|
23 | CommentNotifier.created(@comment, user).deliver_now
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/ticketee/app/uploaders/attachment_uploader.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | class AttachmentUploader < CarrierWave::Uploader::Base
4 |
5 | # Include RMagick or MiniMagick support:
6 | # include CarrierWave::RMagick
7 | # include CarrierWave::MiniMagick
8 |
9 | # Override the directory where uploaded files will be stored.
10 | # This is a sensible default for uploaders that are meant to be mounted:
11 | def store_dir
12 | "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
13 | end
14 |
15 | # Provide a default URL as a default if there hasn't been a file uploaded:
16 | # def default_url
17 | # # For Rails 3.1+ asset pipeline compatibility:
18 | # # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
19 | #
20 | # "/images/fallback/" + [version_name, "default.png"].compact.join('_')
21 | # end
22 |
23 | # Process files as they are uploaded:
24 | # process :scale => [200, 300]
25 | #
26 | # def scale(width, height)
27 | # # do something
28 | # end
29 |
30 | # Create different versions of your uploaded files:
31 | # version :thumb do
32 | # process :resize_to_fit => [50, 50]
33 | # end
34 |
35 | # Add a white list of extensions which are allowed to be uploaded.
36 | # For images you might use something like this:
37 | # def extension_white_list
38 | # %w(jpg jpeg gif png)
39 | # end
40 |
41 | # Override the filename of the uploaded files:
42 | # Avoid using model.id or version_name here, see uploader/store.rb for details.
43 | # def filename
44 | # "something.jpg" if original_filename
45 | # end
46 |
47 | end
48 |
--------------------------------------------------------------------------------
/ticketee/app/views/admin/application/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
Welcome to Ticketee's Admin Lounge. Please enjoy your stay.
8 |
9 |
10 |
11 |
Admin Links
12 |
13 | <%= link_to "Users", admin_users_path %>
14 | <%= link_to "States", admin_states_path %>
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ticketee/app/views/admin/projects/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= simple_form_for([:admin, project]) do |f| %>
2 | <%= f.input :name %>
3 | <%= f.input :description %>
4 |
5 | <%= f.button :submit, class: 'btn-primary' %>
6 | <% end %>
7 |
--------------------------------------------------------------------------------
/ticketee/app/views/admin/projects/new.html.erb:
--------------------------------------------------------------------------------
1 |
4 |
5 | <%= render "form", project: @project %>
--------------------------------------------------------------------------------
/ticketee/app/views/admin/states/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= simple_form_for [:admin, state] do |f| %>
2 | <%= f.input :name %>
3 | <%= f.input :color %>
4 |
5 | <%= f.submit class: 'btn btn-primary' %>
6 | <% end %>
7 |
--------------------------------------------------------------------------------
/ticketee/app/views/admin/states/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 | States
3 |
4 |
5 | <%= link_to "New State", new_admin_state_path, class: "new" %>
6 |
7 |
8 |
9 |
10 | <% @states.each do |state| %>
11 |
12 | <%= render "states/state", state: state %>
13 | <% if state.default? %>
14 | (Default)
15 | <% else %>
16 | <%= link_to "Make Default", make_default_admin_state_path(state) %>
17 | <% end %>
18 |
19 | <% end %>
20 |
21 |
--------------------------------------------------------------------------------
/ticketee/app/views/admin/states/new.html.erb:
--------------------------------------------------------------------------------
1 |
4 |
5 | <%= render "form", state: @state %>
6 |
--------------------------------------------------------------------------------
/ticketee/app/views/admin/users/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= simple_form_for [:admin, user] do |f| %>
2 | <%= f.input :email %>
3 | <%= f.input :password %>
4 | <%= f.input :admin, label: "Is an admin?" %>
5 |
6 |
9 |
10 |
11 | <% projects.each do |project| %>
12 |
13 | <%= label_tag dom_id(project), project.name %>
14 |
15 | <%= select_tag dom_id(project), options_for_select(roles,
16 | user.role_on(project)), name: "roles[#{project.id}]",
17 | include_blank: true %>
18 |
19 |
20 | <% end %>
21 |
22 |
23 | <%= f.button :submit, class: "btn btn-primary" %>
24 | <% end %>
25 |
--------------------------------------------------------------------------------
/ticketee/app/views/admin/users/edit.html.erb:
--------------------------------------------------------------------------------
1 |
4 |
5 | <%= render "form", user: @user, projects: @projects %>
6 |
--------------------------------------------------------------------------------
/ticketee/app/views/admin/users/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 | Users
3 |
4 |
5 |
6 | <%= link_to "New User", new_admin_user_path, class: "new" %>
7 |
8 |
9 |
10 |
11 |
12 | <% @users.each do |user| %>
13 | <%= link_to user, [:admin, user] %>
14 | <% end %>
15 |
16 |
--------------------------------------------------------------------------------
/ticketee/app/views/admin/users/new.html.erb:
--------------------------------------------------------------------------------
1 |
4 |
5 | <%= render "form", user: @user, projects: @projects %>
6 |
--------------------------------------------------------------------------------
/ticketee/app/views/admin/users/show.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= @user %>
3 |
4 |
5 |
6 | <%= link_to "Edit User", [:edit, :admin, @user], class: "edit" %>
7 |
8 |
9 | <%= link_to "Archive User", [:archive, :admin, @user], method: :patch,
10 | data: { confirm: "Are you sure you want to archive this user?"},
11 | class: "delete" %>
12 |
13 |
14 |
15 |
16 |
19 |
20 | <% if @user.roles.any? %>
21 |
22 | <% @user.roles.each do |role| %>
23 | <%= role.project.name %>: <%= role.role.titleize %>
24 | <% end %>
25 |
26 | <% else %>
27 | This user has no roles.
28 | <% end %>
29 |
--------------------------------------------------------------------------------
/ticketee/app/views/attachments/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= f.simple_fields_for :attachments, child_index: index do |ff| %>
2 | <%= ff.input :file, as: :file, label: "File ##{index += 1}" %>
3 | <%= ff.input :file_cache, as: :hidden %>
4 | <% end %>
5 |
--------------------------------------------------------------------------------
/ticketee/app/views/attachments/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= fields_for @ticket do |f| %>
2 | <%= render partial: "form", locals: { f: f, index: @index } %>
3 | <% end %>
4 |
--------------------------------------------------------------------------------
/ticketee/app/views/comment_notifier/created.text.erb:
--------------------------------------------------------------------------------
1 | Hello!
2 |
3 | <%= @ticket.name %> for <%= @project.name %> has been updated.
4 |
5 | <%= @comment.author.email %> wrote:
6 |
7 | <%= @comment.text %>
8 |
9 | You can view this ticket online by going to:
10 | <%= project_ticket_url(@project, @ticket) %>
11 |
--------------------------------------------------------------------------------
/ticketee/app/views/comments/_comment.html.erb:
--------------------------------------------------------------------------------
1 |
8 |
9 | <%= state_transition_for(comment) %>
10 |
--------------------------------------------------------------------------------
/ticketee/app/views/comments/_form.html.erb:
--------------------------------------------------------------------------------
1 |
4 |
5 | <%= simple_form_for [ticket, comment] do |f| %>
6 | <%= f.input :text %>
7 | <% if policy(ticket).change_state? %>
8 | <%= f.association :state %>
9 | <% end %>
10 | <% if policy(ticket).tag? %>
11 | <%= render "tags/form", f: f %>
12 | <% end %>
13 |
14 | <%= f.submit class: "btn btn-primary" %>
15 | <% end %>
16 |
--------------------------------------------------------------------------------
/ticketee/app/views/devise/confirmations/new.html.erb:
--------------------------------------------------------------------------------
1 | Resend confirmation instructions
2 |
3 | <%= simple_form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
4 | <%= f.error_notification %>
5 | <%= f.full_error :confirmation_token %>
6 |
7 |
8 | <%= f.input :email, required: true, autofocus: true %>
9 |
10 |
11 |
12 | <%= f.button :submit, "Resend confirmation instructions" %>
13 |
14 | <% end %>
15 |
16 | <%= render "devise/shared/links" %>
17 |
--------------------------------------------------------------------------------
/ticketee/app/views/devise/mailer/confirmation_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Welcome <%= @email %>!
2 |
3 | You can confirm your account email through the link below:
4 |
5 | <%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
6 |
--------------------------------------------------------------------------------
/ticketee/app/views/devise/mailer/reset_password_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Someone has requested a link to change your password. You can do this through the link below.
4 |
5 | <%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>
6 |
7 | If you didn't request this, please ignore this email.
8 | Your password won't change until you access the link above and create a new one.
9 |
--------------------------------------------------------------------------------
/ticketee/app/views/devise/mailer/unlock_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Your account has been locked due to an excessive number of unsuccessful sign in attempts.
4 |
5 | Click the link below to unlock your account:
6 |
7 | <%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>
8 |
--------------------------------------------------------------------------------
/ticketee/app/views/devise/passwords/edit.html.erb:
--------------------------------------------------------------------------------
1 | Change your password
2 |
3 | <%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
4 | <%= f.error_notification %>
5 |
6 | <%= f.input :reset_password_token, as: :hidden %>
7 | <%= f.full_error :reset_password_token %>
8 |
9 |
10 | <%= f.input :password, label: "New password", required: true, autofocus: true %>
11 | <%= f.input :password_confirmation, label: "Confirm your new password", required: true %>
12 |
13 |
14 |
15 | <%= f.button :submit, "Change my password" %>
16 |
17 | <% end %>
18 |
19 | <%= render "devise/shared/links" %>
20 |
--------------------------------------------------------------------------------
/ticketee/app/views/devise/passwords/new.html.erb:
--------------------------------------------------------------------------------
1 | Forgot your password?
2 |
3 | <%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
4 | <%= f.error_notification %>
5 |
6 |
7 | <%= f.input :email, required: true, autofocus: true %>
8 |
9 |
10 |
11 | <%= f.button :submit, "Send me reset password instructions" %>
12 |
13 | <% end %>
14 |
15 | <%= render "devise/shared/links" %>
16 |
--------------------------------------------------------------------------------
/ticketee/app/views/devise/registrations/edit.html.erb:
--------------------------------------------------------------------------------
1 | Edit <%= resource_name.to_s.humanize %>
2 |
3 | <%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
4 | <%= f.error_notification %>
5 |
6 |
17 |
18 |
19 | <%= f.button :submit, "Update" %>
20 |
21 | <% end %>
22 |
23 | Cancel my account
24 |
25 | Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>
26 |
27 | <%= link_to "Back", :back %>
28 |
--------------------------------------------------------------------------------
/ticketee/app/views/devise/registrations/new.html.erb:
--------------------------------------------------------------------------------
1 |
4 |
5 | <%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
6 |
7 | <%= f.input :email, required: true, autofocus: true %>
8 | <%= f.input :password, required: true, hint: ("#{@minimum_password_length} characters minimum" if @validatable) %>
9 | <%= f.input :password_confirmation, required: true %>
10 |
11 |
12 |
13 | <%= f.button :submit, "Sign up", class: "btn btn-primary" %>
14 |
15 | <% end %>
16 |
17 | <%= render "devise/shared/links" %>
18 |
--------------------------------------------------------------------------------
/ticketee/app/views/devise/sessions/new.html.erb:
--------------------------------------------------------------------------------
1 |
4 |
5 | <%= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
6 |
7 | <%= f.input :email, required: false, autofocus: true %>
8 | <%= f.input :password, required: false %>
9 | <%= f.input :remember_me, as: :boolean if devise_mapping.rememberable? %>
10 |
11 |
12 |
13 | <%= f.button :submit, "Sign in", class: "btn btn-primary" %>
14 |
15 | <% end %>
16 |
17 | <%= render "devise/shared/links" %>
18 |
--------------------------------------------------------------------------------
/ticketee/app/views/devise/shared/_links.html.erb:
--------------------------------------------------------------------------------
1 | <%- if controller_name != 'sessions' %>
2 | <%= link_to "Log in", new_session_path(resource_name) %>
3 | <% end -%>
4 |
5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end -%>
8 |
9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end -%>
12 |
13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end -%>
16 |
17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end -%>
20 |
21 | <%- if devise_mapping.omniauthable? %>
22 | <%- resource_class.omniauth_providers.each do |provider| %>
23 | <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>
24 | <% end -%>
25 | <% end -%>
26 |
--------------------------------------------------------------------------------
/ticketee/app/views/devise/unlocks/new.html.erb:
--------------------------------------------------------------------------------
1 | Resend unlock instructions
2 |
3 | <%= simple_form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
4 | <%= f.error_notification %>
5 | <%= f.full_error :unlock_token %>
6 |
7 |
8 | <%= f.input :email, required: true, autofocus: true %>
9 |
10 |
11 |
12 | <%= f.button :submit, "Resend unlock instructions" %>
13 |
14 | <% end %>
15 |
16 | <%= render "devise/shared/links" %>
17 |
--------------------------------------------------------------------------------
/ticketee/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <% if content_for?(:title) %>
6 | <%= yield(:title) %>
7 | <% else %>
8 | Ticketee
9 | <% end %>
10 |
11 | <%= stylesheet_link_tag 'application', media: 'all' %>
12 | <%= javascript_include_tag 'application' %>
13 | <%= csrf_meta_tags %>
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
37 |
38 |
39 |
40 | ">
41 | <%= link_to "Home", root_path %>
42 |
43 | <% admins_only do %>
44 |
45 | <%= link_to "Admin", admin_root_path %>
46 |
47 | <% end %>
48 | <% unless user_signed_in? %>
49 | ">
50 | <%= link_to "Sign up", new_user_registration_path %>
51 |
52 | ">
53 | <%= link_to "Sign in", new_user_session_path %>
54 |
55 | <% end %>
56 |
57 | <% if user_signed_in? %>
58 |
59 |
60 | Signed in as <%= current_user.email %>
61 |
62 |
63 | <%= link_to "Sign out", destroy_user_session_path,
64 | method: :delete %>
65 |
66 |
67 |
68 | <% end %>
69 |
70 |
71 |
72 |
73 |
74 | <% flash.each do |key, message| %>
75 |
76 | <%= message %>
77 |
78 | <% end %>
79 |
80 | <%= yield %>
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/ticketee/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= yield %>
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ticketee/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/ticketee/app/views/projects/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= simple_form_for(project) do |f| %>
2 | <%= f.input :name %>
3 | <%= f.input :description %>
4 |
5 | <%= f.button :submit, class: 'btn-primary' %>
6 | <% end %>
--------------------------------------------------------------------------------
/ticketee/app/views/projects/edit.html.erb:
--------------------------------------------------------------------------------
1 |
4 |
5 | <%= render "form", project: @project %>
--------------------------------------------------------------------------------
/ticketee/app/views/projects/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 | Projects
3 |
4 | <% admins_only do %>
5 |
6 |
7 | <%= link_to "New Project", new_admin_project_path, class: "new" %>
8 |
9 |
10 | <% end %>
11 |
12 |
13 |
14 | <% @projects.each do |project| %>
15 |
<%= link_to project.name, project %>
16 |
<%= project.description %>
17 | <% end %>
18 |
19 |
--------------------------------------------------------------------------------
/ticketee/app/views/projects/show.html.erb:
--------------------------------------------------------------------------------
1 | <% title(@project.name, "Projects") %>
2 |
3 |
4 | <%= @project.name %>
5 |
6 |
7 | <% if policy(@project).update? %>
8 | <%= link_to "Edit Project", edit_project_path(@project),
9 | class: "edit" %>
10 | <% end %>
11 | <% admins_only do %>
12 | <%= link_to "Delete Project", admin_project_path(@project),
13 | method: :delete,
14 | data: { confirm: "Are you sure you want to delete this project?" },
15 | class: "delete" %>
16 | <% end %>
17 |
18 |
19 |
20 | <%= @project.description %>
21 |
22 |
23 | Tickets
24 |
25 |
26 |
27 | <%= form_tag search_project_tickets_path(@project), method: :get,
28 | class: "form-inline" do %>
29 | <%= label_tag "search", "Search", class: "sr-only" %>
30 | <%= text_field_tag "search", params[:search], class: "form-control" %>
31 | <%= submit_tag "Search", class: "btn btn-default" %>
32 | <% end %>
33 |
34 | <% if policy(Ticket.new(project: @project)).create? %>
35 | <%= link_to "New Ticket", new_project_ticket_path(@project),
36 | class: "new" %>
37 | <% end %>
38 |
39 |
40 |
41 |
42 | <% @tickets.each do |ticket| %>
43 |
44 | #<%= ticket.id %> -
45 | <%= link_to ticket.name, [@project, ticket] %>
46 | <%= render ticket.state if ticket.state %>
47 |
48 | <% end %>
49 |
50 |
--------------------------------------------------------------------------------
/ticketee/app/views/states/_state.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= state -%>
3 |
4 |
--------------------------------------------------------------------------------
/ticketee/app/views/tags/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= f.input :tag_names, label: "Tags" %>
2 |
--------------------------------------------------------------------------------
/ticketee/app/views/tags/_tag.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% if policy(ticket).tag? %>
3 | <%= link_to " ".html_safe,
4 | remove_ticket_tag_path(ticket, tag), method: :delete, remote: true,
5 | class: "remove", title: "remove" %>
6 | <% end %>
7 | <%= link_to tag.name, search_project_tickets_path(ticket.project,
8 | search: "tag:#{tag.name}") %>
9 |
10 |
--------------------------------------------------------------------------------
/ticketee/app/views/tickets/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= simple_form_for([project, ticket]) do |f| %>
2 | <%= f.input :name %>
3 | <%= f.input :description %>
4 | <%= render "tags/form", f: f %>
5 |
6 |
7 |
Attachments
8 | <%= render partial: "attachments/form", locals: { f: f, index: 0 } %>
9 |
10 |
11 | <%= link_to "Add another file", new_attachment_path, remote: true,
12 | id: "add_file", data: { params: {index: ticket.attachments.size} } %>
13 |
14 |
15 | <%= f.button :submit, class: "btn-primary" %>
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/ticketee/app/views/tickets/edit.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | Edit Ticket
4 | <%= @project.name %>
5 |
6 |
7 |
8 | <%= render "form", project: @project, ticket: @ticket %>
9 |
--------------------------------------------------------------------------------
/ticketee/app/views/tickets/new.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | New Ticket
4 | <%= @project.name %>
5 |
6 |
7 |
8 | <%= render "form", project: @project, ticket: @ticket %>
--------------------------------------------------------------------------------
/ticketee/app/views/tickets/show.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%= @project.name %>
4 |
5 |
6 |
7 | <%= @ticket.name %>
8 |
9 |
10 | <% if policy(@ticket).update? %>
11 | <%= link_to "Edit Ticket", [:edit, @project, @ticket],
12 | class: "edit" %>
13 | <% end %>
14 | <% if policy(@ticket).destroy? %>
15 | <%= link_to "Delete Ticket", [@project, @ticket],
16 | method: :delete,
17 | data: { confirm: "Are you sure you want to delete this ticket?" },
18 | class: "delete" %>
19 | <% end %>
20 |
21 |
22 |
23 |
24 |
25 | Author:
26 | <%= @ticket.author.email %>
27 |
28 |
29 | Created:
30 | <%= time_ago_in_words(@ticket.created_at) %> ago
31 |
32 | <% if @ticket.state.present? %>
33 |
34 | State:
35 | <%= render @ticket.state %>
36 |
37 | <% end %>
38 | <% if @ticket.tags.any? %>
39 |
40 | Tags:
41 | <%= render @ticket.tags, ticket: @ticket %>
42 |
43 | <% end %>
44 |
45 | Watchers:
46 |
47 | <%= toggle_watching_button(@ticket) %>
48 | <%= @ticket.watchers.map(&:email).to_sentence %>
49 |
50 |
51 |
52 |
53 | <%= simple_format(@ticket.description) %>
54 |
55 | <% if @ticket.attachments.any? %>
56 |
Attachments
57 |
58 | <% @ticket.attachments.each do |attachment| %>
59 |
60 | <%= link_to File.basename(attachment.file.url),
61 | attachment_path(attachment) %>
62 | (<%= number_to_human_size(attachment.file.size) %>)
63 |
64 | <% end %>
65 |
66 | <% end %>
67 |
68 |
69 |
72 |
73 |
80 |
81 | <% if policy(@comment).create? %>
82 | <%= render "comments/form", ticket: @ticket, comment: @comment %>
83 | <% end %>
84 |
--------------------------------------------------------------------------------
/ticketee/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/ticketee/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 |
--------------------------------------------------------------------------------
/ticketee/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 |
--------------------------------------------------------------------------------
/ticketee/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 |
4 | # path to your application root.
5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
6 |
7 | Dir.chdir APP_ROOT do
8 | # This script is a starting point to setup your application.
9 | # Add necessary setup steps to this file:
10 |
11 | puts "== Installing dependencies =="
12 | system "gem install bundler --conservative"
13 | system "bundle check || bundle install"
14 |
15 | # puts "\n== Copying sample files =="
16 | # unless File.exist?("config/database.yml")
17 | # system "cp config/database.yml.sample config/database.yml"
18 | # end
19 |
20 | puts "\n== Preparing database =="
21 | system "bin/rake db:setup"
22 |
23 | puts "\n== Removing old logs and tempfiles =="
24 | system "rm -f log/*"
25 | system "rm -rf tmp/cache"
26 |
27 | puts "\n== Restarting application server =="
28 | system "touch tmp/restart.txt"
29 | end
30 |
--------------------------------------------------------------------------------
/ticketee/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 }
12 | gem "spring", match[1]
13 | require "spring/binstub"
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/ticketee/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 |
--------------------------------------------------------------------------------
/ticketee/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'rails/all'
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module Ticketee
10 | class Application < Rails::Application
11 | # Settings in config/environments/* take precedence over those specified here.
12 | # Application configuration should go into files in config/initializers
13 | # -- all .rb files in that directory are automatically loaded.
14 |
15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
17 | # config.time_zone = 'Central Time (US & Canada)'
18 |
19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
21 | # config.i18n.default_locale = :de
22 |
23 | # Do not swallow errors in after_commit/after_rollback callbacks.
24 | config.active_record.raise_in_transactional_callbacks = true
25 |
26 | #require "link_jumbler"
27 | #config.middleware.use LinkJumbler, { "e" => "a" }
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/ticketee/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
2 |
3 | require 'bundler/setup' # Set up gems listed in the Gemfile.
4 |
--------------------------------------------------------------------------------
/ticketee/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: 5
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/ticketee/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 |
--------------------------------------------------------------------------------
/ticketee/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 | end
42 |
--------------------------------------------------------------------------------
/ticketee/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 file 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 | config.action_mailer.default_url_options = {
34 | host: "ticketee.dev"
35 | }
36 |
37 | # Randomize the order test cases are executed.
38 | config.active_support.test_order = :random
39 |
40 | # Print deprecation notices to the stderr.
41 | config.active_support.deprecation = :stderr
42 |
43 | # Raises error for missing translations
44 | # config.action_view.raise_on_missing_translations = true
45 |
46 | config.action_controller.action_on_unpermitted_parameters = :raise
47 | end
48 |
--------------------------------------------------------------------------------
/ticketee/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
9 | # 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 |
--------------------------------------------------------------------------------
/ticketee/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 |
--------------------------------------------------------------------------------
/ticketee/config/initializers/carrierwave.rb:
--------------------------------------------------------------------------------
1 | CarrierWave.configure do |config|
2 | config.root = Rails.root
3 |
4 | if Rails.env.production?
5 | config.storage = :fog
6 | config.fog_credentials = {
7 | provider: "AWS",
8 | aws_access_key_id: ENV["S3_KEY"],
9 | aws_secret_access_key: ENV["S3_SECRET"],
10 | region: ENV["S3_REGION"]
11 | }
12 | config.fog_directory = ENV["S3_BUCKET"]
13 | else
14 | config.storage = :file
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/ticketee/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
4 |
--------------------------------------------------------------------------------
/ticketee/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 |
--------------------------------------------------------------------------------
/ticketee/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 | ActiveSupport::Inflector.inflections(:en) do |inflect|
14 | inflect.acronym "API"
15 | end
16 |
--------------------------------------------------------------------------------
/ticketee/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 |
--------------------------------------------------------------------------------
/ticketee/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: '_ticketee_session'
4 |
--------------------------------------------------------------------------------
/ticketee/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 |
--------------------------------------------------------------------------------
/ticketee/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 | hello: "Hello world"
24 |
--------------------------------------------------------------------------------
/ticketee/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 |
--------------------------------------------------------------------------------
/ticketee/config/routes.rb:
--------------------------------------------------------------------------------
1 | require "heartbeat/application"
2 |
3 | Rails.application.routes.draw do
4 | mount Heartbeat::Application, at: "/heartbeat"
5 |
6 | namespace :admin do
7 | root "application#index"
8 |
9 | resources :projects, only: [:new, :create, :destroy]
10 | resources :users do
11 | member do
12 | patch :archive
13 | end
14 | end
15 | resources :states, only: [:index, :new, :create] do
16 | member do
17 | get :make_default
18 | end
19 | end
20 | end
21 |
22 | namespace :api do
23 | namespace :v2 do
24 | mount API::V2::Tickets, at: "/projects/:project_id/tickets"
25 | end
26 |
27 | resources :projects, only: [] do
28 | resources :tickets
29 | end
30 | end
31 |
32 | devise_for :users
33 | root "projects#index"
34 |
35 | resources :projects, only: [:index, :show, :edit, :update] do
36 | resources :tickets do
37 | collection do
38 | get :search
39 | end
40 |
41 | member do
42 | post :watch
43 | end
44 | end
45 | end
46 |
47 | resources :tickets, only: [] do
48 | resources :comments, only: [:create]
49 | resources :tags, only: [] do
50 | member do
51 | delete :remove
52 | end
53 | end
54 | end
55 |
56 | resources :attachments, only: [:show, :new]
57 | end
58 |
--------------------------------------------------------------------------------
/ticketee/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: 517d2e7cb3575560b8f07caf66530fc4e650496f93aaec9bd2a0dd0f8f7cc498b273494db869e3a23e9fe1cd6604bfd12d8225e89bd20a3efed9c32c09cc043c
15 |
16 | test:
17 | secret_key_base: 1cf2478216f8cd5013d57fda067ecd66e1397d1d8c94e7330fa2b902ac6db346824928b6c0856621afebb73704ad01a0cf86fa10f98d48b8109153cd9a511e1f
18 |
19 | # Do not keep production secrets in the repository,
20 | # instead read values from the environment.
21 | production:
22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
23 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150314100956_create_projects.rb:
--------------------------------------------------------------------------------
1 | class CreateProjects < ActiveRecord::Migration
2 | def change
3 | create_table :projects do |t|
4 | t.string :name
5 | t.string :description
6 |
7 | t.timestamps null: false
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150318033648_create_tickets.rb:
--------------------------------------------------------------------------------
1 | class CreateTickets < ActiveRecord::Migration
2 | def change
3 | create_table :tickets do |t|
4 | t.string :name
5 | t.text :description
6 | t.references :project, index: true, foreign_key: true
7 |
8 | t.timestamps null: false
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150321110052_devise_create_users.rb:
--------------------------------------------------------------------------------
1 | class DeviseCreateUsers < ActiveRecord::Migration
2 | def change
3 | create_table(:users) do |t|
4 | ## Database authenticatable
5 | t.string :email, null: false, default: ""
6 | t.string :encrypted_password, null: false, default: ""
7 |
8 | ## Recoverable
9 | t.string :reset_password_token
10 | t.datetime :reset_password_sent_at
11 |
12 | ## Rememberable
13 | t.datetime :remember_created_at
14 |
15 | ## Trackable
16 | t.integer :sign_in_count, default: 0, null: false
17 | t.datetime :current_sign_in_at
18 | t.datetime :last_sign_in_at
19 | t.string :current_sign_in_ip
20 | t.string :last_sign_in_ip
21 |
22 | ## Confirmable
23 | # t.string :confirmation_token
24 | # t.datetime :confirmed_at
25 | # t.datetime :confirmation_sent_at
26 | # t.string :unconfirmed_email # Only if using reconfirmable
27 |
28 | ## Lockable
29 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
30 | # t.string :unlock_token # Only if unlock strategy is :email or :both
31 | # t.datetime :locked_at
32 |
33 |
34 | t.timestamps
35 | end
36 |
37 | add_index :users, :email, unique: true
38 | add_index :users, :reset_password_token, unique: true
39 | # add_index :users, :confirmation_token, unique: true
40 | # add_index :users, :unlock_token, unique: true
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150322065031_add_author_to_tickets.rb:
--------------------------------------------------------------------------------
1 | class AddAuthorToTickets < ActiveRecord::Migration
2 | def change
3 | add_reference :tickets, :author, index: true
4 | add_foreign_key :tickets, :users, column: :author_id
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150322084252_add_admin_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddAdminToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :admin, :boolean, default: false
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150323072600_add_archived_at_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddArchivedAtToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :archived_at, :timestamp
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150325092856_create_roles.rb:
--------------------------------------------------------------------------------
1 | class CreateRoles < ActiveRecord::Migration
2 | def change
3 | create_table :roles do |t|
4 | t.references :user, index: true, foreign_key: true
5 | t.string :role
6 | t.references :project, index: true, foreign_key: true
7 |
8 | t.timestamps null: false
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150401104001_add_attachment_to_tickets.rb:
--------------------------------------------------------------------------------
1 | class AddAttachmentToTickets < ActiveRecord::Migration
2 | def change
3 | add_column :tickets, :attachment, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150402090612_create_attachments.rb:
--------------------------------------------------------------------------------
1 | class CreateAttachments < ActiveRecord::Migration
2 | def change
3 | create_table :attachments do |t|
4 | t.string :file
5 | t.references :ticket, index: true, foreign_key: true
6 |
7 | t.timestamps null: false
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150402090619_remove_attachment_from_tickets.rb:
--------------------------------------------------------------------------------
1 | class RemoveAttachmentFromTickets < ActiveRecord::Migration
2 | def change
3 | remove_column :tickets, :attachment, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150403021520_create_comments.rb:
--------------------------------------------------------------------------------
1 | class CreateComments < ActiveRecord::Migration
2 | def change
3 | create_table :comments do |t|
4 | t.text :text
5 | t.references :ticket, index: true, foreign_key: true
6 | t.references :author, index: true
7 |
8 | t.timestamps null: false
9 | end
10 |
11 | add_foreign_key :comments, :users, column: :author_id
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150403044120_create_states.rb:
--------------------------------------------------------------------------------
1 | class CreateStates < ActiveRecord::Migration
2 | def change
3 | create_table :states do |t|
4 | t.string :name
5 | t.string :color
6 | end
7 |
8 | add_reference :tickets, :state, index: true, foreign_key: true
9 | add_reference :comments, :state, foreign_key: true
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150403070314_add_previous_state_to_comments.rb:
--------------------------------------------------------------------------------
1 | class AddPreviousStateToComments < ActiveRecord::Migration
2 | def change
3 | add_reference :comments, :previous_state, index: true
4 | add_foreign_key :comments, :states, column: :previous_state_id
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150403084623_add_default_to_states.rb:
--------------------------------------------------------------------------------
1 | class AddDefaultToStates < ActiveRecord::Migration
2 | def change
3 | add_column :states, :default, :boolean, default: false
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150404113718_create_tags.rb:
--------------------------------------------------------------------------------
1 | class CreateTags < ActiveRecord::Migration
2 | def change
3 | create_table :tags do |t|
4 | t.string :name
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150404113820_create_join_table_tags_tickets.rb:
--------------------------------------------------------------------------------
1 | class CreateJoinTableTagsTickets < ActiveRecord::Migration
2 | def change
3 | create_join_table :tags, :tickets do |t|
4 | t.index [:tag_id, :ticket_id]
5 | t.index [:ticket_id, :tag_id]
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150405052036_create_join_table_ticket_watchers.rb:
--------------------------------------------------------------------------------
1 | class CreateJoinTableTicketWatchers < ActiveRecord::Migration
2 | def change
3 | create_join_table :tickets, :users, table_name: :ticket_watchers do |t|
4 | # t.index [:ticket_id, :user_id]
5 | # t.index [:user_id, :ticket_id]
6 | end
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/ticketee/db/migrate/20150406055658_add_api_key_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddAPIKeyToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :api_key, :string
4 | add_index :users, :api_key
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/ticketee/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 |
9 | unless User.exists?(email: "admin@ticketee.com")
10 | User.create!(email: "admin@ticketee.com", password: "password", admin: true)
11 | end
12 |
13 | unless User.exists?(email: "viewer@ticketee.com")
14 | User.create!(email: "viewer@ticketee.com", password: "password")
15 | end
16 |
17 | ["Sublime Text 3", "Internet Explorer"].each do |name|
18 | unless Project.exists?(name: name)
19 | Project.create!(name: name, description: "A sample project about #{name}")
20 | end
21 | end
22 |
23 | unless State.exists?
24 | State.create(name: "New", color: "#0066CC")
25 | State.create(name: "Open", color: "#008000")
26 | State.create(name: "Closed", color: "#990000")
27 | State.create(name: "Awesome", color: "#663399")
28 | end
29 |
--------------------------------------------------------------------------------
/ticketee/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/ticketee/lib/assets/.keep
--------------------------------------------------------------------------------
/ticketee/lib/heartbeat/application.rb:
--------------------------------------------------------------------------------
1 | module Heartbeat
2 | class Application
3 | def self.call(env)
4 | default_headers = {"Content-Type" => "text/plain"}
5 |
6 | if env["PATH_INFO"] =~ /200/
7 | body = "Success!"
8 | status = 200
9 | else
10 | body = "Failure!"
11 | status = 500
12 | end
13 |
14 | [status, default_headers, ["#{env["PATH_INFO"]} == #{body}"]]
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/ticketee/lib/heartbeat/config.ru:
--------------------------------------------------------------------------------
1 | heartbeat_root = File.expand_path(File.dirname(__FILE__))
2 | require heartbeat_root + "/application"
3 | require heartbeat_root + "/test_application"
4 |
5 | app = Rack::Builder.app do
6 | map "/test" do
7 | run Heartbeat::Application
8 | end
9 |
10 | map "/" do
11 | run Heartbeat::TestApplication
12 | end
13 | end
14 |
15 | run app
16 |
--------------------------------------------------------------------------------
/ticketee/lib/heartbeat/test_application.rb:
--------------------------------------------------------------------------------
1 | module Heartbeat
2 | class TestApplication
3 | def self.call(env)
4 | default_headers = {"Content-Type" => "text/html"}
5 | body = %Q{
6 |
7 |
8 |
9 | Success or FAILURE?!
10 |
11 |
12 | Success or FAILURE?!
13 |
16 |
17 |
20 |
21 |
22 | }
23 |
24 | [200, default_headers, [body]]
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/ticketee/lib/link_jumbler.rb:
--------------------------------------------------------------------------------
1 | require "nokogiri"
2 |
3 | class LinkJumbler
4 | def initialize(app, letters)
5 | @app = app
6 | @letters = letters
7 | end
8 |
9 | def call(env)
10 | status, headers, response = @app.call(env)
11 | if headers['Content-Type'].include?("text/html")
12 | body = Nokogiri::HTML(response.body)
13 | body.css("a").each do |a|
14 | @letters.each do |find, replace|
15 | a.content = a.content.gsub(find.to_s, replace.to_s)
16 | end
17 | end
18 | else
19 | body = response.body
20 | end
21 |
22 | [status, headers, Rack::Response.new(body.to_s)]
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/ticketee/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/ticketee/lib/tasks/.keep
--------------------------------------------------------------------------------
/ticketee/lib/templates/erb/scaffold/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%%= simple_form_for(@<%= singular_table_name %>) do |f| %>
2 | <%%= f.error_notification %>
3 |
4 |
5 | <%- attributes.each do |attribute| -%>
6 | <%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %>
7 | <%- end -%>
8 |
9 |
10 |
11 | <%%= f.button :submit %>
12 |
13 | <%% end %>
14 |
--------------------------------------------------------------------------------
/ticketee/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/ticketee/log/.keep
--------------------------------------------------------------------------------
/ticketee/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/ticketee/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/ticketee/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/ticketee/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/ticketee/public/favicon.ico
--------------------------------------------------------------------------------
/ticketee/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 |
--------------------------------------------------------------------------------
/ticketee/spec/controllers/admin/application_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe Admin::ApplicationController, type: :controller do
4 | let(:user) { FactoryGirl.create(:user) }
5 |
6 | before do
7 | allow(controller).to receive(:authenticate_user!)
8 | allow(controller).to receive(:current_user).and_return(user)
9 | end
10 |
11 | context "non-admin users" do
12 | it "are not able to access the index action" do
13 | get :index
14 |
15 | expect(response).to redirect_to "/"
16 | expect(flash[:alert]).to eq "You must be an admin to do that."
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/ticketee/spec/controllers/admin/projects_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Admin::ProjectsController, type: :controller do
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/spec/controllers/admin/states_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Admin::StatesController, type: :controller do
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/spec/controllers/api/tickets_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe API::TicketsController, type: :controller do
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/spec/controllers/attachments_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe AttachmentsController, type: :controller do
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/spec/controllers/comments_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe CommentsController, type: :controller do
4 | let(:user) { FactoryGirl.create(:user) }
5 | let(:project) { Project.create!(name: "Ticketee") }
6 | let(:state) { State.create!(name: "Hacked") }
7 |
8 | let(:ticket) do
9 | project.tickets.create(name: "State transitions",
10 | description: "Can't be hacked.", author: user)
11 | end
12 |
13 | context "a user without permission to set state" do
14 | before :each do
15 | assign_role!(user, :editor, project)
16 | sign_in user
17 | end
18 |
19 | it "cannot transition a state by passing through state_id" do
20 | post :create, { comment: { text: "Did I hack it??",
21 | state_id: state.id },
22 | ticket_id: ticket.id }
23 | ticket.reload
24 | expect(ticket.state).to be_nil
25 | end
26 | end
27 |
28 | context "a user without permission to tag a ticket" do
29 | before do
30 | assign_role!(user, :editor, project)
31 | sign_in user
32 | end
33 |
34 | it "cannot tag a ticket when creating a comment" do
35 | post :create, { comment: { text: "Tag!",
36 | tag_names: "one two" },
37 | ticket_id: ticket.id }
38 | ticket.reload
39 | expect(ticket.tags).to be_empty
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/ticketee/spec/controllers/projects_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe ProjectsController, type: :controller do
4 | it "handles a missing project correctly" do
5 | get :show, id: "not-here"
6 |
7 | expect(response).to redirect_to(projects_path)
8 |
9 | message = "The project you were looking for could not be found."
10 | expect(flash[:alert]).to eq message
11 | end
12 |
13 | it "handles permission errors by redirecting to a safe place" do
14 | allow(controller).to receive(:current_user)
15 |
16 | project = FactoryGirl.create(:project)
17 | get :show, id: project
18 |
19 | expect(response).to redirect_to(root_path)
20 | message = "You aren't allowed to do that."
21 | expect(flash[:alert]).to eq message
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/ticketee/spec/controllers/tags_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe TagsController, type: :controller do
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/spec/controllers/tickets_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe TicketsController, type: :controller do
4 | let(:project) { FactoryGirl.create(:project) }
5 | let(:user) { FactoryGirl.create(:user) }
6 |
7 | before :each do
8 | assign_role!(user, :editor, project)
9 | sign_in user
10 | end
11 |
12 | it "can create tickets, but not tag them" do
13 | post :create, ticket: { name: "New ticket!",
14 | description: "Brand spankin' new",
15 | tag_names: "these are tags" },
16 | project_id: project.id
17 | expect(Ticket.last.tags).to be_empty
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/ticketee/spec/factories/attachment_factory.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :attachment do
3 | transient do
4 | file_to_attach "spec/fixtures/speed.txt"
5 | end
6 |
7 | file { File.open file_to_attach }
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/ticketee/spec/factories/comment_factory.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :comment do
3 | text { "A comment describing some changes that should be made
4 | to this ticket." }
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/ticketee/spec/factories/project_factory.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :project do
3 | name "Example project"
4 | end
5 | end
--------------------------------------------------------------------------------
/ticketee/spec/factories/state_factory.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :state do
3 | name "A state"
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/ticketee/spec/factories/ticket_factory.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :ticket do
3 | name "Example ticket"
4 | description "An example ticket, nothing more"
5 | end
6 | end
--------------------------------------------------------------------------------
/ticketee/spec/factories/user_factory.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :user do
3 | sequence(:email) { |n| "test#{n}@example.com" }
4 | password "password"
5 |
6 | trait :admin do
7 | admin true
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/ticketee/spec/features/admin/archiving_users_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "An admin can archive users" do
4 | let(:admin_user) { FactoryGirl.create(:user, :admin) }
5 | let(:user) { FactoryGirl.create(:user) }
6 |
7 | before do
8 | login_as(admin_user)
9 | end
10 |
11 | scenario "successfully" do
12 | visit admin_user_path(user)
13 | click_link "Archive User"
14 |
15 | expect(page).to have_content "User has been archived"
16 | expect(page).not_to have_content user.email
17 | end
18 |
19 | scenario "but cannot archive themselves" do
20 | visit admin_user_path(admin_user)
21 | click_link "Archive User"
22 |
23 | expect(page).to have_content "You cannot archive yourself!"
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/ticketee/spec/features/admin/creating_projects_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can create new projects" do
4 | before do
5 | login_as(FactoryGirl.create(:user, :admin))
6 |
7 | visit "/"
8 |
9 | click_link "New Project"
10 | end
11 |
12 | scenario "with valid attributes" do
13 | fill_in "Name", with: "Sublime Text 3"
14 | fill_in "Description", with: "A text editor for everyone"
15 | click_button "Create Project"
16 |
17 | expect(page).to have_content "Project has been created."
18 |
19 | project = Project.find_by(name: "Sublime Text 3")
20 | expect(page.current_url).to eq project_url(project)
21 |
22 | title = "Sublime Text 3 - Projects - Ticketee"
23 | expect(page).to have_title title
24 | end
25 |
26 | scenario "when providing invalid attributes" do
27 | click_button "Create Project"
28 |
29 | expect(page).to have_content "Project has not been created."
30 | expect(page).to have_content "Name can't be blank"
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/ticketee/spec/features/admin/creating_states_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Admins can create new states for tickets" do
4 | before do
5 | login_as(FactoryGirl.create(:user, :admin))
6 | end
7 |
8 | scenario "with valid details" do
9 | visit admin_root_path
10 | click_link "States"
11 | click_link "New State"
12 |
13 | fill_in "Name", with: "Won't Fix"
14 | fill_in "Color", with: "orange"
15 | click_button "Create State"
16 |
17 | expect(page).to have_content "State has been created."
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/ticketee/spec/features/admin/creating_users_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Admins can create new users" do
4 | let(:admin) { FactoryGirl.create(:user, :admin) }
5 |
6 | before do
7 | login_as(admin)
8 | visit "/"
9 | click_link "Admin"
10 | click_link "Users"
11 | click_link "New User"
12 | end
13 |
14 | scenario "with valid credentials" do
15 | fill_in "Email", with: "newbie@example.com"
16 | fill_in "Password", with: "password"
17 | click_button "Create User"
18 | expect(page).to have_content "User has been created."
19 | end
20 |
21 | scenario "when the new user is an admin" do
22 | fill_in "Email", with: "admin@example.com"
23 | fill_in "Password", with: "password"
24 | check "Is an admin?"
25 | click_button "Create User"
26 | expect(page).to have_content "User has been created."
27 | expect(page).to have_content "admin@example.com (Admin)"
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/ticketee/spec/features/admin/deleting_projects_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can delete projects" do
4 | before do
5 | login_as(FactoryGirl.create(:user, :admin))
6 | end
7 |
8 | scenario "successfully" do
9 | FactoryGirl.create(:project, name: "Sublime Text 3")
10 |
11 | visit "/"
12 | click_link "Sublime Text 3"
13 | click_link "Delete Project"
14 |
15 | expect(page).to have_content "Project has been deleted."
16 | expect(page.current_url).to eq projects_url
17 | expect(page).to have_no_content "Sublime Text 3"
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/ticketee/spec/features/admin/editing_users_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Admins can change a user's details" do
4 | let(:admin_user) { FactoryGirl.create(:user, :admin) }
5 | let(:user) { FactoryGirl.create(:user) }
6 |
7 | before do
8 | login_as(admin_user)
9 | visit admin_user_path(user)
10 | click_link "Edit User"
11 | end
12 |
13 | scenario "with valid details" do
14 | fill_in "Email", with: "newguy@example.com"
15 | click_button "Update User"
16 |
17 | expect(page).to have_content "User has been updated."
18 | expect(page).to have_content "newguy@example.com"
19 | expect(page).to_not have_content user.email
20 | end
21 |
22 | scenario "when toggling a user's admin ability" do
23 | check "Is an admin?"
24 | click_button "Update User"
25 |
26 | expect(page).to have_content "User has been updated."
27 | expect(page).to have_content "#{user.email} (Admin)"
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/ticketee/spec/features/admin/managing_roles_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Admins can manage a user's roles" do
4 | let(:admin) { FactoryGirl.create(:user, :admin) }
5 | let(:user) { FactoryGirl.create(:user) }
6 |
7 | let!(:ie) { FactoryGirl.create(:project, name: "Internet Explorer") }
8 | let!(:st3) { FactoryGirl.create(:project, name: "Sublime Text 3") }
9 |
10 | before do
11 | login_as(admin)
12 | end
13 |
14 | scenario "when assigning roles to an existing user" do
15 | visit admin_user_path(user)
16 | click_link "Edit User"
17 |
18 | select "Viewer", from: "Internet Explorer"
19 | select "Manager", from: "Sublime Text 3"
20 |
21 | click_button "Update User"
22 | expect(page).to have_content "User has been updated"
23 |
24 | click_link user.email
25 | expect(page).to have_content "Internet Explorer: Viewer"
26 | expect(page).to have_content "Sublime Text 3: Manager"
27 | end
28 |
29 | scenario "when assigning roles to a new user" do
30 | visit new_admin_user_path
31 |
32 | fill_in "Email", with: "newuser@ticketee.com"
33 | fill_in "Password", with: "password"
34 |
35 | select "Editor", from: "Internet Explorer"
36 | click_button "Create User"
37 |
38 | click_link "newuser@ticketee.com"
39 | expect(page).to have_content "Internet Explorer: Editor"
40 | expect(page).not_to have_content "Sublime Text 3"
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/ticketee/spec/features/admin/managing_states_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Admins can manage states" do
4 | let!(:state) { FactoryGirl.create :state, name: "New" }
5 |
6 | before do
7 | login_as(FactoryGirl.create(:user, :admin))
8 | visit admin_states_path
9 | end
10 |
11 | scenario "and mark a state as default" do
12 | within list_item("New") do
13 | click_link "Make Default"
14 | end
15 |
16 | expect(page).to have_content "'New' is now the default state."
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/ticketee/spec/features/creating_comments_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can comment on tickets" do
4 | let(:user) { FactoryGirl.create(:user) }
5 | let(:project) { FactoryGirl.create(:project) }
6 | let(:ticket) { FactoryGirl.create(:ticket,
7 | project: project, author: user) }
8 |
9 | before do
10 | login_as(user)
11 | assign_role!(user, :manager, project)
12 | end
13 |
14 | scenario "with valid attributes" do
15 | visit project_ticket_path(project, ticket)
16 |
17 | fill_in "Text", with: "Added a comment!"
18 | click_button "Create Comment"
19 |
20 | expect(page).to have_content "Comment has been created."
21 | within("#comments") do
22 | expect(page).to have_content "Added a comment!"
23 | end
24 | end
25 |
26 | scenario "with invalid attributes" do
27 | visit project_ticket_path(project, ticket)
28 | click_button "Create Comment"
29 |
30 | expect(page).to have_content "Comment has not been created."
31 | end
32 |
33 | scenario "when changing a ticket's state" do
34 | FactoryGirl.create(:state, name: "Open")
35 |
36 | visit project_ticket_path(project, ticket)
37 | fill_in "Text", with: "This is a real issue"
38 | select "Open", from: "State"
39 | click_button "Create Comment"
40 |
41 | expect(page).to have_content "Comment has been created."
42 | within("#ticket .state") do
43 | expect(page).to have_content "Open"
44 | end
45 | within("#comments") do
46 | expect(page).to have_content "state changed to Open"
47 | end
48 | end
49 |
50 | scenario "but cannot change the state without permission" do
51 | assign_role!(user, :editor, project)
52 | visit project_ticket_path(project, ticket)
53 |
54 | expect(page).not_to have_select "State"
55 | end
56 |
57 | scenario "when adding a new tag to a ticket" do
58 | visit project_ticket_path(project, ticket)
59 | expect(page).not_to have_content "bug"
60 |
61 | fill_in "Text", with: "Adding the bug tag"
62 | fill_in "Tags", with: "bug"
63 | click_button "Create Comment"
64 |
65 | expect(page).to have_content "Comment has been created."
66 | within("#ticket #tags") do
67 | expect(page).to have_content "bug"
68 | end
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/ticketee/spec/features/deleting_tags_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can delete unwanted tags from a ticket" do
4 | let(:user) { FactoryGirl.create(:user) }
5 | let(:project) { FactoryGirl.create(:project) }
6 | let(:ticket) do
7 | FactoryGirl.create(:ticket, project: project,
8 | tag_names: "ThisTagMustDie", author: user)
9 | end
10 |
11 | before do
12 | login_as(user)
13 | assign_role!(user, :manager, project)
14 | visit project_ticket_path(project, ticket)
15 | end
16 |
17 | scenario "successfully", js: true do
18 | within tag("ThisTagMustDie") do
19 | click_link "remove"
20 | end
21 | expect(page).to_not have_content "ThisTagMustDie"
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/ticketee/spec/features/deleting_tickets_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can delete tickets" do
4 | let(:author) { FactoryGirl.create(:user) }
5 | let(:project) { FactoryGirl.create(:project) }
6 | let(:ticket) do
7 | FactoryGirl.create(:ticket, project: project, author: author)
8 | end
9 |
10 | before do
11 | login_as(author)
12 | assign_role!(author, :manager, project)
13 | visit project_ticket_path(project, ticket)
14 | end
15 |
16 | scenario "successfully" do
17 | click_link "Delete Ticket"
18 |
19 | expect(page).to have_content "Ticket has been deleted."
20 | expect(page.current_url).to eq project_url(project)
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/ticketee/spec/features/editing_projects_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Project managers can edit existing projects" do
4 | let(:user) { FactoryGirl.create(:user) }
5 | let(:project) { FactoryGirl.create(:project, name: "Sublime Text 3") }
6 |
7 | before do
8 | login_as(user)
9 | assign_role!(user, :manager, project)
10 |
11 | visit "/"
12 | click_link "Sublime Text 3"
13 | click_link "Edit Project"
14 | end
15 |
16 | scenario "with valid attributes" do
17 | fill_in "Name", with: "Sublime Text 4 beta"
18 | click_button "Update Project"
19 |
20 | expect(page).to have_content "Project has been updated."
21 | expect(page).to have_content "Sublime Text 4 beta"
22 | end
23 |
24 | scenario "when providing invalid attributes" do
25 | fill_in "Name", with: ""
26 | click_button "Update Project"
27 |
28 | expect(page).to have_content "Project has not been updated."
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/ticketee/spec/features/editing_tickets_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can edit existing tickets" do
4 | let(:author) { FactoryGirl.create(:user) }
5 | let(:project) { FactoryGirl.create(:project) }
6 | let(:ticket) do
7 | FactoryGirl.create(:ticket, project: project, author: author)
8 | end
9 |
10 | before do
11 | assign_role!(author, :editor, project)
12 | login_as(author)
13 |
14 | visit project_ticket_path(project, ticket)
15 | click_link "Edit Ticket"
16 | end
17 |
18 | scenario "with valid attributes" do
19 | fill_in "Name", with: "Make it really shiny!"
20 | click_button "Update Ticket"
21 |
22 | expect(page).to have_content "Ticket has been updated."
23 |
24 | within("#ticket h2") do
25 | expect(page).to have_content "Make it really shiny!"
26 | expect(page).not_to have_content ticket.name
27 | end
28 | end
29 |
30 | scenario "with invalid attributes" do
31 | fill_in "Name", with: ""
32 | click_button "Update Ticket"
33 |
34 | expect(page).to have_content "Ticket has not been updated."
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/ticketee/spec/features/hidden_links_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can only see the appropriate links" do
4 | let(:project) { FactoryGirl.create(:project) }
5 | let(:user) { FactoryGirl.create(:user) }
6 | let(:admin) { FactoryGirl.create(:user, :admin) }
7 | let(:ticket) do
8 | FactoryGirl.create(:ticket, project: project, author: user)
9 | end
10 |
11 | context "anonymous users" do
12 | scenario "cannot see the New Project link" do
13 | visit "/"
14 | expect(page).not_to have_link "New Project"
15 | end
16 | end
17 |
18 | context "non-admin users (project viewers)" do
19 | before do
20 | login_as(user)
21 | assign_role!(user, :viewer, project)
22 | end
23 |
24 | scenario "cannot see the New Project link" do
25 | visit "/"
26 | expect(page).not_to have_link "New Project"
27 | end
28 |
29 | scenario "cannot see the Edit Project link" do
30 | visit project_path(project)
31 | expect(page).not_to have_link "Edit Project"
32 | end
33 |
34 | scenario "cannot see the Delete Project link" do
35 | visit project_path(project)
36 | expect(page).not_to have_link "Delete Project"
37 | end
38 |
39 | scenario "cannot see the New Ticket link" do
40 | visit project_path(project)
41 | expect(page).not_to have_link "New Ticket"
42 | end
43 |
44 | scenario "cannot see the Edit Ticket link" do
45 | visit project_ticket_path(project, ticket)
46 | expect(page).not_to have_link "Edit Ticket"
47 | end
48 |
49 | scenario "cannot see the Delete Ticket link" do
50 | visit project_ticket_path(project, ticket)
51 | expect(page).not_to have_link "Delete Ticket"
52 | end
53 |
54 | scenario "cannot see the New Comment form" do
55 | visit project_ticket_path(project, ticket)
56 | expect(page).not_to have_heading "New Comment"
57 | end
58 | end
59 |
60 | context "admin users" do
61 | before { login_as(admin) }
62 |
63 | scenario "can see the New Project link" do
64 | visit "/"
65 | expect(page).to have_link "New Project"
66 | end
67 |
68 | scenario "can see the Edit Project link" do
69 | visit project_path(project)
70 | expect(page).to have_link "Edit Project"
71 | end
72 |
73 | scenario "can see the Delete Project link" do
74 | visit project_path(project)
75 | expect(page).to have_link "Delete Project"
76 | end
77 |
78 | scenario "can see the New Ticket link" do
79 | visit project_path(project)
80 | expect(page).to have_link "New Ticket"
81 | end
82 |
83 | scenario "can see the Edit Ticket link" do
84 | visit project_ticket_path(project, ticket)
85 | expect(page).to have_link "Edit Ticket"
86 | end
87 |
88 | scenario "can see the Delete Ticket link" do
89 | visit project_ticket_path(project, ticket)
90 | expect(page).to have_link "Delete Ticket"
91 | end
92 |
93 | scenario "can see the New Comment form" do
94 | visit project_ticket_path(project, ticket)
95 | expect(page).to have_heading "New Comment"
96 | end
97 | end
98 | end
99 |
--------------------------------------------------------------------------------
/ticketee/spec/features/searching_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can search for tickets matching specific criteria" do
4 | let(:user) { FactoryGirl.create(:user) }
5 | let(:project) { FactoryGirl.create(:project) }
6 | let(:open) { State.create(name: "Open", default: true) }
7 | let(:closed) { State.create(name: "Closed") }
8 |
9 | let!(:ticket_1) do
10 | FactoryGirl.create(:ticket, name: "Create projects",
11 | project: project, author: user, tag_names: "iteration_1",
12 | state: open)
13 | end
14 |
15 | let!(:ticket_2) do
16 | FactoryGirl.create(:ticket, name: "Create users",
17 | project: project, author: user, tag_names: "iteration_2",
18 | state: closed)
19 | end
20 |
21 | before do
22 | assign_role!(user, :manager, project)
23 | login_as(user)
24 | visit project_path(project)
25 | end
26 |
27 | scenario "searching by tag" do
28 | fill_in "Search", with: "tag:iteration_1"
29 | click_button "Search"
30 | within("#tickets") do
31 | expect(page).to have_link "Create projects"
32 | expect(page).to_not have_link "Create users"
33 | end
34 | end
35 |
36 | scenario "searching by state" do
37 | fill_in "Search", with: "state:Open"
38 | click_button "Search"
39 | within("#tickets") do
40 | expect(page).to have_link "Create projects"
41 | expect(page).to_not have_link "Create users"
42 | end
43 | end
44 |
45 | scenario "when clicking on a tag" do
46 | click_link "Create projects"
47 | click_link "iteration_1"
48 | within("#tickets") do
49 | expect(page).to have_content "Create projects"
50 | expect(page).to_not have_content "Create users"
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/ticketee/spec/features/signing_in_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can sign in" do
4 | let!(:user) { FactoryGirl.create(:user) }
5 |
6 | scenario "with valid credentials" do
7 | visit "/"
8 | click_link "Sign in"
9 | fill_in "Email", with: user.email
10 | fill_in "Password", with: "password"
11 | click_button "Sign in"
12 |
13 | expect(page).to have_content "Signed in successfully."
14 | expect(page).to have_content "Signed in as #{user.email}"
15 | end
16 |
17 | scenario "unless they are archived" do
18 | user.archive
19 |
20 | visit "/"
21 | click_link "Sign in"
22 | fill_in "Email", with: user.email
23 | fill_in "Password", with: "password"
24 | click_button "Sign in"
25 |
26 | expect(page).to have_content "Your account has been archived."
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/ticketee/spec/features/signing_out_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Signed-in users can sign out" do
4 | let!(:user) { FactoryGirl.create(:user) }
5 |
6 | before do
7 | login_as(user)
8 | end
9 |
10 | scenario do
11 | visit "/"
12 | click_link "Sign out"
13 | expect(page).to have_content "Signed out successfully."
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/ticketee/spec/features/signing_up_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can sign up" do
4 | scenario "when providing valid details" do
5 | visit "/"
6 | click_link "Sign up"
7 | fill_in "Email", with: "test@example.com"
8 | fill_in "user_password", with: "password"
9 | fill_in "Password confirmation", with: "password"
10 | click_button "Sign up"
11 | expect(page).to have_content("You have signed up successfully.")
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/ticketee/spec/features/ticket_notifications_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can receive notifications about ticket updates" do
4 | let(:alice) { FactoryGirl.create(:user, email: "alice@example.com") }
5 | let(:bob) { FactoryGirl.create(:user, email: "bob@example.com") }
6 | let(:project) { FactoryGirl.create(:project) }
7 | let(:ticket) do
8 | FactoryGirl.create(:ticket, project: project, author: alice)
9 | end
10 |
11 | before do
12 | assign_role!(alice, :manager, project)
13 | assign_role!(bob, :manager, project)
14 |
15 | login_as(bob)
16 | visit project_ticket_path(project, ticket)
17 | end
18 |
19 | scenario "ticket authors automatically receive notifications" do
20 | fill_in "Text", with: "Is it out yet?"
21 | click_button "Create Comment"
22 |
23 | email = find_email!(alice.email)
24 | expected_subject = "[ticketee] #{project.name} - #{ticket.name}"
25 | expect(email.subject).to eq expected_subject
26 |
27 | click_first_link_in_email(email)
28 | expect(current_path).to eq project_ticket_path(project, ticket)
29 | end
30 |
31 | scenario "comment authors are automatically subscribed to a ticket" do
32 | fill_in "Text", with: "Is it out yet?"
33 | click_button "Create Comment"
34 | click_link "Sign out"
35 |
36 | reset_mailer
37 |
38 | login_as(alice)
39 | visit project_ticket_path(project, ticket)
40 | fill_in "Text", with: "Not yet - sorry!"
41 | click_button "Create Comment"
42 |
43 | expect(page).to have_content "Comment has been created."
44 | expect(unread_emails_for(bob.email).count).to eq 1
45 | expect(unread_emails_for(alice.email).count).to eq 0
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/ticketee/spec/features/viewing_attachments_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can view a ticket's attached files" do
4 | let(:user) { FactoryGirl.create :user }
5 | let(:project) { FactoryGirl.create :project }
6 | let(:ticket) { FactoryGirl.create :ticket, project: project,
7 | author: user }
8 | let!(:attachment) { FactoryGirl.create :attachment, ticket: ticket,
9 | file_to_attach: "spec/fixtures/speed.txt" }
10 |
11 | before do
12 | assign_role!(user, :viewer, project)
13 | login_as(user)
14 | end
15 |
16 | scenario "successfully" do
17 | visit project_ticket_path(project, ticket)
18 | click_link "speed.txt"
19 |
20 | expect(current_path).to eq attachment_path(attachment)
21 | expect(page).to have_content "The blink tag can blink faster"
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/ticketee/spec/features/viewing_projects_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can view projects" do
4 | let(:user) { FactoryGirl.create(:user) }
5 | let(:project) { FactoryGirl.create(:project, name: "Sublime Text 3") }
6 |
7 | before do
8 | login_as(user)
9 | assign_role!(user, :viewer, project)
10 | end
11 |
12 | scenario "with the project details" do
13 | visit "/"
14 | click_link "Sublime Text 3"
15 | expect(page.current_url).to eq project_url(project)
16 | end
17 |
18 | scenario "unless they do not have permission" do
19 | FactoryGirl.create(:project, name: "Hidden")
20 | visit "/"
21 | expect(page).not_to have_content "Hidden"
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/ticketee/spec/features/viewing_tickets_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can view tickets" do
4 | before do
5 | author = FactoryGirl.create(:user)
6 |
7 | sublime = FactoryGirl.create(:project, name: "Sublime Text 3")
8 | assign_role!(author, :viewer, sublime)
9 | FactoryGirl.create(:ticket, project: sublime,
10 | author: author, name: "Make it shiny!",
11 | description: "Gradients! Starbursts! Oh my!")
12 |
13 | ie = FactoryGirl.create(:project, name: "Internet Explorer")
14 | assign_role!(author, :viewer, ie)
15 | FactoryGirl.create(:ticket, project: ie, author: author,
16 | name: "Standards compliance", description: "Isn't a joke.")
17 |
18 | login_as(author)
19 | visit "/"
20 | end
21 |
22 | scenario "for a given project" do
23 | click_link "Sublime Text 3"
24 |
25 | expect(page).to have_content "Make it shiny!"
26 | expect(page).to_not have_content "Standards compliance"
27 |
28 | click_link "Make it shiny!"
29 | within("#ticket h2") do
30 | expect(page).to have_content "Make it shiny!"
31 | end
32 |
33 | expect(page).to have_content "Gradients! Starbursts! Oh my!"
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/ticketee/spec/features/watching_tickets_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.feature "Users can watch and unwatch tickets" do
4 | let(:user) { FactoryGirl.create(:user) }
5 | let(:project) { FactoryGirl.create(:project) }
6 | let(:ticket) do
7 | FactoryGirl.create(:ticket, project: project, author: user)
8 | end
9 |
10 | before do
11 | assign_role!(user, "viewer", project)
12 | login_as(user)
13 | visit project_ticket_path(project, ticket)
14 | end
15 |
16 | scenario "successfully" do
17 | within("#watchers") do
18 | expect(page).to have_content user.email
19 | end
20 |
21 | click_link "Unwatch"
22 | expect(page).to have_content "You are no longer watching this " +
23 | "ticket."
24 |
25 | within("#watchers") do
26 | expect(page).to_not have_content user.email
27 | end
28 |
29 | click_link "Watch"
30 | expect(page).to have_content "You are now watching this ticket."
31 |
32 | within("#watchers") do
33 | expect(page).to have_content user.email
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/ticketee/spec/fixtures/gradient.txt:
--------------------------------------------------------------------------------
1 | Everything looks better with a gradient!
2 |
--------------------------------------------------------------------------------
/ticketee/spec/fixtures/speed.txt:
--------------------------------------------------------------------------------
1 | The blink tag can blink faster if you use the speed="hyper" attribute.
2 |
--------------------------------------------------------------------------------
/ticketee/spec/fixtures/spin.txt:
--------------------------------------------------------------------------------
1 | Spinning blink tags have a 200% higher click rate!
2 |
--------------------------------------------------------------------------------
/ticketee/spec/mailers/comment_notifier_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe CommentNotifier, type: :mailer do
4 | describe "created" do
5 | let(:project) { FactoryGirl.create(:project) }
6 | let(:ticket_owner) { FactoryGirl.create(:user) }
7 | let(:ticket) do
8 | FactoryGirl.create(:ticket,
9 | project: project, author: ticket_owner)
10 | end
11 |
12 | let(:commenter) { FactoryGirl.create(:user) }
13 | let(:comment) do
14 | Comment.new(ticket: ticket, author: commenter,
15 | text: "Test comment")
16 | end
17 |
18 | let(:email) do
19 | CommentNotifier.created(comment, ticket_owner)
20 | end
21 |
22 | it "sends out an email notification about a new comment" do
23 | expect(email.to).to include ticket_owner.email
24 | title = "#{ticket.name} for #{project.name} has been updated."
25 | expect(email.body.to_s).to include title
26 | expect(email.body.to_s).to include "#{commenter.email} wrote:"
27 | expect(email.body.to_s).to include comment.text
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/ticketee/spec/mailers/previews/comment_notifier_preview.rb:
--------------------------------------------------------------------------------
1 | # Preview all emails at http://localhost:3000/rails/mailers/comment_notifier
2 | class CommentNotifierPreview < ActionMailer::Preview
3 |
4 | end
5 |
--------------------------------------------------------------------------------
/ticketee/spec/policies/attachment_policy_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe AttachmentPolicy do
4 | context "permissions" do
5 | subject { AttachmentPolicy.new(user, attachment) }
6 |
7 | let(:user) { FactoryGirl.create(:user) }
8 | let(:project) { FactoryGirl.create(:project) }
9 | let(:ticket) { FactoryGirl.create(:ticket, project: project) }
10 | let(:attachment) { FactoryGirl.create(:attachment, ticket: ticket) }
11 |
12 | context "for anonymous users" do
13 | let(:user) { nil }
14 | it { should_not permit_action :show }
15 | end
16 |
17 | context "for viewers of the project" do
18 | before { assign_role!(user, :viewer, project) }
19 | it { should permit_action :show }
20 | end
21 |
22 | context "for editors of the project" do
23 | before { assign_role!(user, :editor, project) }
24 | it { should permit_action :show }
25 | end
26 |
27 | context "for managers of the project" do
28 | before { assign_role!(user, :manager, project) }
29 | it { should permit_action :show }
30 | end
31 |
32 | context "for managers of other projects" do
33 | before do
34 | assign_role!(user, :manager, FactoryGirl.create(:project))
35 | end
36 | it { should_not permit_action :show }
37 | end
38 |
39 | context "for administrators" do
40 | let(:user) { FactoryGirl.create :user, :admin }
41 | it { should permit_action :show }
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/ticketee/spec/policies/comment_policy_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe CommentPolicy do
4 | context "permissions" do
5 | subject { CommentPolicy.new(user, comment) }
6 |
7 | let(:user) { FactoryGirl.create(:user) }
8 | let(:project) { FactoryGirl.create(:project) }
9 | let(:ticket) { FactoryGirl.create(:ticket, project: project) }
10 | let(:comment) { FactoryGirl.create(:comment, ticket: ticket) }
11 |
12 | context "for anonymous users" do
13 | let(:user) { nil }
14 | it { should_not permit_action :create }
15 | end
16 |
17 | context "for viewers of the project" do
18 | before { assign_role!(user, :viewer, project) }
19 | it { should_not permit_action :create }
20 | end
21 |
22 | context "for editors of the project" do
23 | before { assign_role!(user, :editor, project) }
24 | it { should permit_action :create }
25 | end
26 |
27 | context "for managers of the project" do
28 | before { assign_role!(user, :manager, project) }
29 | it { should permit_action :create }
30 | end
31 |
32 | context "for managers of other projects" do
33 | before do
34 | assign_role!(user, :manager, FactoryGirl.create(:project))
35 | end
36 | it { should_not permit_action :create }
37 | end
38 |
39 | context "for administrators" do
40 | let(:user) { FactoryGirl.create :user, :admin }
41 | it { should permit_action :create }
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/ticketee/spec/policies/project_policy_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe ProjectPolicy do
4 |
5 | let(:user) { User.new }
6 |
7 | subject { ProjectPolicy }
8 |
9 | context "policy_scope" do
10 | subject { Pundit.policy_scope(user, Project) }
11 |
12 | let!(:project) { FactoryGirl.create :project }
13 | let(:user) { FactoryGirl.create :user }
14 |
15 | it "is empty for anonymous users" do
16 | expect(Pundit.policy_scope(nil, Project)).to be_empty
17 | end
18 |
19 | it "includes projects a user is allowed to view" do
20 | assign_role!(user, :viewer, project)
21 | expect(subject).to include(project)
22 | end
23 |
24 | it "doesn't include projects a user is not allowed to view" do
25 | expect(subject).to be_empty
26 | end
27 |
28 | it "returns all projects for admins" do
29 | user.admin = true
30 | expect(subject).to include(project)
31 | end
32 | end
33 |
34 | context "permissions" do
35 | subject { ProjectPolicy.new(user, project) }
36 |
37 | let(:user) { FactoryGirl.create(:user) }
38 | let(:project) { FactoryGirl.create(:project) }
39 |
40 | context "for anonymous users" do
41 | let(:user) { nil }
42 |
43 | it { should_not permit_action :show }
44 | it { should_not permit_action :update }
45 | end
46 |
47 | context "for viewers of the project" do
48 | before { assign_role!(user, :viewer, project) }
49 |
50 | it { should permit_action :show }
51 | it { should_not permit_action :update }
52 | end
53 |
54 | context "for editors of the project" do
55 | before { assign_role!(user, :editor, project) }
56 |
57 | it { should permit_action :show }
58 | it { should_not permit_action :update }
59 | end
60 |
61 | context "for managers of the project" do
62 | before { assign_role!(user, :manager, project) }
63 |
64 | it { should permit_action :show }
65 | it { should permit_action :update }
66 | end
67 |
68 | context "for managers of other projects" do
69 | before do
70 | assign_role!(user, :manager, FactoryGirl.create(:project))
71 | end
72 |
73 | it { should_not permit_action :show }
74 | it { should_not permit_action :update }
75 | end
76 |
77 | context "for administrators" do
78 | let(:user) { FactoryGirl.create :user, :admin }
79 |
80 | it { should permit_action :show }
81 | it { should permit_action :update }
82 | end
83 | end
84 | end
85 |
--------------------------------------------------------------------------------
/ticketee/spec/policies/ticket_policy_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe TicketPolicy do
4 | context "permissions" do
5 | subject { TicketPolicy.new(user, ticket) }
6 |
7 | let(:user) { FactoryGirl.create(:user) }
8 | let(:project) { FactoryGirl.create(:project) }
9 | let(:ticket) { FactoryGirl.create(:ticket, project: project) }
10 |
11 | context "for anonymous users" do
12 | let(:user) { nil }
13 |
14 | it { should_not permit_action :show }
15 | it { should_not permit_action :create }
16 | it { should_not permit_action :update }
17 | it { should_not permit_action :destroy }
18 | it { should_not permit_action :change_state }
19 | it { should_not permit_action :tag }
20 | end
21 |
22 | context "for viewers of the project" do
23 | before { assign_role!(user, :viewer, project) }
24 |
25 | it { should permit_action :show }
26 | it { should_not permit_action :create }
27 | it { should_not permit_action :update }
28 | it { should_not permit_action :destroy }
29 | it { should_not permit_action :change_state }
30 | it { should_not permit_action :tag }
31 | end
32 |
33 | context "for editors of the project" do
34 | before { assign_role!(user, :editor, project) }
35 |
36 | it { should permit_action :show }
37 | it { should permit_action :create }
38 | it { should_not permit_action :update }
39 | it { should_not permit_action :destroy }
40 | it { should_not permit_action :change_state }
41 | it { should_not permit_action :tag }
42 |
43 | context "when the editor created the ticket" do
44 | before { ticket.author = user }
45 |
46 | it { should permit_action :update }
47 | end
48 | end
49 |
50 | context "for managers of the project" do
51 | before { assign_role!(user, :manager, project) }
52 |
53 | it { should permit_action :show }
54 | it { should permit_action :create }
55 | it { should permit_action :update }
56 | it { should permit_action :destroy }
57 | it { should permit_action :change_state }
58 | it { should permit_action :tag }
59 | end
60 |
61 | context "for managers of other projects" do
62 | before do
63 | assign_role!(user, :manager, FactoryGirl.create(:project))
64 | end
65 |
66 | it { should_not permit_action :show }
67 | it { should_not permit_action :create }
68 | it { should_not permit_action :update }
69 | it { should_not permit_action :destroy }
70 | it { should_not permit_action :change_state }
71 | it { should_not permit_action :tag }
72 | end
73 |
74 | context "for administrators" do
75 | let(:user) { FactoryGirl.create :user, :admin }
76 |
77 | it { should permit_action :show }
78 | it { should permit_action :create }
79 | it { should permit_action :update }
80 | it { should permit_action :destroy }
81 | it { should permit_action :change_state }
82 | it { should permit_action :tag }
83 | end
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/ticketee/spec/rails_helper.rb:
--------------------------------------------------------------------------------
1 | # This file is copied to spec/ when you run 'rails generate rspec:install'
2 | ENV['RAILS_ENV'] ||= 'test'
3 | require 'spec_helper'
4 | require File.expand_path('../../config/environment', __FILE__)
5 | require 'rspec/rails'
6 | # Add additional requires below this line. Rails is not loaded until this point!
7 | require "pundit/rspec"
8 |
9 | # Requires supporting ruby files with custom matchers and macros, etc, in
10 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
11 | # run as spec files by default. This means that files in spec/support that end
12 | # in _spec.rb will both be required and run as specs, causing the specs to be
13 | # run twice. It is recommended that you do not name files matching this glob to
14 | # end with _spec.rb. You can configure this pattern with the --pattern
15 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
16 | #
17 | # The following line is provided for convenience purposes. It has the downside
18 | # of increasing the boot-up time by auto-requiring all files in the support
19 | # directory. Alternatively, in the individual `*_spec.rb` files, manually
20 | # require only the support files necessary.
21 | #
22 | Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
23 |
24 | # Checks for pending migrations before tests are run.
25 | # If you are not using ActiveRecord, you can remove this line.
26 | ActiveRecord::Migration.maintain_test_schema!
27 |
28 | RSpec.configure do |config|
29 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
30 | config.fixture_path = "#{::Rails.root}/spec/fixtures"
31 |
32 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
33 | # examples within a transaction, remove the following line or assign false
34 | # instead of true.
35 | config.use_transactional_fixtures = false
36 |
37 | # RSpec Rails can automatically mix in different behaviours to your tests
38 | # based on their file location, for example enabling you to call `get` and
39 | # `post` in specs under `spec/controllers`.
40 | #
41 | # You can disable this behaviour by removing the line below, and instead
42 | # explicitly tag your specs with their type, e.g.:
43 | #
44 | # RSpec.describe UsersController, :type => :controller do
45 | # # ...
46 | # end
47 | #
48 | # The different available types are documented in the features, such as in
49 | # https://relishapp.com/rspec/rspec-rails/docs
50 | config.infer_spec_type_from_file_location!
51 |
52 | config.include Warden::Test::Helpers, type: :feature
53 | config.after(type: :feature) { Warden.test_reset! }
54 | config.include Devise::TestHelpers, type: :controller
55 | end
56 |
--------------------------------------------------------------------------------
/ticketee/spec/requests/api/tickets_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "Tickets API" do
4 | let(:user) { FactoryGirl.create(:user) }
5 | let(:project) { FactoryGirl.create(:project) }
6 | let(:state) { FactoryGirl.create(:state, name: "Open") }
7 | let(:ticket) do
8 | FactoryGirl.create(:ticket, project: project, state: state)
9 | end
10 |
11 | before do
12 | assign_role!(user, :manager, project)
13 | user.generate_api_key
14 | end
15 |
16 | context "as an authenticated user" do
17 | let(:headers) do
18 | { "HTTP_AUTHORIZATION" => "Token token=#{user.api_key}" }
19 | end
20 |
21 | it "retrieves a ticket's information" do
22 | get api_project_ticket_path(project, ticket, format: :json),
23 | {}, headers
24 | expect(response.status).to eq 200
25 |
26 | json = TicketSerializer.new(ticket).to_json
27 | expect(response.body).to eq json
28 | end
29 |
30 | it "can create a ticket" do
31 | params = {
32 | format: "json",
33 | ticket: {
34 | name: "Test Ticket",
35 | description: "Just testing things out."
36 | }
37 | }
38 |
39 | post api_project_tickets_path(project, params), {}, headers
40 | expect(response.status).to eq 201
41 |
42 | json = TicketSerializer.new(Ticket.last).to_json
43 | expect(response.body).to eq json
44 | end
45 |
46 | it "cannot create a ticket with invalid data" do
47 | params = {
48 | format: "json",
49 | ticket: {
50 | name: "", description: ""
51 | }
52 | }
53 | post api_project_tickets_path(project, params), {}, headers
54 |
55 | expect(response.status).to eq 422
56 | json = {
57 | "errors" => [
58 | "Name can't be blank",
59 | "Description can't be blank",
60 | "Description is too short (minimum is 10 characters)"
61 | ]
62 | }
63 | expect(JSON.parse(response.body)).to eq json
64 | end
65 |
66 | context "without permission to view the project" do
67 | before do
68 | user.roles.delete_all
69 | end
70 |
71 | it "responds with a 403" do
72 | get api_project_ticket_path(project, ticket, format: "json"),
73 | {}, headers
74 | expect(response.status).to eq 403
75 | error = { "error" => "Unauthorized" }
76 | expect(JSON.parse(response.body)).to eq error
77 | end
78 | end
79 | end
80 |
81 | context "as an unauthenticated user" do
82 | it "responds with a 401" do
83 | get api_project_ticket_path(project, ticket, format: 'json')
84 | expect(response.status).to eq 401
85 | error = { "error" => "Unauthorized" }
86 | expect(JSON.parse(response.body)).to eq error
87 | end
88 | end
89 | end
90 |
--------------------------------------------------------------------------------
/ticketee/spec/requests/api/v2/tickets_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | describe API::V2::Tickets do
4 | let(:project) { FactoryGirl.create(:project) }
5 | let(:user) { FactoryGirl.create(:user) }
6 | let(:ticket) { FactoryGirl.create(:ticket, project: project) }
7 | let(:url) { "/api/v2/projects/#{project.id}/tickets/#{ticket.id}" }
8 | let(:headers) do
9 | { "HTTP_AUTHORIZATION" => "Token token=#{user.api_key}" }
10 | end
11 |
12 | before do
13 | assign_role!(user, :manager, project)
14 | user.generate_api_key
15 | end
16 |
17 | context "successful requests" do
18 | it "can view a ticket's details" do
19 | get url, {}, headers
20 |
21 | expect(response.status).to eq 200
22 | json = TicketSerializer.new(ticket).to_json
23 | expect(response.body).to eq json
24 | end
25 | end
26 |
27 | context "unsuccessful requests" do
28 | it "doesn't allow requests that don't pass through an API key" do
29 | get url
30 | expect(response.status).to eq 401
31 | expect(response.body).to include "Unauthenticated"
32 | end
33 |
34 | it "doesn't allow requests that pass an invalid API key" do
35 | get url, {}, { "HTTP_AUTHORIZATION" => "Token token=notavalidkey" }
36 | expect(response.status).to eql 401
37 | expect(response.body).to include "Unauthenticated"
38 | end
39 |
40 | it "doesn't allow access a ticket that the user doesn't have permission to" do
41 | project.roles.delete_all
42 | get url, {}, headers
43 | expect(response.status).to eq 404
44 | end
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/ticketee/spec/support/authorization_helpers.rb:
--------------------------------------------------------------------------------
1 | module AuthorizationHelpers
2 | def assign_role!(user, role, project)
3 | Role.where(user: user, project: project).delete_all
4 | Role.create!(user: user, role: role, project: project)
5 | end
6 | end
7 |
8 | RSpec.configure do |c|
9 | c.include AuthorizationHelpers
10 | end
11 |
--------------------------------------------------------------------------------
/ticketee/spec/support/capybara_finders.rb:
--------------------------------------------------------------------------------
1 | module CapybaraFinders
2 | def list_item(content)
3 | find("ul:not(.actions) li", text: content)
4 | end
5 |
6 | def tag(content)
7 | find("div.tag", text: content)
8 | end
9 | end
10 |
11 | RSpec.configure do |c|
12 | c.include CapybaraFinders, type: :feature
13 | end
14 |
--------------------------------------------------------------------------------
/ticketee/spec/support/capybara_matchers.rb:
--------------------------------------------------------------------------------
1 | module CapybaraMatchers
2 | def has_heading?(text)
3 | has_css?("h1, h2, h3, h4, h5, h6", text: text)
4 | end
5 | end
6 |
7 | Capybara::Session.include(CapybaraMatchers)
8 |
--------------------------------------------------------------------------------
/ticketee/spec/support/database_cleaning.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | config.before(:suite) do
3 | DatabaseCleaner.strategy = :deletion
4 | DatabaseCleaner.clean_with(:deletion)
5 | end
6 |
7 | config.before(:each) do
8 | DatabaseCleaner.start
9 | end
10 |
11 | config.after(:each) do
12 | DatabaseCleaner.clean
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/ticketee/spec/support/email_spec.rb:
--------------------------------------------------------------------------------
1 | require "email_spec"
2 |
3 | RSpec.configure do |config|
4 | config.include EmailSpec::Helpers
5 | config.include EmailSpec::Matchers
6 | end
7 |
--------------------------------------------------------------------------------
/ticketee/spec/support/pundit_matcher.rb:
--------------------------------------------------------------------------------
1 | RSpec::Matchers.define :permit_action do |action|
2 | match do |policy|
3 | policy.public_send("#{action}?")
4 | end
5 |
6 | failure_message do |policy|
7 | "#{policy.class} does not allow #{policy.user || "nil"} to " +
8 | "perform :#{action}? on #{policy.record}."
9 | end
10 |
11 | failure_message_when_negated do |policy|
12 | "#{policy.class} does not forbid #{policy.user || "nil"} from " +
13 | "performing :#{action}? on #{policy.record}."
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/ticketee/vendor/assets/javascripts/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/ticketee/vendor/assets/javascripts/.keep
--------------------------------------------------------------------------------
/ticketee/vendor/assets/stylesheets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rubysherpas/r4ia_examples/05d2d758da9f83caa0df51878448a30ef807513d/ticketee/vendor/assets/stylesheets/.keep
--------------------------------------------------------------------------------
/ticketee_api/tickets.rb:
--------------------------------------------------------------------------------
1 | require "httparty"
2 |
3 | token = "YOUR_TOKEN_GOES_HERE"
4 | url = "http://localhost:3000/api/projects/1/tickets/1.json"
5 |
6 | response = HTTParty.get(url,
7 | headers: {
8 | "Authorization" => "Token token=#{token}"
9 | }
10 | )
11 |
12 | puts response.parsed_response
13 |
--------------------------------------------------------------------------------
There are no comments for this ticket.
78 | <% end %> 79 |