diff options
author | Matthew Lemon <y@yulqen.org> | 2023-12-20 20:34:25 +0000 |
---|---|---|
committer | Matthew Lemon <y@yulqen.org> | 2023-12-20 20:34:25 +0000 |
commit | 6b1a1834ad1715145f047790c8391b8a5558bece (patch) | |
tree | e20cdddb20477b56a013eb3bc357b9b5ff0488a3 | |
parent | a3e98876e2f88b69fef2a9da7d65b3704b6bf0d2 (diff) |
Created new Operation model and fixed Event associations with it
29 files changed, 498 insertions, 5 deletions
diff --git a/app/controllers/operations_controller.rb b/app/controllers/operations_controller.rb new file mode 100644 index 0000000..5081932 --- /dev/null +++ b/app/controllers/operations_controller.rb @@ -0,0 +1,70 @@ +class OperationsController < ApplicationController + before_action :set_operation, only: %i[ show edit update destroy ] + + # GET /operations or /operations.json + def index + @operations = Operation.all + end + + # GET /operations/1 or /operations/1.json + def show + end + + # GET /operations/new + def new + @operation = Operation.new + end + + # GET /operations/1/edit + def edit + end + + # POST /operations or /operations.json + def create + @operation = Operation.new(operation_params) + + respond_to do |format| + if @operation.save + format.html { redirect_to operation_url(@operation), notice: "Operation was successfully created." } + format.json { render :show, status: :created, location: @operation } + else + format.html { render :new, status: :unprocessable_entity } + format.json { render json: @operation.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /operations/1 or /operations/1.json + def update + respond_to do |format| + if @operation.update(operation_params) + format.html { redirect_to operation_url(@operation), notice: "Operation was successfully updated." } + format.json { render :show, status: :ok, location: @operation } + else + format.html { render :edit, status: :unprocessable_entity } + format.json { render json: @operation.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /operations/1 or /operations/1.json + def destroy + @operation.destroy! + + respond_to do |format| + format.html { redirect_to operations_url, notice: "Operation was successfully destroyed." } + format.json { head :no_content } + end + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_operation + @operation = Operation.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def operation_params + params.require(:operation).permit(:name) + end +end diff --git a/app/helpers/operations_helper.rb b/app/helpers/operations_helper.rb new file mode 100644 index 0000000..d114c7c --- /dev/null +++ b/app/helpers/operations_helper.rb @@ -0,0 +1,2 @@ +module OperationsHelper +end diff --git a/app/models/event.rb b/app/models/event.rb index aaa4e6e..27f9aec 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -2,7 +2,7 @@ class Event < ApplicationRecord validates :name, presence: true, length: { maximum: 50 } validates :date, presence: true - belongs_to :organisation, optional: true + belongs_to :operation, optional: true def month_year_check(year, month) if self.date.year == year && self.date.month == month diff --git a/app/models/operation.rb b/app/models/operation.rb new file mode 100644 index 0000000..5506ec3 --- /dev/null +++ b/app/models/operation.rb @@ -0,0 +1,6 @@ +class Operation < ApplicationRecord + validates :name, presence: true, length: { maximum: 35 } + + has_many :events + belongs_to :organisation +end diff --git a/app/models/organisation.rb b/app/models/organisation.rb index 4177c35..cda399f 100644 --- a/app/models/organisation.rb +++ b/app/models/organisation.rb @@ -1,5 +1,5 @@ class Organisation < ApplicationRecord - has_many :events, dependent: :destroy + has_many :operations, dependent: :destroy validates :name, presence: true, length: { maximum: 25 } end diff --git a/app/views/operations/_form.html.erb b/app/views/operations/_form.html.erb new file mode 100644 index 0000000..adadad8 --- /dev/null +++ b/app/views/operations/_form.html.erb @@ -0,0 +1,22 @@ +<%= form_with(model: operation) do |form| %> + <% if operation.errors.any? %> + <div style="color: red"> + <h2><%= pluralize(operation.errors.count, "error") %> prohibited this operation from being saved:</h2> + + <ul> + <% operation.errors.each do |error| %> + <li><%= error.full_message %></li> + <% end %> + </ul> + </div> + <% end %> + + <div> + <%= form.label :name, style: "display: block" %> + <%= form.text_field :name %> + </div> + + <div> + <%= form.submit %> + </div> +<% end %> diff --git a/app/views/operations/_operation.html.erb b/app/views/operations/_operation.html.erb new file mode 100644 index 0000000..6349848 --- /dev/null +++ b/app/views/operations/_operation.html.erb @@ -0,0 +1,7 @@ +<div id="<%= dom_id operation %>"> + <p> + <strong>Name:</strong> + <%= operation.name %> + </p> + +</div> diff --git a/app/views/operations/_operation.json.jbuilder b/app/views/operations/_operation.json.jbuilder new file mode 100644 index 0000000..8016989 --- /dev/null +++ b/app/views/operations/_operation.json.jbuilder @@ -0,0 +1,2 @@ +json.extract! operation, :id, :name, :created_at, :updated_at +json.url operation_url(operation, format: :json) diff --git a/app/views/operations/edit.html.erb b/app/views/operations/edit.html.erb new file mode 100644 index 0000000..650350a --- /dev/null +++ b/app/views/operations/edit.html.erb @@ -0,0 +1,10 @@ +<h1>Editing operation</h1> + +<%= render "form", operation: @operation %> + +<br> + +<div> + <%= link_to "Show this operation", @operation %> | + <%= link_to "Back to operations", operations_path %> +</div> diff --git a/app/views/operations/index.html.erb b/app/views/operations/index.html.erb new file mode 100644 index 0000000..8bf9009 --- /dev/null +++ b/app/views/operations/index.html.erb @@ -0,0 +1,14 @@ +<p style="color: green"><%= notice %></p> + +<h1>Operations</h1> + +<div id="operations"> + <% @operations.each do |operation| %> + <%= render operation %> + <p> + <%= link_to "Show this operation", operation %> + </p> + <% end %> +</div> + +<%= link_to "New operation", new_operation_path %> diff --git a/app/views/operations/index.json.jbuilder b/app/views/operations/index.json.jbuilder new file mode 100644 index 0000000..bef1085 --- /dev/null +++ b/app/views/operations/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @operations, partial: "operations/operation", as: :operation diff --git a/app/views/operations/new.html.erb b/app/views/operations/new.html.erb new file mode 100644 index 0000000..be6230d --- /dev/null +++ b/app/views/operations/new.html.erb @@ -0,0 +1,9 @@ +<h1>New operation</h1> + +<%= render "form", operation: @operation %> + +<br> + +<div> + <%= link_to "Back to operations", operations_path %> +</div> diff --git a/app/views/operations/show.html.erb b/app/views/operations/show.html.erb new file mode 100644 index 0000000..d19705b --- /dev/null +++ b/app/views/operations/show.html.erb @@ -0,0 +1,10 @@ +<p style="color: green"><%= notice %></p> + +<%= render @operation %> + +<div> + <%= link_to "Edit this operation", edit_operation_path(@operation) %> | + <%= link_to "Back to operations", operations_path %> + + <%= button_to "Destroy this operation", @operation, method: :delete %> +</div> diff --git a/app/views/operations/show.json.jbuilder b/app/views/operations/show.json.jbuilder new file mode 100644 index 0000000..ada5250 --- /dev/null +++ b/app/views/operations/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "operations/operation", operation: @operation diff --git a/config/routes.rb b/config/routes.rb index 9a4a7ce..69b2f10 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,5 @@ Rails.application.routes.draw do + resources :operations resources :organisations resources :events #get 'pages/index' diff --git a/db/migrate/20231220194440_create_operations.rb b/db/migrate/20231220194440_create_operations.rb new file mode 100644 index 0000000..85a2ee3 --- /dev/null +++ b/db/migrate/20231220194440_create_operations.rb @@ -0,0 +1,9 @@ +class CreateOperations < ActiveRecord::Migration[7.1] + def change + create_table :operations do |t| + t.string :name + + t.timestamps + end + end +end diff --git a/db/migrate/20231220195147_remove_organisation_from_events.rb b/db/migrate/20231220195147_remove_organisation_from_events.rb new file mode 100644 index 0000000..5198e98 --- /dev/null +++ b/db/migrate/20231220195147_remove_organisation_from_events.rb @@ -0,0 +1,5 @@ +class RemoveOrganisationFromEvents < ActiveRecord::Migration[7.1] + def change + remove_reference :events, :organisation, null: false, foreign_key: true + end +end diff --git a/db/migrate/20231220195530_add_operation_to_events.rb b/db/migrate/20231220195530_add_operation_to_events.rb new file mode 100644 index 0000000..b95ded0 --- /dev/null +++ b/db/migrate/20231220195530_add_operation_to_events.rb @@ -0,0 +1,5 @@ +class AddOperationToEvents < ActiveRecord::Migration[7.1] + def change + add_reference :events, :operation, null: false, foreign_key: true + end +end diff --git a/db/migrate/20231220202718_add_organisation_ref_to_operations.rb b/db/migrate/20231220202718_add_organisation_ref_to_operations.rb new file mode 100644 index 0000000..939c6e5 --- /dev/null +++ b/db/migrate/20231220202718_add_organisation_ref_to_operations.rb @@ -0,0 +1,5 @@ +class AddOrganisationRefToOperations < ActiveRecord::Migration[7.1] + def change + add_reference :operations, :organisation, null: false, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 07579bb..4ad3bbd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,14 +10,22 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2023_12_18_190546) do +ActiveRecord::Schema[7.1].define(version: 2023_12_20_202718) do create_table "events", force: :cascade do |t| t.date "date" t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "operation_id", null: false + t.index ["operation_id"], name: "index_events_on_operation_id" + end + + create_table "operations", force: :cascade do |t| + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.integer "organisation_id", null: false - t.index ["organisation_id"], name: "index_events_on_organisation_id" + t.index ["organisation_id"], name: "index_operations_on_organisation_id" end create_table "organisations", force: :cascade do |t| @@ -26,5 +34,6 @@ ActiveRecord::Schema[7.1].define(version: 2023_12_18_190546) do t.datetime "updated_at", null: false end - add_foreign_key "events", "organisations" + add_foreign_key "events", "operations" + add_foreign_key "operations", "organisations" end diff --git a/spec/factories/operations.rb b/spec/factories/operations.rb new file mode 100644 index 0000000..87ee08f --- /dev/null +++ b/spec/factories/operations.rb @@ -0,0 +1,10 @@ +FactoryBot.define do + factory :operation do + name { "MyString" } + organisation + end + + factory :organisation do + name { "Singo Ltd" } + end +end diff --git a/spec/helpers/operations_helper_spec.rb b/spec/helpers/operations_helper_spec.rb new file mode 100644 index 0000000..b0d4550 --- /dev/null +++ b/spec/helpers/operations_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the OperationsHelper. For example: +# +# describe OperationsHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe OperationsHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/operation_spec.rb b/spec/models/operation_spec.rb new file mode 100644 index 0000000..a46a528 --- /dev/null +++ b/spec/models/operation_spec.rb @@ -0,0 +1,33 @@ +require 'rails_helper' + +RSpec.describe Operation, type: :model do + let(:organisation) { + org = build(:organisation) + # Operation.create!( + # name: "MyString" + # ) + } + + # before(:each) do + # assign(:operation, operation) + # end + subject { described_class.new(name: "Spuds", organisation: organisation) } + + describe "existence" do + it "exists!" do + expect(subject).to be_valid + end + end + + describe "validations" do + it "is invalid without a name" do + subject.name = nil + expect(subject).to be_invalid + end + + it "is invalid with a name longer than 35 chars" do + subject.name = "a" * 36 + expect(subject).to be_invalid + end + end +end diff --git a/spec/requests/operations_spec.rb b/spec/requests/operations_spec.rb new file mode 100644 index 0000000..1bb4111 --- /dev/null +++ b/spec/requests/operations_spec.rb @@ -0,0 +1,135 @@ +require 'rails_helper' + +# This spec was generated by rspec-rails when you ran the scaffold generator. +# It demonstrates how one might use RSpec to test the controller code that +# was generated by Rails when you ran the scaffold generator. +# +# It assumes that the implementation code is generated by the rails scaffold +# generator. If you are using any extension libraries to generate different +# controller code, this generated spec may or may not pass. +# +# It only uses APIs available in rails and/or rspec-rails. There are a number +# of tools you can use to make these specs even more expressive, but we're +# sticking to rails and rspec-rails APIs to keep things simple and stable. + +RSpec.describe "/operations", type: :request do + + # This should return the minimal set of attributes required to create a valid + # Operation. As you add validations to Operation, be sure to + # adjust the attributes here as well. + let(:valid_attributes) { + skip("Add a hash of attributes valid for your model") + } + + let(:invalid_attributes) { + skip("Add a hash of attributes invalid for your model") + } + + describe "GET /index" do + it "renders a successful response" do + Operation.create! valid_attributes + get operations_url + expect(response).to be_successful + end + end + + describe "GET /show" do + it "renders a successful response" do + operation = Operation.create! valid_attributes + get operation_url(operation) + expect(response).to be_successful + end + end + + describe "GET /new" do + it "renders a successful response" do + get new_operation_url + expect(response).to be_successful + end + end + + describe "GET /edit" do + it "renders a successful response" do + operation = Operation.create! valid_attributes + get edit_operation_url(operation) + expect(response).to be_successful + end + end + + describe "POST /create" do + context "with valid parameters" do + it "creates a new Operation" do + expect { + post operations_url, params: { operation: valid_attributes } + }.to change(Operation, :count).by(1) + end + + it "redirects to the created operation" do + post operations_url, params: { operation: valid_attributes } + expect(response).to redirect_to(operation_url(Operation.last)) + end + end + + context "with invalid parameters" do + it "does not create a new Operation" do + expect { + post operations_url, params: { operation: invalid_attributes } + }.to change(Operation, :count).by(0) + end + + + it "renders a response with 422 status (i.e. to display the 'new' template)" do + post operations_url, params: { operation: invalid_attributes } + expect(response).to have_http_status(:unprocessable_entity) + end + + end + end + + describe "PATCH /update" do + context "with valid parameters" do + let(:new_attributes) { + skip("Add a hash of attributes valid for your model") + } + + it "updates the requested operation" do + operation = Operation.create! valid_attributes + patch operation_url(operation), params: { operation: new_attributes } + operation.reload + skip("Add assertions for updated state") + end + + it "redirects to the operation" do + operation = Operation.create! valid_attributes + patch operation_url(operation), params: { operation: new_attributes } + operation.reload + expect(response).to redirect_to(operation_url(operation)) + end + end + + context "with invalid parameters" do + + it "renders a response with 422 status (i.e. to display the 'edit' template)" do + operation = Operation.create! valid_attributes + patch operation_url(operation), params: { operation: invalid_attributes } + expect(response).to have_http_status(:unprocessable_entity) + end + + end + end + + describe "DELETE /destroy" do + it "destroys the requested operation" do + operation = Operation.create! valid_attributes + expect { + delete operation_url(operation) + }.to change(Operation, :count).by(-1) + end + + it "redirects to the operations list" do + operation = Operation.create! valid_attributes + delete operation_url(operation) + expect(response).to redirect_to(operations_url) + end + end +end diff --git a/spec/routing/operations_routing_spec.rb b/spec/routing/operations_routing_spec.rb new file mode 100644 index 0000000..e323736 --- /dev/null +++ b/spec/routing/operations_routing_spec.rb @@ -0,0 +1,38 @@ +require "rails_helper" + +RSpec.describe OperationsController, type: :routing do + describe "routing" do + it "routes to #index" do + expect(get: "/operations").to route_to("operations#index") + end + + it "routes to #new" do + expect(get: "/operations/new").to route_to("operations#new") + end + + it "routes to #show" do + expect(get: "/operations/1").to route_to("operations#show", id: "1") + end + + it "routes to #edit" do + expect(get: "/operations/1/edit").to route_to("operations#edit", id: "1") + end + + + it "routes to #create" do + expect(post: "/operations").to route_to("operations#create") + end + + it "routes to #update via PUT" do + expect(put: "/operations/1").to route_to("operations#update", id: "1") + end + + it "routes to #update via PATCH" do + expect(patch: "/operations/1").to route_to("operations#update", id: "1") + end + + it "routes to #destroy" do + expect(delete: "/operations/1").to route_to("operations#destroy", id: "1") + end + end +end diff --git a/spec/views/operations/edit.html.erb_spec.rb b/spec/views/operations/edit.html.erb_spec.rb new file mode 100644 index 0000000..3a2b5f0 --- /dev/null +++ b/spec/views/operations/edit.html.erb_spec.rb @@ -0,0 +1,22 @@ +require 'rails_helper' + +RSpec.describe "operations/edit", type: :view do + let(:operation) { + Operation.create!( + name: "MyString" + ) + } + + before(:each) do + assign(:operation, operation) + end + + it "renders the edit operation form" do + render + + assert_select "form[action=?][method=?]", operation_path(operation), "post" do + + assert_select "input[name=?]", "operation[name]" + end + end +end diff --git a/spec/views/operations/index.html.erb_spec.rb b/spec/views/operations/index.html.erb_spec.rb new file mode 100644 index 0000000..5fa64fe --- /dev/null +++ b/spec/views/operations/index.html.erb_spec.rb @@ -0,0 +1,20 @@ +require 'rails_helper' + +RSpec.describe "operations/index", type: :view do + before(:each) do + assign(:operations, [ + Operation.create!( + name: "Name" + ), + Operation.create!( + name: "Name" + ) + ]) + end + + it "renders a list of operations" do + render + cell_selector = Rails::VERSION::STRING >= '7' ? 'div>p' : 'tr>td' + assert_select cell_selector, text: Regexp.new("Name".to_s), count: 2 + end +end diff --git a/spec/views/operations/new.html.erb_spec.rb b/spec/views/operations/new.html.erb_spec.rb new file mode 100644 index 0000000..cea6080 --- /dev/null +++ b/spec/views/operations/new.html.erb_spec.rb @@ -0,0 +1,18 @@ +require 'rails_helper' + +RSpec.describe "operations/new", type: :view do + before(:each) do + assign(:operation, Operation.new( + name: "MyString" + )) + end + + it "renders new operation form" do + render + + assert_select "form[action=?][method=?]", operations_path, "post" do + + assert_select "input[name=?]", "operation[name]" + end + end +end diff --git a/spec/views/operations/show.html.erb_spec.rb b/spec/views/operations/show.html.erb_spec.rb new file mode 100644 index 0000000..7affaf4 --- /dev/null +++ b/spec/views/operations/show.html.erb_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe "operations/show", type: :view do + before(:each) do + assign(:operation, Operation.create!( + name: "Name" + )) + end + + it "renders attributes in <p>" do + render + expect(rendered).to match(/Name/) + end +end |