From 992138fa713b98824c73cd284acbfeaa334eb958 Mon Sep 17 00:00:00 2001 From: Paul DobbinSchmaltz Date: Fri, 15 Nov 2024 11:54:26 -0600 Subject: [PATCH] Extract object inspection from *::Console objects Moving object inspection into *::Console objects was an experiment in processing optimization. But, I think it was a failed one. It just made it too hard to see the pretty inspection results, especially when chaining methods together in e.g. ARel queries. Also, the Console object accessor / insulation layer was always a bit of a doubling up on processing optimization. ObjectInspector itself has scopes for dealing with this sort of thing, wherever necessary. For this project, however, I can't think of any relevant cases (yet). --- app/models/board.rb | 35 ++++++++------- app/models/board/settings.rb | 12 ++--- app/models/calc_3bv.rb | 43 +++++++++++------- app/models/cell.rb | 30 +++++++------ app/models/coordinates.rb | 16 +++---- app/models/game.rb | 45 +++++++++++-------- app/models/grid.rb | 35 +++++++-------- app/models/null_coordinates.rb | 5 +-- app/models/pattern.rb | 26 ++++++----- app/models/pattern/settings.rb | 8 ++-- app/models/transactions/cell_transaction.rb | 8 ++-- app/models/transactions/game_transaction.rb | 8 ++-- .../transactions/user_update_transaction.rb | 8 ++-- app/models/user.rb | 29 ++++-------- lib/console_object_behaviors.rb | 4 -- 15 files changed, 155 insertions(+), 157 deletions(-) diff --git a/app/models/board.rb b/app/models/board.rb index d1cb51de..b55ceeb2 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -93,6 +93,24 @@ def validate_settings errors.merge!(settings.errors) end + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper + + def inspect_identification = identify + + def inspect_info(scope:) + scope.join_info([ + "#{Emoji.cell} x#{cells_count} (#{dimensions})", + "#{Emoji.revealed_cell} #{revealed_cells_count}", + "#{Emoji.mine} #{mines}", + "#{Emoji.flag} #{flags_count}", + ]) + end + + def cells_count = cells.size + def revealed_cells_count = cells.count(&:revealed?) + end + # Board::Console acts like a {Board} but otherwise handles IRB # Console-specific methods/logic. class Console @@ -126,22 +144,5 @@ def reset! self end - - private - - def inspect_info(scope:) - scope.join_info([ - "#{Emoji.cell} x#{cells_count} (#{dimensions})", - "#{Emoji.revealed_cell} #{revealed_cells_count}", - "#{Emoji.mine} #{mines}", - "#{Emoji.flag} #{flags_count}", - ]) - end - - def cells_count = cells.size - - def revealed_cells_count - cells.count(&:revealed?) - end end end diff --git a/app/models/board/settings.rb b/app/models/board/settings.rb index 012c85f5..75e55322 100644 --- a/app/models/board/settings.rb +++ b/app/models/board/settings.rb @@ -23,8 +23,6 @@ class Board::Settings include ActiveModel::Model include ActiveModel::Attributes - include ObjectInspector::InspectorsHelper - attribute :type, :string attribute :name, :string attribute :width, :integer, default: -> { RANGES.fetch(:width).begin } @@ -118,11 +116,13 @@ def to_h = { type:, name:, width:, height:, mines: }.tap(&:compact!) def as_json = to_h def to_a = to_h.values - private + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper - def inspect_identification = identify(:width, :height, :mines) + def inspect_identification = identify(:width, :height, :mines) - def inspect_name - pattern? ? "#{type} (#{name.inspect})" : type + def inspect_name + pattern? ? "#{type} (#{name.inspect})" : type + end end end diff --git a/app/models/calc_3bv.rb b/app/models/calc_3bv.rb index fa03d404..9f08bb55 100644 --- a/app/models/calc_3bv.rb +++ b/app/models/calc_3bv.rb @@ -42,12 +42,17 @@ def process_cells def count = cells.sum(&:count) + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper + + def inspect_identification = identify(:grid) + end + # :reek:InstanceVariableAssumption # Calc3BV::Cell wraps {Cell} for performing 3BV scoring functions. class Cell include WrapMethodBehaviors - include ObjectInspector::InspectorsHelper delegate_missing_to :to_model @@ -94,17 +99,21 @@ def count = @count.to_i attr_reader :calculator - def inspect_flags(scope:) - scope.join_flags([ - (Emoji.mine if mine?), - (visited? ? Emoji.eyes : "🙈"), - ]) - end - - def inspect_name = @count.inspect - def neighbors = calculator.cells_at(neighboring_coordinates) def neighboring_coordinates = coordinates.neighbors + + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper + + def inspect_flags(scope:) + scope.join_flags([ + (Emoji.mine if mine?), + (visited? ? Emoji.eyes : "🙈"), + ]) + end + + def inspect_name = @count.inspect + end end # Calc3BV::Console acts like a {Calc3BV} but otherwise handles IRB @@ -113,8 +122,7 @@ class Console include ConsoleObjectBehaviors # @example - # calc = Calc3BV.new(Board.last.grid).cons - # calc.call + # Calc3BV.new(Board.last.grid).cons.call def call process_cells count @@ -129,17 +137,18 @@ def process_cells # Show where the clicks came from. # # @example - # calc = Calc3BV.new(Board.last.grid).cons - # calc.call - # calc.render # Compare to `calc.grid.render` + # # Compare output against `calc.grid.render`: + # Calc3BV.new(Board.last.grid).cons.render def render cells.map(&:count).in_groups_of(grid.columns_count) end def grid = super.console - private + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper - def inspect_identification = identify(:grid, klass: __class__) + def inspect_identification = identify(:grid, klass: __class__) + end end end diff --git a/app/models/cell.rb b/app/models/cell.rb index 586c9a0d..5e77699b 100644 --- a/app/models/cell.rb +++ b/app/models/cell.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true # :reek:TooManyMethods +# :reek:RepeatedConditional # Cell represents a clickable position on the {Board}. Cells: # - may secretly contain a mine @@ -141,19 +142,10 @@ def neighboring_flags_count @neighboring_flags_count ||= neighbors.count(&:flagged?) end - # Cell::Console acts like a {Cell} but otherwise handles IRB Console-specific - # methods/logic. - class Console - include ConsoleObjectBehaviors - - def coordinates = super.console - def neighbors = super.map(&:console) + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper - def render(cells_count: nil) - "#{current_state}#{coordinates.console.render(cells_count:)}" - end - - private + def inspect_identification = identify def inspect_flags(scope:) scope.join_flags([ @@ -168,8 +160,20 @@ def inspect_issues "INCORRECTLY_FLAGGED" if incorrectly_flagged? end - def inspect_info = coordinates.console + def inspect_info = coordinates def inspect_name = value.inspect + end + + # Cell::Console acts like a {Cell} but otherwise handles IRB Console-specific + # methods/logic. + class Console + include ConsoleObjectBehaviors + + def render(cells_count: value) + "#{current_state}#{coordinates.console.render(cells_count:)}" + end + + private def current_state if revealed? diff --git a/app/models/coordinates.rb b/app/models/coordinates.rb index d0175f68..09995ac5 100644 --- a/app/models/coordinates.rb +++ b/app/models/coordinates.rb @@ -45,6 +45,14 @@ def to_a [x, y] end + def to_s + inspect + end + + def inspect + "(#{x}, #{y})" + end + # Coordinates::Console acts like a {Coordinates} but otherwise handles IRB # Console-specific methods/logic. class Console @@ -59,16 +67,8 @@ def render(cells_count:) "(#{pad(x, cells_count:)}, #{pad(y, cells_count:)})" end - def to_s - inspect - end - private - def inspect - "(#{x}, #{y})" - end - def pad(value, cells_count:) return value if cells_count <= BEGINNER_DIFFICULTY_LEVEL_CELLS_COUNT diff --git a/app/models/game.rb b/app/models/game.rb index a6e3b5e9..56c0c657 100644 --- a/app/models/game.rb +++ b/app/models/game.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true # :reek:TooManyConstants +# :reek:TooManyMethods # Game represents a game of Minesweeper Alliance. It handles creation of new # Games and keeps track of the {#status} of each Game in the database, win or @@ -190,6 +191,31 @@ def set_stats CalcStats.(self) end + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper + + def inspect_identification = identify + + def inspect_flags(scope:) + scope.join_flags([ + type, + status, + ]) + end + + def inspect_info(scope:) + return unless score || bbbv || efficiency + + scope.join_info([ + "Score: #{score.inspect}", + "3BV: #{bbbv.inspect}", + "Efficiency: #{efficiency.inspect}", + ]) + end + + def inspect_name = display_id + end + # Game::CalcStats determines and updates the given {Game} object with relevant # statistical values (on {Game} end). class CalcStats @@ -268,25 +294,6 @@ def reset! private - def inspect_flags(scope:) - scope.join_flags([ - type, - status, - ]) - end - - def inspect_info(scope:) - return unless score || bbbv || efficiency - - scope.join_info([ - "Score: #{score.inspect}", - "3BV: #{bbbv.inspect}", - "Efficiency: #{efficiency.inspect}", - ]) - end - - def inspect_name = display_id - # :reek:TooManyStatements def do_reset check_for_current_game diff --git a/app/models/grid.rb b/app/models/grid.rb index 5b9f1dd0..2fd26075 100644 --- a/app/models/grid.rb +++ b/app/models/grid.rb @@ -19,6 +19,7 @@ def initialize(cells, context: nil) end def cells_count = cells.size + def rows_count = count def columns_count = first.size # rubocop:disable Layout/LineLength @@ -58,6 +59,20 @@ def organizer context&.transpose? ? TRANSPOSE_ORGANIZER : STANDARD_ORGANIZER end + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper + + def inspect_identification = self.class.name + + def inspect_info + "#{Emoji.cell} x#{cells_count} (#{dimensions})" + end + + def dimensions + "#{rows_count}x#{columns_count}" + end + end + # rubocop:enable Layout/LineLength # Grid::Console acts like a {Grid} but otherwise handles IRB Console-specific @@ -65,16 +80,6 @@ def organizer class Console include ConsoleObjectBehaviors - def cells = super.map(&:console) - - def to_h - super.transform_values { |row| row.map(&:console) } - end - - def to_a - super.map { |row| row.map(&:console) } - end - # @example # 0 => ◻️ (0, 0) ◻️ (1, 0) ◻️ (2, 0) # 1 => ◻️ (0, 1) ◻️ (1, 1) ◻️ (2, 1) @@ -89,16 +94,6 @@ def render private - def inspect_identification = __model__.class.name - - def inspect_info - "#{Emoji.cell} x#{cells_count} (#{dimensions})" - end - - def dimensions - "#{count}x#{to_a.first.size}" - end - # rubocop:disable Rails/Output def render_row(y:, row:) print "#{pad(y)}: " diff --git a/app/models/null_coordinates.rb b/app/models/null_coordinates.rb index a60e24e2..5d9abd4b 100644 --- a/app/models/null_coordinates.rb +++ b/app/models/null_coordinates.rb @@ -11,11 +11,10 @@ def y = nil def neighbors = [] def to_h = {} def to_json(*) = "{}" + def to_s = inspect + def render = inspect def inspect "(nil, nil)" end - - def to_s = inspect - def render = inspect end diff --git a/app/models/pattern.rb b/app/models/pattern.rb index 9bd660b8..6744c4c0 100644 --- a/app/models/pattern.rb +++ b/app/models/pattern.rb @@ -17,8 +17,6 @@ class Pattern < ApplicationRecord self.implicit_order_column = "created_at" - include ObjectInspector::InspectorsHelper - attribute :coordinates_array, Type::CoordinatesArray.new validates :name, @@ -73,18 +71,24 @@ def reset private - def inspect_info(scope:) - scope.join_info([ - dimensions, - "#{Emoji.flag} x#{flags_count}", - ]) - end - - def inspect_name = name - def validate_settings return if settings.valid? errors.merge!(settings.errors) end + + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper + + def inspect_identification = identify + + def inspect_info(scope:) + scope.join_info([ + dimensions, + "#{Emoji.flag} x#{flags_count}", + ]) + end + + def inspect_name = name + end end diff --git a/app/models/pattern/settings.rb b/app/models/pattern/settings.rb index 77c78065..3f714c39 100644 --- a/app/models/pattern/settings.rb +++ b/app/models/pattern/settings.rb @@ -11,8 +11,6 @@ class Pattern::Settings include ActiveModel::Model include ActiveModel::Attributes - include ObjectInspector::InspectorsHelper - attribute :width, :integer, default: -> { RANGES.fetch(:width).begin } attribute :height, :integer, default: -> { RANGES.fetch(:height).begin } @@ -43,7 +41,9 @@ def to_h = { width:, height: } def to_a = to_h.values def as_json = to_h - private + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper - def inspect_identification = identify(:width, :height) + def inspect_identification = identify(:width, :height) + end end diff --git a/app/models/transactions/cell_transaction.rb b/app/models/transactions/cell_transaction.rb index d6937cd3..81b1bd71 100644 --- a/app/models/transactions/cell_transaction.rb +++ b/app/models/transactions/cell_transaction.rb @@ -39,12 +39,10 @@ def self.exists_between?(user:, cell:) for_user(user).for_cell(cell).exists? end - # CellTransaction::Console acts like a {CellTransaction} but otherwise handles - # IRB Console-specific methods/logic. - class Console - include ConsoleObjectBehaviors + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper - private + def inspect_identification = identify def inspect_info [ diff --git a/app/models/transactions/game_transaction.rb b/app/models/transactions/game_transaction.rb index 9c23c6ba..87f610e3 100644 --- a/app/models/transactions/game_transaction.rb +++ b/app/models/transactions/game_transaction.rb @@ -32,12 +32,10 @@ def self.exists_between?(user:, game:) for_user(user).for_game(game).exists? end - # GameTransaction::Console acts like a {GameTransaction} but otherwise handles - # IRB Console-specific methods/logic. - class Console - include ConsoleObjectBehaviors + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper - private + def inspect_identification = identify def inspect_info [ diff --git a/app/models/transactions/user_update_transaction.rb b/app/models/transactions/user_update_transaction.rb index 6d000c4b..42f94b81 100644 --- a/app/models/transactions/user_update_transaction.rb +++ b/app/models/transactions/user_update_transaction.rb @@ -21,12 +21,10 @@ def self.create_for(user:, change_set:) user.user_update_transactions.create!(change_set:) end - # UserUpdateTransaction::Console acts like a {UserUpdateTransaction} but - # otherwise handles IRB Console-specific methods/logic. - class Console - include ConsoleObjectBehaviors + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper - private + def inspect_identification = identify def inspect_info [ diff --git a/app/models/user.rb b/app/models/user.rb index bf831f74..9d999aa0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -72,6 +72,15 @@ def completed_games_count def bests = @bests ||= Bests.new(self) + concerning :ObjectInspection do + include ObjectInspector::InspectorsHelper + + def inspect_identification = identify(:truncated_id) + def truncated_id = id[TRUNCATED_ID_RANGE] + + def inspect_info = time_zone + end + # User::Bests is a specialization on User for finding "best" (e.g. # top-scoring) {Game}s. class Bests @@ -100,24 +109,4 @@ def _score_arel = games.by_score_asc def _bbbvps_arel = games.by_bbbvps_desc def _efficiency_arel = games.by_efficiency_desc end - - # User::Console acts like a {User} but otherwise handles IRB Console-specific - # methods/logic. - class Console - TRUNCATED_ID_RANGE = (-7..) - - include ConsoleObjectBehaviors - - private - - def inspect_identification - identify(:truncated_id, klass: __class__) - end - - def inspect_info - time_zone - end - - def truncated_id = id[TRUNCATED_ID_RANGE] - end end diff --git a/lib/console_object_behaviors.rb b/lib/console_object_behaviors.rb index 0b5bfe88..d66a2826 100644 --- a/lib/console_object_behaviors.rb +++ b/lib/console_object_behaviors.rb @@ -8,8 +8,6 @@ module ConsoleObjectBehaviors extend ActiveSupport::Concern - include ObjectInspector::InspectorsHelper - included do # *::Console::Error represents any StandardError related to Console # processing on the including object. @@ -65,6 +63,4 @@ def method_missing(...) def respond_to_missing?(method, include_private = true) __model__.respond_to?(method, include_private) || super end - - def inspect_identification(...) = __model__.identify(...) end