├── log └── .keep ├── storage └── .keep ├── tmp ├── .keep └── pids │ └── .keep ├── vendor └── .keep ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── public ├── favicon.ico ├── apple-touch-icon.png ├── apple-touch-icon-precomposed.png ├── robots.txt ├── 500.html ├── 422.html └── 404.html ├── test ├── helpers │ └── .keep ├── models │ ├── .keep │ ├── user_test.rb │ ├── ticket_test.rb │ └── event_test.rb ├── system │ ├── .keep │ ├── welcomes_test.rb │ └── events_test.rb ├── controllers │ ├── .keep │ ├── sessions_controller_test.rb │ ├── tickets_controller_test.rb │ ├── retirements_controller_test.rb │ ├── welcome_controller_test.rb │ ├── status_controller_test.rb │ └── events_controller_test.rb ├── integration │ └── .keep ├── application_system_test_case.rb ├── factories │ ├── users.rb │ └── events.rb ├── test_helper.rb └── sign_in_helper.rb ├── .ruby-version ├── app ├── assets │ ├── images │ │ └── .keep │ ├── config │ │ └── manifest.js │ └── stylesheets │ │ ├── events.scss │ │ ├── sessions.scss │ │ ├── tickets.scss │ │ ├── welcome.scss │ │ ├── retirements.scss │ │ └── application.css ├── models │ ├── concerns │ │ └── .keep │ ├── application_record.rb │ ├── ticket.rb │ ├── user.rb │ └── event.rb ├── controllers │ ├── concerns │ │ └── .keep │ ├── status_controller.rb │ ├── retirements_controller.rb │ ├── sessions_controller.rb │ ├── welcome_controller.rb │ ├── application_controller.rb │ ├── tickets_controller.rb │ └── events_controller.rb ├── views │ ├── status │ │ └── index.json.jbuilder │ ├── events │ │ ├── create.js.erb │ │ ├── update.js.erb │ │ ├── new.html.haml │ │ ├── edit.html.haml │ │ └── show.html.haml │ ├── kaminari │ │ ├── _gap.html.haml │ │ ├── _last_page.html.haml │ │ ├── _first_page.html.haml │ │ ├── _next_page.html.haml │ │ ├── _prev_page.html.haml │ │ ├── _page.html.haml │ │ └── _paginator.html.haml │ ├── retirements │ │ ├── create.js.erb │ │ └── new.html.haml │ ├── tickets │ │ └── create.js.erb │ ├── application │ │ └── _errors.html.haml │ ├── welcome │ │ └── index.html.haml │ └── layouts │ │ └── application.html.haml ├── helpers │ ├── events_helper.rb │ ├── tickets_helper.rb │ ├── welcome_helper.rb │ ├── sessions_helper.rb │ ├── retirements_helper.rb │ └── application_helper.rb ├── jobs │ └── application_job.rb ├── javascript │ ├── get_form_turbolinks.js │ └── packs │ │ └── application.js └── forms │ └── event_search_form.rb ├── .browserslistrc ├── config ├── webpack │ ├── environment.js │ ├── test.js │ ├── production.js │ └── development.js ├── spring.rb ├── environment.rb ├── initializers │ ├── mime_types.rb │ ├── filter_parameter_logging.rb │ ├── rack_profiler.rb │ ├── application_controller_renderer.rb │ ├── cookies_serializer.rb │ ├── omniauth.rb │ ├── backtrace_silencers.rb │ ├── wrap_parameters.rb │ ├── assets.rb │ ├── inflections.rb │ └── content_security_policy.rb ├── locales │ ├── kaminari_ja.yml │ ├── ja.yml │ └── en.yml ├── boot.rb ├── routes.rb ├── credentials.yml.enc ├── database.yml ├── application.rb ├── storage.yml ├── puma.rb ├── environments │ ├── test.rb │ ├── development.rb │ └── production.rb └── webpacker.yml ├── config.ru ├── .rubocop.yml ├── Rakefile ├── bin ├── rake ├── rails ├── yarn ├── webpack ├── webpack-dev-server ├── spring ├── setup └── bundle ├── postcss.config.js ├── db ├── seeds.rb ├── migrate │ ├── 20200707073202_create_tickets.rb │ ├── 20200707061657_create_users.rb │ ├── 20200707064259_create_events.rb │ └── 20200707093324_create_active_storage_tables.active_storage.rb └── schema.rb ├── package.json ├── .gitignore ├── README.md ├── .github └── workflows │ └── ruby.yml ├── babel.config.js ├── Gemfile └── Gemfile.lock /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /storage/.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/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/system/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tmp/pids/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.6.6 2 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/status/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.status "ok" -------------------------------------------------------------------------------- /app/helpers/events_helper.rb: -------------------------------------------------------------------------------- 1 | module EventsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/tickets_helper.rb: -------------------------------------------------------------------------------- 1 | module TicketsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/welcome_helper.rb: -------------------------------------------------------------------------------- 1 | module WelcomeHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/retirements_helper.rb: -------------------------------------------------------------------------------- 1 | module RetirementsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /config/webpack/environment.js: -------------------------------------------------------------------------------- 1 | const { environment } = require('@rails/webpacker') 2 | 3 | module.exports = environment 4 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /app/views/events/create.js.erb: -------------------------------------------------------------------------------- 1 | document.getElementById("errors").innerHTML = "<%= j render("errors", errors: @event.errors) %>" -------------------------------------------------------------------------------- /app/views/events/update.js.erb: -------------------------------------------------------------------------------- 1 | document.getElementById("errors").innerHTML = "<%= j render("errors", errors: @event.errors) %>" -------------------------------------------------------------------------------- /app/views/kaminari/_gap.html.haml: -------------------------------------------------------------------------------- 1 | %li.page-item.disabled 2 | = link_to raw(t 'views.pagination.truncate'), '#', class: 'page-link' 3 | -------------------------------------------------------------------------------- /app/views/retirements/create.js.erb: -------------------------------------------------------------------------------- 1 | document.getElementById("errors").innerHTML = "<%= j render("errors", errors: @current_user.errors) %>" -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | Spring.watch( 2 | ".ruby-version", 3 | ".rbenv-vars", 4 | "tmp/restart.txt", 5 | "tmp/caching-dev.txt" 6 | ) 7 | -------------------------------------------------------------------------------- /app/views/tickets/create.js.erb: -------------------------------------------------------------------------------- 1 | document.getElementById("createTicketErrors").innerHTML = "<%= j render("errors", errors: @ticket.errors) %>" -------------------------------------------------------------------------------- /app/views/application/_errors.html.haml: -------------------------------------------------------------------------------- 1 | .alert.alert-danger 2 | %ul.mb-0 3 | - errors.full_messages.each do |message| 4 | %li= message -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def url_for_github(user) 3 | "https://github.com/#{user.name}" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/status_controller.rb: -------------------------------------------------------------------------------- 1 | class StatusController < ApplicationController 2 | skip_before_action :authenticate 3 | def index 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/views/kaminari/_last_page.html.haml: -------------------------------------------------------------------------------- 1 | %li.page-item 2 | = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, remote: remote, class: 'page-link' 3 | -------------------------------------------------------------------------------- /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/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/kaminari/_first_page.html.haml: -------------------------------------------------------------------------------- 1 | %li.page-item 2 | = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, remote: remote, class: 'page-link' 3 | -------------------------------------------------------------------------------- /test/models/ticket_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TicketTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/views/kaminari/_next_page.html.haml: -------------------------------------------------------------------------------- 1 | %li.page-item 2 | = link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, rel: 'next', remote: remote, class: 'page-link' 3 | -------------------------------------------------------------------------------- /app/views/kaminari/_prev_page.html.haml: -------------------------------------------------------------------------------- 1 | %li.page-item 2 | = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, rel: 'prev', remote: remote, class: 'page-link' 3 | -------------------------------------------------------------------------------- /config/webpack/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /app/models/ticket.rb: -------------------------------------------------------------------------------- 1 | class Ticket < ApplicationRecord 2 | belongs_to :user, optional: true 3 | belongs_to :event 4 | 5 | validates :comment, length: { maximum: 30 }, allow_blank: true 6 | end 7 | -------------------------------------------------------------------------------- /config/webpack/production.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/webpack/development.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - rubocop-performance 3 | - rubocop-rails 4 | 5 | AllCops: 6 | DisabledByDefault: true 7 | 8 | Lint: 9 | Enabled: true 10 | 11 | Security: 12 | Enabled: true 13 | -------------------------------------------------------------------------------- /test/controllers/sessions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/tickets_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TicketsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /config/locales/kaminari_ja.yml: -------------------------------------------------------------------------------- 1 | ja: 2 | views: 3 | pagination: 4 | first: "« 最初" 5 | last: "最後 »" 6 | previous: "‹ 前" 7 | next: "次 ›" 8 | truncate: "…" -------------------------------------------------------------------------------- /test/controllers/retirements_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RetirementsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/events.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the events controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/sessions.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the sessions controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/tickets.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the tickets controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/welcome.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the welcome controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/retirements.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the retirements controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: https://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /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 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400] 5 | include SignInHelper 6 | end 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/initializers/rack_profiler.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | if Rails.env.development? 4 | require "rack-mini-profiler" 5 | 6 | # initialization is skipped so trigger it 7 | Rack::MiniProfilerRails.initialize!(Rails.application) 8 | end 9 | -------------------------------------------------------------------------------- /test/controllers/welcome_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class WelcomeControllerTest < ActionDispatch::IntegrationTest 4 | test "should get index" do 5 | get root_url 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /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/kaminari/_page.html.haml: -------------------------------------------------------------------------------- 1 | - if page.current? 2 | %li.page-item.active 3 | = content_tag :a, page, data: { remote: remote }, rel: page.rel, class: 'page-link' 4 | - else 5 | %li.page-item 6 | = link_to page, url, remote: remote, rel: page.rel, class: 'page-link' 7 | -------------------------------------------------------------------------------- /app/controllers/retirements_controller.rb: -------------------------------------------------------------------------------- 1 | class RetirementsController < ApplicationController 2 | def new 3 | end 4 | 5 | def create 6 | if current_user.destroy 7 | reset_session 8 | redirect_to root_path, notice: "退会完了しました" 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user, aliases: [:owner] do 3 | provider { "github" } 4 | sequence(:uid) { |i| "uid#{i}" } 5 | sequence(:name) { |i| "name#{i}" } 6 | sequence(:image_url) { |i| "http://example.com/image#{i}.jpg"} 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-import'), 4 | require('postcss-flexbugs-fixes'), 5 | require('postcss-preset-env')({ 6 | autoprefixer: { 7 | flexbox: 'no-2009' 8 | }, 9 | stage: 3 10 | }) 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /config/locales/ja.yml: -------------------------------------------------------------------------------- 1 | ja: 2 | activerecord: 3 | models: 4 | event: イベント 5 | ticket: チケット 6 | attributes: 7 | event: 8 | name: 名前 9 | place: 場所 10 | start_at: 開始時間 11 | end_at: 終了時間 12 | content: 内容 13 | image: 画像 14 | ticket: 15 | comment: コメント -------------------------------------------------------------------------------- /test/factories/events.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :event do 3 | owner 4 | sequence(:name) { |i| "イベント名#{i}" } 5 | sequence(:place) { |i| "イベント開催場所#{i}" } 6 | sequence(:content) { |i| "イベント本文#{i}" } 7 | start_at { rand(1..30).days.from_now } 8 | end_at { start_at + rand(1..30).hours } 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path('..', __dir__) 3 | Dir.chdir(APP_ROOT) do 4 | begin 5 | exec "yarnpkg", *ARGV 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/retirements/new.html.haml: -------------------------------------------------------------------------------- 1 | %h1.mt-3 退会の確認 2 | 3 | #errors 4 | 5 | %p 退会した場合、ユーザに関するデータがすべて削除されます。 6 | %p ただし、以下の場合は退会できません。 7 | 8 | %ul 9 | %li 10 | 公開中の未終了イベントがある場合 11 | %li 12 | 未終了の参加イベントがある場合 13 | %hr 14 | 15 | = link_to "退会する", retirements_path, method: :post, remote: true, class: "btn btn-danger", data: { confirm: "本当に退会しますか?" } -------------------------------------------------------------------------------- /test/controllers/status_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class StatusControllerTest < ActionDispatch::IntegrationTest 4 | test "GET /status" do 5 | get "/status" 6 | assert_response(:success) 7 | assert_equal({ status: "ok" }.to_json, @response.body) 8 | assert_equal("application/json", @response.media_type) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /config/initializers/omniauth.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.middleware.use OmniAuth::Builder do 2 | if Rails.env.development? || Rails.env.test? 3 | provider :github, "YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET" 4 | else 5 | provider :github, 6 | Rails.application.credentials.github[:client_id], 7 | Rails.application.credentials.github[:client_secret] 8 | end 9 | end -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20200707073202_create_tickets.rb: -------------------------------------------------------------------------------- 1 | class CreateTickets < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :tickets do |t| 4 | t.references :user 5 | t.references :event, null: false, foreign_key: true, index: false 6 | t.string :comment 7 | t.timestamps 8 | end 9 | 10 | add_index :tickets, %i[event_id user_id], unique: true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20200707061657_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :users do |t| 4 | t.string :provider, null: false 5 | t.string :uid, null: false 6 | t.string :name, null: false 7 | t.string :image_url, null: false 8 | t.timestamps 9 | end 10 | 11 | add_index :users, %i[provider uid], unique: true 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awesome_events", 3 | "private": true, 4 | "dependencies": { 5 | "@rails/activestorage": "^6.0.0", 6 | "@rails/ujs": "^6.0.0", 7 | "@rails/webpacker": "4.2.2", 8 | "bootstrap": "4.4.1", 9 | "jquery": "3.5.1", 10 | "popper.js": "1.16.1", 11 | "turbolinks": "^5.2.0" 12 | }, 13 | "version": "0.1.0", 14 | "devDependencies": { 15 | "webpack-dev-server": "^3.11.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | skip_before_action :authenticate, only: :create 3 | 4 | def create 5 | user = User.find_or_create_from_auth_hash!(request.env["omniauth.auth"]) 6 | session[:user_id] = user.id 7 | redirect_to root_path, notice: "ログインしました" 8 | end 9 | 10 | def destroy 11 | reset_session 12 | redirect_to root_path, notice: "ログアウトしました" 13 | end 14 | end -------------------------------------------------------------------------------- /app/controllers/welcome_controller.rb: -------------------------------------------------------------------------------- 1 | class WelcomeController < ApplicationController 2 | skip_before_action :authenticate 3 | 4 | def index 5 | @event_search_form = EventSearchForm.new(event_search_form_params) 6 | @events = @event_search_form.search 7 | end 8 | 9 | private 10 | 11 | def event_search_form_params 12 | params.fetch(:event_search_form, {}).permit(:keyword, :start_at).merge(page: params[:page]) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root "welcome#index" 3 | get "/auth/:provider/callback" => "sessions#create" 4 | delete "/logout" => "sessions#destroy" 5 | 6 | resource :retirements, only: %i[new create] 7 | 8 | resources :events, only: %i[new create show edit update destroy] do 9 | resources :tickets, only: %i[new create destroy] 10 | end 11 | get 'status' => 'status#index', defaults: { format: 'json' } 12 | end 13 | -------------------------------------------------------------------------------- /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/javascript/get_form_turbolinks.js: -------------------------------------------------------------------------------- 1 | import Turbolinks from "turbolinks" 2 | 3 | document.addEventListener("turbolinks:load", function(event) { 4 | const forms = document.querySelectorAll("form[method=get][data-remote=true]") 5 | for (const form of forms) { 6 | form.addEventListener("ajax:beforeSend", function (event) { 7 | const options = event.detail[1] 8 | 9 | Turbolinks.visit(options.url) 10 | event.preventDefault() 11 | }) 12 | } 13 | }) -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | U56WukeIPxLkQXVHYhg9k0m5evN9MI4b9bZwXZeqmbh/tVdsqBISHhJ1OMHBLT3R/bIXK6XQFI5u6o+Z9phoLzEIeQJzXrOfqJJjcLu95QCVJ/cdf0eWnJ8aVVLPaOIinPga+9zgrzVyWdSHMTS1gFUeOIhL4g8rgEDgRq7KJ3L/YlR3cQe31o5oYslL6WFx68phYNc1eWXzUYcLr5lfrFzYSeGSUj4/VEXU+oCs2lYNGjZJZFZSjG5FZ/Vy5GpkElrKNDIw1GcYVlNQFctOynX7lEzg11vW4Ezu5A/MH81kiym+FUowSDbgplUOkBZ6C3aeBcmwPoOCpKxb2KFvohlRdzgY3Igu+HyucDcVggPjs7OKZuNtWgKxciSFS+bqVZYswXqjKiu1gHTx7P/nI36a9PInNnktAmFD--Y1/KbSDOvl3kDAt9--juCYgn8212rfZnW6adCL5A== -------------------------------------------------------------------------------- /db/migrate/20200707064259_create_events.rb: -------------------------------------------------------------------------------- 1 | class CreateEvents < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :events do |t| 4 | t.bigint :owner_id 5 | t.string :name, null: false 6 | t.string :place, null: false 7 | t.datetime :start_at, null: false 8 | t.datetime :end_at, null: false 9 | t.text :content, null: false 10 | t.timestamps 11 | end 12 | 13 | add_index :events, :owner_id 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /bin/webpack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "bundler/setup" 11 | 12 | require "webpacker" 13 | require "webpacker/webpack_runner" 14 | 15 | APP_ROOT = File.expand_path("..", __dir__) 16 | Dir.chdir(APP_ROOT) do 17 | Webpacker::WebpackRunner.run(ARGV) 18 | end 19 | -------------------------------------------------------------------------------- /app/views/kaminari/_paginator.html.haml: -------------------------------------------------------------------------------- 1 | = paginator.render do 2 | %nav 3 | %ul.pagination 4 | = first_page_tag unless current_page.first? 5 | = prev_page_tag unless current_page.first? 6 | - each_page do |page| 7 | - if page.left_outer? || page.right_outer? || page.inside_window? 8 | = page_tag page 9 | - elsif !page.was_truncated? 10 | = gap_tag 11 | = next_page_tag unless current_page.last? 12 | = last_page_tag unless current_page.last? 13 | -------------------------------------------------------------------------------- /bin/webpack-dev-server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "bundler/setup" 11 | 12 | require "webpacker" 13 | require "webpacker/dev_server_runner" 14 | 15 | APP_ROOT = File.expand_path("..", __dir__) 16 | Dir.chdir(APP_ROOT) do 17 | Webpacker::DevServerRunner.run(ARGV) 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | before_action :authenticate 3 | helper_method :logged_in?, :current_user 4 | 5 | private 6 | 7 | def logged_in? 8 | !!current_user 9 | end 10 | 11 | def current_user 12 | return unless session[:user_id] 13 | @current_user ||= User.find(session[:user_id]) 14 | end 15 | 16 | def authenticate 17 | return if logged_in? 18 | redirect_to root_path, alert: "ログインしてください" 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | 3 | # SimpleCov 4 | # require 'simplecov' 5 | # SimpleCov.start 'rails' 6 | 7 | require_relative '../config/environment' 8 | require_relative 'sign_in_helper' 9 | require 'rails/test_help' 10 | require 'minitest/mock' 11 | 12 | class ActiveSupport::TestCase 13 | # Run tests in parallel with specified workers 14 | parallelize(workers: :number_of_processors) 15 | 16 | parallelize_setup do |worker| 17 | # For Searchkick 18 | Searchkick.index_suffix = worker 19 | Event.reindex 20 | Searchkick.disable_callbacks 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/forms/event_search_form.rb: -------------------------------------------------------------------------------- 1 | class EventSearchForm 2 | include ActiveModel::Model 3 | include ActiveModel::Attributes 4 | 5 | attribute :keyword, :string 6 | attribute :page, :integer 7 | 8 | def search 9 | Event.search( 10 | keyword_for_search, 11 | where: { start_at: { gte: start_at } }, 12 | page: page, 13 | per_page: 10 14 | ) 15 | end 16 | 17 | def start_at 18 | @start_at || Time.current 19 | end 20 | 21 | def start_at=(new_start_at) 22 | @start_at = new_start_at.in_time_zone 23 | end 24 | 25 | private 26 | 27 | def keyword_for_search 28 | keyword.presence || "*" 29 | end 30 | end -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/controllers/tickets_controller.rb: -------------------------------------------------------------------------------- 1 | class TicketsController < ApplicationController 2 | def new 3 | raise ActionController::RoutingError, "ログイン状態で TicketsController#new にアクセス" 4 | end 5 | 6 | def create 7 | event = Event.find(params[:event_id]) 8 | @ticket = current_user.tickets.build do |t| 9 | t.event = event 10 | t.comment = params[:ticket][:comment] 11 | end 12 | if @ticket.save 13 | redirect_to event, notice: "このイベントに参加表明しました" 14 | end 15 | end 16 | 17 | def destroy 18 | ticket = current_user.tickets.find_by!(event_id: params[:event_id]) 19 | ticket.destroy! 20 | redirect_to event_path(params[:event_id]), notice: "このイベントの参加をキャンセルしました" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/sign_in_helper.rb: -------------------------------------------------------------------------------- 1 | module SignInHelper 2 | def sign_in_as(user) 3 | OmniAuth.config.test_mode = true 4 | OmniAuth.config.add_mock( 5 | user.provider, 6 | uid: user.uid, 7 | info: { nickname: user.name, 8 | image: user.image_url }) 9 | 10 | case 11 | when respond_to?(:visit) 12 | visit root_url 13 | click_on "GitHubでログイン" 14 | when respond_to?(:get) 15 | get "/auth/github/callback" 16 | else 17 | raise NotImplementedError.new 18 | end 19 | @current_user = user 20 | end 21 | 22 | def current_user 23 | @current_user 24 | end 25 | end 26 | 27 | class ActionDispatch::IntegrationTest #((2)) 28 | include SignInHelper #((3)) 29 | end -------------------------------------------------------------------------------- /app/views/welcome/index.html.haml: -------------------------------------------------------------------------------- 1 | %h1 イベント一覧 2 | 3 | = form_with(model: @event_search_form, url: root_path, method: :get) do |f| 4 | .form-group 5 | = f.label :keyword, "キーワード" 6 | = f.text_field :keyword, class: "form-control" 7 | .form-group 8 | = f.label :start_at, "以降に開催されるイベント" 9 | = f.datetime_field :start_at, class: "form-control" 10 | .form-group 11 | = f.submit "検索", class: "btn btn-primary" 12 | 13 | %ul.list-group.mb-3 14 | - @events.each do |event| 15 | = link_to(event, class: "list-group-item list-group-item-action") do 16 | %h5.list-group-item-heading= event.name 17 | %p.mb-1= "#{l(event.start_at, format: :long)} - #{l(event.end_at, format: :long)}" 18 | 19 | = paginate @events 20 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 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 | -------------------------------------------------------------------------------- /test/system/welcomes_test.rb: -------------------------------------------------------------------------------- 1 | require "application_system_test_case" 2 | 3 | class WelcomesTest < ApplicationSystemTestCase 4 | test "/ ページを表示" do 5 | visit root_url 6 | 7 | assert_selector "h1", text: "イベント一覧" 8 | end 9 | 10 | test "/ ページを表示して、未来のイベントは表示、過去のイベントは非表示であること" do 11 | Searchkick.enable_callbacks 12 | 13 | future_event = FactoryBot.create(:event, start_at: Time.zone.now + 3.day) 14 | past_event = FactoryBot.create(:event, start_at: Time.zone.now + 1.day) 15 | Event.search_index.refresh 16 | 17 | travel_to Time.zone.now + 2.day do 18 | visit root_url 19 | assert_selector "h5", text: future_event.name 20 | assert_no_selector "h5", text: past_event.name 21 | end 22 | 23 | Searchkick.disable_callbacks 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, 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 | -------------------------------------------------------------------------------- /test/controllers/events_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class EventsControllerTest < ActionDispatch::IntegrationTest 4 | test "自分がつくったイベントは削除できる" do 5 | event_owner = FactoryBot.create(:user) 6 | event = FactoryBot.create(:event, owner: event_owner) 7 | sign_in_as event_owner 8 | assert_difference("Event.count", -1) do 9 | delete event_url(event) 10 | end 11 | end 12 | 13 | test "他の人がつくったイベントは削除できない" do 14 | event_owner = FactoryBot.create(:user) 15 | event = FactoryBot.create(:event, owner: event_owner) 16 | sign_in_user = FactoryBot.create(:user) 17 | sign_in_as sign_in_user 18 | assert_difference("Event.count", 0) do 19 | assert_raises(ActiveRecord::RecordNotFound) do 20 | delete event_url(event) 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/views/events/new.html.haml: -------------------------------------------------------------------------------- 1 | - now = Time.zone.now 2 | 3 | %h1.mt-2 イベント作成 4 | 5 | = form_with(model: @event, class: "form-horizontal") do |f| 6 | #errors 7 | .form-group 8 | = f.label :name 9 | = f.text_field :name, class: "form-control" 10 | .form-group 11 | = f.label :place 12 | = f.text_field :place, class: "form-control" 13 | .form-group 14 | = f.label :start_at 15 | %div 16 | = f.datetime_select :start_at, start_year: now.year, end_year: now.year + 1 17 | .form-group 18 | = f.label :end_at 19 | %div 20 | = f.datetime_select :end_at, start_year: now.year, end_year: now.year + 1 21 | .form-group 22 | = f.label :content 23 | = f.text_area :content, class: "form-control", row: 10 24 | .form-group 25 | = f.label :image 26 | = f.file_field :image, class: "form-control-file" 27 | = f.submit class: "btn btn-primary" -------------------------------------------------------------------------------- /app/javascript/packs/application.js: -------------------------------------------------------------------------------- 1 | // This file is automatically compiled by Webpack, along with any other files 2 | // present in this directory. You're encouraged to place your actual application logic in 3 | // a relevant structure within app/javascript and only use these pack files to reference 4 | // that code so it'll be compiled. 5 | 6 | require("@rails/ujs").start() 7 | require("turbolinks").start() 8 | require("@rails/activestorage").start() 9 | require("get_form_turbolinks") 10 | 11 | import "bootstrap" 12 | import "bootstrap/scss/bootstrap.scss" 13 | 14 | // Uncomment to copy all static images under ../images to the output folder and reference 15 | // them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>) 16 | // or the `imagePath` JavaScript helper below. 17 | // 18 | // const images = require.context('../images', true) 19 | // const imagePath = (name) => images(name, true) 20 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | require "active_record/railtie" 8 | require "active_storage/engine" 9 | require "action_controller/railtie" 10 | # require "action_mailer/railtie" 11 | # require "action_mailbox/engine" 12 | # require "action_text/engine" 13 | require "action_view/railtie" 14 | # require "action_cable/engine" 15 | require "sprockets/railtie" 16 | require "rails/test_unit/railtie" 17 | 18 | # Require the gems listed in Gemfile, including any gems 19 | # you've limited to :test, :development, or :production. 20 | Bundler.require(*Rails.groups) 21 | 22 | module AwesomeEvents 23 | class Application < Rails::Application 24 | config.load_defaults 6.0 25 | config.time_zone = "Tokyo" 26 | config.i18n.default_locale = :ja 27 | config.active_storage.variant_processor = :vips 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /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 https://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /.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 | /db/*.sqlite3-* 14 | 15 | # Ignore all logfiles and tempfiles. 16 | /log/* 17 | /tmp/* 18 | !/log/.keep 19 | !/tmp/.keep 20 | 21 | # Ignore pidfiles, but keep the directory. 22 | /tmp/pids/* 23 | !/tmp/pids/ 24 | !/tmp/pids/.keep 25 | 26 | # Ignore uploaded files in development. 27 | /storage/* 28 | !/storage/.keep 29 | 30 | /public/assets 31 | .byebug_history 32 | 33 | # Ignore master key for decrypting credentials and more. 34 | /config/master.key 35 | 36 | /public/packs 37 | /public/packs-test 38 | /node_modules 39 | /yarn-error.log 40 | yarn-debug.log* 41 | .yarn-integrity 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # awesome events 2 | 3 | これは、パーフェクトRuby on Rails第2版の第6章以降で作成されたサンプルアプリケーションです。 4 | 5 | イベント情報を登録/編集したり、イベントに参加登録したりできます。 6 | 7 | ## 前提条件 8 | 9 | 次のライブラリをインストールしておいてください。詳しくは書籍を参考にしてください。 10 | 11 | * Ruby 2.6.6 12 | * Google Chrome 13 | * Node.js 14 | * Yarn 15 | * libvips 16 | * Elasticsearch 17 | * Japanese (kuromoji) Analysis Plugin 18 | 19 | ## セットアップ方法 20 | 21 | ```sh 22 | https://github.com/perfect-ruby-on-rails/awesome_events.git 23 | cd awesome_events 24 | bundle install 25 | yarn install 26 | bin/rails db:setup 27 | ``` 28 | 29 | 第6章 OAuthを利用して「GitHubでログイン」機能を作る を参考に、GitHubアプリケーションを登録します。手に入れたClient IDとClient Secretを`config/initializers/omniauth.rb`に設定します。 30 | 31 | 次のコマンドでElasticsearchを起動します 32 | 33 | ```sh 34 | elasticsearch 35 | ``` 36 | 37 | 次のコマンドでサーバが立ち上がり、 http://localhost:3000 でアクセスできます。 38 | 39 | ```sh 40 | ./bin/rails s 41 | ``` 42 | 43 | ## テストの実行方法 44 | 45 | ### システムテストの実行方法 46 | 47 | elasticsearchを起動した状態で次のコマンドを実行します。 48 | 49 | ```sh 50 | bin/rails test:system 51 | ``` 52 | 53 | ### システムテスト以外のテストの実行方法 54 | 55 | 次のコマンドを実行します。 56 | 57 | ```sh 58 | bin/rails test 59 | ``` 60 | 61 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | before_destroy :check_all_events_finished 3 | 4 | has_many :created_events, class_name: "Event", foreign_key: "owner_id", dependent: :nullify 5 | has_many :tickets, dependent: :nullify 6 | has_many :participating_events, through: :tickets, source: :event 7 | 8 | def self.find_or_create_from_auth_hash!(auth_hash) 9 | provider = auth_hash[:provider] 10 | uid = auth_hash[:uid] 11 | nickname = auth_hash[:info][:nickname] 12 | image_url = auth_hash[:info][:image] 13 | 14 | User.find_or_create_by!(provider: provider, uid: uid) do |user| # ((1)) 15 | user.name = nickname 16 | user.image_url = image_url 17 | end 18 | end 19 | 20 | private 21 | 22 | def check_all_events_finished 23 | now = Time.zone.now 24 | if created_events.where(":now < end_at", now: now).exists? 25 | errors[:base] << "公開中の未終了イベントが存在します。" 26 | end 27 | 28 | if participating_events.where(":now < end_at", now: now).exists? 29 | errors[:base] << "未終了の参加イベントが存在します。" 30 | end 31 | 32 | throw(:abort) unless errors.empty? 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /db/migrate/20200707093324_create_active_storage_tables.active_storage.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from active_storage (originally 20170806125915) 2 | class CreateActiveStorageTables < ActiveRecord::Migration[5.2] 3 | def change 4 | create_table :active_storage_blobs do |t| 5 | t.string :key, null: false 6 | t.string :filename, null: false 7 | t.string :content_type 8 | t.text :metadata 9 | t.bigint :byte_size, null: false 10 | t.string :checksum, null: false 11 | t.datetime :created_at, null: false 12 | 13 | t.index [ :key ], unique: true 14 | end 15 | 16 | create_table :active_storage_attachments do |t| 17 | t.string :name, null: false 18 | t.references :record, null: false, polymorphic: true, index: false 19 | t.references :blob, null: false 20 | 21 | t.datetime :created_at, null: false 22 | 23 | t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true 24 | t.foreign_key :active_storage_blobs, column: :blob_id 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path('..', __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to setup or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at anytime and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies 21 | # system('bin/yarn') 22 | 23 | # puts "\n== Copying sample files ==" 24 | # unless File.exist?('config/database.yml') 25 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml' 26 | # end 27 | 28 | puts "\n== Preparing database ==" 29 | system! 'bin/rails db:prepare' 30 | 31 | puts "\n== Removing old logs and tempfiles ==" 32 | system! 'bin/rails log:clear tmp:clear' 33 | 34 | puts "\n== Restarting application server ==" 35 | system! 'bin/rails restart' 36 | end 37 | -------------------------------------------------------------------------------- /app/views/events/edit.html.haml: -------------------------------------------------------------------------------- 1 | - now = Time.zone.now 2 | 3 | %h1.mt-2 イベント情報編集 4 | 5 | = form_with(model: @event, class: "form-horizontal") do |f| 6 | #errors 7 | .form-group 8 | = f.label :name 9 | = f.text_field :name, class: "form-control" 10 | .form-group 11 | = f.label :place 12 | = f.text_field :place, class: "form-control" 13 | .form-group 14 | = f.label :start_at 15 | %div 16 | = f.datetime_select :start_at, start_year: now.year, end_year: now.year + 1 17 | .form-group 18 | = f.label :end_at 19 | %div 20 | = f.datetime_select :end_at, start_year: now.year, end_year: now.year + 1 21 | .form-group 22 | = f.label :content 23 | = f.text_area :content, class: "form-control", row: 10 24 | .form-group 25 | = f.label :image 26 | - if @event.image.attached? && @event.image.blob&.persisted? 27 | = image_tag(@event.image.variant(resize_to_fit: [200, 200]), class: "img-thumbnail d-block mb-3") 28 | = f.file_field :image, class: "form-control-file" 29 | - if @event.image.attached? && @event.image.blob&.persisted? 30 | .checkbox 31 | %label 32 | = f.check_box :remove_image 33 | 画像を削除する 34 | = f.submit class: "btn btn-primary" -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /.github/workflows/ruby.yml: -------------------------------------------------------------------------------- 1 | name: Rails Tests 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - name: Set up Ruby 2.6 9 | uses: ruby/setup-ruby@v1 10 | with: 11 | ruby-version: 2.6 12 | - name: Configure sysctl limits 13 | run: | 14 | sudo swapoff -a 15 | sudo sysctl -w vm.swappiness=1 16 | sudo sysctl -w fs.file-max=262144 17 | sudo sysctl -w vm.max_map_count=262144 18 | - name: Run Elasticsearch 19 | uses: perfect-ruby-on-rails/elasticsearch-with-plugins-action@v1 20 | with: 21 | stack-version: 7.6.0 22 | plugins: | 23 | analysis-kuromoji 24 | - name: Build and test 25 | env: 26 | RAILS_ENV: test 27 | run: | 28 | sudo apt-get -yqq install libsqlite3-dev 29 | bundle install --jobs 4 --retry 3 30 | bin/rails db:create 31 | bin/rails db:migrate 32 | bin/yarn install 33 | bin/rails test 34 | bin/rails test:system 35 | # - name: Upload 36 | # uses: actions/upload-artifact@v2 37 | # if: failure() 38 | # with: 39 | # name: my-uploads 40 | # path: tmp/screenshots/ 41 | -------------------------------------------------------------------------------- /app/controllers/events_controller.rb: -------------------------------------------------------------------------------- 1 | class EventsController < ApplicationController 2 | skip_before_action :authenticate, only: :show 3 | 4 | def show 5 | @event = Event.find(params[:id]) 6 | @ticket = current_user && current_user.tickets.find_by(event: @event) 7 | @tickets = @event.tickets.includes(:user).order(:created_at) 8 | end 9 | 10 | def new 11 | @event = current_user.created_events.build 12 | end 13 | 14 | def create 15 | @event = current_user.created_events.build(event_params) 16 | 17 | if @event.save 18 | redirect_to @event, notice: "作成しました" 19 | end 20 | end 21 | 22 | def edit 23 | @event = current_user.created_events.find(params[:id]) 24 | end 25 | 26 | def update 27 | @event = current_user.created_events.find(params[:id]) 28 | if @event.update(event_params) 29 | redirect_to @event, notice: "更新しました" 30 | end 31 | end 32 | 33 | def destroy 34 | @event = current_user.created_events.find(params[:id]) 35 | @event.destroy! 36 | redirect_to root_path, notice: "削除しました" 37 | end 38 | 39 | private 40 | 41 | def event_params 42 | params.require(:event).permit( 43 | :name, :place, :image, :remove_image, :content, :start_at, :end_at 44 | ) 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %meta{ content: "text/html; charset=UTF-8", "http-equiv": "Content-Type" } 5 | %title AwesomeEvents 6 | = csrf_meta_tags 7 | = csp_meta_tag 8 | = stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" 9 | = javascript_pack_tag "application", "data-turbolinks-track": "reload" 10 | %body 11 | %header.navbar.navbar-expand-sm.navbar-light.bg-light 12 | .container 13 | = link_to "AwesomeEvents", root_path, class: "navbar-brand mr-auto" 14 | %ul.navbar-nav 15 | %li.nav-item 16 | = link_to "イベントを作る", new_event_path, class: "nav-link" 17 | - if logged_in? 18 | %li.nav-item 19 | = link_to "退会", new_retirements_path, class: "nav-link" 20 | %li.nav-item 21 | = link_to "ログアウト", logout_path, class: "nav-link", method: :delete 22 | - else 23 | %li.nav-item 24 | = link_to "GitHubでログイン", "/auth/github", class: "nav-link", method: :post 25 | .container 26 | - if flash.notice 27 | .alert.alert-success 28 | = flash.notice 29 | - if flash.alert 30 | .alert.alert-danger 31 | = flash.alert 32 | = yield -------------------------------------------------------------------------------- /test/models/event_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class EventTest < ActiveSupport::TestCase 4 | test '#created_by? owner_id と 引数の#id が同じとき' do 5 | event = FactoryBot.create(:event) 6 | user = MiniTest::Mock.new.expect(:id, event.owner_id) 7 | assert_equal(true, event.created_by?(user)) 8 | user.verify 9 | end 10 | 11 | test '#created_by? owner_id と 引数の#id が異なるとき' do 12 | event = FactoryBot.create(:event) 13 | another_user = FactoryBot.create(:user) 14 | assert_equal(false, event.created_by?(another_user)) 15 | end 16 | 17 | test '#created_by? 引数が nil なとき' do 18 | event = FactoryBot.create(:event) 19 | assert_equal(false, event.created_by?(nil)) 20 | end 21 | 22 | test 'start_at_should_be_before_end_at validation OK' do 23 | start_at = rand(1..30).days.from_now 24 | end_at = start_at + rand(1..30).hours 25 | event = FactoryBot.build(:event, 26 | start_at: start_at, end_at: end_at) 27 | event.valid? 28 | assert_empty(event.errors[:start_at]) 29 | end 30 | 31 | test 'start_at_should_be_before_end_at validation error' do 32 | start_at = rand(1..30).days.from_now 33 | end_at = start_at - rand(1..30).hours 34 | event = FactoryBot.build(:event, 35 | start_at: start_at, end_at: end_at) 36 | event.valid? 37 | assert_not_empty(event.errors[:start_at]) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /app/models/event.rb: -------------------------------------------------------------------------------- 1 | class Event < ApplicationRecord 2 | searchkick language: "japanese" 3 | 4 | has_one_attached :image 5 | has_many :tickets, dependent: :destroy 6 | belongs_to :owner, class_name: "User" 7 | 8 | validates :image, 9 | content_type: [:png, :jpg, :jpeg], 10 | size: { less_than_or_equal_to: 10.megabytes }, 11 | dimension: { width: { max: 2000 } , height: { max: 2000 } } 12 | validates :name, length: { maximum: 50 }, presence: true 13 | validates :place, length: { maximum: 100 }, presence: true 14 | validates :content, length: { maximum: 2000 }, presence: true 15 | validates :start_at, presence: true 16 | validates :end_at, presence: true 17 | validate :start_at_should_be_before_end_at 18 | 19 | attr_accessor :remove_image 20 | 21 | before_save :remove_image_if_user_accept 22 | 23 | def created_by?(user) 24 | return false unless user 25 | owner_id == user.id 26 | end 27 | 28 | private 29 | 30 | def remove_image_if_user_accept 31 | self.image = nil if ActiveRecord::Type::Boolean.new.cast(remove_image) 32 | end 33 | 34 | def start_at_should_be_before_end_at 35 | return unless start_at && end_at 36 | 37 | if start_at >= end_at 38 | errors.add(:start_at, "は終了時間よりも前に設定してください") 39 | end 40 | end 41 | 42 | def search_data 43 | { 44 | name: name, 45 | place: place, 46 | content: content, 47 | owner_name: owner&.name, 48 | start_at: start_at 49 | } 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.config.content_security_policy do |policy| 8 | # policy.default_src :self, :https 9 | # policy.font_src :self, :https, :data 10 | # policy.img_src :self, :https, :data 11 | # policy.object_src :none 12 | # policy.script_src :self, :https 13 | # policy.style_src :self, :https 14 | # # If you are using webpack-dev-server then specify webpack-dev-server host 15 | # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development? 16 | 17 | # # Specify URI for violation reports 18 | # # policy.report_uri "/csp-violation-report-endpoint" 19 | # end 20 | 21 | # If you are using UJS then enable automatic nonce generation 22 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 23 | 24 | # Set the nonce only to specific directives 25 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src) 26 | 27 | # Report CSP violations to a specified URI 28 | # For further information see the following documentation: 29 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 30 | # Rails.application.config.content_security_policy_report_only = true 31 | -------------------------------------------------------------------------------- /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 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 9 | threads min_threads_count, max_threads_count 10 | 11 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 12 | # 13 | port ENV.fetch("PORT") { 3000 } 14 | 15 | # Specifies the `environment` that Puma will run in. 16 | # 17 | environment ENV.fetch("RAILS_ENV") { "development" } 18 | 19 | # Specifies the `pidfile` that Puma will use. 20 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } 21 | 22 | # Specifies the number of `workers` to boot in clustered mode. 23 | # Workers are forked web server processes. If using threads and workers together 24 | # the concurrency of the application would be max `threads` * `workers`. 25 | # Workers do not work on JRuby or Windows (both of which do not support 26 | # processes). 27 | # 28 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 29 | 30 | # Use the `preload_app!` method when specifying a `workers` number. 31 | # This directive tells Puma to first boot the application and load code 32 | # before forking the application. This takes advantage of Copy On Write 33 | # process behavior so workers use less memory. 34 | # 35 | # preload_app! 36 | 37 | # Allow puma to be restarted by `rails restart` command. 38 | plugin :tmp_restart 39 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # The test environment is used exclusively to run your application's 2 | # test suite. You never need to work with it otherwise. Remember that 3 | # your test database is "scratch space" for the test suite and is wiped 4 | # and recreated between test runs. Don't rely on the data there! 5 | 6 | Rails.application.configure do 7 | # Settings specified here will take precedence over those in config/application.rb. 8 | 9 | config.cache_classes = false 10 | config.action_view.cache_template_loading = true 11 | 12 | # Do not eager load code on boot. This avoids loading your whole application 13 | # just for the purpose of running a single test. If you are using a tool that 14 | # preloads Rails for running tests, you may have to set it to true. 15 | config.eager_load = false 16 | 17 | # Configure public file server for tests with Cache-Control for performance. 18 | config.public_file_server.enabled = true 19 | config.public_file_server.headers = { 20 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}" 21 | } 22 | 23 | # Show full error reports and disable caching. 24 | config.consider_all_requests_local = true 25 | config.action_controller.perform_caching = false 26 | config.cache_store = :null_store 27 | 28 | # Raise exceptions instead of rendering exception templates. 29 | config.action_dispatch.show_exceptions = false 30 | 31 | # Disable request forgery protection in test environment. 32 | config.action_controller.allow_forgery_protection = false 33 | 34 | # Store uploaded files on the local file system in a temporary directory. 35 | config.active_storage.service = :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/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |If you are the application owner check the logs for more information.
64 |Maybe you tried to change something you didn't have access to.
63 |If you are the application owner check the logs for more information.
65 |You may have mistyped the address or the page may have moved.
63 |If you are the application owner check the logs for more information.
65 |