From 36fe8e2de9e22d8eb96ef42e1087e7e4705d1cab Mon Sep 17 00:00:00 2001 From: Greg Date: Thu, 18 Apr 2019 17:08:27 +0200 Subject: [PATCH 01/12] Setup the project --- lecture_5/homework/.rspec | 1 + lecture_5/homework/Gemfile | 2 ++ lecture_5/homework/Gemfile.lock | 8 ++++++++ lecture_5/homework/db/schema.rb | 3 ++- 4 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 lecture_5/homework/.rspec diff --git a/lecture_5/homework/.rspec b/lecture_5/homework/.rspec new file mode 100644 index 00000000..c99d2e73 --- /dev/null +++ b/lecture_5/homework/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/lecture_5/homework/Gemfile b/lecture_5/homework/Gemfile index f10bbf07..a054281b 100644 --- a/lecture_5/homework/Gemfile +++ b/lecture_5/homework/Gemfile @@ -35,7 +35,9 @@ gem 'active_model_serializers' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console + gem 'awesome_print' gem 'byebug', platforms: %i[mri mingw x64_mingw] + gem 'factory_bot_rails' end group :development do diff --git a/lecture_5/homework/Gemfile.lock b/lecture_5/homework/Gemfile.lock index 411593e8..562eaf9e 100644 --- a/lecture_5/homework/Gemfile.lock +++ b/lecture_5/homework/Gemfile.lock @@ -49,6 +49,7 @@ GEM tzinfo (~> 1.1) arel (9.0.0) ast (2.4.0) + awesome_print (1.8.0) bootsnap (1.4.1) msgpack (~> 1.0) builder (3.2.3) @@ -59,6 +60,11 @@ GEM crass (1.0.4) diff-lcs (1.3) erubi (1.8.0) + factory_bot (5.0.2) + activesupport (>= 4.2.0) + factory_bot_rails (5.0.2) + factory_bot (~> 5.0.2) + railties (>= 4.2.0) ffi (1.10.0) globalid (0.4.2) activesupport (>= 4.2.0) @@ -177,8 +183,10 @@ PLATFORMS DEPENDENCIES active_model_serializers + awesome_print bootsnap (>= 1.1.0) byebug + factory_bot_rails listen (>= 3.0.5, < 3.2) pg puma (~> 3.11) diff --git a/lecture_5/homework/db/schema.rb b/lecture_5/homework/db/schema.rb index 13f968ab..579f0b47 100644 --- a/lecture_5/homework/db/schema.rb +++ b/lecture_5/homework/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_04_05_090544) do +ActiveRecord::Schema.define(version: 2019_04_14_111449) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -20,6 +20,7 @@ t.string "type", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "granary", default: 0, null: false end create_table "clans", force: :cascade do |t| From 1b200c6b564b9d6a7d95c1c237b944c6e72a0678 Mon Sep 17 00:00:00 2001 From: Greg Date: Thu, 18 Apr 2019 17:17:09 +0200 Subject: [PATCH 02/12] List all buildings --- .../app/controllers/buildings_controller.rb | 10 +- .../homework/app/queries/buildings_queries.rb | 7 ++ .../homework/spec/factories/buildings.rb | 9 ++ lecture_5/homework/spec/rails_helper.rb | 61 ++++++++++++ .../homework/spec/requests/buildings_spec.rb | 23 +++++ lecture_5/homework/spec/spec_helper.rb | 96 +++++++++++++++++++ 6 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 lecture_5/homework/app/queries/buildings_queries.rb create mode 100644 lecture_5/homework/spec/factories/buildings.rb create mode 100644 lecture_5/homework/spec/rails_helper.rb create mode 100644 lecture_5/homework/spec/requests/buildings_spec.rb create mode 100644 lecture_5/homework/spec/spec_helper.rb diff --git a/lecture_5/homework/app/controllers/buildings_controller.rb b/lecture_5/homework/app/controllers/buildings_controller.rb index 9b7e9677..2131425f 100644 --- a/lecture_5/homework/app/controllers/buildings_controller.rb +++ b/lecture_5/homework/app/controllers/buildings_controller.rb @@ -1,7 +1,15 @@ # frozen_string_literal: true class BuildingsController < ApplicationController - def index; end + def index + render json: buildings, status: 200 + end def show; end + + private + + def buildings + @buildings ||= BuildingsQueries.buildings(relation: Building) + end end diff --git a/lecture_5/homework/app/queries/buildings_queries.rb b/lecture_5/homework/app/queries/buildings_queries.rb new file mode 100644 index 00000000..0482e87b --- /dev/null +++ b/lecture_5/homework/app/queries/buildings_queries.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class BuildingsQueries + def self.buildings(relation:) + relation.all + end +end diff --git a/lecture_5/homework/spec/factories/buildings.rb b/lecture_5/homework/spec/factories/buildings.rb new file mode 100644 index 00000000..4759d6b9 --- /dev/null +++ b/lecture_5/homework/spec/factories/buildings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :building, class: Building do + sequence(:name) { |i| "Building_#{i}" } + type { 'Buildings::Walls' } + sequence(:granary) { |i| i * 100 } + end +end diff --git a/lecture_5/homework/spec/rails_helper.rb b/lecture_5/homework/spec/rails_helper.rb new file mode 100644 index 00000000..d73d80bd --- /dev/null +++ b/lecture_5/homework/spec/rails_helper.rb @@ -0,0 +1,61 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +require 'spec_helper' +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../../config/environment', __FILE__) +# Prevent database truncation if the environment is production +abort("The Rails environment is running in production mode!") if Rails.env.production? +require 'rspec/rails' +# Add additional requires below this line. Rails is not loaded until this point! + +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } + +# Checks for pending migrations and applies them before tests are run. +# If you are not using ActiveRecord, you can remove these lines. +begin + ActiveRecord::Migration.maintain_test_schema! +rescue ActiveRecord::PendingMigrationError => e + puts e.to_s.strip + exit 1 +end +RSpec.configure do |config| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # RSpec Rails can automatically mix in different behaviours to your tests + # based on their file location, for example enabling you to call `get` and + # `post` in specs under `spec/controllers`. + # + # You can disable this behaviour by removing the line below, and instead + # explicitly tag your specs with their type, e.g.: + # + # RSpec.describe UsersController, :type => :controller do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://relishapp.com/rspec/rspec-rails/docs + config.infer_spec_type_from_file_location! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") +end diff --git a/lecture_5/homework/spec/requests/buildings_spec.rb b/lecture_5/homework/spec/requests/buildings_spec.rb new file mode 100644 index 00000000..b3d71ed7 --- /dev/null +++ b/lecture_5/homework/spec/requests/buildings_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Buildings API', type: :request do + describe 'GET /buildings' do + context 'when there are no buildings' do + it 'responds with 200' do + get '/buildings' + expect(response).to have_http_status(200) + end + end + + context 'with existing buildings' do + before { FactoryBot.create_list(:building, 4) } + + it 'responds with 200' do + get '/buildings' + expect(response).to have_http_status(200) + end + end + end +end diff --git a/lecture_5/homework/spec/spec_helper.rb b/lecture_5/homework/spec/spec_helper.rb new file mode 100644 index 00000000..ce33d66d --- /dev/null +++ b/lecture_5/homework/spec/spec_helper.rb @@ -0,0 +1,96 @@ +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + config.disable_monkey_patching! + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end From 87de878da6d8f64b763762cfbf6f17bf9f89c618 Mon Sep 17 00:00:00 2001 From: Greg Date: Thu, 18 Apr 2019 23:31:35 +0200 Subject: [PATCH 03/12] Display no buildings message --- .../app/controllers/buildings_controller.rb | 2 + .../controllers/clans/warriors_controller.rb | 2 +- lecture_5/homework/app/models/building.rb | 4 + lecture_5/homework/app/nulls/no_buildings.rb | 7 ++ .../app/services/reports/siege_report.rb | 3 +- lecture_5/homework/spec/rails_helper.rb | 6 +- .../homework/spec/requests/buildings_spec.rb | 14 ++- lecture_5/homework/spec/spec_helper.rb | 98 +++++++++---------- 8 files changed, 78 insertions(+), 58 deletions(-) create mode 100644 lecture_5/homework/app/nulls/no_buildings.rb diff --git a/lecture_5/homework/app/controllers/buildings_controller.rb b/lecture_5/homework/app/controllers/buildings_controller.rb index 2131425f..0c8a8578 100644 --- a/lecture_5/homework/app/controllers/buildings_controller.rb +++ b/lecture_5/homework/app/controllers/buildings_controller.rb @@ -11,5 +11,7 @@ def show; end def buildings @buildings ||= BuildingsQueries.buildings(relation: Building) + + { message: NoBuildings.new.buildings } if @buildings.empty? end end diff --git a/lecture_5/homework/app/controllers/clans/warriors_controller.rb b/lecture_5/homework/app/controllers/clans/warriors_controller.rb index 6ccf3ff9..acab2d6a 100644 --- a/lecture_5/homework/app/controllers/clans/warriors_controller.rb +++ b/lecture_5/homework/app/controllers/clans/warriors_controller.rb @@ -10,7 +10,7 @@ def index warriors = clan.warriors if params.key?(:alive) - if params[:alive].to_i == 0 + if params[:alive].to_i.zero? render json: warriors.dead else render json: warriors.alive diff --git a/lecture_5/homework/app/models/building.rb b/lecture_5/homework/app/models/building.rb index 23c41918..544ec475 100644 --- a/lecture_5/homework/app/models/building.rb +++ b/lecture_5/homework/app/models/building.rb @@ -5,4 +5,8 @@ class Building < ApplicationRecord validates :granary, presence: true, numericality: { greater_than_or_equal_to: 0, only_integer: true } + + def buildings + super || NoBuildings.new.buildings + end end diff --git a/lecture_5/homework/app/nulls/no_buildings.rb b/lecture_5/homework/app/nulls/no_buildings.rb new file mode 100644 index 00000000..8ac50c16 --- /dev/null +++ b/lecture_5/homework/app/nulls/no_buildings.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class NoBuildings + def buildings + 'There are no buildings' + end +end diff --git a/lecture_5/homework/app/services/reports/siege_report.rb b/lecture_5/homework/app/services/reports/siege_report.rb index 16a0b3a4..9d1f4e61 100644 --- a/lecture_5/homework/app/services/reports/siege_report.rb +++ b/lecture_5/homework/app/services/reports/siege_report.rb @@ -2,8 +2,7 @@ module Reports class SiegeReport - def initialize(building:) - end + def initialize(building:); end def call raise NotImprementedYet diff --git a/lecture_5/homework/spec/rails_helper.rb b/lecture_5/homework/spec/rails_helper.rb index d73d80bd..74c5ac36 100644 --- a/lecture_5/homework/spec/rails_helper.rb +++ b/lecture_5/homework/spec/rails_helper.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + # This file is copied to spec/ when you run 'rails generate rspec:install' require 'spec_helper' ENV['RAILS_ENV'] ||= 'test' -require File.expand_path('../../config/environment', __FILE__) +require File.expand_path('../config/environment', __dir__) # Prevent database truncation if the environment is production -abort("The Rails environment is running in production mode!") if Rails.env.production? +abort('The Rails environment is running in production mode!') if Rails.env.production? require 'rspec/rails' # Add additional requires below this line. Rails is not loaded until this point! diff --git a/lecture_5/homework/spec/requests/buildings_spec.rb b/lecture_5/homework/spec/requests/buildings_spec.rb index b3d71ed7..66cd6a6f 100644 --- a/lecture_5/homework/spec/requests/buildings_spec.rb +++ b/lecture_5/homework/spec/requests/buildings_spec.rb @@ -3,11 +3,17 @@ require 'rails_helper' RSpec.describe 'Buildings API', type: :request do + subject { get '/buildings' } + describe 'GET /buildings' do + it 'responds with 200' do + subject + expect(response).to have_http_status(200) + end context 'when there are no buildings' do - it 'responds with 200' do - get '/buildings' - expect(response).to have_http_status(200) + it 'return "There are no buildings" message' do + subject + expect(response.body).to include('There are no buildings') end end @@ -15,7 +21,7 @@ before { FactoryBot.create_list(:building, 4) } it 'responds with 200' do - get '/buildings' + subject expect(response).to have_http_status(200) end end diff --git a/lecture_5/homework/spec/spec_helper.rb b/lecture_5/homework/spec/spec_helper.rb index ce33d66d..01f7c974 100644 --- a/lecture_5/homework/spec/spec_helper.rb +++ b/lecture_5/homework/spec/spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file was generated by the `rails generate rspec:install` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause @@ -44,53 +46,51 @@ # triggering implicit auto-inclusion in groups with matching metadata. config.shared_context_metadata_behavior = :apply_to_host_groups -# The settings below are suggested to provide a good initial experience -# with RSpec, but feel free to customize to your heart's content. -=begin - # This allows you to limit a spec run to individual examples or groups - # you care about by tagging them with `:focus` metadata. When nothing - # is tagged with `:focus`, all examples get run. RSpec also provides - # aliases for `it`, `describe`, and `context` that include `:focus` - # metadata: `fit`, `fdescribe` and `fcontext`, respectively. - config.filter_run_when_matching :focus - - # Allows RSpec to persist some state between runs in order to support - # the `--only-failures` and `--next-failure` CLI options. We recommend - # you configure your source control system to ignore this file. - config.example_status_persistence_file_path = "spec/examples.txt" - - # Limits the available syntax to the non-monkey patched syntax that is - # recommended. For more details, see: - # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ - # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ - # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode - config.disable_monkey_patching! - - # Many RSpec users commonly either run the entire suite or an individual - # file, and it's useful to allow more verbose output when running an - # individual spec file. - if config.files_to_run.one? - # Use the documentation formatter for detailed output, - # unless a formatter has already been configured - # (e.g. via a command-line flag). - config.default_formatter = "doc" - end - - # Print the 10 slowest examples and example groups at the - # end of the spec run, to help surface which specs are running - # particularly slow. - config.profile_examples = 10 - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = :random - - # Seed global randomization in this process using the `--seed` CLI option. - # Setting this allows you to use `--seed` to deterministically reproduce - # test failures related to randomization by passing the same `--seed` value - # as the one that triggered the failure. - Kernel.srand config.seed -=end + # The settings below are suggested to provide a good initial experience + # with RSpec, but feel free to customize to your heart's content. + # # This allows you to limit a spec run to individual examples or groups + # # you care about by tagging them with `:focus` metadata. When nothing + # # is tagged with `:focus`, all examples get run. RSpec also provides + # # aliases for `it`, `describe`, and `context` that include `:focus` + # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + # config.filter_run_when_matching :focus + # + # # Allows RSpec to persist some state between runs in order to support + # # the `--only-failures` and `--next-failure` CLI options. We recommend + # # you configure your source control system to ignore this file. + # config.example_status_persistence_file_path = "spec/examples.txt" + # + # # Limits the available syntax to the non-monkey patched syntax that is + # # recommended. For more details, see: + # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + # config.disable_monkey_patching! + # + # # Many RSpec users commonly either run the entire suite or an individual + # # file, and it's useful to allow more verbose output when running an + # # individual spec file. + # if config.files_to_run.one? + # # Use the documentation formatter for detailed output, + # # unless a formatter has already been configured + # # (e.g. via a command-line flag). + # config.default_formatter = "doc" + # end + # + # # Print the 10 slowest examples and example groups at the + # # end of the spec run, to help surface which specs are running + # # particularly slow. + # config.profile_examples = 10 + # + # # Run specs in random order to surface order dependencies. If you find an + # # order dependency and want to debug it, you can fix the order by providing + # # the seed, which is printed after each run. + # # --seed 1234 + # config.order = :random + # + # # Seed global randomization in this process using the `--seed` CLI option. + # # Setting this allows you to use `--seed` to deterministically reproduce + # # test failures related to randomization by passing the same `--seed` value + # # as the one that triggered the failure. + # Kernel.srand config.seed end From a8319124fcceaf39ee139782bd1da01eb2459040 Mon Sep 17 00:00:00 2001 From: Greg Date: Wed, 24 Apr 2019 21:22:46 +0200 Subject: [PATCH 04/12] Add NullObject pattern to handle cases when there is no buildings --- lecture_5/homework/Gemfile | 1 + lecture_5/homework/Gemfile.lock | 2 ++ .../app/controllers/buildings_controller.rb | 6 ++++-- lecture_5/homework/app/models/building.rb | 4 ---- lecture_5/homework/app/nulls/no_buildings.rb | 2 +- .../homework/app/queries/buildings_queries.rb | 2 +- lecture_5/homework/spec/factories/buildings.rb | 2 +- lecture_5/homework/spec/rails_helper.rb | 6 ++++++ .../homework/spec/requests/buildings_spec.rb | 16 ++++++++++++---- 9 files changed, 28 insertions(+), 13 deletions(-) diff --git a/lecture_5/homework/Gemfile b/lecture_5/homework/Gemfile index a054281b..07436e96 100644 --- a/lecture_5/homework/Gemfile +++ b/lecture_5/homework/Gemfile @@ -37,6 +37,7 @@ group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'awesome_print' gem 'byebug', platforms: %i[mri mingw x64_mingw] + gem 'database_cleaner' gem 'factory_bot_rails' end diff --git a/lecture_5/homework/Gemfile.lock b/lecture_5/homework/Gemfile.lock index 562eaf9e..8baee5bf 100644 --- a/lecture_5/homework/Gemfile.lock +++ b/lecture_5/homework/Gemfile.lock @@ -58,6 +58,7 @@ GEM activesupport concurrent-ruby (1.1.5) crass (1.0.4) + database_cleaner (1.7.0) diff-lcs (1.3) erubi (1.8.0) factory_bot (5.0.2) @@ -186,6 +187,7 @@ DEPENDENCIES awesome_print bootsnap (>= 1.1.0) byebug + database_cleaner factory_bot_rails listen (>= 3.0.5, < 3.2) pg diff --git a/lecture_5/homework/app/controllers/buildings_controller.rb b/lecture_5/homework/app/controllers/buildings_controller.rb index 0c8a8578..c421802c 100644 --- a/lecture_5/homework/app/controllers/buildings_controller.rb +++ b/lecture_5/homework/app/controllers/buildings_controller.rb @@ -10,8 +10,10 @@ def show; end private def buildings - @buildings ||= BuildingsQueries.buildings(relation: Building) + @buildings ||= fetch_buildings.present? ? fetch_buildings : NoBuildings.new.buildings + end - { message: NoBuildings.new.buildings } if @buildings.empty? + def fetch_buildings + BuildingsQueries.buildings(relation: Building) end end diff --git a/lecture_5/homework/app/models/building.rb b/lecture_5/homework/app/models/building.rb index 544ec475..23c41918 100644 --- a/lecture_5/homework/app/models/building.rb +++ b/lecture_5/homework/app/models/building.rb @@ -5,8 +5,4 @@ class Building < ApplicationRecord validates :granary, presence: true, numericality: { greater_than_or_equal_to: 0, only_integer: true } - - def buildings - super || NoBuildings.new.buildings - end end diff --git a/lecture_5/homework/app/nulls/no_buildings.rb b/lecture_5/homework/app/nulls/no_buildings.rb index 8ac50c16..4930fd08 100644 --- a/lecture_5/homework/app/nulls/no_buildings.rb +++ b/lecture_5/homework/app/nulls/no_buildings.rb @@ -2,6 +2,6 @@ class NoBuildings def buildings - 'There are no buildings' + { message: 'There are no buildings' } end end diff --git a/lecture_5/homework/app/queries/buildings_queries.rb b/lecture_5/homework/app/queries/buildings_queries.rb index 0482e87b..c428dc81 100644 --- a/lecture_5/homework/app/queries/buildings_queries.rb +++ b/lecture_5/homework/app/queries/buildings_queries.rb @@ -2,6 +2,6 @@ class BuildingsQueries def self.buildings(relation:) - relation.all + relation.includes(:warriors).all end end diff --git a/lecture_5/homework/spec/factories/buildings.rb b/lecture_5/homework/spec/factories/buildings.rb index 4759d6b9..f01664cd 100644 --- a/lecture_5/homework/spec/factories/buildings.rb +++ b/lecture_5/homework/spec/factories/buildings.rb @@ -3,7 +3,7 @@ FactoryBot.define do factory :building, class: Building do sequence(:name) { |i| "Building_#{i}" } - type { 'Buildings::Walls' } + sequence(:type) { |i| "Buildings::#{i.even? ? 'Walls' : 'Stronghold'}" } sequence(:granary) { |i| i * 100 } end end diff --git a/lecture_5/homework/spec/rails_helper.rb b/lecture_5/homework/spec/rails_helper.rb index 74c5ac36..429ba04c 100644 --- a/lecture_5/homework/spec/rails_helper.rb +++ b/lecture_5/homework/spec/rails_helper.rb @@ -36,6 +36,8 @@ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" + config.include FactoryBot::Syntax::Methods + # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. @@ -60,4 +62,8 @@ config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") + + config.before(:example) do + DatabaseCleaner.clean_with(:truncation) + end end diff --git a/lecture_5/homework/spec/requests/buildings_spec.rb b/lecture_5/homework/spec/requests/buildings_spec.rb index 66cd6a6f..b9283970 100644 --- a/lecture_5/homework/spec/requests/buildings_spec.rb +++ b/lecture_5/homework/spec/requests/buildings_spec.rb @@ -3,27 +3,35 @@ require 'rails_helper' RSpec.describe 'Buildings API', type: :request do - subject { get '/buildings' } - describe 'GET /buildings' do + subject { get '/buildings' } + it 'responds with 200' do subject expect(response).to have_http_status(200) end + context 'when there are no buildings' do it 'return "There are no buildings" message' do subject - expect(response.body).to include('There are no buildings') + response_json = JSON.parse(response.body) + expect(response_json['message']).to include('There are no buildings') end end context 'with existing buildings' do - before { FactoryBot.create_list(:building, 4) } + before { create_list(:building, 4) } it 'responds with 200' do subject expect(response).to have_http_status(200) end + + it 'includes building data' do + subject + response_json = JSON.parse(response.body) + expect(response_json['data'].size).to eq(4) + end end end end From 93d7a7948af021e6c429006088dd9673d5816ed4 Mon Sep 17 00:00:00 2001 From: Greg Date: Wed, 24 Apr 2019 22:53:22 +0200 Subject: [PATCH 05/12] Add GET /buildings/:id endpoint --- .../app/controllers/buildings_controller.rb | 8 +++- .../homework/app/queries/buildings_queries.rb | 4 ++ .../homework/spec/requests/buildings_spec.rb | 44 +++++++++++++++++-- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/lecture_5/homework/app/controllers/buildings_controller.rb b/lecture_5/homework/app/controllers/buildings_controller.rb index c421802c..dc8bd575 100644 --- a/lecture_5/homework/app/controllers/buildings_controller.rb +++ b/lecture_5/homework/app/controllers/buildings_controller.rb @@ -5,7 +5,9 @@ def index render json: buildings, status: 200 end - def show; end + def show + render json: building + end private @@ -16,4 +18,8 @@ def buildings def fetch_buildings BuildingsQueries.buildings(relation: Building) end + + def building + @building = BuildingsQueries.building(relation: Building, id: params[:id]) + end end diff --git a/lecture_5/homework/app/queries/buildings_queries.rb b/lecture_5/homework/app/queries/buildings_queries.rb index c428dc81..037593c8 100644 --- a/lecture_5/homework/app/queries/buildings_queries.rb +++ b/lecture_5/homework/app/queries/buildings_queries.rb @@ -4,4 +4,8 @@ class BuildingsQueries def self.buildings(relation:) relation.includes(:warriors).all end + + def self.building(relation:, id:) + relation.find(id) + end end diff --git a/lecture_5/homework/spec/requests/buildings_spec.rb b/lecture_5/homework/spec/requests/buildings_spec.rb index b9283970..4873b46d 100644 --- a/lecture_5/homework/spec/requests/buildings_spec.rb +++ b/lecture_5/homework/spec/requests/buildings_spec.rb @@ -11,7 +11,7 @@ expect(response).to have_http_status(200) end - context 'when there are no buildings' do + context 'when there are no Buildings' do it 'return "There are no buildings" message' do subject response_json = JSON.parse(response.body) @@ -19,7 +19,7 @@ end end - context 'with existing buildings' do + context 'with existing Buildings' do before { create_list(:building, 4) } it 'responds with 200' do @@ -27,11 +27,49 @@ expect(response).to have_http_status(200) end - it 'includes building data' do + it 'includes Building data' do subject response_json = JSON.parse(response.body) expect(response_json['data'].size).to eq(4) end end end + + describe 'GET /buildings/:id' do + let(:building_id) { 1 } + subject { get "/buildings/#{building_id}" } + + context 'when Building is not found' do + it 'responds with 404' do + subject + expect(response).to have_http_status(404) + end + + it 'includes the "Couldn\'t find Building with \'id\'=1" message' do + subject + response_json = JSON.parse(response.body) + expect(response_json['error']).to include("Couldn't find Building with 'id'=1") + end + end + + context 'when Building is found' do + before { create(:building, id: building_id, name: 'Building_1') } + + it 'responds with 200' do + subject + expect(response).to have_http_status(200) + end + + it 'returns data of Building with id = 1' do + subject + response_json = JSON.parse(response.body) + expect(response_json['data']).to include( + 'id' => building_id.to_s, + 'attributes' => hash_including( + 'name' => 'Building_1' + ) + ) + end + end + end end From d6d0b79627a238fd494367515da0364d8f09a460 Mon Sep 17 00:00:00 2001 From: Greg Date: Thu, 25 Apr 2019 23:44:32 +0200 Subject: [PATCH 06/12] Add siege_ability field --- lecture_5/homework/app/serializers/building_serializer.rb | 2 +- .../20190425195137_add_siege_ability_to_buildings.rb | 5 +++++ lecture_5/homework/db/schema.rb | 3 ++- lecture_5/homework/spec/factories/buildings.rb | 1 + lecture_5/homework/spec/requests/buildings_spec.rb | 7 ++++--- 5 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 lecture_5/homework/db/migrate/20190425195137_add_siege_ability_to_buildings.rb diff --git a/lecture_5/homework/app/serializers/building_serializer.rb b/lecture_5/homework/app/serializers/building_serializer.rb index 899c9aa0..92430c53 100644 --- a/lecture_5/homework/app/serializers/building_serializer.rb +++ b/lecture_5/homework/app/serializers/building_serializer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class BuildingSerializer < ActiveModel::Serializer - attributes :name + attributes :name, :siege_ability has_many :warriors end diff --git a/lecture_5/homework/db/migrate/20190425195137_add_siege_ability_to_buildings.rb b/lecture_5/homework/db/migrate/20190425195137_add_siege_ability_to_buildings.rb new file mode 100644 index 00000000..63632bc5 --- /dev/null +++ b/lecture_5/homework/db/migrate/20190425195137_add_siege_ability_to_buildings.rb @@ -0,0 +1,5 @@ +class AddSiegeAbilityToBuildings < ActiveRecord::Migration[5.2] + def change + add_column :buildings, :siege_ability, :float, default: 0.0, null: false + end +end diff --git a/lecture_5/homework/db/schema.rb b/lecture_5/homework/db/schema.rb index 579f0b47..a96677f3 100644 --- a/lecture_5/homework/db/schema.rb +++ b/lecture_5/homework/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_04_14_111449) do +ActiveRecord::Schema.define(version: 2019_04_25_195137) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -21,6 +21,7 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "granary", default: 0, null: false + t.float "siege_ability", default: 0.0, null: false end create_table "clans", force: :cascade do |t| diff --git a/lecture_5/homework/spec/factories/buildings.rb b/lecture_5/homework/spec/factories/buildings.rb index f01664cd..37233f09 100644 --- a/lecture_5/homework/spec/factories/buildings.rb +++ b/lecture_5/homework/spec/factories/buildings.rb @@ -3,6 +3,7 @@ FactoryBot.define do factory :building, class: Building do sequence(:name) { |i| "Building_#{i}" } + sequence(:siege_ability) { 0.0 } sequence(:type) { |i| "Buildings::#{i.even? ? 'Walls' : 'Stronghold'}" } sequence(:granary) { |i| i * 100 } end diff --git a/lecture_5/homework/spec/requests/buildings_spec.rb b/lecture_5/homework/spec/requests/buildings_spec.rb index 4873b46d..4f468f84 100644 --- a/lecture_5/homework/spec/requests/buildings_spec.rb +++ b/lecture_5/homework/spec/requests/buildings_spec.rb @@ -65,9 +65,10 @@ response_json = JSON.parse(response.body) expect(response_json['data']).to include( 'id' => building_id.to_s, - 'attributes' => hash_including( - 'name' => 'Building_1' - ) + 'attributes' => { + 'name' => 'Building_1', + 'siege-ability' => 0.0 + } ) end end From c7073f7d1a18dbc10f354703e040a379f6d2dee6 Mon Sep 17 00:00:00 2001 From: Greg Date: Sat, 27 Apr 2019 20:06:36 +0200 Subject: [PATCH 07/12] Compute siege report when there is no army in the building --- .../app/services/reports/siege_report.rb | 20 +++++++++++++++++-- ...25195137_add_siege_ability_to_buildings.rb | 2 +- lecture_5/homework/db/schema.rb | 2 +- .../homework/spec/factories/buildings.rb | 3 +-- .../homework/spec/requests/buildings_spec.rb | 2 +- .../services/reports/siege_report_spec.rb | 19 ++++++++++++++++++ 6 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 lecture_5/homework/spec/services/reports/siege_report_spec.rb diff --git a/lecture_5/homework/app/services/reports/siege_report.rb b/lecture_5/homework/app/services/reports/siege_report.rb index 9d1f4e61..9c8d10c4 100644 --- a/lecture_5/homework/app/services/reports/siege_report.rb +++ b/lecture_5/homework/app/services/reports/siege_report.rb @@ -2,10 +2,26 @@ module Reports class SiegeReport - def initialize(building:); end + def initialize(building:) + @building = building + end def call - raise NotImprementedYet + no_army? ? no_siege_ability : create_siege_report + end + + private + + def no_army? + @building.warriors.empty? + end + + def no_siege_ability + @building.siege_ability = 0 + end + + def create_siege_report + @building.siege_ability end end end diff --git a/lecture_5/homework/db/migrate/20190425195137_add_siege_ability_to_buildings.rb b/lecture_5/homework/db/migrate/20190425195137_add_siege_ability_to_buildings.rb index 63632bc5..ce361b71 100644 --- a/lecture_5/homework/db/migrate/20190425195137_add_siege_ability_to_buildings.rb +++ b/lecture_5/homework/db/migrate/20190425195137_add_siege_ability_to_buildings.rb @@ -1,5 +1,5 @@ class AddSiegeAbilityToBuildings < ActiveRecord::Migration[5.2] def change - add_column :buildings, :siege_ability, :float, default: 0.0, null: false + add_column :buildings, :siege_ability, :integer end end diff --git a/lecture_5/homework/db/schema.rb b/lecture_5/homework/db/schema.rb index a96677f3..289fb16b 100644 --- a/lecture_5/homework/db/schema.rb +++ b/lecture_5/homework/db/schema.rb @@ -21,7 +21,7 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "granary", default: 0, null: false - t.float "siege_ability", default: 0.0, null: false + t.integer "siege_ability" end create_table "clans", force: :cascade do |t| diff --git a/lecture_5/homework/spec/factories/buildings.rb b/lecture_5/homework/spec/factories/buildings.rb index 37233f09..a379ba96 100644 --- a/lecture_5/homework/spec/factories/buildings.rb +++ b/lecture_5/homework/spec/factories/buildings.rb @@ -3,8 +3,7 @@ FactoryBot.define do factory :building, class: Building do sequence(:name) { |i| "Building_#{i}" } - sequence(:siege_ability) { 0.0 } sequence(:type) { |i| "Buildings::#{i.even? ? 'Walls' : 'Stronghold'}" } - sequence(:granary) { |i| i * 100 } + granary { 100 } end end diff --git a/lecture_5/homework/spec/requests/buildings_spec.rb b/lecture_5/homework/spec/requests/buildings_spec.rb index 4f468f84..92160d0c 100644 --- a/lecture_5/homework/spec/requests/buildings_spec.rb +++ b/lecture_5/homework/spec/requests/buildings_spec.rb @@ -67,7 +67,7 @@ 'id' => building_id.to_s, 'attributes' => { 'name' => 'Building_1', - 'siege-ability' => 0.0 + 'siege-ability' => 0 } ) end diff --git a/lecture_5/homework/spec/services/reports/siege_report_spec.rb b/lecture_5/homework/spec/services/reports/siege_report_spec.rb new file mode 100644 index 00000000..59371d5c --- /dev/null +++ b/lecture_5/homework/spec/services/reports/siege_report_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Reports::SiegeReport, type: :service do + subject(:siege_report) { described_class.new(params).call } + + let(:params) { { building: building } } + let(:building) { create(:building, warriors: army) } + let(:army) { [] } + + describe '#call' do + context 'with building without army' do + it 'returns report with siege ability equals to 0' do + expect(subject).to eq(0.0) + end + end + end +end From ee6fcf3755532d42db8ffbec6eabc5cd9c5dcc3a Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 28 Apr 2019 10:40:00 +0200 Subject: [PATCH 08/12] Handle preparing reports when building has an army --- .../homework/app/queries/buildings_queries.rb | 8 +++++ .../app/services/reports/siege_report.rb | 32 ++++++++++++++++++- lecture_5/homework/spec/factories/clans.rb | 7 ++++ lecture_5/homework/spec/factories/warriors.rb | 23 +++++++++++++ .../services/reports/siege_report_spec.rb | 28 +++++++++++++++- 5 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 lecture_5/homework/spec/factories/clans.rb create mode 100644 lecture_5/homework/spec/factories/warriors.rb diff --git a/lecture_5/homework/app/queries/buildings_queries.rb b/lecture_5/homework/app/queries/buildings_queries.rb index 037593c8..ac9224cd 100644 --- a/lecture_5/homework/app/queries/buildings_queries.rb +++ b/lecture_5/homework/app/queries/buildings_queries.rb @@ -8,4 +8,12 @@ def self.buildings(relation:) def self.building(relation:, id:) relation.find(id) end + + def self.hussars(relation:, building:) + relation.find(building.id).warriors.where(type: 'Warriors::Hussar').count + end + + def self.samurais(relation:, building:) + relation.find(building.id).warriors.where(type: 'Warriors::Samurai').count + end end diff --git a/lecture_5/homework/app/services/reports/siege_report.rb b/lecture_5/homework/app/services/reports/siege_report.rb index 9c8d10c4..99e62b79 100644 --- a/lecture_5/homework/app/services/reports/siege_report.rb +++ b/lecture_5/homework/app/services/reports/siege_report.rb @@ -21,7 +21,37 @@ def no_siege_ability end def create_siege_report - @building.siege_ability + daily_food_demand = compute_daily_food_demand + result = compute_siege_ability(daily_food_demand) + save_siege_report_result(result) + end + + def compute_daily_food_demand + stronghold_staff + hussars + samurais + end + + def stronghold_staff + 10 + end + + def hussars + BuildingsQueries.hussars(relation: Building, building: @building) * 2 + end + + def samurais + BuildingsQueries.samurais(relation: Building, building: @building) + end + + def compute_siege_ability(daily_food_demand) + granary / daily_food_demand + end + + def granary + @building.granary + end + + def save_siege_report_result(result) + @building.siege_ability = result end end end diff --git a/lecture_5/homework/spec/factories/clans.rb b/lecture_5/homework/spec/factories/clans.rb new file mode 100644 index 00000000..106801f7 --- /dev/null +++ b/lecture_5/homework/spec/factories/clans.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :clan, class: Clan do + sequence(:name) { |i| "Clan number #{i}" } + end +end diff --git a/lecture_5/homework/spec/factories/warriors.rb b/lecture_5/homework/spec/factories/warriors.rb new file mode 100644 index 00000000..b1c49f28 --- /dev/null +++ b/lecture_5/homework/spec/factories/warriors.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :warrior, class: Warrior do + association(:clan) + association(:building) + sequence(:type) { |i| i.even? ? 'Warriors::Hussar' : 'Warriors::Samurai' } + sequence(:name) { |i| "Warrior #{i}" } + preferred_weapon_kind { 'melee' } + armor_quality { 0 } + number_of_battles { 0 } + join_date { Time.now } + death_date { nil } + + trait :hussar do + type { 'Warriors::Hussar' } + end + + trait :samurai do + type { 'Warriors::Samurai' } + end + end +end diff --git a/lecture_5/homework/spec/services/reports/siege_report_spec.rb b/lecture_5/homework/spec/services/reports/siege_report_spec.rb index 59371d5c..bc9de3d9 100644 --- a/lecture_5/homework/spec/services/reports/siege_report_spec.rb +++ b/lecture_5/homework/spec/services/reports/siege_report_spec.rb @@ -12,7 +12,33 @@ describe '#call' do context 'with building without army' do it 'returns report with siege ability equals to 0' do - expect(subject).to eq(0.0) + expect(siege_report).to eq(0) + end + end + + context 'with building which has an army' do + context 'when 10 samurais' do + let(:army) { create_list(:warrior, 10, :samurai) } + + it 'returns report with siege ability equals to 5' do + expect(siege_report).to eq(5) + end + end + + context 'when 10 hussars' do + let(:army) { create_list(:warrior, 10, :hussar) } + + it 'returns report with siege ability equals to 3' do + expect(siege_report).to eq(3) + end + end + + context 'when 5 hussars and 5 samurais' do + let(:army) { create_list(:warrior, 10) } + + it 'returns report with siege ability equals to 4' do + expect(siege_report).to eq(4) + end end end end From 081b99814902db0d639a1b8e6ad393d2a94dcff9 Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 28 Apr 2019 13:50:13 +0200 Subject: [PATCH 09/12] Refactor methods names and dry out query --- .../homework/app/queries/buildings_queries.rb | 8 ++------ .../app/services/reports/siege_report.rb | 20 +++++++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lecture_5/homework/app/queries/buildings_queries.rb b/lecture_5/homework/app/queries/buildings_queries.rb index ac9224cd..afc41631 100644 --- a/lecture_5/homework/app/queries/buildings_queries.rb +++ b/lecture_5/homework/app/queries/buildings_queries.rb @@ -9,11 +9,7 @@ def self.building(relation:, id:) relation.find(id) end - def self.hussars(relation:, building:) - relation.find(building.id).warriors.where(type: 'Warriors::Hussar').count - end - - def self.samurais(relation:, building:) - relation.find(building.id).warriors.where(type: 'Warriors::Samurai').count + def self.count_warriors_of_specified_type(relation:, building:, type:) + relation.find(building.id).warriors.where(type: type).count end end diff --git a/lecture_5/homework/app/services/reports/siege_report.rb b/lecture_5/homework/app/services/reports/siege_report.rb index 99e62b79..b0966179 100644 --- a/lecture_5/homework/app/services/reports/siege_report.rb +++ b/lecture_5/homework/app/services/reports/siege_report.rb @@ -7,7 +7,7 @@ def initialize(building:) end def call - no_army? ? no_siege_ability : create_siege_report + no_army? ? no_siege_ability : compute_siege_ability end private @@ -20,9 +20,9 @@ def no_siege_ability @building.siege_ability = 0 end - def create_siege_report + def compute_siege_ability daily_food_demand = compute_daily_food_demand - result = compute_siege_ability(daily_food_demand) + result = siege_ability(daily_food_demand) save_siege_report_result(result) end @@ -35,14 +35,22 @@ def stronghold_staff end def hussars - BuildingsQueries.hussars(relation: Building, building: @building) * 2 + BuildingsQueries.count_warriors_of_specified_type( + relation: Building, + building: @building, + type: 'Warriors::Hussar' + ) * 2 end def samurais - BuildingsQueries.samurais(relation: Building, building: @building) + BuildingsQueries.count_warriors_of_specified_type( + relation: Building, + building: @building, + type: 'Warriors::Samurai' + ) end - def compute_siege_ability(daily_food_demand) + def siege_ability(daily_food_demand) granary / daily_food_demand end From 7c2d767c88ce97f5c4ce2af2edad60704de17fa4 Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 28 Apr 2019 13:51:59 +0200 Subject: [PATCH 10/12] Add specs checking another edge cases --- .../app/services/reports/siege_report.rb | 2 +- .../spec/services/reports/siege_report_spec.rb | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lecture_5/homework/app/services/reports/siege_report.rb b/lecture_5/homework/app/services/reports/siege_report.rb index b0966179..2e8124b0 100644 --- a/lecture_5/homework/app/services/reports/siege_report.rb +++ b/lecture_5/homework/app/services/reports/siege_report.rb @@ -39,7 +39,7 @@ def hussars relation: Building, building: @building, type: 'Warriors::Hussar' - ) * 2 + ) * 2 end def samurais diff --git a/lecture_5/homework/spec/services/reports/siege_report_spec.rb b/lecture_5/homework/spec/services/reports/siege_report_spec.rb index bc9de3d9..4934e9f4 100644 --- a/lecture_5/homework/spec/services/reports/siege_report_spec.rb +++ b/lecture_5/homework/spec/services/reports/siege_report_spec.rb @@ -40,6 +40,23 @@ expect(siege_report).to eq(4) end end + + context 'when the number of warriors exceeds daily granary abilities' do + let(:army) { create_list(:warrior, 100) } + + it 'returns report with siege ability equals to 0' do + expect(siege_report).to eq(0) + end + end + end + + context 'with building which has an army but the granary is empty' do + let(:building) { create(:building, warriors: army, granary: 0) } + let(:army) { create_list(:warrior, 10) } + + it 'returns report with siege ability equals to 0' do + expect(siege_report).to eq(0) + end end end end From 10b857521fa62a83546179165a168bceeb7c0d18 Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 28 Apr 2019 14:11:33 +0200 Subject: [PATCH 11/12] Change ActiveModelSerializer config to set in attrs names underscore instead of dash --- lecture_5/homework/app/controllers/buildings_controller.rb | 2 +- .../homework/config/initializers/active_model_serializers.rb | 1 + lecture_5/homework/spec/requests/buildings_spec.rb | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lecture_5/homework/app/controllers/buildings_controller.rb b/lecture_5/homework/app/controllers/buildings_controller.rb index dc8bd575..a0a7bd4c 100644 --- a/lecture_5/homework/app/controllers/buildings_controller.rb +++ b/lecture_5/homework/app/controllers/buildings_controller.rb @@ -20,6 +20,6 @@ def fetch_buildings end def building - @building = BuildingsQueries.building(relation: Building, id: params[:id]) + @building ||= BuildingsQueries.building(relation: Building, id: params[:id]) end end diff --git a/lecture_5/homework/config/initializers/active_model_serializers.rb b/lecture_5/homework/config/initializers/active_model_serializers.rb index 6f857680..56035bc5 100644 --- a/lecture_5/homework/config/initializers/active_model_serializers.rb +++ b/lecture_5/homework/config/initializers/active_model_serializers.rb @@ -1,3 +1,4 @@ # frozen_string_literal: true ActiveModelSerializers.config.adapter = :json_api +ActiveModel::Serializer.config.key_transform = :underscore diff --git a/lecture_5/homework/spec/requests/buildings_spec.rb b/lecture_5/homework/spec/requests/buildings_spec.rb index 92160d0c..4620ec49 100644 --- a/lecture_5/homework/spec/requests/buildings_spec.rb +++ b/lecture_5/homework/spec/requests/buildings_spec.rb @@ -67,7 +67,7 @@ 'id' => building_id.to_s, 'attributes' => { 'name' => 'Building_1', - 'siege-ability' => 0 + 'siege_ability' => 0 } ) end From 23b840b9da1b327a5f1a58a51788a3bde6aded04 Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 28 Apr 2019 14:49:35 +0200 Subject: [PATCH 12/12] Add updating database records --- lecture_5/homework/app/queries/buildings_queries.rb | 4 ++++ lecture_5/homework/app/services/reports/siege_report.rb | 6 ++++++ .../20190428121458_compute_siege_abilities_of_buildings.rb | 7 +++++++ lecture_5/homework/db/schema.rb | 2 +- lecture_5/homework/spec/requests/buildings_spec.rb | 2 +- 5 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 lecture_5/homework/db/migrate/20190428121458_compute_siege_abilities_of_buildings.rb diff --git a/lecture_5/homework/app/queries/buildings_queries.rb b/lecture_5/homework/app/queries/buildings_queries.rb index afc41631..0d0e605d 100644 --- a/lecture_5/homework/app/queries/buildings_queries.rb +++ b/lecture_5/homework/app/queries/buildings_queries.rb @@ -12,4 +12,8 @@ def self.building(relation:, id:) def self.count_warriors_of_specified_type(relation:, building:, type:) relation.find(building.id).warriors.where(type: type).count end + + def self.update_siege_ability(relation:, building:, result:) + relation.find(building.id).update(siege_ability: result) + end end diff --git a/lecture_5/homework/app/services/reports/siege_report.rb b/lecture_5/homework/app/services/reports/siege_report.rb index 2e8124b0..b6d1be4f 100644 --- a/lecture_5/homework/app/services/reports/siege_report.rb +++ b/lecture_5/homework/app/services/reports/siege_report.rb @@ -17,6 +17,7 @@ def no_army? end def no_siege_ability + save_siege_report_result(0) @building.siege_ability = 0 end @@ -59,6 +60,11 @@ def granary end def save_siege_report_result(result) + BuildingsQueries.update_siege_ability( + relation: Building, + building: @building, + result: result + ) @building.siege_ability = result end end diff --git a/lecture_5/homework/db/migrate/20190428121458_compute_siege_abilities_of_buildings.rb b/lecture_5/homework/db/migrate/20190428121458_compute_siege_abilities_of_buildings.rb new file mode 100644 index 00000000..d9eb2f4d --- /dev/null +++ b/lecture_5/homework/db/migrate/20190428121458_compute_siege_abilities_of_buildings.rb @@ -0,0 +1,7 @@ +class ComputeSiegeAbilitiesOfBuildings < ActiveRecord::Migration[5.2] + def change + Building.find_each do |building| + Reports::SiegeReport.new(building: building).call + end + end +end diff --git a/lecture_5/homework/db/schema.rb b/lecture_5/homework/db/schema.rb index 289fb16b..80b5adc2 100644 --- a/lecture_5/homework/db/schema.rb +++ b/lecture_5/homework/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_04_25_195137) do +ActiveRecord::Schema.define(version: 2019_04_28_121458) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/lecture_5/homework/spec/requests/buildings_spec.rb b/lecture_5/homework/spec/requests/buildings_spec.rb index 4620ec49..e78f547f 100644 --- a/lecture_5/homework/spec/requests/buildings_spec.rb +++ b/lecture_5/homework/spec/requests/buildings_spec.rb @@ -67,7 +67,7 @@ 'id' => building_id.to_s, 'attributes' => { 'name' => 'Building_1', - 'siege_ability' => 0 + 'siege_ability' => nil } ) end