├── log └── .keep ├── tmp └── .keep ├── vendor └── .keep ├── lib ├── assets │ └── .keep ├── tasks │ └── .keep └── templates │ └── erb │ └── scaffold │ └── _form.html.erb ├── public ├── favicon.ico ├── apple-touch-icon.png ├── apple-touch-icon-precomposed.png ├── robots.txt ├── 500.html ├── 422.html └── 404.html ├── test ├── helpers │ └── .keep ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── reply_test.rb │ ├── role_test.rb │ ├── user_test.rb │ ├── channel_test.rb │ └── discussion_test.rb ├── system │ ├── .keep │ ├── channels_test.rb │ └── discussions_test.rb ├── controllers │ ├── .keep │ ├── home_controller_test.rb │ ├── channels_controller_test.rb │ └── discussions_controller_test.rb ├── fixtures │ ├── .keep │ ├── files │ │ └── .keep │ ├── replies.yml │ ├── channels.yml │ ├── discussions.yml │ ├── roles.yml │ └── users.yml ├── integration │ └── .keep ├── application_system_test_case.rb └── test_helper.rb ├── app ├── assets │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── channels │ │ │ └── .keep │ │ ├── home.coffee │ │ ├── channels.coffee │ │ ├── discussions.coffee │ │ ├── cable.js │ │ └── application.js │ ├── config │ │ └── manifest.js │ └── stylesheets │ │ ├── home.scss │ │ ├── channels.scss │ │ ├── discussions.scss │ │ ├── application.scss │ │ └── _functions.scss ├── models │ ├── concerns │ │ └── .keep │ ├── application_record.rb │ ├── ability.rb │ ├── reply.rb │ ├── channel.rb │ ├── role.rb │ ├── discussion.rb │ └── user.rb ├── controllers │ ├── concerns │ │ └── .keep │ ├── home_controller.rb │ ├── application_controller.rb │ ├── registrations_controller.rb │ ├── replies_controller.rb │ ├── channels_controller.rb │ └── discussions_controller.rb ├── views │ ├── layouts │ │ ├── mailer.text.erb │ │ ├── mailer.html.erb │ │ └── application.html.erb │ ├── channels │ │ ├── show.json.jbuilder │ │ ├── index.json.jbuilder │ │ ├── edit.html.erb │ │ ├── _channel.json.jbuilder │ │ ├── new.html.erb │ │ ├── _form.html.erb │ │ ├── show.html.erb │ │ └── index.html.erb │ ├── discussions │ │ ├── show.json.jbuilder │ │ ├── index.json.jbuilder │ │ ├── index.html.erb │ │ ├── edit.html.erb │ │ ├── _discussion.json.jbuilder │ │ ├── new.html.erb │ │ ├── _sidebar.html.erb │ │ ├── _form.html.erb │ │ └── show.html.erb │ ├── home │ │ └── index.html.erb │ ├── replies │ │ ├── create.js.erb │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ └── _reply.html.erb │ ├── devise │ │ ├── mailer │ │ │ ├── password_change.html.erb │ │ │ ├── confirmation_instructions.html.erb │ │ │ ├── unlock_instructions.html.erb │ │ │ ├── email_changed.html.erb │ │ │ └── reset_password_instructions.html.erb │ │ ├── unlocks │ │ │ └── new.html.erb │ │ ├── confirmations │ │ │ └── new.html.erb │ │ ├── passwords │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ │ ├── sessions │ │ │ └── new.html.erb │ │ ├── shared │ │ │ └── _links.html.erb │ │ └── registrations │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ └── shared │ │ └── _discussions.html.erb ├── helpers │ ├── home_helper.rb │ ├── channels_helper.rb │ ├── discussions_helper.rb │ └── application_helper.rb ├── jobs │ └── application_job.rb ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb └── mailers │ └── application_mailer.rb ├── package.json ├── bin ├── bundle ├── rake ├── rails ├── yarn ├── spring ├── update └── setup ├── config ├── spring.rb ├── boot.rb ├── environment.rb ├── initializers │ ├── mime_types.rb │ ├── filter_parameter_logging.rb │ ├── application_controller_renderer.rb │ ├── cookies_serializer.rb │ ├── rolify.rb │ ├── backtrace_silencers.rb │ ├── wrap_parameters.rb │ ├── assets.rb │ ├── inflections.rb │ ├── friendly_id.rb │ ├── simple_form.rb │ └── devise.rb ├── cable.yml ├── routes.rb ├── application.rb ├── database.yml ├── locales │ ├── simple_form.en.yml │ ├── en.yml │ └── devise.en.yml ├── secrets.yml ├── environments │ ├── test.rb │ ├── development.rb │ └── production.rb └── puma.rb ├── config.ru ├── db ├── migrate │ ├── 20180202063241_add_slug_to_channels.rb │ ├── 20180202063252_add_slug_to_replies.rb │ ├── 20180202033447_add_username_to_users.rb │ ├── 20180202035622_add_user_id_to_replies.rb │ ├── 20180202063229_add_slug_to_discussions.rb │ ├── 20180202034853_add_user_id_to_discussions.rb │ ├── 20180202035605_add_discussion_id_to_replies.rb │ ├── 20180202035731_add_channel_id_to_discussions.rb │ ├── 20180202035801_add_discussion_id_to_channels.rb │ ├── 20180202035015_create_replies.rb │ ├── 20180202035406_create_channels.rb │ ├── 20180202034154_create_discussions.rb │ ├── 20180202061631_rolify_create_roles.rb │ ├── 20180202063043_create_friendly_id_slugs.rb │ └── 20180202032652_devise_create_users.rb ├── seeds.rb └── schema.rb ├── Rakefile ├── .gitignore ├── README.md ├── Guardfile ├── Gemfile └── Gemfile.lock /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/system/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/helpers/home_helper.rb: -------------------------------------------------------------------------------- 1 | module HomeHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/channels_helper.rb: -------------------------------------------------------------------------------- 1 | module ChannelsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/views/channels/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.partial! "channels/channel", channel: @channel 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discussions", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /app/views/channels/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.array! @channels, partial: 'channels/channel', as: :channel 2 | -------------------------------------------------------------------------------- /app/views/discussions/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.partial! "discussions/discussion", discussion: @discussion 2 | -------------------------------------------------------------------------------- /app/views/home/index.html.erb: -------------------------------------------------------------------------------- 1 |

Home#index

2 |

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

3 | -------------------------------------------------------------------------------- /app/views/replies/create.js.erb: -------------------------------------------------------------------------------- 1 | $("<%= escape_javascript(render(@reply)) %>").appendTo('#discussion-replies'); -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | def index 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/views/discussions/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.array! @discussions, partial: 'discussions/discussion', as: :discussion 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /app/views/channels/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Editing Channel

2 | 3 | <%= render 'form', channel: @channel %> 4 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/views/discussions/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render 'shared/discussions' %> 3 | <%= render 'sidebar' %> 4 |
-------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/views/discussions/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Editing Discussion

2 | 3 | <%= render 'form', discussion: @discussion %> 4 | 5 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | end 4 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /app/views/channels/_channel.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! channel, :id, :channel, :created_at, :updated_at 2 | json.url channel_url(channel, format: :json) 3 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /test/models/reply_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ReplyTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/role_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RoleTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/views/devise/mailer/password_change.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

We're contacting you to notify you that your password has been changed.

4 | -------------------------------------------------------------------------------- /test/models/channel_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ChannelTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/views/discussions/_discussion.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! discussion, :id, :title, :content, :created_at, :updated_at 2 | json.url discussion_url(discussion, format: :json) 3 | -------------------------------------------------------------------------------- /test/models/discussion_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DiscussionTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20180202063241_add_slug_to_channels.rb: -------------------------------------------------------------------------------- 1 | class AddSlugToChannels < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :channels, :slug, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180202063252_add_slug_to_replies.rb: -------------------------------------------------------------------------------- 1 | class AddSlugToReplies < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :replies, :slug, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/fixtures/replies.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | reply: MyText 5 | 6 | two: 7 | reply: MyText 8 | -------------------------------------------------------------------------------- /app/views/discussions/new.html.erb: -------------------------------------------------------------------------------- 1 |

New Discussion

2 | 3 | <%= render 'form', discussion: @discussion %> 4 | 5 | <%= link_to 'Back', discussions_path %> 6 | -------------------------------------------------------------------------------- /db/migrate/20180202033447_add_username_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddUsernameToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :username, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180202035622_add_user_id_to_replies.rb: -------------------------------------------------------------------------------- 1 | class AddUserIdToReplies < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :replies, :user_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180202063229_add_slug_to_discussions.rb: -------------------------------------------------------------------------------- 1 | class AddSlugToDiscussions < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :discussions, :slug, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/fixtures/channels.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | channel: MyString 5 | 6 | two: 7 | channel: MyString 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20180202034853_add_user_id_to_discussions.rb: -------------------------------------------------------------------------------- 1 | class AddUserIdToDiscussions < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :discussions, :user_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180202035605_add_discussion_id_to_replies.rb: -------------------------------------------------------------------------------- 1 | class AddDiscussionIdToReplies < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :replies, :discussion_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180202035731_add_channel_id_to_discussions.rb: -------------------------------------------------------------------------------- 1 | class AddChannelIdToDiscussions < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :discussions, :channel_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180202035801_add_discussion_id_to_channels.rb: -------------------------------------------------------------------------------- 1 | class AddDiscussionIdToChannels < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :channels, :discussion_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/home.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the home controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/channels.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Channels controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/views/channels/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Create a new channel

4 | <%= render 'form', channel: @channel %> 5 |
6 |
-------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | channel_prefix: discussions_production 11 | -------------------------------------------------------------------------------- /app/assets/stylesheets/discussions.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Discussions controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /db/migrate/20180202035015_create_replies.rb: -------------------------------------------------------------------------------- 1 | class CreateReplies < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :replies do |t| 4 | t.text :reply 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/fixtures/discussions.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | title: MyString 5 | content: MyText 6 | 7 | two: 8 | title: MyString 9 | content: MyText 10 | -------------------------------------------------------------------------------- /db/migrate/20180202035406_create_channels.rb: -------------------------------------------------------------------------------- 1 | class CreateChannels < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :channels do |t| 4 | t.string :channel 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/assets/javascripts/home.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels.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 | -------------------------------------------------------------------------------- /test/controllers/home_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class HomeControllerTest < ActionDispatch::IntegrationTest 4 | test "should get index" do 5 | get home_index_url 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /app/assets/javascripts/discussions.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 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | resources :channels 3 | resources :discussions do 4 | resources :replies 5 | end 6 | 7 | root 'discussions#index' 8 | 9 | devise_for :users, controllers: { registrations: 'registrations' } 10 | end 11 | -------------------------------------------------------------------------------- /test/system/channels_test.rb: -------------------------------------------------------------------------------- 1 | require "application_system_test_case" 2 | 3 | class ChannelsTest < ApplicationSystemTestCase 4 | # test "visiting the index" do 5 | # visit channels_url 6 | # 7 | # assert_selector "h1", text: "Channel" 8 | # end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20180202034154_create_discussions.rb: -------------------------------------------------------------------------------- 1 | class CreateDiscussions < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :discussions do |t| 4 | t.string :title 5 | t.text :content 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/system/discussions_test.rb: -------------------------------------------------------------------------------- 1 | require "application_system_test_case" 2 | 3 | class DiscussionsTest < ApplicationSystemTestCase 4 | # test "visiting the index" do 5 | # visit discussions_url 6 | # 7 | # assert_selector "h1", text: "Discussion" 8 | # end 9 | end 10 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /app/helpers/discussions_helper.rb: -------------------------------------------------------------------------------- 1 | module DiscussionsHelper 2 | 3 | def discussion_author(discussion) 4 | user_signed_in? && current_user.id == discussion.user_id 5 | end 6 | 7 | def reply_author(reply) 8 | user_signed_in? && current_user.id == reply.user_id 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /app/models/ability.rb: -------------------------------------------------------------------------------- 1 | class Ability 2 | include CanCan::Ability 3 | 4 | def initialize(user) 5 | user ||= User.new # guest user (not logged in) 6 | if user.has_role? :admin 7 | can :manage, :all 8 | else 9 | can :read, :all 10 | end 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/models/reply.rb: -------------------------------------------------------------------------------- 1 | class Reply < ApplicationRecord 2 | belongs_to :discussion 3 | belongs_to :user 4 | validates :reply, presence: true 5 | 6 | extend FriendlyId 7 | friendly_id :reply, use: [:slugged, :finders] 8 | 9 | def should_generate_new_friendly_id? 10 | reply_changed? 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /app/models/channel.rb: -------------------------------------------------------------------------------- 1 | class Channel < ApplicationRecord 2 | has_many :discussions 3 | has_many :users, through: :discussions 4 | resourcify 5 | 6 | extend FriendlyId 7 | friendly_id :channel, use: [:slugged, :finders] 8 | 9 | def should_generate_new_friendly_id? 10 | channel_changed? 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../config/environment', __FILE__) 2 | require 'rails/test_help' 3 | 4 | class ActiveSupport::TestCase 5 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 6 | fixtures :all 7 | 8 | # Add more helper methods to be used by all tests here... 9 | end 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/views/devise/mailer/email_changed.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @email %>!

2 | 3 | <% if @resource.try(:unconfirmed_email?) %> 4 |

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

5 | <% else %> 6 |

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

7 | <% end %> 8 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | VENDOR_PATH = File.expand_path('..', __dir__) 3 | Dir.chdir(VENDOR_PATH) do 4 | begin 5 | exec "yarnpkg #{ARGV.join(" ")}" 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/channels/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= simple_form_for(@channel) do |f| %> 2 | <%= f.error_notification %> 3 | 4 |
5 |
6 | <%= f.input :channel, input_html: { class: 'input' }, wrapper: false, label_html: { class: 'label' } %> 7 |
8 |
9 | 10 | <%= f.button :submit, class: 'button is-info' %> 11 | <% end %> 12 | -------------------------------------------------------------------------------- /app/models/role.rb: -------------------------------------------------------------------------------- 1 | class Role < ApplicationRecord 2 | has_and_belongs_to_many :users, :join_table => :users_roles 3 | 4 | 5 | belongs_to :resource, 6 | :polymorphic => true, 7 | :optional => true 8 | 9 | 10 | validates :resource_type, 11 | :inclusion => { :in => Rolify.resource_types }, 12 | :allow_nil => true 13 | 14 | scopify 15 | end 16 | -------------------------------------------------------------------------------- /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 rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /app/models/discussion.rb: -------------------------------------------------------------------------------- 1 | class Discussion < ApplicationRecord 2 | belongs_to :channel 3 | belongs_to :user 4 | has_many :replies, dependent: :destroy 5 | 6 | validates :title, :content, presence: true 7 | resourcify 8 | 9 | extend FriendlyId 10 | friendly_id :title, use: [:slugged, :finders] 11 | 12 | def should_generate_new_friendly_id? 13 | title_changed? 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/views/replies/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= simple_form_for([@discussion, @discussion.replies.build], remote: true) do |f| %> 2 | 3 |
4 |
5 | <%= f.input :reply, input_html: { class: 'textarea' }, wrapper: false, label_html: { class: 'label' } %> 6 |
7 |
8 | 9 | <%= f.button :submit, 'Leave a reply', class:'button is-info' %> 10 | 11 | <% end %> -------------------------------------------------------------------------------- /test/fixtures/roles.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | one: {} 8 | # column: value 9 | # 10 | two: {} 11 | # column: value 12 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | one: {} 8 | # column: value 9 | # 10 | two: {} 11 | # column: value 12 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | rolify 3 | # Include default devise modules. Others available are: 4 | # :confirmable, :lockable, :timeoutable and :omniauthable 5 | devise :database_authenticatable, :registerable, 6 | :recoverable, :rememberable, :trackable, :validatable 7 | 8 | has_many :discussions, dependent: :destroy 9 | has_many :channels, through: :discussions 10 | end 11 | -------------------------------------------------------------------------------- /app/views/replies/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Editing Reply

2 | 3 | <%= simple_form_for([@discussion, @reply]) do |f| %> 4 |
5 |
6 | <%= f.input :reply, input_html: { class: 'textarea' }, wrapper: false, label_html: { class: 'label' } %> 7 |
8 |
9 | 10 | <%= f.button :submit, 'Update reply', class:'button is-info' %> 11 | <% end %> -------------------------------------------------------------------------------- /app/controllers/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class RegistrationsController < Devise::RegistrationsController 2 | 3 | private 4 | 5 | def sign_up_params 6 | params.require(:user).permit(:username, :email, :password, :password_confirmation) 7 | end 8 | 9 | def account_update_params 10 | params.require(:user).permit(:username, :email, :password, :password_confirmation, :current_password) 11 | end 12 | 13 | end -------------------------------------------------------------------------------- /app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command. 3 | // 4 | //= require action_cable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /app/views/discussions/_sidebar.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Channels

3 | 4 | 9 | 10 |
11 | 12 | <% if has_role?(:admin) %> 13 | <%= link_to 'New Channel', new_channel_path, class:'button is-info' %> 14 | <% end %> 15 | 16 |
-------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/initializers/rolify.rb: -------------------------------------------------------------------------------- 1 | Rolify.configure do |config| 2 | # By default ORM adapter is ActiveRecord. uncomment to use mongoid 3 | # config.use_mongoid 4 | 5 | # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false 6 | # config.use_dynamic_shortcuts 7 | 8 | # Configuration to remove roles from database once the last resource is removed. Default is: true 9 | # config.remove_role_if_empty = false 10 | end 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/views/channels/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

<%= @channel.channel %>

4 |
5 |
6 | <% if has_role?(:admin) %> 7 | <%= link_to 'Edit Channel', edit_channel_path(@channel), class:"button" %> 8 | <% end %> 9 |
10 |
11 |
12 | 13 |
14 | <%= render 'shared/discussions' %> 15 | <%= render 'discussions/sidebar' %> 16 |
17 | 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /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 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20180202061631_rolify_create_roles.rb: -------------------------------------------------------------------------------- 1 | class RolifyCreateRoles < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table(:roles) do |t| 4 | t.string :name 5 | t.references :resource, :polymorphic => true 6 | 7 | t.timestamps 8 | end 9 | 10 | create_table(:users_roles, :id => false) do |t| 11 | t.references :user 12 | t.references :role 13 | end 14 | 15 | add_index(:roles, :name) 16 | add_index(:roles, [ :name, :resource_type, :resource_id ]) 17 | add_index(:users_roles, [ :user_id, :role_id ]) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /.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 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | /node_modules 21 | /yarn-error.log 22 | 23 | .byebug_history 24 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 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 Discussions 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 5.1 13 | 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration should go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded. 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /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 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /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: <%= ENV.fetch("RAILS_MAX_THREADS") { 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20180202063043_create_friendly_id_slugs.rb: -------------------------------------------------------------------------------- 1 | class CreateFriendlyIdSlugs < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :friendly_id_slugs do |t| 4 | t.string :slug, :null => false 5 | t.integer :sluggable_id, :null => false 6 | t.string :sluggable_type, :limit => 50 7 | t.string :scope 8 | t.datetime :created_at 9 | end 10 | add_index :friendly_id_slugs, :sluggable_id 11 | add_index :friendly_id_slugs, [:slug, :sluggable_type], length: { slug: 140, sluggable_type: 50 } 12 | add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], length: { slug: 70, sluggable_type: 50, scope: 70 }, unique: true 13 | add_index :friendly_id_slugs, :sluggable_type 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /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, or any plugin's 5 | // 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. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require rails-ujs 15 | //= require turbolinks 16 | //= require_tree . 17 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 |

Forgot your password?

7 | 8 | <%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> 9 | <%= f.error_notification %> 10 | 11 |
12 |
13 | <%= f.input :email, required: true, autofocus: true, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" } %> 14 |
15 |
16 | 17 | <%= f.button :submit, "Send me reset password instructions", class:"button is-info" %> 18 | 19 | <% end %> 20 |
21 | <%= render "devise/shared/links" %> 22 | 23 |
24 |
25 |
26 |
-------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | require 'redcarpet/render_strip' 3 | 4 | def has_role?(role) 5 | current_user && current_user.has_role?(role) 6 | end 7 | 8 | class CodeRayify < Redcarpet::Render::HTML 9 | def block_code(code, language) 10 | CodeRay.scan(code,language).div 11 | end 12 | end 13 | 14 | def markdown(text) 15 | coderayified = CodeRayify.new(:filter_html => true, :hard_wrap => true) 16 | options = { 17 | fenced_code_blocks: true, 18 | no_intra_emphasis: true, 19 | autolink: true, 20 | lax_html_blocks: true 21 | } 22 | markdown_to_html = Redcarpet::Markdown.new(coderayified, options) 23 | markdown_to_html.render(text).html_safe 24 | end 25 | 26 | def strip_markdown(text) 27 | markdown_to_plain_text = Redcarpet::Markdown.new(Redcarpet::Render::StripDown) 28 | markdown_to_plain_text.render(text).html_safe 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /app/views/discussions/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= simple_form_for(@discussion) do |f| %> 2 | <%= f.error_notification %> 3 | 4 |
5 |
6 | <%= f.input :title, required: true, input_html: { class: 'input' }, wrapper: false, label_html: { class: "label" } %> 7 |
8 |
9 | 10 |
11 |
12 | <%= f.input :content, required: true, input_html: { class: 'textarea' }, wrapper: false, label_html: { class: "label" } %> 13 |
14 |
15 | 16 |
17 | 18 |
19 | 20 | <%= f.input_field :channel_id, collection:@channels.map { |c| [c.channel, c.id] }, prompt: "Select channel" %> 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 |
29 |
30 | <%= f.button :submit, class:"button is-info" %> 31 |
32 |
33 | 34 | <% end %> 35 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # Install JavaScript dependencies if using Yarn 22 | # system('bin/yarn') 23 | 24 | 25 | # puts "\n== Copying sample files ==" 26 | # unless File.exist?('config/database.yml') 27 | # cp 'config/database.yml.sample', 'config/database.yml' 28 | # end 29 | 30 | puts "\n== Preparing database ==" 31 | system! 'bin/rails db:setup' 32 | 33 | puts "\n== Removing old logs and tempfiles ==" 34 | system! 'bin/rails log:clear tmp:clear' 35 | 36 | puts "\n== Restarting application server ==" 37 | system! 'bin/rails restart' 38 | end 39 | -------------------------------------------------------------------------------- /app/views/channels/index.html.erb: -------------------------------------------------------------------------------- 1 |

Channels

2 | 3 | <% if has_role?(:admin) %> 4 |
5 |
6 |
7 | <%= link_to 'New Channel', new_channel_path, class:'button is-dark' %> 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | <% @channels.each do |channel| %> 20 | 21 | 22 | 29 | 30 | <% end %> 31 | 32 | 33 |
Channel
<%= link_to channel.channel, channel %> 23 |
24 | <%= link_to 'View Channel', channel, class:'button' %> 25 | <%= link_to 'Edit Channel', edit_channel_path(channel), class:'button' %> 26 | <%= link_to 'Delete', channel, class:'button is-danger', method: :delete, data: { confirm: "Are you sure you want to delete this channel? " } %> 27 |
28 |
34 | 35 | <% else %> 36 |

Sorry! You don't have access this page.

37 | <% end %> -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Log in

6 | <%= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> 7 | 8 |
9 |
10 | <%= f.input :email, required: false, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" } %> 11 |
12 |
13 | 14 |
15 |
16 | <%= f.input :password, required: false, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" } %> 17 |
18 |
19 | 20 |
21 |
22 | <%= f.input :remember_me, wrapper: false, as: :boolean if devise_mapping.rememberable? %> 23 |
24 |
25 | 26 | <%= f.button :submit, "Log in", class:"button is-info is-medium" %> 27 | <% end %> 28 |
29 | <%= render "devise/shared/links" %> 30 |
31 |
32 |
33 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Let's Build: With Ruby on Rails - Discussion Forum](https://i.imgur.com/GA9Azed.jpg) 2 | 3 | # Let's Build: With Ruby on Rails - Discussion Forum 4 | 5 | Continuing my long stent of Ruby on Rails builds is yet another build which focuses more on relationships in a real-world discussion forum application. 6 | 7 | The application on the outside looks a little bleak but I promise there is a lot going on under the hood of which you could extend to be something very feature rich. 8 | 9 | Follow along with more videos on [YouTube](https://www.youtube.com/playlist?list=PL01nNIgQ4uxNkDZNMON-TrzDVNIk3cOz4) or my [original blog post](https://web-crunch.com/lets-build-with-ruby-on-rails-discussion-forum) and be sure to download the source code on this repo to both explore the application and refer to it as you build alongside me. 10 | 11 | If you enjoy these Let's Builds I can't thank you enough for tuning in. I hate asking for anything in return so only if you insist you can [buy my a coffee](https://buymeacoff.ee/webcrunch) to keep these going. Also, be sure to subscribe to my [YouTube channel](https://youtube.com/c/webcrunch) and [newsletter](https://web-crunch.com/subscribe) for automatic updates. 12 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 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, or any plugin's 6 | * 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 other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | 17 | @import "bulma"; 18 | @import "functions"; 19 | 20 | .image { 21 | border-radius: 50%; 22 | img { 23 | border-radius: 50%; 24 | } 25 | } 26 | 27 | .notification:not(:last-child) { 28 | margin-bottom: 0; 29 | } 30 | 31 | .textarea { 32 | height: 250px; 33 | font-family: Monaco, san-serif; 34 | font-size: .9rem; 35 | padding: 1rem; 36 | } 37 | 38 | .discussion-title { 39 | margin-bottom: 0; 40 | } -------------------------------------------------------------------------------- /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 #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %>
24 | <% end -%> 25 | <% end -%> 26 | -------------------------------------------------------------------------------- /test/controllers/channels_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ChannelsControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @channel = channels(:one) 6 | end 7 | 8 | test "should get index" do 9 | get channels_url 10 | assert_response :success 11 | end 12 | 13 | test "should get new" do 14 | get new_channel_url 15 | assert_response :success 16 | end 17 | 18 | test "should create channel" do 19 | assert_difference('Channel.count') do 20 | post channels_url, params: { channel: { channel: @channel.channel } } 21 | end 22 | 23 | assert_redirected_to channel_url(Channel.last) 24 | end 25 | 26 | test "should show channel" do 27 | get channel_url(@channel) 28 | assert_response :success 29 | end 30 | 31 | test "should get edit" do 32 | get edit_channel_url(@channel) 33 | assert_response :success 34 | end 35 | 36 | test "should update channel" do 37 | patch channel_url(@channel), params: { channel: { channel: @channel.channel } } 38 | assert_redirected_to channel_url(@channel) 39 | end 40 | 41 | test "should destroy channel" do 42 | assert_difference('Channel.count', -1) do 43 | delete channel_url(@channel) 44 | end 45 | 46 | assert_redirected_to channels_url 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /app/views/replies/_reply.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | <%= gravatar_image_tag(reply.user.email, class: 'border-radius-50', size: 48, alt: reply.user.username) %> 6 |
7 |
8 |
9 |
10 |

11 | <%= reply.user.username %> <%= time_ago_in_words(reply.created_at) %> 12 |
13 | <%= markdown(reply.reply) %> 14 |

15 |
16 | <% if reply_author(reply) || has_role?(:admin) %> 17 | 32 | <% end %> 33 |
34 |
35 |
-------------------------------------------------------------------------------- /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 `rails 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 | # Shared secrets are available across all environments. 14 | 15 | # shared: 16 | # api_key: a1B2c3D4e5F6 17 | 18 | # Environmental secrets are only available for that specific environment. 19 | 20 | development: 21 | secret_key_base: 923c607a51f2efb2fbc9ca9d09b459e7e49245ef8e2169f21de8d0fe427f9732367fa50d30eba0918e1cc6e230e5615143752e827be697774b46b53aaadb6dfe 22 | 23 | test: 24 | secret_key_base: 2de118f012988fd5f7746a5a87af0c0af1aa902eb3639835c5eb78de6c52ab0a88949300799a3fedff120fe1b000078d155ec62d2ab6f8bc73d0457cfd7bdfc8 25 | 26 | # Do not keep production secrets in the unencrypted secrets file. 27 | # Instead, either read values from the environment. 28 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets 29 | # and move the `production:` environment over there. 30 | 31 | production: 32 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 33 | -------------------------------------------------------------------------------- /test/controllers/discussions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DiscussionsControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @discussion = discussions(:one) 6 | end 7 | 8 | test "should get index" do 9 | get discussions_url 10 | assert_response :success 11 | end 12 | 13 | test "should get new" do 14 | get new_discussion_url 15 | assert_response :success 16 | end 17 | 18 | test "should create discussion" do 19 | assert_difference('Discussion.count') do 20 | post discussions_url, params: { discussion: { content: @discussion.content, title: @discussion.title } } 21 | end 22 | 23 | assert_redirected_to discussion_url(Discussion.last) 24 | end 25 | 26 | test "should show discussion" do 27 | get discussion_url(@discussion) 28 | assert_response :success 29 | end 30 | 31 | test "should get edit" do 32 | get edit_discussion_url(@discussion) 33 | assert_response :success 34 | end 35 | 36 | test "should update discussion" do 37 | patch discussion_url(@discussion), params: { discussion: { content: @discussion.content, title: @discussion.title } } 38 | assert_redirected_to discussion_url(@discussion) 39 | end 40 | 41 | test "should destroy discussion" do 42 | assert_difference('Discussion.count', -1) do 43 | delete discussion_url(@discussion) 44 | end 45 | 46 | assert_redirected_to discussions_url 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 |

Change your password

7 | 8 | <%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> 9 | <%= f.error_notification %> 10 | 11 | <%= f.input :reset_password_token, as: :hidden %> 12 | <%= f.full_error :reset_password_token %> 13 | 14 |
15 |
16 | <%= f.input :password, label: "New password", required: true, autofocus: true, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" }, hint: ("#{@minimum_password_length} characters minimum" if @minimum_password_length) %> 17 |
18 |
19 | 20 |
21 |
22 | <%= f.input :password_confirmation, label: "Confirm your new password", input_html: { class: "input"}, wrapper: false, label_html: { class: "label" }, required: true %> 23 |
24 |
25 | 26 |
27 | <%= f.button :submit, "Change my password" %> 28 |
29 | <% end %> 30 | 31 | <%= render "devise/shared/links" %> 32 | 33 |
34 |
35 |
36 |
-------------------------------------------------------------------------------- /app/views/discussions/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

<%= @discussion.title %>

4 |

by <%= @discussion.user.username %> in <%= link_to @discussion.channel.channel, @discussion.channel %>

5 |
6 |
7 |
8 | <% if discussion_author(@discussion) %> 9 |
10 | <%= link_to 'Edit Discussion', edit_discussion_path(@discussion), class:'button'%> 11 | <%= link_to 'Delete', discussion_path(@discussion), method: :delete, data: { confirm: "Delete discussion?" }, class:'button' %> 12 |
13 | <% end %> 14 |
15 |
16 |
<%= markdown(@discussion.content) %>
17 | 18 |

<%= @discussion.replies.count %> Replies

19 | 20 |
21 | <%= render @discussion.replies %> 22 |
23 | 24 |
25 | 26 |

Leave a reply

27 | <% if user_signed_in? %> 28 | <%= render 'replies/form' %> 29 | <% else %> 30 |

To reply you need to <%= link_to 'login', new_user_session_path %>. Don't have an account? 31 | <%= link_to 'Sign up', new_user_registration_path %> for one.

32 | <% end %> 33 | 34 |
35 | 36 | <%= render 'sidebar' %> 37 | 38 |
-------------------------------------------------------------------------------- /app/views/shared/_discussions.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Latest Discussions

3 |
4 | <% @discussions.each do |discussion| %> 5 |
6 |
7 | <%= gravatar_image_tag(discussion.user.email, class:'border-radius-50', size: 48, alt: discussion.user.username) %> 8 |
9 |
10 | <%= link_to discussion do %> 11 |

<%= discussion.title %>

12 | <% end %> 13 |
14 | <%= truncate(strip_markdown(discussion.content), length: 140) %> 15 |

Posted <%= time_ago_in_words(discussion.created_at) %> ago in 16 | <% if discussion.channel %> 17 | <%= link_to discussion.channel.channel, discussion.channel %> 18 | <% end %> 19 | by <%= discussion.user.username %> 20 | 21 | 22 |

23 |
24 |
25 | 26 | <% if discussion_author(discussion) || has_role?(:admin) %> 27 |
28 | <%= link_to 'Edit', edit_discussion_path(discussion), class:'button' %> 29 | <%= link_to 'Delete', discussion, method: :delete, data: { confirm: 'Are you sure?' }, class: "button" %> 30 |
31 | <% end %> 32 | 33 |
34 | <% end %> 35 |
36 |
-------------------------------------------------------------------------------- /db/migrate/20180202032652_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class DeviseCreateUsers < ActiveRecord::Migration[5.1] 4 | def change 5 | create_table :users do |t| 6 | ## Database authenticatable 7 | t.string :email, null: false, default: "" 8 | t.string :encrypted_password, null: false, default: "" 9 | 10 | ## Recoverable 11 | t.string :reset_password_token 12 | t.datetime :reset_password_sent_at 13 | 14 | ## Rememberable 15 | t.datetime :remember_created_at 16 | 17 | ## Trackable 18 | t.integer :sign_in_count, default: 0, null: false 19 | t.datetime :current_sign_in_at 20 | t.datetime :last_sign_in_at 21 | t.string :current_sign_in_ip 22 | t.string :last_sign_in_ip 23 | 24 | ## Confirmable 25 | # t.string :confirmation_token 26 | # t.datetime :confirmed_at 27 | # t.datetime :confirmation_sent_at 28 | # t.string :unconfirmed_email # Only if using reconfirmable 29 | 30 | ## Lockable 31 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 32 | # t.string :unlock_token # Only if unlock strategy is :email or :both 33 | # t.datetime :locked_at 34 | 35 | 36 | t.timestamps null: false 37 | end 38 | 39 | add_index :users, :email, unique: true 40 | add_index :users, :reset_password_token, unique: true 41 | # add_index :users, :confirmation_token, unique: true 42 | # add_index :users, :unlock_token, unique: true 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Sign up

6 | 7 | <%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> 8 | <%= f.error_notification %> 9 | 10 | 11 | 12 |
13 |
14 | <%= f.input :username, required: true, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" } %> 15 |
16 |
17 | 18 | 19 |
20 |
21 | <%= f.input :email, required: true, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" } %> 22 |
23 |
24 | 25 |
26 |
27 | <%= f.input :password, required: true, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" }, hint: ("#{@minimum_password_length} characters minimum" if @minimum_password_length) %> 28 |
29 |
30 | 31 |
32 |
33 | <%= f.input :password_confirmation, required: true, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" } %> 34 |
35 |
36 | 37 | <%= f.button :submit, "Sign up", class:"button is-info is-medium" %> 38 | 39 | <% end %> 40 |
41 | <%= render "devise/shared/links" %> 42 |
43 |
44 |
45 |
-------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # A sample Guardfile 2 | # More info at https://github.com/guard/guard#readme 3 | 4 | ## Uncomment and set this to only include directories you want to watch 5 | # directories %w(app lib config test spec features) \ 6 | # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")} 7 | 8 | ## Note: if you are using the `directories` clause above and you are not 9 | ## watching the project directory ('.'), then you will want to move 10 | ## the Guardfile to a watched dir and symlink it back, e.g. 11 | # 12 | # $ mkdir config 13 | # $ mv Guardfile config/ 14 | # $ ln -s config/Guardfile . 15 | # 16 | # and, you'll have to watch "config/Guardfile" instead of "Guardfile" 17 | 18 | guard 'livereload' do 19 | extensions = { 20 | css: :css, 21 | scss: :css, 22 | sass: :css, 23 | js: :js, 24 | coffee: :js, 25 | html: :html, 26 | png: :png, 27 | gif: :gif, 28 | jpg: :jpg, 29 | jpeg: :jpeg, 30 | # less: :less, # uncomment if you want LESS stylesheets done in browser 31 | } 32 | 33 | rails_view_exts = %w(erb haml slim) 34 | 35 | # file types LiveReload may optimize refresh for 36 | compiled_exts = extensions.values.uniq 37 | watch(%r{public/.+\.(#{compiled_exts * '|'})}) 38 | 39 | extensions.each do |ext, type| 40 | watch(%r{ 41 | (?:app|vendor) 42 | (?:/assets/\w+/(?[^.]+) # path+base without extension 43 | (?\.#{ext})) # matching extension (must be first encountered) 44 | (?:\.\w+|$) # other extensions 45 | }x) do |m| 46 | path = m[1] 47 | "/assets/#{path}.#{type}" 48 | end 49 | end 50 | 51 | # file needing a full reload of the page anyway 52 | watch(%r{app/views/.+\.(#{rails_view_exts * '|'})$}) 53 | watch(%r{app/helpers/.+\.rb}) 54 | watch(%r{config/locales/.+\.yml}) 55 | end 56 | -------------------------------------------------------------------------------- /app/controllers/replies_controller.rb: -------------------------------------------------------------------------------- 1 | class RepliesController < ApplicationController 2 | before_action :authenticate_user! 3 | before_action :set_reply, only: [:edit, :update, :show, :destroy] 4 | before_action :set_discussion, only: [:create, :edit, :show, :update, :destroy] 5 | 6 | def create 7 | @reply = @discussion.replies.create(params[:reply].permit(:reply, :discussion_id)) 8 | @reply.user_id = current_user.id 9 | 10 | respond_to do |format| 11 | if @reply.save 12 | format.html { redirect_to discussion_path(@discussion) } 13 | format.js # renders create.js.erb 14 | else 15 | format.html { redirect_to discussion_path(@discussion), notice: "Reply did not save. Please try again."} 16 | format.js 17 | end 18 | end 19 | end 20 | 21 | def new 22 | end 23 | 24 | 25 | def destroy 26 | @reply = @discussion.replies.find(params[:id]) 27 | @reply.destroy 28 | redirect_to discussion_path(@discussion) 29 | end 30 | 31 | def edit 32 | @discussion = Discussion.find(params[:discussion_id]) 33 | @reply = @discussion.replies.find(params[:id]) 34 | end 35 | 36 | def update 37 | @reply = @discussion.replies.find(params[:id]) 38 | respond_to do |format| 39 | if @reply.update(reply_params) 40 | format.html { redirect_to discussion_path(@discussion), notice: 'Reply was successfully updated.' } 41 | else 42 | format.html { render :edit } 43 | format.json { render json: @reply.errors, status: :unprocessable_entity } 44 | end 45 | end 46 | end 47 | 48 | def show 49 | end 50 | 51 | private 52 | 53 | def set_discussion 54 | @discussion = Discussion.find(params[:discussion_id]) 55 | end 56 | 57 | def set_reply 58 | @reply = Reply.find(params[:id]) 59 | end 60 | 61 | def reply_params 62 | params.require(:reply).permit(:reply) 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | # Debug mode disables concatenation and preprocessing of assets. 41 | # This option may cause significant delays in view rendering with a large 42 | # number of complex assets. 43 | config.assets.debug = true 44 | 45 | # Suppress logger output for asset requests. 46 | config.assets.quiet = true 47 | 48 | # Raises error for missing translations 49 | # config.action_view.raise_on_missing_translations = true 50 | 51 | # Use an evented file watcher to asynchronously detect changes in source code, 52 | # routes, locales, etc. This feature depends on the listen gem. 53 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 54 | 55 | # Devise mailer 56 | config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } 57 | end 58 | -------------------------------------------------------------------------------- /app/controllers/channels_controller.rb: -------------------------------------------------------------------------------- 1 | class ChannelsController < ApplicationController 2 | before_action :set_channel, only: [:show, :edit, :update, :destroy] 3 | 4 | # GET /channels 5 | # GET /channels.json 6 | def index 7 | @channels = Channel.all 8 | @discussions = Discussion.all.order('created_at desc') 9 | end 10 | 11 | # GET /channels/1 12 | # GET /channels/1.json 13 | def show 14 | @discussions = Discussion.where('channel_id = ?', @channel.id) 15 | @channels = Channel.all 16 | end 17 | 18 | # GET /channels/new 19 | def new 20 | @channel = Channel.new 21 | end 22 | 23 | # GET /channels/1/edit 24 | def edit 25 | end 26 | 27 | # POST /channels 28 | # POST /channels.json 29 | def create 30 | @channel = Channel.new(channel_params) 31 | 32 | respond_to do |format| 33 | if @channel.save 34 | format.html { redirect_to channels_path, notice: 'Channel was successfully created.' } 35 | format.json { render :show, status: :created, location: @channel } 36 | else 37 | format.html { render :new } 38 | format.json { render json: @channel.errors, status: :unprocessable_entity } 39 | end 40 | end 41 | end 42 | 43 | # PATCH/PUT /channels/1 44 | # PATCH/PUT /channels/1.json 45 | def update 46 | respond_to do |format| 47 | if @channel.update(channel_params) 48 | format.html { redirect_to channels_path, notice: 'Channel was successfully updated.' } 49 | format.json { render :show, status: :ok, location: @channel } 50 | else 51 | format.html { render :edit } 52 | format.json { render json: @channel.errors, status: :unprocessable_entity } 53 | end 54 | end 55 | end 56 | 57 | # DELETE /channels/1 58 | # DELETE /channels/1.json 59 | def destroy 60 | @channel.destroy 61 | respond_to do |format| 62 | format.html { redirect_to channels_url, notice: 'Channel was successfully destroyed.' } 63 | format.json { head :no_content } 64 | end 65 | end 66 | 67 | private 68 | # Use callbacks to share common setup or constraints between actions. 69 | def set_channel 70 | @channel = Channel.find(params[:id]) 71 | end 72 | 73 | # Never trust parameters from the scary internet, only allow the white list through. 74 | def channel_params 75 | params.require(:channel).permit(:channel) 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # If you are preloading your application and using Active Record, it's 36 | # recommended that you close any connections to the database before workers 37 | # are forked to prevent connection leakage. 38 | # 39 | # before_fork do 40 | # ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) 41 | # end 42 | 43 | # The code in the `on_worker_boot` will be called if you are using 44 | # clustered mode by specifying a number of `workers`. After each worker 45 | # process is booted, this block will be run. If you are using the `preload_app!` 46 | # option, you will want to use this block to reconnect to any threads 47 | # or connections that may have been created at application boot, as Ruby 48 | # cannot share connections between processes. 49 | # 50 | # on_worker_boot do 51 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 52 | # end 53 | # 54 | 55 | # Allow puma to be restarted by `rails restart` command. 56 | plugin :tmp_restart 57 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | git_source(:github) do |repo_name| 4 | repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") 5 | "https://github.com/#{repo_name}.git" 6 | end 7 | 8 | 9 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 10 | gem 'rails', '~> 5.1.4' 11 | # Use sqlite3 as the database for Active Record 12 | gem 'sqlite3' 13 | # Use Puma as the app server 14 | gem 'puma', '~> 3.7' 15 | # Use SCSS for stylesheets 16 | gem 'sass-rails', '~> 5.0' 17 | # Use Uglifier as compressor for JavaScript assets 18 | gem 'uglifier', '>= 1.3.0' 19 | # See https://github.com/rails/execjs#readme for more supported runtimes 20 | # gem 'therubyracer', platforms: :ruby 21 | 22 | # Use CoffeeScript for .coffee assets and views 23 | gem 'coffee-rails', '~> 4.2' 24 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks 25 | gem 'turbolinks', '~> 5' 26 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 27 | gem 'jbuilder', '~> 2.5' 28 | # Use Redis adapter to run Action Cable in production 29 | # gem 'redis', '~> 3.0' 30 | # Use ActiveModel has_secure_password 31 | # gem 'bcrypt', '~> 3.1.7' 32 | 33 | # Use Capistrano for deployment 34 | # gem 'capistrano-rails', group: :development 35 | gem 'bulma-rails', '~> 0.6.2' 36 | gem 'simple_form', '~> 3.5' 37 | gem 'devise', '~> 4.4', '>= 4.4.1' 38 | gem 'gravatar_image_tag', '~> 1.2' 39 | gem 'jquery-rails', '~> 4.3', '>= 4.3.1' 40 | gem 'rolify', '~> 5.2' 41 | gem 'cancancan', '~> 2.1', '>= 2.1.3' 42 | gem 'friendly_id', '~> 5.2', '>= 5.2.3' 43 | gem 'redcarpet', '~> 3.4' 44 | gem 'coderay', '~> 1.1', '>= 1.1.2' 45 | 46 | 47 | group :development, :test do 48 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 49 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] 50 | # Adds support for Capybara system testing and selenium driver 51 | gem 'capybara', '~> 2.13' 52 | gem 'selenium-webdriver' 53 | gem 'guard', '~> 2.14', '>= 2.14.2' 54 | gem 'guard-livereload', '~> 2.5', require: false 55 | end 56 | 57 | group :development do 58 | # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. 59 | gem 'web-console', '>= 3.3.0' 60 | gem 'listen', '>= 3.0.5', '< 3.2' 61 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 62 | gem 'spring' 63 | gem 'spring-watcher-listen', '~> 2.0.0' 64 | end 65 | 66 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 67 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 68 | -------------------------------------------------------------------------------- /app/controllers/discussions_controller.rb: -------------------------------------------------------------------------------- 1 | class DiscussionsController < ApplicationController 2 | before_action :set_discussion, only: [:show, :edit, :update, :destroy] 3 | before_action :find_channels, only: [:index, :show, :new, :edit] 4 | before_action :authenticate_user!, except: [:index, :show] 5 | 6 | # GET /discussions 7 | # GET /discussions.json 8 | def index 9 | @discussions = Discussion.all.order('created_at desc') 10 | end 11 | 12 | # GET /discussions/1 13 | # GET /discussions/1.json 14 | def show 15 | @discussions = Discussion.all.order('created_at desc') 16 | end 17 | 18 | # GET /discussions/new 19 | def new 20 | @discussion = current_user.discussions.build 21 | end 22 | 23 | # GET /discussions/1/edit 24 | def edit 25 | end 26 | 27 | # POST /discussions 28 | # POST /discussions.json 29 | def create 30 | @discussion = current_user.discussions.build(discussion_params) 31 | 32 | respond_to do |format| 33 | if @discussion.save 34 | format.html { redirect_to @discussion, notice: 'Discussion was successfully created.' } 35 | format.json { render :show, status: :created, location: @discussion } 36 | else 37 | format.html { render :new } 38 | format.json { render json: @discussion.errors, status: :unprocessable_entity } 39 | end 40 | end 41 | end 42 | 43 | # PATCH/PUT /discussions/1 44 | # PATCH/PUT /discussions/1.json 45 | def update 46 | respond_to do |format| 47 | if @discussion.update(discussion_params) 48 | format.html { redirect_to @discussion, notice: 'Discussion was successfully updated.' } 49 | format.json { render :show, status: :ok, location: @discussion } 50 | else 51 | format.html { render :edit } 52 | format.json { render json: @discussion.errors, status: :unprocessable_entity } 53 | end 54 | end 55 | end 56 | 57 | # DELETE /discussions/1 58 | # DELETE /discussions/1.json 59 | def destroy 60 | @discussion.destroy 61 | respond_to do |format| 62 | format.html { redirect_to discussions_url, notice: 'Discussion was successfully destroyed.' } 63 | format.json { head :no_content } 64 | end 65 | end 66 | 67 | private 68 | # Use callbacks to share common setup or constraints between actions. 69 | def set_discussion 70 | @discussion = Discussion.find(params[:id]) 71 | end 72 | 73 | def find_channels 74 | @channels = Channel.all.order('created_at desc') 75 | end 76 | 77 | # Never trust parameters from the scary internet, only allow the white list through. 78 | def discussion_params 79 | params.require(:discussion).permit(:title, :content, :channel_id) 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 |

Edit <%= resource_name.to_s.humanize %>

7 | 8 | <% if has_role?(:admin) %> 9 |
10 |

You are currently an admin of the Discussions application.

11 |
12 |
13 | <% end %> 14 | 15 | <%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> 16 | <%= f.error_notification %> 17 | 18 | 19 |
20 |
21 | <%= f.input :username, required: true, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" } %> 22 |
23 |
24 | 25 |
26 |
27 | <%= f.input :email, required: true, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" } %> 28 |
29 |
30 | 31 |
32 | <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> 33 |

Currently waiting confirmation for: <%= resource.unconfirmed_email %>

34 | <% end %> 35 |
36 | 37 |
38 |
39 | <%= f.input :password, autocomplete: "off", hint: "leave it blank if you don't want to change it", required: false, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" } %> 40 |
41 |
42 | 43 |
44 |
45 | <%= f.input :password_confirmation, required: false, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" } %> 46 |
47 |
48 | 49 |
50 |
51 | <%= f.input :current_password, hint: "we need your current password to confirm your changes", required: true, input_html: { class: "input"}, wrapper: false, label_html: { class: "label" } %> 52 |
53 |
54 | 55 | <%= f.button :submit, "Update", class:"button is-info" %> 56 | 57 | <% end %> 58 | 59 |
60 |

Cancel my account

61 |

Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>

62 | 63 |
64 |
65 |
66 |
-------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Discussions 5 | <%= csrf_meta_tags %> 6 | 7 | 8 | <%= stylesheet_link_tag "https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" %> 9 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> 10 | <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> 11 | 12 | 13 | 14 | 15 | <% if flash[:notice] %> 16 |
17 |

<%= notice %>

18 |
19 | <% end %> 20 | <% if flash[:alert] %> 21 |
22 |

<%= alert %>

23 |
24 | <% end %> 25 | 74 | 75 |
76 |
77 | <%= yield %> 78 |
79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /config/initializers/friendly_id.rb: -------------------------------------------------------------------------------- 1 | # FriendlyId Global Configuration 2 | # 3 | # Use this to set up shared configuration options for your entire application. 4 | # Any of the configuration options shown here can also be applied to single 5 | # models by passing arguments to the `friendly_id` class method or defining 6 | # methods in your model. 7 | # 8 | # To learn more, check out the guide: 9 | # 10 | # http://norman.github.io/friendly_id/file.Guide.html 11 | 12 | FriendlyId.defaults do |config| 13 | # ## Reserved Words 14 | # 15 | # Some words could conflict with Rails's routes when used as slugs, or are 16 | # undesirable to allow as slugs. Edit this list as needed for your app. 17 | config.use :reserved 18 | 19 | config.reserved_words = %w(new edit index session login logout users admin 20 | stylesheets assets javascripts images) 21 | 22 | # ## Friendly Finders 23 | # 24 | # Uncomment this to use friendly finders in all models. By default, if 25 | # you wish to find a record by its friendly id, you must do: 26 | # 27 | # MyModel.friendly.find('foo') 28 | # 29 | # If you uncomment this, you can do: 30 | # 31 | # MyModel.find('foo') 32 | # 33 | # This is significantly more convenient but may not be appropriate for 34 | # all applications, so you must explicity opt-in to this behavior. You can 35 | # always also configure it on a per-model basis if you prefer. 36 | # 37 | # Something else to consider is that using the :finders addon boosts 38 | # performance because it will avoid Rails-internal code that makes runtime 39 | # calls to `Module.extend`. 40 | # 41 | # config.use :finders 42 | # 43 | # ## Slugs 44 | # 45 | # Most applications will use the :slugged module everywhere. If you wish 46 | # to do so, uncomment the following line. 47 | # 48 | # config.use :slugged 49 | # 50 | # By default, FriendlyId's :slugged addon expects the slug column to be named 51 | # 'slug', but you can change it if you wish. 52 | # 53 | # config.slug_column = 'slug' 54 | # 55 | # By default, slug has no size limit, but you can change it if you wish. 56 | # 57 | # config.slug_limit = 255 58 | # 59 | # When FriendlyId can not generate a unique ID from your base method, it appends 60 | # a UUID, separated by a single dash. You can configure the character used as the 61 | # separator. If you're upgrading from FriendlyId 4, you may wish to replace this 62 | # with two dashes. 63 | # 64 | # config.sequence_separator = '-' 65 | # 66 | # Note that you must use the :slugged addon **prior** to the line which 67 | # configures the sequence separator, or else FriendlyId will raise an undefined 68 | # method error. 69 | # 70 | # ## Tips and Tricks 71 | # 72 | # ### Controlling when slugs are generated 73 | # 74 | # As of FriendlyId 5.0, new slugs are generated only when the slug field is 75 | # nil, but if you're using a column as your base method can change this 76 | # behavior by overriding the `should_generate_new_friendly_id?` method that 77 | # FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave 78 | # more like 4.0. 79 | # 80 | # config.use Module.new { 81 | # def should_generate_new_friendly_id? 82 | # slug.blank? || _changed? 83 | # end 84 | # } 85 | # 86 | # FriendlyId uses Rails's `parameterize` method to generate slugs, but for 87 | # languages that don't use the Roman alphabet, that's not usually sufficient. 88 | # Here we use the Babosa library to transliterate Russian Cyrillic slugs to 89 | # ASCII. If you use this, don't forget to add "babosa" to your Gemfile. 90 | # 91 | # config.use Module.new { 92 | # def normalize_friendly_id(text) 93 | # text.to_slug.normalize! :transliterations => [:russian, :latin] 94 | # end 95 | # } 96 | end 97 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20180202063252) do 14 | 15 | create_table "channels", force: :cascade do |t| 16 | t.string "channel" 17 | t.datetime "created_at", null: false 18 | t.datetime "updated_at", null: false 19 | t.integer "discussion_id" 20 | t.string "slug" 21 | end 22 | 23 | create_table "discussions", force: :cascade do |t| 24 | t.string "title" 25 | t.text "content" 26 | t.datetime "created_at", null: false 27 | t.datetime "updated_at", null: false 28 | t.integer "user_id" 29 | t.integer "channel_id" 30 | t.string "slug" 31 | end 32 | 33 | create_table "friendly_id_slugs", force: :cascade do |t| 34 | t.string "slug", null: false 35 | t.integer "sluggable_id", null: false 36 | t.string "sluggable_type", limit: 50 37 | t.string "scope" 38 | t.datetime "created_at" 39 | t.index ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true 40 | t.index ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type" 41 | t.index ["sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_id" 42 | t.index ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type" 43 | end 44 | 45 | create_table "replies", force: :cascade do |t| 46 | t.text "reply" 47 | t.datetime "created_at", null: false 48 | t.datetime "updated_at", null: false 49 | t.integer "discussion_id" 50 | t.integer "user_id" 51 | t.string "slug" 52 | end 53 | 54 | create_table "roles", force: :cascade do |t| 55 | t.string "name" 56 | t.string "resource_type" 57 | t.integer "resource_id" 58 | t.datetime "created_at", null: false 59 | t.datetime "updated_at", null: false 60 | t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id" 61 | t.index ["name"], name: "index_roles_on_name" 62 | t.index ["resource_type", "resource_id"], name: "index_roles_on_resource_type_and_resource_id" 63 | end 64 | 65 | create_table "users", force: :cascade do |t| 66 | t.string "email", default: "", null: false 67 | t.string "encrypted_password", default: "", null: false 68 | t.string "reset_password_token" 69 | t.datetime "reset_password_sent_at" 70 | t.datetime "remember_created_at" 71 | t.integer "sign_in_count", default: 0, null: false 72 | t.datetime "current_sign_in_at" 73 | t.datetime "last_sign_in_at" 74 | t.string "current_sign_in_ip" 75 | t.string "last_sign_in_ip" 76 | t.datetime "created_at", null: false 77 | t.datetime "updated_at", null: false 78 | t.string "username" 79 | t.index ["email"], name: "index_users_on_email", unique: true 80 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true 81 | end 82 | 83 | create_table "users_roles", id: false, force: :cascade do |t| 84 | t.integer "user_id" 85 | t.integer "role_id" 86 | t.index ["role_id"], name: "index_users_roles_on_role_id" 87 | t.index ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id" 88 | t.index ["user_id"], name: "index_users_roles_on_user_id" 89 | end 90 | 91 | end 92 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Attempt to read encrypted secrets from `config/secrets.yml.enc`. 18 | # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or 19 | # `config/secrets.yml.key`. 20 | config.read_encrypted_secrets = true 21 | 22 | # Disable serving static files from the `/public` folder by default since 23 | # Apache or NGINX already handles this. 24 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 25 | 26 | # Compress JavaScripts and CSS. 27 | config.assets.js_compressor = :uglifier 28 | # config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = false 32 | 33 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 34 | 35 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 36 | # config.action_controller.asset_host = 'http://assets.example.com' 37 | 38 | # Specifies the header that your server uses for sending files. 39 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 41 | 42 | # Mount Action Cable outside main process or domain 43 | # config.action_cable.mount_path = nil 44 | # config.action_cable.url = 'wss://example.com/cable' 45 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 46 | 47 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 48 | # config.force_ssl = true 49 | 50 | # Use the lowest log level to ensure availability of diagnostic information 51 | # when problems arise. 52 | config.log_level = :debug 53 | 54 | # Prepend all log lines with the following tags. 55 | config.log_tags = [ :request_id ] 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Use a real queuing backend for Active Job (and separate queues per environment) 61 | # config.active_job.queue_adapter = :resque 62 | # config.active_job.queue_name_prefix = "discussions_#{Rails.env}" 63 | config.action_mailer.perform_caching = false 64 | 65 | # Ignore bad email addresses and do not raise email delivery errors. 66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 67 | # config.action_mailer.raise_delivery_errors = false 68 | 69 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 70 | # the I18n.default_locale when a translation cannot be found). 71 | config.i18n.fallbacks = true 72 | 73 | # Send deprecation notices to registered listeners. 74 | config.active_support.deprecation = :notify 75 | 76 | # Use default logging formatter so that PID and timestamp are not suppressed. 77 | config.log_formatter = ::Logger::Formatter.new 78 | 79 | # Use a different logger for distributed setups. 80 | # require 'syslog/logger' 81 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 82 | 83 | if ENV["RAILS_LOG_TO_STDOUT"].present? 84 | logger = ActiveSupport::Logger.new(STDOUT) 85 | logger.formatter = config.log_formatter 86 | config.logger = ActiveSupport::TaggedLogging.new(logger) 87 | end 88 | 89 | # Do not dump schema after migrations. 90 | config.active_record.dump_schema_after_migration = false 91 | end 92 | -------------------------------------------------------------------------------- /config/locales/devise.en.yml: -------------------------------------------------------------------------------- 1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n 2 | 3 | en: 4 | devise: 5 | confirmations: 6 | confirmed: "Your email address has been successfully confirmed." 7 | send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." 8 | send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." 9 | failure: 10 | already_authenticated: "You are already signed in." 11 | inactive: "Your account is not activated yet." 12 | invalid: "Invalid %{authentication_keys} or password." 13 | locked: "Your account is locked." 14 | last_attempt: "You have one more attempt before your account is locked." 15 | not_found_in_database: "Invalid %{authentication_keys} or password." 16 | timeout: "Your session expired. Please sign in again to continue." 17 | unauthenticated: "You need to sign in or sign up before continuing." 18 | unconfirmed: "You have to confirm your email address before continuing." 19 | mailer: 20 | confirmation_instructions: 21 | subject: "Confirmation instructions" 22 | reset_password_instructions: 23 | subject: "Reset password instructions" 24 | unlock_instructions: 25 | subject: "Unlock instructions" 26 | email_changed: 27 | subject: "Email Changed" 28 | password_change: 29 | subject: "Password Changed" 30 | omniauth_callbacks: 31 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"." 32 | success: "Successfully authenticated from %{kind} account." 33 | passwords: 34 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." 35 | send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." 36 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." 37 | updated: "Your password has been changed successfully. You are now signed in." 38 | updated_not_active: "Your password has been changed successfully." 39 | registrations: 40 | destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." 41 | signed_up: "Welcome! You have signed up successfully." 42 | signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." 43 | signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." 44 | signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." 45 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." 46 | updated: "Your account has been updated successfully." 47 | sessions: 48 | signed_in: "Signed in successfully." 49 | signed_out: "Signed out successfully." 50 | already_signed_out: "Signed out successfully." 51 | unlocks: 52 | send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." 53 | send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." 54 | unlocked: "Your account has been unlocked successfully. Please sign in to continue." 55 | errors: 56 | messages: 57 | already_confirmed: "was already confirmed, please try signing in" 58 | confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" 59 | expired: "has expired, please request a new one" 60 | not_found: "not found" 61 | not_locked: "was not locked" 62 | not_saved: 63 | one: "1 error prohibited this %{resource} from being saved:" 64 | other: "%{count} errors prohibited this %{resource} from being saved:" 65 | -------------------------------------------------------------------------------- /config/initializers/simple_form.rb: -------------------------------------------------------------------------------- 1 | # Use this setup block to configure all options available in SimpleForm. 2 | SimpleForm.setup do |config| 3 | # Wrappers are used by the form builder to generate a 4 | # complete input. You can remove any component from the 5 | # wrapper, change the order or even add your own to the 6 | # stack. The options given below are used to wrap the 7 | # whole input. 8 | config.wrappers :default, class: :input, 9 | hint_class: :field_with_hint, error_class: :field_with_errors do |b| 10 | ## Extensions enabled by default 11 | # Any of these extensions can be disabled for a 12 | # given input by passing: `f.input EXTENSION_NAME => false`. 13 | # You can make any of these extensions optional by 14 | # renaming `b.use` to `b.optional`. 15 | 16 | # Determines whether to use HTML5 (:email, :url, ...) 17 | # and required attributes 18 | b.use :html5 19 | 20 | # Calculates placeholders automatically from I18n 21 | # You can also pass a string as f.input placeholder: "Placeholder" 22 | b.use :placeholder 23 | 24 | ## Optional extensions 25 | # They are disabled unless you pass `f.input EXTENSION_NAME => true` 26 | # to the input. If so, they will retrieve the values from the model 27 | # if any exists. If you want to enable any of those 28 | # extensions by default, you can change `b.optional` to `b.use`. 29 | 30 | # Calculates maxlength from length validations for string inputs 31 | # and/or database column lengths 32 | b.optional :maxlength 33 | 34 | # Calculate minlength from length validations for string inputs 35 | b.optional :minlength 36 | 37 | # Calculates pattern from format validations for string inputs 38 | b.optional :pattern 39 | 40 | # Calculates min and max from length validations for numeric inputs 41 | b.optional :min_max 42 | 43 | # Calculates readonly automatically from readonly attributes 44 | b.optional :readonly 45 | 46 | ## Inputs 47 | b.use :label_input 48 | b.use :hint, wrap_with: { tag: :span, class: :hint } 49 | b.use :error, wrap_with: { tag: :span, class: :error } 50 | 51 | ## full_messages_for 52 | # If you want to display the full error message for the attribute, you can 53 | # use the component :full_error, like: 54 | # 55 | # b.use :full_error, wrap_with: { tag: :span, class: :error } 56 | end 57 | 58 | # The default wrapper to be used by the FormBuilder. 59 | config.default_wrapper = :default 60 | 61 | # Define the way to render check boxes / radio buttons with labels. 62 | # Defaults to :nested for bootstrap config. 63 | # inline: input + label 64 | # nested: label > input 65 | config.boolean_style = :nested 66 | 67 | # Default class for buttons 68 | config.button_class = 'btn' 69 | 70 | # Method used to tidy up errors. Specify any Rails Array method. 71 | # :first lists the first message for each field. 72 | # Use :to_sentence to list all errors for each field. 73 | # config.error_method = :first 74 | 75 | # Default tag used for error notification helper. 76 | config.error_notification_tag = :div 77 | 78 | # CSS class to add for error notification helper. 79 | config.error_notification_class = 'error_notification' 80 | 81 | # ID to add for error notification helper. 82 | # config.error_notification_id = nil 83 | 84 | # Series of attempts to detect a default label method for collection. 85 | # config.collection_label_methods = [ :to_label, :name, :title, :to_s ] 86 | 87 | # Series of attempts to detect a default value method for collection. 88 | # config.collection_value_methods = [ :id, :to_s ] 89 | 90 | # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none. 91 | # config.collection_wrapper_tag = nil 92 | 93 | # You can define the class to use on all collection wrappers. Defaulting to none. 94 | # config.collection_wrapper_class = nil 95 | 96 | # You can wrap each item in a collection of radio/check boxes with a tag, 97 | # defaulting to :span. 98 | # config.item_wrapper_tag = :span 99 | 100 | # You can define a class to use in all item wrappers. Defaulting to none. 101 | # config.item_wrapper_class = nil 102 | 103 | # How the label text should be generated altogether with the required text. 104 | # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" } 105 | 106 | # You can define the class to use on all labels. Default is nil. 107 | # config.label_class = nil 108 | 109 | # You can define the default class to be used on forms. Can be overriden 110 | # with `html: { :class }`. Defaulting to none. 111 | # config.default_form_class = nil 112 | 113 | # You can define which elements should obtain additional classes 114 | # config.generate_additional_classes_for = [:wrapper, :label, :input] 115 | 116 | # Whether attributes are required by default (or not). Default is true. 117 | # config.required_by_default = true 118 | 119 | # Tell browsers whether to use the native HTML5 validations (novalidate form option). 120 | # These validations are enabled in SimpleForm's internal config but disabled by default 121 | # in this configuration, which is recommended due to some quirks from different browsers. 122 | # To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations, 123 | # change this configuration to true. 124 | config.browser_validations = false 125 | 126 | # Collection of methods to detect if a file type was given. 127 | # config.file_methods = [ :mounted_as, :file?, :public_filename ] 128 | 129 | # Custom mappings for input types. This should be a hash containing a regexp 130 | # to match as key, and the input type that will be used when the field name 131 | # matches the regexp as value. 132 | # config.input_mappings = { /count/ => :integer } 133 | 134 | # Custom wrappers for input types. This should be a hash containing an input 135 | # type as key and the wrapper that will be used for all inputs with specified type. 136 | # config.wrapper_mappings = { string: :prepend } 137 | 138 | # Namespaces where SimpleForm should look for custom input classes that 139 | # override default inputs. 140 | # config.custom_inputs_namespaces << "CustomInputs" 141 | 142 | # Default priority for time_zone inputs. 143 | # config.time_zone_priority = nil 144 | 145 | # Default priority for country inputs. 146 | # config.country_priority = nil 147 | 148 | # When false, do not use translations for labels. 149 | # config.translate_labels = true 150 | 151 | # Automatically discover new inputs in Rails' autoload path. 152 | # config.inputs_discovery = true 153 | 154 | # Cache SimpleForm inputs discovery 155 | # config.cache_discovery = !Rails.env.development? 156 | 157 | # Default class for inputs 158 | # config.input_class = nil 159 | 160 | # Define the default class of the input wrapper of the boolean input. 161 | config.boolean_label_class = 'checkbox' 162 | 163 | # Defines if the default input wrapper class should be included in radio 164 | # collection wrappers. 165 | # config.include_default_input_wrapper_class = true 166 | 167 | # Defines which i18n scope will be used in Simple Form. 168 | # config.i18n_scope = 'simple_form' 169 | end 170 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (5.1.4) 5 | actionpack (= 5.1.4) 6 | nio4r (~> 2.0) 7 | websocket-driver (~> 0.6.1) 8 | actionmailer (5.1.4) 9 | actionpack (= 5.1.4) 10 | actionview (= 5.1.4) 11 | activejob (= 5.1.4) 12 | mail (~> 2.5, >= 2.5.4) 13 | rails-dom-testing (~> 2.0) 14 | actionpack (5.1.4) 15 | actionview (= 5.1.4) 16 | activesupport (= 5.1.4) 17 | rack (~> 2.0) 18 | rack-test (>= 0.6.3) 19 | rails-dom-testing (~> 2.0) 20 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 21 | actionview (5.1.4) 22 | activesupport (= 5.1.4) 23 | builder (~> 3.1) 24 | erubi (~> 1.4) 25 | rails-dom-testing (~> 2.0) 26 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 27 | activejob (5.1.4) 28 | activesupport (= 5.1.4) 29 | globalid (>= 0.3.6) 30 | activemodel (5.1.4) 31 | activesupport (= 5.1.4) 32 | activerecord (5.1.4) 33 | activemodel (= 5.1.4) 34 | activesupport (= 5.1.4) 35 | arel (~> 8.0) 36 | activesupport (5.1.4) 37 | concurrent-ruby (~> 1.0, >= 1.0.2) 38 | i18n (~> 0.7) 39 | minitest (~> 5.1) 40 | tzinfo (~> 1.1) 41 | addressable (2.5.2) 42 | public_suffix (>= 2.0.2, < 4.0) 43 | arel (8.0.0) 44 | bcrypt (3.1.11) 45 | bindex (0.5.0) 46 | builder (3.2.3) 47 | bulma-rails (0.6.2) 48 | sass (~> 3.2) 49 | byebug (10.0.0) 50 | cancancan (2.1.3) 51 | capybara (2.17.0) 52 | addressable 53 | mini_mime (>= 0.1.3) 54 | nokogiri (>= 1.3.3) 55 | rack (>= 1.0.0) 56 | rack-test (>= 0.5.4) 57 | xpath (>= 2.0, < 4.0) 58 | childprocess (0.8.0) 59 | ffi (~> 1.0, >= 1.0.11) 60 | coderay (1.1.2) 61 | coffee-rails (4.2.2) 62 | coffee-script (>= 2.2.0) 63 | railties (>= 4.0.0) 64 | coffee-script (2.4.1) 65 | coffee-script-source 66 | execjs 67 | coffee-script-source (1.12.2) 68 | concurrent-ruby (1.0.5) 69 | crass (1.0.3) 70 | devise (4.4.1) 71 | bcrypt (~> 3.0) 72 | orm_adapter (~> 0.1) 73 | railties (>= 4.1.0, < 5.2) 74 | responders 75 | warden (~> 1.2.3) 76 | em-websocket (0.5.1) 77 | eventmachine (>= 0.12.9) 78 | http_parser.rb (~> 0.6.0) 79 | erubi (1.7.0) 80 | eventmachine (1.2.5) 81 | execjs (2.7.0) 82 | ffi (1.9.18) 83 | formatador (0.2.5) 84 | friendly_id (5.2.3) 85 | activerecord (>= 4.0.0) 86 | globalid (0.4.1) 87 | activesupport (>= 4.2.0) 88 | gravatar_image_tag (1.2.0) 89 | guard (2.14.2) 90 | formatador (>= 0.2.4) 91 | listen (>= 2.7, < 4.0) 92 | lumberjack (>= 1.0.12, < 2.0) 93 | nenv (~> 0.1) 94 | notiffany (~> 0.0) 95 | pry (>= 0.9.12) 96 | shellany (~> 0.0) 97 | thor (>= 0.18.1) 98 | guard-compat (1.2.1) 99 | guard-livereload (2.5.2) 100 | em-websocket (~> 0.5) 101 | guard (~> 2.8) 102 | guard-compat (~> 1.0) 103 | multi_json (~> 1.8) 104 | http_parser.rb (0.6.0) 105 | i18n (0.9.3) 106 | concurrent-ruby (~> 1.0) 107 | jbuilder (2.7.0) 108 | activesupport (>= 4.2.0) 109 | multi_json (>= 1.2) 110 | jquery-rails (4.3.1) 111 | rails-dom-testing (>= 1, < 3) 112 | railties (>= 4.2.0) 113 | thor (>= 0.14, < 2.0) 114 | listen (3.1.5) 115 | rb-fsevent (~> 0.9, >= 0.9.4) 116 | rb-inotify (~> 0.9, >= 0.9.7) 117 | ruby_dep (~> 1.2) 118 | loofah (2.1.1) 119 | crass (~> 1.0.2) 120 | nokogiri (>= 1.5.9) 121 | lumberjack (1.0.12) 122 | mail (2.7.0) 123 | mini_mime (>= 0.1.1) 124 | method_source (0.9.0) 125 | mini_mime (1.0.0) 126 | mini_portile2 (2.3.0) 127 | minitest (5.11.3) 128 | multi_json (1.13.1) 129 | nenv (0.3.0) 130 | nio4r (2.2.0) 131 | nokogiri (1.8.2) 132 | mini_portile2 (~> 2.3.0) 133 | notiffany (0.1.1) 134 | nenv (~> 0.1) 135 | shellany (~> 0.0) 136 | orm_adapter (0.5.0) 137 | pry (0.11.3) 138 | coderay (~> 1.1.0) 139 | method_source (~> 0.9.0) 140 | public_suffix (3.0.1) 141 | puma (3.11.2) 142 | rack (2.0.4) 143 | rack-test (0.8.2) 144 | rack (>= 1.0, < 3) 145 | rails (5.1.4) 146 | actioncable (= 5.1.4) 147 | actionmailer (= 5.1.4) 148 | actionpack (= 5.1.4) 149 | actionview (= 5.1.4) 150 | activejob (= 5.1.4) 151 | activemodel (= 5.1.4) 152 | activerecord (= 5.1.4) 153 | activesupport (= 5.1.4) 154 | bundler (>= 1.3.0) 155 | railties (= 5.1.4) 156 | sprockets-rails (>= 2.0.0) 157 | rails-dom-testing (2.0.3) 158 | activesupport (>= 4.2.0) 159 | nokogiri (>= 1.6) 160 | rails-html-sanitizer (1.0.3) 161 | loofah (~> 2.0) 162 | railties (5.1.4) 163 | actionpack (= 5.1.4) 164 | activesupport (= 5.1.4) 165 | method_source 166 | rake (>= 0.8.7) 167 | thor (>= 0.18.1, < 2.0) 168 | rake (12.3.0) 169 | rb-fsevent (0.10.2) 170 | rb-inotify (0.9.10) 171 | ffi (>= 0.5.0, < 2) 172 | redcarpet (3.4.0) 173 | responders (2.4.0) 174 | actionpack (>= 4.2.0, < 5.3) 175 | railties (>= 4.2.0, < 5.3) 176 | rolify (5.2.0) 177 | ruby_dep (1.5.0) 178 | rubyzip (1.2.1) 179 | sass (3.5.5) 180 | sass-listen (~> 4.0.0) 181 | sass-listen (4.0.0) 182 | rb-fsevent (~> 0.9, >= 0.9.4) 183 | rb-inotify (~> 0.9, >= 0.9.7) 184 | sass-rails (5.0.7) 185 | railties (>= 4.0.0, < 6) 186 | sass (~> 3.1) 187 | sprockets (>= 2.8, < 4.0) 188 | sprockets-rails (>= 2.0, < 4.0) 189 | tilt (>= 1.1, < 3) 190 | selenium-webdriver (3.8.0) 191 | childprocess (~> 0.5) 192 | rubyzip (~> 1.0) 193 | shellany (0.0.1) 194 | simple_form (3.5.0) 195 | actionpack (> 4, < 5.2) 196 | activemodel (> 4, < 5.2) 197 | spring (2.0.2) 198 | activesupport (>= 4.2) 199 | spring-watcher-listen (2.0.1) 200 | listen (>= 2.7, < 4.0) 201 | spring (>= 1.2, < 3.0) 202 | sprockets (3.7.1) 203 | concurrent-ruby (~> 1.0) 204 | rack (> 1, < 3) 205 | sprockets-rails (3.2.1) 206 | actionpack (>= 4.0) 207 | activesupport (>= 4.0) 208 | sprockets (>= 3.0.0) 209 | sqlite3 (1.3.13) 210 | thor (0.20.0) 211 | thread_safe (0.3.6) 212 | tilt (2.0.8) 213 | turbolinks (5.1.0) 214 | turbolinks-source (~> 5.1) 215 | turbolinks-source (5.1.0) 216 | tzinfo (1.2.4) 217 | thread_safe (~> 0.1) 218 | uglifier (4.1.5) 219 | execjs (>= 0.3.0, < 3) 220 | warden (1.2.7) 221 | rack (>= 1.0) 222 | web-console (3.5.1) 223 | actionview (>= 5.0) 224 | activemodel (>= 5.0) 225 | bindex (>= 0.4.0) 226 | railties (>= 5.0) 227 | websocket-driver (0.6.5) 228 | websocket-extensions (>= 0.1.0) 229 | websocket-extensions (0.1.3) 230 | xpath (3.0.0) 231 | nokogiri (~> 1.8) 232 | 233 | PLATFORMS 234 | ruby 235 | 236 | DEPENDENCIES 237 | bulma-rails (~> 0.6.2) 238 | byebug 239 | cancancan (~> 2.1, >= 2.1.3) 240 | capybara (~> 2.13) 241 | coderay (~> 1.1, >= 1.1.2) 242 | coffee-rails (~> 4.2) 243 | devise (~> 4.4, >= 4.4.1) 244 | friendly_id (~> 5.2, >= 5.2.3) 245 | gravatar_image_tag (~> 1.2) 246 | guard (~> 2.14, >= 2.14.2) 247 | guard-livereload (~> 2.5) 248 | jbuilder (~> 2.5) 249 | jquery-rails (~> 4.3, >= 4.3.1) 250 | listen (>= 3.0.5, < 3.2) 251 | puma (~> 3.7) 252 | rails (~> 5.1.4) 253 | redcarpet (~> 3.4) 254 | rolify (~> 5.2) 255 | sass-rails (~> 5.0) 256 | selenium-webdriver 257 | simple_form (~> 3.5) 258 | spring 259 | spring-watcher-listen (~> 2.0.0) 260 | sqlite3 261 | turbolinks (~> 5) 262 | tzinfo-data 263 | uglifier (>= 1.3.0) 264 | web-console (>= 3.3.0) 265 | 266 | BUNDLED WITH 267 | 1.16.0 268 | -------------------------------------------------------------------------------- /app/assets/stylesheets/_functions.scss: -------------------------------------------------------------------------------- 1 | // Border 2 | // ----------------------------------- 3 | .border-light { border: 1px solid #ddd; } 4 | .border-radius-2 { border-radius: 2px; } 5 | .border-radius-4 { border-radius: 4px; } 6 | .border-radius-6 { border-radius: 6px; } 7 | .border-radius-50 { border-radius: 50%; } 8 | 9 | .bb { border-bottom: 1px solid #ddd; } 10 | 11 | .bb-not-last { 12 | border-bottom: 1px solid #ddd; 13 | &:last-of-type { 14 | border-bottom: 0; 15 | } 16 | } 17 | 18 | 19 | // Spacing 20 | // Base: p = padding; m = margin 21 | // Modifiers: a = all; h = horizontal; v = vertical; t,r,b,l = top, etc. 22 | // Factors: 10 through 100 23 | // =========================================================================== 24 | 25 | $spacing-10: 10px; 26 | $spacing-20: 20px; 27 | $spacing-30: 30px; 28 | $spacing-40: 40px; 29 | $spacing-50: 50px; 30 | $spacing-60: 60px; 31 | $spacing-70: 70px; 32 | $spacing-80: 80px; 33 | $spacing-90: 90px; 34 | $spacing-100: 100px; 35 | 36 | // Padding 37 | // ------------------------------------ 38 | 39 | .pa0 { padding: 0; } 40 | .pa10 { padding: $spacing-10; } 41 | .pa20 { padding: $spacing-20; } 42 | .pa30 { padding: $spacing-30; } 43 | .pa40 { padding: $spacing-40; } 44 | .pa50 { padding: $spacing-50; } 45 | .pa60 { padding: $spacing-60; } 46 | .pa70 { padding: $spacing-70; } 47 | .pa80 { padding: $spacing-80; } 48 | .pa90 { padding: $spacing-90; } 49 | .pa100 { padding: $spacing-100; } 50 | 51 | .pl0 { padding-left: 0; } 52 | .pl10 { padding-left: $spacing-10; } 53 | .pl20 { padding-left: $spacing-20; } 54 | .pl30 { padding-left: $spacing-30; } 55 | .pl40 { padding-left: $spacing-40; } 56 | .pl50 { padding-left: $spacing-50; } 57 | .pl60 { padding-left: $spacing-60; } 58 | .pl70 { padding-left: $spacing-70; } 59 | .pl80 { padding-left: $spacing-80; } 60 | .pl90 { padding-left: $spacing-90; } 61 | .pl100 { padding-left: $spacing-100; } 62 | 63 | .pr0 { padding-right: 0; } 64 | .pr10 { padding-right: $spacing-10; } 65 | .pr20 { padding-right: $spacing-20; } 66 | .pr30 { padding-right: $spacing-30; } 67 | .pr40 { padding-right: $spacing-40; } 68 | .pr50 { padding-right: $spacing-50; } 69 | .pr60 { padding-right: $spacing-60; } 70 | .pr70 { padding-right: $spacing-70; } 71 | .pr80 { padding-right: $spacing-80; } 72 | .pr90 { padding-right: $spacing-90; } 73 | .pr100 { padding-right: $spacing-100; } 74 | 75 | .pb0 { padding-bottom: 0; } 76 | .pb10 { padding-bottom: $spacing-10; } 77 | .pb20 { padding-bottom: $spacing-20; } 78 | .pb30 { padding-bottom: $spacing-30; } 79 | .pb40 { padding-bottom: $spacing-40; } 80 | .pb50 { padding-bottom: $spacing-50; } 81 | .pb60 { padding-bottom: $spacing-60; } 82 | .pb70 { padding-bottom: $spacing-70; } 83 | .pb80 { padding-bottom: $spacing-80; } 84 | .pb90 { padding-bottom: $spacing-90; } 85 | .pb100 { padding-bottom: $spacing-100; } 86 | 87 | .pt0 { padding-top: 0; } 88 | .pt10 { padding-top: $spacing-10; } 89 | .pt20 { padding-top: $spacing-20; } 90 | .pt30 { padding-top: $spacing-30; } 91 | .pt40 { padding-top: $spacing-40; } 92 | .pt50 { padding-top: $spacing-50; } 93 | .pt60 { padding-top: $spacing-60; } 94 | .pt70 { padding-top: $spacing-70; } 95 | .pt80 { padding-top: $spacing-80; } 96 | .pt90 { padding-top: $spacing-90; } 97 | .pt100 { padding-top: $spacing-100; } 98 | 99 | .pv0 { padding-top: 0; padding-bottom: 0; } 100 | .pv10 { padding-top: $spacing-10; padding-bottom: $spacing-10; } 101 | .pv20 { padding-top: $spacing-20; padding-bottom: $spacing-20; } 102 | .pv30 { padding-top: $spacing-30; padding-bottom: $spacing-30; } 103 | .pv40 { padding-top: $spacing-40; padding-bottom: $spacing-40; } 104 | .pv50 { padding-top: $spacing-50; padding-bottom: $spacing-50; } 105 | .pv60 { padding-top: $spacing-60; padding-bottom: $spacing-60; } 106 | .pv70 { padding-top: $spacing-70; padding-bottom: $spacing-70; } 107 | .pv80 { padding-top: $spacing-80; padding-bottom: $spacing-80; } 108 | .pv90 { padding-top: $spacing-90; padding-bottom: $spacing-90; } 109 | .pv100 { padding-top: $spacing-100;padding-bottom: $spacing-100; } 110 | 111 | .ph0 { padding-left: 0; padding-right: 0; } 112 | .ph10 { padding-left: $spacing-10; padding-right: $spacing-10; } 113 | .ph20 { padding-left: $spacing-20; padding-right: $spacing-20; } 114 | .ph30 { padding-left: $spacing-30; padding-right: $spacing-30; } 115 | .ph40 { padding-left: $spacing-40; padding-right: $spacing-40; } 116 | .ph50 { padding-left: $spacing-50; padding-right: $spacing-50; } 117 | .ph60 { padding-left: $spacing-60; padding-right: $spacing-60; } 118 | .ph70 { padding-left: $spacing-70; padding-right: $spacing-70; } 119 | .ph80 { padding-left: $spacing-80; padding-right: $spacing-80; } 120 | .ph90 { padding-left: $spacing-90; padding-right: $spacing-90; } 121 | .ph100 { padding-left: $spacing-100;padding-right: $spacing-100; } 122 | 123 | // Margins 124 | // ------------------------------------ 125 | 126 | .m0auto { margin: 0 auto; } 127 | 128 | .ma0 { margin: 0; } 129 | .ma10 { margin: $spacing-10; } 130 | .ma20 { margin: $spacing-20; } 131 | .ma30 { margin: $spacing-30; } 132 | .ma40 { margin: $spacing-40; } 133 | .ma50 { margin: $spacing-50; } 134 | .ma60 { margin: $spacing-60; } 135 | .ma70 { margin: $spacing-70; } 136 | .ma80 { margin: $spacing-80; } 137 | .ma90 { margin: $spacing-90; } 138 | .ma100 { margin: $spacing-100; } 139 | 140 | .ml0 { margin-left: 0; } 141 | .ml10 { margin-left: $spacing-10; } 142 | .ml20 { margin-left: $spacing-20; } 143 | .ml30 { margin-left: $spacing-30; } 144 | .ml40 { margin-left: $spacing-40; } 145 | .ml50 { margin-left: $spacing-50; } 146 | .ml60 { margin-left: $spacing-60; } 147 | .ml70 { margin-left: $spacing-70; } 148 | .ml80 { margin-left: $spacing-80; } 149 | .ml90 { margin-left: $spacing-90; } 150 | .ml100 { margin-left: $spacing-100; } 151 | 152 | .mr0 { margin-right: 0; } 153 | .mr10 { margin-right: $spacing-10; } 154 | .mr20 { margin-right: $spacing-20; } 155 | .mr30 { margin-right: $spacing-30; } 156 | .mr40 { margin-right: $spacing-40; } 157 | .mr50 { margin-right: $spacing-50; } 158 | .mr60 { margin-right: $spacing-60; } 159 | .mr70 { margin-right: $spacing-70; } 160 | .mr80 { margin-right: $spacing-80; } 161 | .mr90 { margin-right: $spacing-90; } 162 | .mr100 { margin-right: $spacing-100; } 163 | 164 | .mb0 { margin-bottom: 0; } 165 | .mb10 { margin-bottom: $spacing-10; } 166 | .mb20 { margin-bottom: $spacing-20; } 167 | .mb30 { margin-bottom: $spacing-30; } 168 | .mb40 { margin-bottom: $spacing-40; } 169 | .mb50 { margin-bottom: $spacing-50; } 170 | .mb60 { margin-bottom: $spacing-60; } 171 | .mb70 { margin-bottom: $spacing-70; } 172 | .mb80 { margin-bottom: $spacing-80; } 173 | .mb90 { margin-bottom: $spacing-90; } 174 | .mb100 { margin-bottom: $spacing-100; } 175 | 176 | .mt0 { margin-top: 0; } 177 | .mt10 { margin-top: $spacing-10; } 178 | .mt20 { margin-top: $spacing-20; } 179 | .mt30 { margin-top: $spacing-30; } 180 | .mt40 { margin-top: $spacing-40; } 181 | .mt50 { margin-top: $spacing-50; } 182 | .mt60 { margin-top: $spacing-60; } 183 | .mt70 { margin-top: $spacing-70; } 184 | .mt80 { margin-top: $spacing-80; } 185 | .mt90 { margin-top: $spacing-90; } 186 | .mt100 { margin-top: $spacing-100; } 187 | 188 | .mv0 { margin-top: 0; margin-bottom: 0; } 189 | .mv10 { margin-top: $spacing-10; margin-bottom: $spacing-10; } 190 | .mv20 { margin-top: $spacing-20; margin-bottom: $spacing-20; } 191 | .mv30 { margin-top: $spacing-30; margin-bottom: $spacing-30; } 192 | .mv40 { margin-top: $spacing-40; margin-bottom: $spacing-40; } 193 | .mv50 { margin-top: $spacing-50; margin-bottom: $spacing-50; } 194 | .mv60 { margin-top: $spacing-60; margin-bottom: $spacing-60; } 195 | .mv70 { margin-top: $spacing-70; margin-bottom: $spacing-70; } 196 | .mv80 { margin-top: $spacing-80; margin-bottom: $spacing-80; } 197 | .mv90 { margin-top: $spacing-90; margin-bottom: $spacing-90; } 198 | .mv100 { margin-top: $spacing-100;margin-bottom: $spacing-100; } 199 | 200 | .mh0 { margin-left: 0; margin-right: 0; } 201 | .mh10 { margin-left: $spacing-10; margin-right: $spacing-10; } 202 | .mh20 { margin-left: $spacing-20; margin-right: $spacing-20; } 203 | .mh30 { margin-left: $spacing-30; margin-right: $spacing-30; } 204 | .mh40 { margin-left: $spacing-40; margin-right: $spacing-40; } 205 | .mh50 { margin-left: $spacing-50; margin-right: $spacing-50; } 206 | .mh60 { margin-left: $spacing-60; margin-right: $spacing-60; } 207 | .mh70 { margin-left: $spacing-70; margin-right: $spacing-70; } 208 | .mh80 { margin-left: $spacing-80; margin-right: $spacing-80; } 209 | .mh90 { margin-left: $spacing-90; margin-right: $spacing-90; } 210 | .mh100 { margin-left: $spacing-100;margin-right: $spacing-100; } 211 | -------------------------------------------------------------------------------- /config/initializers/devise.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Use this hook to configure devise mailer, warden hooks and so forth. 4 | # Many of these configuration options can be set straight in your model. 5 | Devise.setup do |config| 6 | # The secret key used by Devise. Devise uses this key to generate 7 | # random tokens. Changing this key will render invalid all existing 8 | # confirmation, reset password and unlock tokens in the database. 9 | # Devise will use the `secret_key_base` as its `secret_key` 10 | # by default. You can change it below and use your own secret key. 11 | # config.secret_key = 'c5f879449406d5fd79f17e77b323bf23039c8481a650d9d967b9b28affb1d327f15576f0da3f8f70dba24be1afe2532c1be2175cf22a06a11ed4f9ef7d154675' 12 | 13 | # ==> Mailer Configuration 14 | # Configure the e-mail address which will be shown in Devise::Mailer, 15 | # note that it will be overwritten if you use your own mailer class 16 | # with default "from" parameter. 17 | config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com' 18 | 19 | # Configure the class responsible to send e-mails. 20 | # config.mailer = 'Devise::Mailer' 21 | 22 | # Configure the parent class responsible to send e-mails. 23 | # config.parent_mailer = 'ActionMailer::Base' 24 | 25 | # ==> ORM configuration 26 | # Load and configure the ORM. Supports :active_record (default) and 27 | # :mongoid (bson_ext recommended) by default. Other ORMs may be 28 | # available as additional gems. 29 | require 'devise/orm/active_record' 30 | 31 | # ==> Configuration for any authentication mechanism 32 | # Configure which keys are used when authenticating a user. The default is 33 | # just :email. You can configure it to use [:username, :subdomain], so for 34 | # authenticating a user, both parameters are required. Remember that those 35 | # parameters are used only when authenticating and not when retrieving from 36 | # session. If you need permissions, you should implement that in a before filter. 37 | # You can also supply a hash where the value is a boolean determining whether 38 | # or not authentication should be aborted when the value is not present. 39 | # config.authentication_keys = [:email] 40 | 41 | # Configure parameters from the request object used for authentication. Each entry 42 | # given should be a request method and it will automatically be passed to the 43 | # find_for_authentication method and considered in your model lookup. For instance, 44 | # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. 45 | # The same considerations mentioned for authentication_keys also apply to request_keys. 46 | # config.request_keys = [] 47 | 48 | # Configure which authentication keys should be case-insensitive. 49 | # These keys will be downcased upon creating or modifying a user and when used 50 | # to authenticate or find a user. Default is :email. 51 | config.case_insensitive_keys = [:email] 52 | 53 | # Configure which authentication keys should have whitespace stripped. 54 | # These keys will have whitespace before and after removed upon creating or 55 | # modifying a user and when used to authenticate or find a user. Default is :email. 56 | config.strip_whitespace_keys = [:email] 57 | 58 | # Tell if authentication through request.params is enabled. True by default. 59 | # It can be set to an array that will enable params authentication only for the 60 | # given strategies, for example, `config.params_authenticatable = [:database]` will 61 | # enable it only for database (email + password) authentication. 62 | # config.params_authenticatable = true 63 | 64 | # Tell if authentication through HTTP Auth is enabled. False by default. 65 | # It can be set to an array that will enable http authentication only for the 66 | # given strategies, for example, `config.http_authenticatable = [:database]` will 67 | # enable it only for database authentication. The supported strategies are: 68 | # :database = Support basic authentication with authentication key + password 69 | # config.http_authenticatable = false 70 | 71 | # If 401 status code should be returned for AJAX requests. True by default. 72 | # config.http_authenticatable_on_xhr = true 73 | 74 | # The realm used in Http Basic Authentication. 'Application' by default. 75 | # config.http_authentication_realm = 'Application' 76 | 77 | # It will change confirmation, password recovery and other workflows 78 | # to behave the same regardless if the e-mail provided was right or wrong. 79 | # Does not affect registerable. 80 | # config.paranoid = true 81 | 82 | # By default Devise will store the user in session. You can skip storage for 83 | # particular strategies by setting this option. 84 | # Notice that if you are skipping storage for all authentication paths, you 85 | # may want to disable generating routes to Devise's sessions controller by 86 | # passing skip: :sessions to `devise_for` in your config/routes.rb 87 | config.skip_session_storage = [:http_auth] 88 | 89 | # By default, Devise cleans up the CSRF token on authentication to 90 | # avoid CSRF token fixation attacks. This means that, when using AJAX 91 | # requests for sign in and sign up, you need to get a new CSRF token 92 | # from the server. You can disable this option at your own risk. 93 | # config.clean_up_csrf_token_on_authentication = true 94 | 95 | # When false, Devise will not attempt to reload routes on eager load. 96 | # This can reduce the time taken to boot the app but if your application 97 | # requires the Devise mappings to be loaded during boot time the application 98 | # won't boot properly. 99 | # config.reload_routes = true 100 | 101 | # ==> Configuration for :database_authenticatable 102 | # For bcrypt, this is the cost for hashing the password and defaults to 11. If 103 | # using other algorithms, it sets how many times you want the password to be hashed. 104 | # 105 | # Limiting the stretches to just one in testing will increase the performance of 106 | # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use 107 | # a value less than 10 in other environments. Note that, for bcrypt (the default 108 | # algorithm), the cost increases exponentially with the number of stretches (e.g. 109 | # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). 110 | config.stretches = Rails.env.test? ? 1 : 11 111 | 112 | # Set up a pepper to generate the hashed password. 113 | # config.pepper = 'abaa3555ec341654d0a689c5ad13a7d183604f3156666015d1130c5ab4f33bcfa879e558f0406869d26fcff3a4a38cfd473b3b16d3f1f0bf5a32d4c087c31644' 114 | 115 | # Send a notification to the original email when the user's email is changed. 116 | # config.send_email_changed_notification = false 117 | 118 | # Send a notification email when the user's password is changed. 119 | # config.send_password_change_notification = false 120 | 121 | # ==> Configuration for :confirmable 122 | # A period that the user is allowed to access the website even without 123 | # confirming their account. For instance, if set to 2.days, the user will be 124 | # able to access the website for two days without confirming their account, 125 | # access will be blocked just in the third day. Default is 0.days, meaning 126 | # the user cannot access the website without confirming their account. 127 | # config.allow_unconfirmed_access_for = 2.days 128 | 129 | # A period that the user is allowed to confirm their account before their 130 | # token becomes invalid. For example, if set to 3.days, the user can confirm 131 | # their account within 3 days after the mail was sent, but on the fourth day 132 | # their account can't be confirmed with the token any more. 133 | # Default is nil, meaning there is no restriction on how long a user can take 134 | # before confirming their account. 135 | # config.confirm_within = 3.days 136 | 137 | # If true, requires any email changes to be confirmed (exactly the same way as 138 | # initial account confirmation) to be applied. Requires additional unconfirmed_email 139 | # db field (see migrations). Until confirmed, new email is stored in 140 | # unconfirmed_email column, and copied to email column on successful confirmation. 141 | config.reconfirmable = true 142 | 143 | # Defines which key will be used when confirming an account 144 | # config.confirmation_keys = [:email] 145 | 146 | # ==> Configuration for :rememberable 147 | # The time the user will be remembered without asking for credentials again. 148 | # config.remember_for = 2.weeks 149 | 150 | # Invalidates all the remember me tokens when the user signs out. 151 | config.expire_all_remember_me_on_sign_out = true 152 | 153 | # If true, extends the user's remember period when remembered via cookie. 154 | # config.extend_remember_period = false 155 | 156 | # Options to be passed to the created cookie. For instance, you can set 157 | # secure: true in order to force SSL only cookies. 158 | # config.rememberable_options = {} 159 | 160 | # ==> Configuration for :validatable 161 | # Range for password length. 162 | config.password_length = 6..128 163 | 164 | # Email regex used to validate email formats. It simply asserts that 165 | # one (and only one) @ exists in the given string. This is mainly 166 | # to give user feedback and not to assert the e-mail validity. 167 | config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ 168 | 169 | # ==> Configuration for :timeoutable 170 | # The time you want to timeout the user session without activity. After this 171 | # time the user will be asked for credentials again. Default is 30 minutes. 172 | # config.timeout_in = 30.minutes 173 | 174 | # ==> Configuration for :lockable 175 | # Defines which strategy will be used to lock an account. 176 | # :failed_attempts = Locks an account after a number of failed attempts to sign in. 177 | # :none = No lock strategy. You should handle locking by yourself. 178 | # config.lock_strategy = :failed_attempts 179 | 180 | # Defines which key will be used when locking and unlocking an account 181 | # config.unlock_keys = [:email] 182 | 183 | # Defines which strategy will be used to unlock an account. 184 | # :email = Sends an unlock link to the user email 185 | # :time = Re-enables login after a certain amount of time (see :unlock_in below) 186 | # :both = Enables both strategies 187 | # :none = No unlock strategy. You should handle unlocking by yourself. 188 | # config.unlock_strategy = :both 189 | 190 | # Number of authentication tries before locking an account if lock_strategy 191 | # is failed attempts. 192 | # config.maximum_attempts = 20 193 | 194 | # Time interval to unlock the account if :time is enabled as unlock_strategy. 195 | # config.unlock_in = 1.hour 196 | 197 | # Warn on the last attempt before the account is locked. 198 | # config.last_attempt_warning = true 199 | 200 | # ==> Configuration for :recoverable 201 | # 202 | # Defines which key will be used when recovering the password for an account 203 | # config.reset_password_keys = [:email] 204 | 205 | # Time interval you can reset your password with a reset password key. 206 | # Don't put a too small interval or your users won't have the time to 207 | # change their passwords. 208 | config.reset_password_within = 6.hours 209 | 210 | # When set to false, does not sign a user in automatically after their password is 211 | # reset. Defaults to true, so a user is signed in automatically after a reset. 212 | # config.sign_in_after_reset_password = true 213 | 214 | # ==> Configuration for :encryptable 215 | # Allow you to use another hashing or encryption algorithm besides bcrypt (default). 216 | # You can use :sha1, :sha512 or algorithms from others authentication tools as 217 | # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20 218 | # for default behavior) and :restful_authentication_sha1 (then you should set 219 | # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper). 220 | # 221 | # Require the `devise-encryptable` gem when using anything other than bcrypt 222 | # config.encryptor = :sha512 223 | 224 | # ==> Scopes configuration 225 | # Turn scoped views on. Before rendering "sessions/new", it will first check for 226 | # "users/sessions/new". It's turned off by default because it's slower if you 227 | # are using only default views. 228 | # config.scoped_views = false 229 | 230 | # Configure the default scope given to Warden. By default it's the first 231 | # devise role declared in your routes (usually :user). 232 | # config.default_scope = :user 233 | 234 | # Set this configuration to false if you want /users/sign_out to sign out 235 | # only the current scope. By default, Devise signs out all scopes. 236 | # config.sign_out_all_scopes = true 237 | 238 | # ==> Navigation configuration 239 | # Lists the formats that should be treated as navigational. Formats like 240 | # :html, should redirect to the sign in page when the user does not have 241 | # access, but formats like :xml or :json, should return 401. 242 | # 243 | # If you have any extra navigational formats, like :iphone or :mobile, you 244 | # should add them to the navigational formats lists. 245 | # 246 | # The "*/*" below is required to match Internet Explorer requests. 247 | # config.navigational_formats = ['*/*', :html] 248 | 249 | # The default HTTP method used to sign out a resource. Default is :delete. 250 | config.sign_out_via = :delete 251 | 252 | # ==> OmniAuth 253 | # Add a new OmniAuth provider. Check the wiki for more information on setting 254 | # up on your models and hooks. 255 | # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' 256 | 257 | # ==> Warden configuration 258 | # If you want to use other strategies, that are not supported by Devise, or 259 | # change the failure app, you can configure them inside the config.warden block. 260 | # 261 | # config.warden do |manager| 262 | # manager.intercept_401 = false 263 | # manager.default_strategies(scope: :user).unshift :some_external_strategy 264 | # end 265 | 266 | # ==> Mountable engine configurations 267 | # When using Devise inside an engine, let's call it `MyEngine`, and this engine 268 | # is mountable, there are some extra configurations to be taken into account. 269 | # The following options are available, assuming the engine is mounted as: 270 | # 271 | # mount MyEngine, at: '/my_engine' 272 | # 273 | # The router that invoked `devise_for`, in the example above, would be: 274 | # config.router_name = :my_engine 275 | # 276 | # When using OmniAuth, Devise cannot automatically set OmniAuth path, 277 | # so you need to do it manually. For the users scope, it would be: 278 | # config.omniauth_path_prefix = '/my_engine/users/auth' 279 | end 280 | --------------------------------------------------------------------------------