diff --git a/2396/1/koans/about_array_assignment.rb b/2396/1/koans/about_array_assignment.rb new file mode 100644 index 000000000..b5dffbc10 --- /dev/null +++ b/2396/1/koans/about_array_assignment.rb @@ -0,0 +1,53 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +class AboutArrayAssignment < Neo::Koan + def test_non_parallel_assignment + names = %w[John Smith] + assert_equal %w[John Smith], names + end + + def test_parallel_assignments + first_name = 'John' + last_name = 'Smith' + assert_equal 'John', first_name + assert_equal 'Smith', last_name + end + + def test_parallel_assignments_with_extra_values + first_name, last_name = %w[John Smith III] + assert_equal 'John', first_name + assert_equal 'Smith', last_name + end + + def test_parallel_assignments_with_splat_operator + first_name, *last_name = %w[John Smith III] + assert_equal 'John', first_name + assert_equal %w[Smith III], last_name + end + + def test_parallel_assignments_with_too_few_variables + first_name, last_name = %w[Cher] + assert_equal 'Cher', first_name + assert_equal nil, last_name + end + + def test_parallel_assignments_with_subarrays + first_name = %w[Willie Rae] + last_name = 'Johnson' + assert_equal %w[Willie Rae], first_name + assert_equal 'Johnson', last_name + end + + def test_parallel_assignment_with_one_variable + first_name, = %w[John Smith] + assert_equal 'John', first_name + end + + def test_swapping_with_parallel_assignment + first_name = 'Roy' + last_name = 'Rob' + first_name, last_name = last_name, first_name + assert_equal 'Rob', first_name + assert_equal 'Roy', last_name + end +end diff --git a/2396/1/koans/about_arrays.rb b/2396/1/koans/about_arrays.rb new file mode 100644 index 000000000..a1edcfb3a --- /dev/null +++ b/2396/1/koans/about_arrays.rb @@ -0,0 +1,116 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This class smells of :reek:UncommunicativeModuleName +class AboutArrays < Neo::Koan + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_creating_arrays + empty_array = [] + assert_equal Array, empty_array.class + assert_equal 0, empty_array.size + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_array_literals + array = [] + assert_equal [], array + + array[0] = 1 + assert_equal [1], array + + array[1] = 2 + assert_equal [1, 2], array + + array << 333 + assert_equal [1, 2, 333], array + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_accessing_array_elements + array = %i[peanut butter and jelly] + + assert_equal :peanut, array[0] + assert_equal :peanut, array.first + assert_equal :jelly, array[3] + assert_equal :jelly, array.last + assert_equal :jelly, array[-1] + assert_equal :butter, array[-3] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_slicing_arrays + array = %i[peanut butter and jelly] + + assert_equal %i[peanut], array[0, 1] + assert_equal %i[peanut butter], array[0, 2] + assert_equal %i[and jelly], array[2, 2] + assert_equal %i[and jelly], array[2, 20] + assert_equal [], array[4, 0] + assert_equal [], array[4, 100] + assert_equal nil, array[5, 0] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_arrays_and_ranges + assert_equal Range, (1..5).class + assert_not_equal [1, 2, 3, 4, 5], (1..5) + assert_equal [1, 2, 3, 4, 5], (1..5).to_a + assert_equal [1, 2, 3, 4], (1...5).to_a + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_slicing_with_ranges + array = %i[peanut butter and jelly] + + assert_equal %i[peanut butter and], array[0..2] + assert_equal %i[peanut butter], array[0...2] + assert_equal %i[and jelly], array[2..-1] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_pushing_and_popping_arrays + array = [1, 2] + array.push(:last) + + assert_equal [1, 2, :last], array + + popped_value = array.pop + assert_equal :last, popped_value + assert_equal [1, 2], array + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_shifting_arrays + array = [1, 2] + array.unshift(:first) + + assert_equal [:first, 1, 2], array + + shifted_value = array.shift + assert_equal :first, shifted_value + assert_equal [1, 2], array + end +end diff --git a/2396/1/koans/about_asserts.rb b/2396/1/koans/about_asserts.rb new file mode 100644 index 000000000..233d4c0d1 --- /dev/null +++ b/2396/1/koans/about_asserts.rb @@ -0,0 +1,38 @@ +# -*- ruby -*- + +require File.expand_path(File.dirname(__FILE__) + '/neo') + +class AboutAsserts < Neo::Koan + # We shall contemplate truth by testing reality, via asserts. + def test_assert_truth + assert true + end + + # Enlightenment may be more easily achieved with appropriate + # messages. + def test_assert_with_message + assert true, 'This should be true -- Please fix this' + end + + # To understand reality, we must compare our expectations against + # reality. + def test_assert_equality + expected_value = 2 + actual_value = 1 + 1 + + assert expected_value == actual_value + end + + # Some ways of asserting equality are better than others. + def test_a_better_way_of_asserting_equality + expected_value = 2 + actual_value = 1 + 1 + + assert_equal expected_value, actual_value + end + + # Sometimes we will ask you to fill in the values + def test_fill_in_values + assert_equal 2, 1 + 1 + end +end diff --git a/2396/1/koans/about_blocks.rb b/2396/1/koans/about_blocks.rb new file mode 100644 index 000000000..d9287749d --- /dev/null +++ b/2396/1/koans/about_blocks.rb @@ -0,0 +1,132 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This class smells of :reek:UncommunicativeModuleName +class AboutBlocks < Neo::Koan + def method_with_block + result = yield + result + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_methods_can_take_blocks + yielded_result = method_with_block { 1 + 2 } + assert_equal 3, yielded_result + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_blocks_can_be_defined_with_do_end_too + yielded_result = method_with_block { 1 + 2 } + assert_equal 3, yielded_result + end + + # ------------------------------------------------------------------ + + def method_with_block_arguments + yield('Jim') + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_blocks_can_take_arguments + method_with_block_arguments do |argument| + assert_equal 'Jim', argument + end + end + + # ------------------------------------------------------------------ + + def many_yields + yield(:peanut) + yield(:butter) + yield(:and) + yield(:jelly) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_methods_can_call_yield_many_times + result = [] + success_result = %i[peanut butter and jelly] + many_yields { |item| result << item } + assert_equal success_result, result + end + + # ------------------------------------------------------------------ + + def yield_tester + if block_given? + yield + else + :no_block + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_methods_can_see_if_they_have_been_called_with_a_block + assert_equal(:with_block, yield_tester { :with_block }) + assert_equal :no_block, yield_tester + end + + # ------------------------------------------------------------------ + + def test_block_can_affect_variables_in_the_code_where_they_are_created + value = :initial_value + method_with_block { value = :modified_in_a_block } + assert_equal :modified_in_a_block, value + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_blocks_can_be_assigned_to_variables_and_called_explicitly + add_one = ->(n) { n + 1 } + assert_equal 11, add_one.call(10) + + # Alternative calling syntax + assert_equal 11, add_one[10] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_stand_alone_blocks_can_be_passed_to_methods_expecting_blocks + make_upper = ->(n) { n.upcase } + result = method_with_block_arguments(&make_upper) + assert_equal 'JIM', result + end + + # ------------------------------------------------------------------ + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def method_with_explicit_block + yield(10) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_methods_can_take_an_explicit_block_argument + assert_equal(20, method_with_explicit_block { |n| n * 2 }) + + add_one = ->(n) { n + 1 } + assert_equal 11, method_with_explicit_block(&add_one) + end +end diff --git a/2396/1/koans/about_class_methods.rb b/2396/1/koans/about_class_methods.rb new file mode 100644 index 000000000..cd5ad1b5c --- /dev/null +++ b/2396/1/koans/about_class_methods.rb @@ -0,0 +1,228 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This class smells of :reek:UncommunicativeModuleName +# This class smells of :reek:TooManyMethods +class AboutClassMethods < Neo::Koan + class Dog + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_objects_are_objects + fido = Dog.new + assert_equal true, fido.is_a?(Object) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_classes_are_classes + assert_equal true, Dog.is_a?(Class) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_classes_are_objects_too + assert_equal true, Dog.is_a?(Object) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_objects_have_methods + fido = Dog.new + assert !fido.methods.empty? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_classes_have_methods + assert !Dog.methods.empty? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_you_can_define_methods_on_individual_objects + fido = Dog.new + def fido.wag + :fidos_wag + end + assert_equal :fidos_wag, fido.wag + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_other_objects_are_not_affected_by_these_singleton_methods + fido = Dog.new + rover = Dog.new + def fido.wag + :fidos_wag + end + + assert_raise(NoMethodError) do + rover.wag + end + end + + # ------------------------------------------------------------------ + + class Dog2 + def wag + :instance_level_wag + end + end + + def Dog2.wag + :class_level_wag + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_since_classes_are_objects_you_can_define_singleton_methods_on_them_to + assert_equal :class_level_wag, Dog2.wag + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_class_methods_are_independent_of_instance_methods + fido = Dog2.new + assert_equal :instance_level_wag, fido.wag + assert_equal :class_level_wag, Dog2.wag + end + + # ------------------------------------------------------------------ + + # This class smells of :reek:Attribute + class Dog + attr_accessor :name + end + + def test_classes_and_instances_do_not_share_instance_variables + fido = Dog.new + fido.name = 'Fido' + assert_equal 'Fido', fido.name + assert_equal 'AboutClassMethods::Dog', Dog.name + end + + # ------------------------------------------------------------------ + + class Dog + def self.a_class_method + :dogs_class_method + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_you_can_define_class_methods_inside_the_class + assert_equal :dogs_class_method, Dog.a_class_method + end + + # ------------------------------------------------------------------ + + LAST_EXPRESSION_IN_CLASS_STATEMENT = class Dog + 21 + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_class_statements_return_the_value_of_their_last_expression + assert_equal 21, LAST_EXPRESSION_IN_CLASS_STATEMENT + end + + # ------------------------------------------------------------------ + + SELF_INSIDE_OF_CLASS_STATEMENT = class Dog + self + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_self_while_inside_class_is_class_object_not_instance + assert_equal true, Dog == SELF_INSIDE_OF_CLASS_STATEMENT + end + + # ------------------------------------------------------------------ + + class Dog + # This method smells of :reek:UncommunicativeMethodName + def self.class_method2 + :another_way_to_write_class_methods + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_you_can_use_self_instead_of_an_explicit_reference_to_dog + assert_equal :another_way_to_write_class_methods, Dog.class_method2 + end + + # ------------------------------------------------------------------ + + class Dog + class << self + def another_class_method + :still_another_way + end + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_heres_still_another_way_to_write_class_methods + assert_equal :still_another_way, Dog.another_class_method + end + + # THINK ABOUT IT: + # + # The two major ways to write class methods are: + # class Demo + # def self.method + # end + # + # class << self + # def class_methods + # end + # end + # end + # + # Which do you prefer and why? + # Are there times you might prefer one over the other? + + # ------------------------------------------------------------------ + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_heres_an_easy_way_to_call_class_methods_from_instance_methods + fido = Dog.new + assert_equal :still_another_way, fido.class.another_class_method + end +end diff --git a/2396/1/koans/about_classes.rb b/2396/1/koans/about_classes.rb new file mode 100644 index 000000000..f66f7b444 --- /dev/null +++ b/2396/1/koans/about_classes.rb @@ -0,0 +1,244 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This class smells of :reek:UncommunicativeModuleName +# This class smells of :reek:TooManyMethods +# This class smells of :reek:InstanceVariableAssumption +class AboutClasses < Neo::Koan + class Dog + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_instances_of_classes_can_be_created_with_new + fido = Dog.new + assert_equal AboutClasses::Dog, fido.class + end + + # This class smells of :reek:Attribute + # ------------------------------------------------------------------ + + class Dog2 + attr_writer :name + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_instance_variables_can_be_set_by_assigning_to_them + fido = Dog2.new + assert_equal [], fido.instance_variables + + fido.name = 'Fido' + assert_equal %i[@name], fido.instance_variables + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_instance_variables_cannot_be_accessed_outside_the_class + fido = Dog2.new + fido.name = 'Fido' + + assert_raise(NoMethodError) do + fido.name + end + + assert_raise(SyntaxError) do + eval <<-RUBY, binding, __FILE__, __LINE__ + 1 + fido.@name + RUBY + # NOTE: Using eval because the above line is a syntax error. + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_you_can_politely_ask_for_instance_variable_values + fido = Dog2.new + fido.name = 'Fido' + + assert_equal 'Fido', fido.instance_variable_get('@name') + end + + # rubocop:disable Style/EvalWithLocation + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_you_can_rip_the_value_out_using_instance_eval + fido = Dog2.new + fido.name = 'Fido' + + assert_equal 'Fido', fido.instance_eval('@name') # string version + assert_equal('Fido', fido.instance_eval { @name }) # block version + end + # rubocop:enable Style/EvalWithLocation + # This class smells of :reek:Attribute + # ------------------------------------------------------------------ + + class Dog3 + attr_accessor :name + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_you_can_create_accessor_methods_to_return_instance_variables + fido = Dog3.new + fido.name = 'Fido' + + assert_equal 'Fido', fido.name + end + # This class smells of :reek:Attribute + # ------------------------------------------------------------------ + + class Dog4 + attr_accessor :name + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_attr_reader_will_automatically_define_an_accessor + fido = Dog4.new + fido.name = 'Fido' + + assert_equal 'Fido', fido.name + end + # This class smells of :reek:Attribute + # ------------------------------------------------------------------ + + class Dog5 + attr_accessor :name + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_attr_accessor_will_automatically_define_both_read_and_write_accessors + fido = Dog5.new + + fido.name = 'Fido' + assert_equal 'Fido', fido.name + end + + # ------------------------------------------------------------------ + + class Dog6 + attr_reader :name + def initialize(initial_name) + @name = initial_name + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_initialize_provides_initial_values_for_instance_variables + fido = Dog6.new('Fido') + assert_equal 'Fido', fido.name + end + + def test_args_to_new_must_match_initialize + assert_raise(ArgumentError) do + Dog6.new + end + # THINK ABOUT IT: + # Why is this so? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_different_objects_have_different_instance_variables + fido = Dog6.new('Fido') + rover = Dog6.new('Rover') + + assert_equal true, rover.name != fido.name + end + + # ------------------------------------------------------------------ + + class Dog7 + attr_reader :name + + def initialize(initial_name) + @name = initial_name + end + + def gett_self + self + end + + def to_s + @name + end + + def inspect + "" + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_inside_a_method_self_refers_to_the_containing_object + fido = Dog7.new('Fido') + + fidos_self = fido.gett_self + assert_equal fido, fidos_self + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_to_s_provides_a_string_version_of_the_object + fido = Dog7.new('Fido') + assert_equal 'Fido', fido.to_s + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_to_s_is_used_in_string_interpolation + fido = Dog7.new('Fido') + assert_equal 'My dog is Fido', "My dog is #{fido}" + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_inspect_provides_a_more_complete_string_version + fido = Dog7.new('Fido') + assert_equal "", fido.inspect + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_all_objects_support_to_s_and_inspect + array = [1, 2, 3] + + assert_equal '[1, 2, 3]', array.to_s + assert_equal '[1, 2, 3]', array.inspect + + assert_equal 'STRING', 'STRING'.to_s + assert_equal '"STRING"', 'STRING'.inspect + end +end diff --git a/2396/1/koans/about_constants.rb b/2396/1/koans/about_constants.rb new file mode 100644 index 000000000..cdb2115cf --- /dev/null +++ b/2396/1/koans/about_constants.rb @@ -0,0 +1,86 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +C = 'top level'.freeze + +class AboutConstants < Neo::Koan + C = 'nested'.freeze + + def test_nested_constants_may_also_be_referenced_with_relative_paths + assert_equal 'nested', C + end + + def test_top_level_constants_are_referenced_by_double_colons + assert_equal 'top level', ::C + end + + def test_nested_constants_are_referenced_by_their_complete_path + assert_equal 'nested', AboutConstants::C + assert_equal 'nested', ::AboutConstants::C + end + + # ------------------------------------------------------------------ + + class Animal + LEGS = 4 + def legs_in_animal + LEGS + end + + class NestedAnimal + def legs_in_nested_animal + LEGS + end + end + end + + def test_nested_classes_inherit_constants_from_enclosing_classes + assert_equal 4, Animal::NestedAnimal.new.legs_in_nested_animal + end + + # ------------------------------------------------------------------ + + class Reptile < Animal + def legs_in_reptile + LEGS + end + end + + def test_subclasses_inherit_constants_from_parent_classes + assert_equal 4, Reptile.new.legs_in_reptile + end + + # ------------------------------------------------------------------ + + class MyAnimals + LEGS = 2 + + class Bird < Animal + def legs_in_bird + LEGS + end + end + end + + def test_who_wins_with_both_nested_and_inherited_constants + assert_equal 2, MyAnimals::Bird.new.legs_in_bird + end + + # QUESTION: Which has precedence: The constant in the lexical scope, + # or the constant from the inheritance hierarchy? + + # ------------------------------------------------------------------ + # rubocop:disable ClassAndModuleChildren + class MyAnimals::Oyster < Animal + def legs_in_oyster + LEGS + end + end + # rubocop:enable ClassAndModuleChildren + + def test_who_wins_with_explicit_scoping_on_class_definition + assert_equal 4, MyAnimals::Oyster.new.legs_in_oyster + end + # QUESTION: Now which has precedence: The constant in the lexical + # scope, or the constant from the inheritance hierarchy? Why is it + # different than the previous answer? +end diff --git a/2396/1/koans/about_control_statements.rb b/2396/1/koans/about_control_statements.rb new file mode 100644 index 000000000..8e6b27ef5 --- /dev/null +++ b/2396/1/koans/about_control_statements.rb @@ -0,0 +1,191 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# rubocop:disable Lint/LiteralAsCondition +# rubocop:disable Lint/UnneededCopDisableDirective +# rubocop:disable Metrics/ClassLength +# rubocop:disable Layout/CommentIndentation +# This class smells of :reek:UncommunicativeModuleName +# This class smells of :reek:RepeatedConditional +class AboutControlStatements < Neo::Koan + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_if_then_else_statements + result = true ? :true_value : :false_value + assert_equal :true_value, result + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_if_then_statements + result = :default_value + result = :true_value if true + assert_equal :true_value, result + end + + # rubocop:disable Metrics/MethodLength + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_if_statements_return_values + value = true ? :true_value : :false_value + assert_equal :true_value, value + + value = false ? :true_value : :false_value + assert_equal :false_value, value + + # NOTE: Actually, EVERY statement in Ruby will return a value, not + # just if statements. + end + + # rubocop:enable Metrics/MethodLength + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_if_statements_with_no_else_with_false_condition_return_value + value = :true_value if false + assert_equal nil, value + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_condition_operators + assert_equal :true_value, (true ? :true_value : :false_value) + assert_equal :false_value, (false ? :true_value : :false_value) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_if_statement_modifiers + result = :default_value + result = :true_value if true + assert_equal :true_value, result + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_unless_statement + result = :default_value + result = :false_value unless false # same as saying 'if !false', which evaluates as 'if true' + assert_equal :false_value, result + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_unless_statement_evaluate_true + result = :default_value + result = :true_value unless true # same as saying 'if !true', which evaluates as 'if false' + assert_equal :default_value, result + end + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + + def test_unless_statement_modifier + result = :default_value + result = :false_value unless false + assert_equal :false_value, result + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_while_statement + i = 1 + result = 1 + while i <= 10 + result *= i + i += 1 + end + assert_equal 3_628_800, result + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_break_statement + i = 1 + result = 1 + loop do + break unless i <= 10 + result *= i + i += 1 + end + assert_equal 3_628_800, result + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_break_statement_returns_values + i = 1 + result = while i <= 10 + break i if i.even? + i += 1 + end + + assert_equal 2, result + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_next_statement + i = 0 + result = [] + while i < 10 + i += 1 + next if (i % 2).zero? + result << i + end + assert_equal [1, 3, 5, 7, 9], result + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_for_statement + array = %w[fish and chips] + result = [] + array.each do |item| + result << item.upcase + end + assert_equal %w[FISH AND CHIPS], result + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_times_statement + sum = 0 + 10.times do + sum += 1 + end + assert_equal 10, sum + end +# rubocop:enable Lint/LiteralAsCondition +# rubocop:enable Metrics/ClassLength +# rubocop:enable Lint/UnneededCopDisableDirective +# rubocop:enable Layout/CommentIndentation +end diff --git a/2396/1/koans/about_dice_project.rb b/2396/1/koans/about_dice_project.rb new file mode 100644 index 000000000..9ddb9c870 --- /dev/null +++ b/2396/1/koans/about_dice_project.rb @@ -0,0 +1,87 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# Implement a DiceSet Class here: +# +class DiceSet + attr_reader :values + + def roll(size) + @values = Array.new(size) { rand(1..6) } + end +end + +# This class smells of :reek:UncommunicativeModuleName +class AboutDiceProject < Neo::Koan + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_can_create_a_dice_set + dice = DiceSet.new + assert_not_nil dice + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_rolling_the_dice_returns_a_set_of_integers_between_1_and_6 + dice = DiceSet.new + + dice.roll(5) + assert dice.values.is_a?(Array), 'should be an array' + assert_equal 5, dice.values.size + dice.values.each do |value| + assert value >= 1 && value <= 6, "value #{value} must be between 1 and 6" + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_dice_values_do_not_change_unless_explicitly_rolled + dice = DiceSet.new + dice.roll(5) + first_time = dice.values + second_time = dice.values + assert_equal first_time, second_time + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_dice_values_should_change_between_rolls + dice = DiceSet.new + + dice.roll(5) + first_time = dice.values + + dice.roll(5) + second_time = dice.values + + assert_not_equal first_time, second_time, + 'Two rolls should not be equal' + + # THINK ABOUT IT: + # + # If the rolls are random, then it is possible (although not + # likely) that two consecutive rolls are equal. What would be a + # better way to test this? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_you_can_roll_different_numbers_of_dice + dice = DiceSet.new + + dice.roll(3) + assert_equal 3, dice.values.size + + dice.roll(1) + assert_equal 1, dice.values.size + end +end diff --git a/2396/1/koans/about_exceptions.rb b/2396/1/koans/about_exceptions.rb new file mode 100644 index 000000000..b4f61d3d2 --- /dev/null +++ b/2396/1/koans/about_exceptions.rb @@ -0,0 +1,86 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This class smells of :reek:UncommunicativeModuleName +class AboutExceptions < Neo::Koan + class MySpecialError < RuntimeError + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_exceptions_inherit_from_exception + assert_equal RuntimeError, MySpecialError.ancestors[1] + assert_equal StandardError, MySpecialError.ancestors[2] + assert_equal Exception, MySpecialError.ancestors[3] + assert_equal Object, MySpecialError.ancestors[4] + end + + # rubocop:disable Metrics/MethodLength + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_rescue_clause + result = nil + begin + raise 'Oops' + rescue StandardError => ex + result = :exception_handled + end + assert_equal :exception_handled, result + assert_equal true, ex.is_a?(StandardError), 'Should be a Standard Error' + assert_equal true, ex.is_a?(RuntimeError), 'Should be a Runtime Error' + assert RuntimeError.ancestors.include?(StandardError), + 'RuntimeError is a subclass of StandardError' + assert_equal 'Oops', ex.message + end + # rubocop:enable Metrics/MethodLength + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_raising_a_particular_error + result = nil + begin + # 'raise' and 'fail' are synonyms + raise MySpecialError, 'My Message' + rescue MySpecialError => ex + result = :exception_handled + end + + assert_equal :exception_handled, result + assert_equal 'My Message', ex.message + end + + # rubocop:disable Lint/HandleExceptions + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_ensure_clause + begin + raise 'Oops' + rescue StandardError + # no code here + ensure + result = :always_run + end + + assert_equal :always_run, result + end + # rubocop:enable Lint/HandleExceptions + + # Sometimes, we must know about the unknown + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_asserting_an_error_is_raised + # A do-end is a block, a topic to explore more later + assert_raise(MySpecialError) do + raise MySpecialError, 'New instances can be raised directly.' + end + end +end diff --git a/2396/1/koans/about_extra_credit.rb b/2396/1/koans/about_extra_credit.rb new file mode 100644 index 000000000..5012edf8c --- /dev/null +++ b/2396/1/koans/about_extra_credit.rb @@ -0,0 +1,8 @@ +# EXTRA CREDIT: +# +# Create a program that will play the Greed Game. +# Rules for the game are in GREED_RULES.TXT. +# +# You already have a DiceSet class and score function you can use. +# Write a player class and a Game class to complete the project. This +# is a free form assignment, so approach it however you desire. diff --git a/2396/1/koans/about_hashes.rb b/2396/1/koans/about_hashes.rb new file mode 100644 index 000000000..2b3181c02 --- /dev/null +++ b/2396/1/koans/about_hashes.rb @@ -0,0 +1,166 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This class smells of :reek:UncommunicativeModuleName +class AboutHashes < Neo::Koan + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_creating_hashes + empty_hash = {} + assert_equal Hash, empty_hash.class + assert_equal({}, empty_hash) + assert_equal 0, empty_hash.size + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_hash_literals + hash = { one: 'uno', two: 'dos' } + assert_equal 2, hash.size + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_accessing_hashes + hash = { one: 'uno', two: 'dos' } + assert_equal 'uno', hash[:one] + assert_equal 'dos', hash[:two] + assert_equal nil, hash[:doesnt_exist] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_accessing_hashes_with_fetch + hash = { one: 'uno' } + assert_equal 'uno', hash.fetch(:one) + assert_raise(Exception) do + hash.fetch(:doesnt_exist) + end + + # THINK ABOUT IT: + # + # Why might you want to use #fetch instead of #[] when accessing hash keys? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_changing_hashes + hash = { one: 'uno', two: 'dos' } + hash[:one] = 'eins' + + expected = { one: 'eins', two: 'dos' } + assert_equal expected, hash + # Bonus Question: Why was "expected" broken out into a variable + # rather than used as a literal? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_hash_is_unordered + hash1 = { one: 'uno', two: 'dos' } + hash2 = { two: 'dos', one: 'uno' } + + assert_equal true, hash1 == hash2 + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_hash_keys + hash = { one: 'uno', two: 'dos' } + assert_equal 2, hash.keys.size + assert_equal true, hash.key?(:one) + assert_equal true, hash.key?(:two) + assert_equal Array, hash.keys.class + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_hash_values + hash = { one: 'uno', two: 'dos' } + assert_equal 2, hash.values.size + assert_equal true, hash.value?('uno') + assert_equal true, hash.value?('dos') + assert_equal Array, hash.values.class + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_combining_hashes + hash = { 'jim' => 53, 'amy' => 20, 'dan' => 23 } + new_hash = hash.merge('jim' => 54, 'jenny' => 26) + + assert_equal true, hash != new_hash + + expected = { 'jim' => 54, 'amy' => 20, 'dan' => 23, 'jenny' => 26 } + assert_equal true, expected == new_hash + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_default_value + hash1 = {} + hash1[:one] = 1 + + assert_equal 1, hash1[:one] + assert_equal nil, hash1[:two] + + hash2 = Hash.new('dos') + hash2[:one] = 1 + + assert_equal 1, hash2[:one] + assert_equal 'dos', hash2[:two] + end + + # rubocop:disable Metrics/AbcSize + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_default_value_is_the_same_object + hash = Hash.new([]) + + hash[:one] << 'uno' + hash[:two] << 'dos' + + assert_equal %w[uno dos], hash[:one] + assert_equal %w[uno dos], hash[:two] + assert_equal %w[uno dos], hash[:three] + + assert_equal true, hash[:one].object_id == hash[:two].object_id + end + # rubocop:enable Metrics/AbcSize + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_default_value_with_block + hash = Hash.new { |h, key| h[key] = [] } + + hash[:one] << 'uno' + hash[:two] << 'dos' + + assert_equal %w[uno], hash[:one] + assert_equal %w[dos], hash[:two] + assert_equal [], hash[:three] + end +end diff --git a/2396/1/koans/about_inheritance.rb b/2396/1/koans/about_inheritance.rb new file mode 100644 index 000000000..caadad88d --- /dev/null +++ b/2396/1/koans/about_inheritance.rb @@ -0,0 +1,84 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +class AboutInheritance < Neo::Koan + class Dog + attr_reader :name + + def initialize(name) + @name = name + end + + def bark + 'WOOF' + end + end + + class Chihuahua < Dog + def wag + :happy + end + + def bark + 'yip' + end + end + + def test_subclasses_have_the_parent_as_an_ancestor + assert_equal true, Chihuahua.ancestors.include?(Dog) + end + + def test_all_classes_ultimately_inherit_from_object + assert_equal true, Chihuahua.ancestors.include?(Object) + end + + def test_subclasses_inherit_behavior_from_parent_class + chico = Chihuahua.new('Chico') + assert_equal 'Chico', chico.name + end + + def test_subclasses_add_new_behavior + chico = Chihuahua.new('Chico') + assert_equal :happy, chico.wag + + assert_raise(NoMethodError) do + fido = Dog.new('Fido') + fido.wag + end + end + + def test_subclasses_can_modify_existing_behavior + chico = Chihuahua.new('Chico') + assert_equal 'yip', chico.bark + + fido = Dog.new('Fido') + assert_equal 'WOOF', fido.bark + end + + # ------------------------------------------------------------------ + + class BullDog < Dog + def bark + super + ', GROWL' + end + end + + def test_subclasses_can_invoke_parent_behavior_via_super + ralph = BullDog.new('Ralph') + assert_equal 'WOOF, GROWL', ralph.bark + end + + # ------------------------------------------------------------------ + + class GreatDane < Dog + def growl + super.bark + ', GROWL' + end + end + + def test_super_does_not_work_cross_method + george = GreatDane.new('George') + assert_raise(NoMethodError) do + george.growl + end + end +end diff --git a/2396/1/koans/about_iteration.rb b/2396/1/koans/about_iteration.rb new file mode 100644 index 000000000..810e9f9fc --- /dev/null +++ b/2396/1/koans/about_iteration.rb @@ -0,0 +1,158 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +class AboutIteration < Neo::Koan + # -- An Aside ------------------------------------------------------ + # Ruby 1.8 stores names as strings. Ruby 1.9 and later stores names + # as symbols. So we use a version dependent method "as_name" to + # convert to the right format in the koans. We will use "as_name" + # whenever comparing to lists of methods. + + in_ruby_version('1.8') do + # This method smells of :reek:UtilityFunction + def as_name(name) + name.to_s + end + end + + in_ruby_version('1.9', '2') do + # This method smells of :reek:UtilityFunction + def as_name(name) + name.to_sym + end + end + + # Ok, now back to the Koans. + # ------------------------------------------------------------------- + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_each_is_a_method_on_arrays + assert_equal true, [].methods.include?(as_name(:each)) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_iterating_with_each + array = [1, 2, 3] + sum = 0 + array.each do |item| + sum += item + end + assert_equal 6, sum + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_each_can_use_curly_brace_blocks_too + array = [1, 2, 3] + sum = 0 + array.each { |item| sum += item } + assert_equal 6, sum + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_break_works_with_each_style_iterations + array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + sum = 0 + array.each do |item| + break if item > 3 + sum += item + end + assert_equal 6, sum + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_collect_transforms_elements_of_an_array + array = [1, 2, 3] + new_array = array.collect { |item| item + 10 } + assert_equal [11, 12, 13], new_array + + # NOTE: 'map' is another name for the 'collect' operation + another_array = array.map { |item| item + 10 } + assert_equal [11, 12, 13], another_array + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_select_selects_certain_items_from_an_array + array = [1, 2, 3, 4, 5, 6] + + even_numbers = array.select { |item| (item % 2).even? } + assert_equal [2, 4, 6], even_numbers + + # NOTE: 'find_all' is another name for the 'select' operation + more_even_numbers = array.find_all { |item| (item % 2).even? } + assert_equal [2, 4, 6], more_even_numbers + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_find_locates_the_first_element_matching_a_criteria + array = %w[Jim Bill Clarence Doug Eli] + + assert_equal('Clarence', array.find { |item| item.size > 4 }) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_inject_will_blow_your_mind + result = [2, 3, 4].inject(0) { |sum, item| sum + item } + assert_equal 9, result + + result2 = [2, 3, 4].inject(1) { |product, item| product * item } + assert_equal 24, result2 + + # Extra Credit: + # Describe in your own words what inject does. + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + # This method smells of :reek:NestedIterators + def test_all_iteration_methods_work_on_any_collection_not_just_arrays + # Ranges act like a collection + result = (1..3).map { |item| item + 10 } + assert_equal [11, 12, 13], result + + # Files act like a collection of lines + File.open('example_file.txt') do |file| + upcase_lines = file.map { |line| line.strip.upcase } + assert_equal %w[THIS IS A TEST], upcase_lines + end + + # NOTE: You can create your own collections that work with each, + # map, select, etc. + end + # Bonus Question: In the previous koan, we saw the construct: + # + # File.open(filename) do |file| + # # code to read 'file' + # end + # + # Why did we do it that way instead of the following? + # + # file = File.open(filename) + # # code to read 'file' + # + # When you get to the "AboutSandwichCode" koan, recheck your answer. +end diff --git a/2396/1/koans/about_keyword_arguments.rb b/2396/1/koans/about_keyword_arguments.rb new file mode 100644 index 000000000..5005edccf --- /dev/null +++ b/2396/1/koans/about_keyword_arguments.rb @@ -0,0 +1,38 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This class smells of :reek:UncommunicativeModuleName +class AboutKeywordArguments < Neo::Koan + def method_with_keyword_arguments(one: 1, two: 'two') + [one, two] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_keyword_arguments + assert_equal Array, method_with_keyword_arguments.class + assert_equal [1, 'two'], method_with_keyword_arguments + assert_equal %w[one two], method_with_keyword_arguments(one: 'one') + assert_equal [1, 2], method_with_keyword_arguments(two: 2) + end + + def method_with_keyword_arguments_with_mandatory_argument(one, two: 2, three: 3) + [one, two, three] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_keyword_arguments_with_wrong_number_of_arguments + exception = assert_raise(ArgumentError) do + method_with_keyword_arguments_with_mandatory_argument + end + assert_match(/wrong number of arguments \(given 0, expected 1\)/, exception.message) + end + + # THINK ABOUT IT: + # + # Keyword arguments always have a default value, making them optional to the caller +end diff --git a/2396/1/koans/about_message_passing.rb b/2396/1/koans/about_message_passing.rb new file mode 100644 index 000000000..602eecd6c --- /dev/null +++ b/2396/1/koans/about_message_passing.rb @@ -0,0 +1,247 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# rubocop:disable Lint/UnneededCopDisableDirective +# rubocop:disable Style/MethodMissing +# rubocop:disable Style/MethodMissingSuper +# rubocop:disable Style/MissingRespondToMissing +# This class smells of :reek:UncommunicativeModuleName +# This class smells of :reek:ManualDispatch +class AboutMessagePassing < Neo::Koan + class MessageCatcher + def caught? + true + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_methods_can_be_called_directly + mc = MessageCatcher.new + + assert mc.caught? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_methods_can_be_invoked_by_sending_the_message + mc = MessageCatcher.new + + assert mc.send(:caught?) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_methods_can_be_invoked_more_dynamically + mc = MessageCatcher.new + + assert mc.send('caught?') + assert mc.send('caught' + '?') # What do you need to add to the first str? + assert mc.send('CAUGHT?'.downcase) # What would you need to do to the str? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_send_with_underscores_will_also_send_messages + mc = MessageCatcher.new + + assert_equal true, mc.__send__(:caught?) + + # THINK ABOUT IT: + # + # Why does Ruby provide both send and __send__ ? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_classes_can_be_asked_if_they_know_how_to_respond + mc = MessageCatcher.new + + assert_equal true, mc.respond_to?(:caught?) + assert_equal false, mc.respond_to?(:does_not_exist) + end + + # ------------------------------------------------------------------ + + class MessageCatcher + def add_a_payload(*args) + args + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_sending_a_message_with_arguments + mc = MessageCatcher.new + + assert_equal [], mc.add_a_payload + assert_equal [], mc.send(:add_a_payload) + + assert_equal [3, 4, nil, 6], mc.add_a_payload(3, 4, nil, 6) + assert_equal [3, 4, nil, 6], mc.send(:add_a_payload, 3, 4, nil, 6) + end + + # NOTE: + # + # Both obj.msg and obj.send(:msg) sends the message named :msg to + # the object. We use "send" when the name of the message can vary + # dynamically (e.g. calculated at run time), but by far the most + # common way of sending a message is just to say: obj.msg. + + # ------------------------------------------------------------------ + + class TypicalObject + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_sending_undefined_messages_to_a_typical_object_results_in_errors + typical = TypicalObject.new + + exception = assert_raise(NoMethodError) do + typical.foobar + end + assert_match(/foobar/, exception.message) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_method_missing_causes_the_no_method_error + typical = TypicalObject.new + + exception = assert_raise(NoMethodError) do + typical.method_missing(:foobar) + end + assert_match(/foobar/, exception.message) + + # THINK ABOUT IT: + # + # If the method :method_missing causes the NoMethodError, then + # what would happen if we redefine method_missing? + # + # NOTE: + # + # In Ruby 1.8 the method_missing method is public and can be + # called as shown above. However, in Ruby 1.9 (and later versions) + # the method_missing method is private. We explicitly made it + # public in the testing framework so this example works in both + # versions of Ruby. Just keep in mind you can't call + # method_missing like that after Ruby 1.9 normally. + # + # Thanks. We now return you to your regularly scheduled Ruby + # Koans. + end + + # ------------------------------------------------------------------ + + class AllMessageCatcher + # This method smells of :reek:UtilityFunction + def method_missing(method_name, *args) + "Someone called #{method_name} with <#{args.join(', ')}>" + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_all_messages_are_caught + catcher = AllMessageCatcher.new + + assert_equal 'Someone called foobar with <>', catcher.foobar + assert_equal 'Someone called foobaz with <1>', catcher.foobaz(1) + assert_equal 'Someone called sum with <1, 2, 3, 4, 5, 6>', + catcher.sum(1, 2, 3, 4, 5, 6) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_catching_messages_makes_respond_to_lie + catcher = AllMessageCatcher.new + + assert_nothing_raised do + catcher.any_method + end + assert_equal false, catcher.respond_to?(:any_method) + end + + # ------------------------------------------------------------------ + + class WellBehavedFooCatcher + def method_missing(method_name, *args, &block) + if method_name.to_s[0, 3] == 'foo' + 'Foo to you too' + else + super(method_name, *args, &block) + end + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_foo_method_are_caught + catcher = WellBehavedFooCatcher.new + + assert_equal 'Foo to you too', catcher.foo_bar + assert_equal 'Foo to you too', catcher.foo_baz + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_non_foo_messages_are_treated_normally + catcher = WellBehavedFooCatcher.new + + assert_raise(NoMethodError) do + catcher.normal_undefined_method + end + end + + # ------------------------------------------------------------------ + + # (note: just reopening class from above) + class WellBehavedFooCatcher + def respond_to?(method_name) + if method_name.to_s[0, 3] == 'foo' + true + else + super(method_name) + end + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + # rubocop:enable Style/MethodMissing + # rubocop:enable Style/MethodMissingSuper + # rubocop:enable Style/MissingRespondToMissing + # rubocop:enable Lint/UnneededCopDisableDirective + def test_explicitly_implementing_respond_to_lets_objects_tell_the_truth + catcher = WellBehavedFooCatcher.new + + assert_equal true, catcher.respond_to?(:foo_bar) + assert_equal false, catcher.respond_to?(:something_else) + end +end diff --git a/2396/1/koans/about_methods.rb b/2396/1/koans/about_methods.rb new file mode 100644 index 000000000..a808e2b91 --- /dev/null +++ b/2396/1/koans/about_methods.rb @@ -0,0 +1,219 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This method smells of :reek:UtilityFunction +def my_global_method(first_term, second_term) + first_term + second_term +end + +# This class smells of :reek:UncommunicativeModuleName +# This class smells of :reek:TooManyMethods +class AboutMethods < Neo::Koan + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_global_methods + assert_equal 5, my_global_method(2, 3) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_global_methods_without_parentheses + result = my_global_method 2, 3 + assert_equal 5, result + end + + # (NOTE: We are Using eval below because the example code is + # considered to be syntactically invalid). + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_sometimes_missing_parentheses_are_ambiguous + eval <<-RUBY, binding, __FILE__, __LINE__ + 1 + assert_equal 5, my_global_method(2, 3) # ENABLE CHECK + RUBY + # + # Ruby doesn't know if you mean: + # + # assert_equal(5, my_global_method(2), 3) + # or + # assert_equal(5, my_global_method(2, 3)) + # + # Rewrite the eval string to continue. + # + end + + # NOTE: wrong number of arguments is not a SYNTAX error, but a + # runtime error. + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_global_methods_with_wrong_number_of_arguments + exception = assert_raise(ArgumentError) do + my_global_method + end + assert_match(/wrong number of arguments \(given 0, expected 2\)/, exception.message) + + exception = assert_raise(ArgumentError) do + my_global_method(1, 2, 3) + end + assert_match(/wrong number of arguments \(given 3, expected 2\)/, exception.message) + end + + # ------------------------------------------------------------------ + + def method_with_defaults(first_item, second_item = :default_value) + [first_item, second_item] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_with_default_values + assert_equal [1, :default_value], method_with_defaults(1) + assert_equal [1, 2], method_with_defaults(1, 2) + end + + # ------------------------------------------------------------------ + + def method_with_var_args(*args) + args + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_with_variable_arguments + assert_equal Array, method_with_var_args.class + assert_equal [], method_with_var_args + assert_equal %i[one], method_with_var_args(:one) + assert_equal %i[one two], method_with_var_args(:one, :two) + end + + # ------------------------------------------------------------------ + + # rubocop:disable Lint/Void + # rubocop:disable Lint/UnreachableCode + def method_with_explicit_return + :a_non_return_value + return :return_value + :another_non_return_value + end + # rubocop:enable Lint/UnreachableCode + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_method_with_explicit_return + assert_equal :return_value, method_with_explicit_return + end + + # ------------------------------------------------------------------ + + def method_without_explicit_return + :a_non_return_value + :return_value + end + # rubocop:enable Lint/Void + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_method_without_explicit_return + assert_equal :return_value, method_without_explicit_return + end + + # ------------------------------------------------------------------ + + # This method smells of :reek:UtilityFunction + def my_method_in_the_same_class(first_term, second_term) + first_term * second_term + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_methods_in_same_class + assert_equal 12, my_method_in_the_same_class(3, 4) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_methods_in_same_class_with_explicit_receiver + assert_equal 12, my_method_in_the_same_class(3, 4) + end + + # ------------------------------------------------------------------ + + private + + def my_private_method + 'a secret' + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_private_methods_without_receiver + assert_equal 'a secret', my_private_method + end + + # rubocop:disable Style/RedundantSelf + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_private_methods_with_an_explicit_receiver + exception = assert_raise(NoMethodError) do + self.my_private_method + end + assert_match(/private method \`my_private_method\' called for/, exception.message) + end + # rubocop:enable Style/RedundantSelf + + # ------------------------------------------------------------------ + + class Dog + def name + 'Fido' + end + + private + + def tail + 'tail' + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_methods_in_other_objects_require_explicit_receiver + rover = Dog.new + assert_equal 'Fido', rover.name + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_calling_private_methods_in_other_objects + rover = Dog.new + assert_raise(NoMethodError) do + rover.tail + end + end +end diff --git a/2396/1/koans/about_modules.rb b/2396/1/koans/about_modules.rb new file mode 100644 index 000000000..482cc0f46 --- /dev/null +++ b/2396/1/koans/about_modules.rb @@ -0,0 +1,88 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This class smells of :reek:UncommunicativeModuleName +class AboutModules < Neo::Koan + module Nameable + # rubocop:disable Naming/AccessorMethodName + def set_name(new_name) + @name = new_name + end + # rubocop:enable Naming/AccessorMethodName + + def here + :in_module + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_cant_instantiate_modules + assert_raise(NoMethodError) do + Nameable.new + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + class Dog + include Nameable + + attr_reader :name + + def initialize + @name = 'Fido' + end + + def bark + 'WOOF' + end + + def here + :in_object + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_normal_methods_are_available_in_the_object + fido = Dog.new + assert_equal 'WOOF', fido.bark + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_module_methods_are_also_available_in_the_object + fido = Dog.new + assert_nothing_raised do + fido.set_name('Rover') + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_module_methods_can_affect_instance_variables_in_the_object + fido = Dog.new + assert_equal 'Fido', fido.name + fido.set_name('Rover') + assert_equal 'Rover', fido.name + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_classes_can_override_module_methods + fido = Dog.new + assert_equal :in_object, fido.here + end +end diff --git a/2396/1/koans/about_nil.rb b/2396/1/koans/about_nil.rb new file mode 100644 index 000000000..a6b47baae --- /dev/null +++ b/2396/1/koans/about_nil.rb @@ -0,0 +1,50 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This class smells of :reek:UncommunicativeModuleName +class AboutNil < Neo::Koan + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_nil_is_an_object + assert_equal true, nil.is_a?(Object), 'Unlike NULL in other languages' + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_you_dont_get_null_pointer_errors_when_calling_methods_on_nil + # What happens when you call a method that doesn't exist. The + # following begin/rescue/end code block captures the exception and + # makes some assertions about it. + + nil.some_method_nil_doesnt_know_about + rescue StandardError => ex + # What exception has been caught? + assert_equal NoMethodError, ex.class + + # What message was attached to the exception? + # (HINT: replace __ with part of the error message.) + assert_match(/undefined method `some_method_nil_doesnt_know_about' for nil:NilClass/, ex.message) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + # This method smeels of :reek:NilCheck + def test_nil_has_a_few_methods_defined_on_it + assert_equal true, nil.nil? + assert_equal '', nil.to_s + assert_equal 'nil', nil.inspect + + # THINK ABOUT IT: + # + # Is it better to use + # obj.nil? + # or + # obj == nil + # Why? + end +end diff --git a/2396/1/koans/about_objects.rb b/2396/1/koans/about_objects.rb new file mode 100644 index 000000000..a71d8c893 --- /dev/null +++ b/2396/1/koans/about_objects.rb @@ -0,0 +1,54 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +class AboutObjects < Neo::Koan + def test_everything_is_an_object + assert_equal true, 1.is_a?(Object) + assert_equal true, 1.5.is_a?(Object) + assert_equal true, 'string'.is_a?(Object) + assert_equal true, nil.is_a?(Object) + assert_equal true, Object.is_a?(Object) + end + + def test_objects_can_be_converted_to_strings + assert_equal '123', 123.to_s + assert_equal '', nil.to_s + end + + def test_objects_can_be_inspected + assert_equal '123', 123.inspect + assert_equal 'nil', nil.inspect + end + + def test_every_object_has_an_id + obj = Object.new + assert_equal Integer, obj.object_id.class + end + + def test_every_object_has_different_id + obj = Object.new + another_obj = Object.new + assert_equal true, obj.object_id != another_obj.object_id + end + + def test_small_integers_have_fixed_ids + assert_equal 1, 0.object_id + assert_equal 3, 1.object_id + assert_equal 5, 2.object_id + assert_equal 201, 100.object_id + + # THINK ABOUT IT: + # What pattern do the object IDs for small integers follow? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_clone_creates_a_different_object + obj = Object.new + copy = obj.clone + + assert_equal true, obj != copy + assert_equal true, obj.object_id != copy.object_id + end +end diff --git a/2396/1/koans/about_open_classes.rb b/2396/1/koans/about_open_classes.rb new file mode 100644 index 000000000..27a4f6eb1 --- /dev/null +++ b/2396/1/koans/about_open_classes.rb @@ -0,0 +1,47 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +class AboutOpenClasses < Neo::Koan + class Dog + def bark + 'WOOF' + end + end + + def test_as_defined_dogs_do_bark + fido = Dog.new + assert_equal 'WOOF', fido.bark + end + + # ------------------------------------------------------------------ + + # Open the existing Dog class and add a new method. + class Dog + def wag + 'HAPPY' + end + end + + def test_after_reopening_dogs_can_both_wag_and_bark + fido = Dog.new + assert_equal 'HAPPY', fido.wag + assert_equal 'WOOF', fido.bark + end + + # ------------------------------------------------------------------ + + # rubocop:disable Style/ClassAndModuleChildren + class ::Integer + def even? + (self % 2).zero? + end + end + # rubocop:enable Style/ClassAndModuleChildren + + def test_even_existing_built_in_classes_can_be_reopened + assert_equal false, 1.even? + assert_equal true, 2.even? + end + + # NOTE: To understand why we need the :: before Integer, you need to + # become enlightened about scope. +end diff --git a/2396/1/koans/about_proxy_object_project.rb b/2396/1/koans/about_proxy_object_project.rb new file mode 100644 index 000000000..ebf8a8380 --- /dev/null +++ b/2396/1/koans/about_proxy_object_project.rb @@ -0,0 +1,225 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# Project: Create a Proxy Class +# +# In this assignment, create a proxy class (one is started for you +# below). You should be able to initialize the proxy object with any +# object. Any messages sent to the proxy object should be forwarded +# to the target object. As each message is sent, the proxy should +# record the name of the method sent. +# +# The proxy class is started for you. You will need to add a method +# missing handler and any other supporting methods. The specification +# of the Proxy class is given in the AboutProxyObjectProject koan. +# rubocop:disable Lint/UnneededCopDisableDirective + +class Proxy + def initialize(target_object) + @object = target_object + # ADD MORE CODE HERE + @messages = Hash.new(0) + end + + # rubocop:disable Style/MethodMissing + # rubocop:disable Style/MethodMissingSuper + # rubocop:disable Style/MissingRespondToMissing + def method_missing(method_name, *args, &block) + @messages[method_name] += 1 + @object.send(method_name, *args, &block) + end + # rubocop:enable Style/MethodMissing + # rubocop:enable Style/MethodMissingSuper + # rubocop:enable Style/MissingRespondToMissing + + def called?(method_name) + @messages.key?(method_name) + end + + def number_of_times_called(method_name) + @messages[method_name] + end + + def messages + @messages.keys + end +end + +# The proxy object should pass the following Koan: +# +class AboutProxyObjectProject < Neo::Koan + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_proxy_method_returns_wrapped_object + # NOTE: The Television class is defined below + tv = Proxy.new(Television.new) + + # HINT: Proxy class is defined above, may need tweaking... + + assert tv.instance_of?(Proxy) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_tv_methods_still_perform_their_function + tv = Proxy.new(Television.new) + + tv.channel = 10 + tv.power + + assert_equal 10, tv.channel + assert tv.on? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_proxy_records_messages_sent_to_tv + tv = Proxy.new(Television.new) + + tv.power + tv.channel = 10 + + assert_equal %i[power channel=], tv.messages + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_proxy_handles_invalid_messages + tv = Proxy.new(Television.new) + + assert_raise(NoMethodError) do + tv.no_such_method + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_proxy_reports_methods_have_been_called + tv = Proxy.new(Television.new) + + tv.power + tv.power + + assert tv.called?(:power) + assert !tv.called?(:channel) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_proxy_counts_method_calls + tv = Proxy.new(Television.new) + + tv.power + tv.channel = 48 + tv.power + + assert_equal 2, tv.number_of_times_called(:power) + assert_equal 1, tv.number_of_times_called(:channel=) + assert_equal 0, tv.number_of_times_called(:on?) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_proxy_can_record_more_than_just_tv_objects + proxy = Proxy.new('Code Mash 2009') + + proxy.upcase! + result = proxy.split + + assert_equal %w[CODE MASH 2009], result + assert_equal %i[upcase! split], proxy.messages + end +end + +# ==================================================================== +# The following code is to support the testing of the Proxy class. No +# changes should be necessary to anything below this comment. + +# This class smells of :reek:InstanceVariableAssumption +# This class smells of :reek:Attribute +# Example class using in the proxy testing above. +class Television + attr_accessor :channel + + def power + @power = if @power == :on + :off + else + :on + end + end + + def on? + @power == :on + end +end + +# Tests for the Television class. All of theses tests should pass. +class TelevisionTest < Neo::Koan + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_it_turns_on + tv = Television.new + + tv.power + assert tv.on? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_it_also_turns_off + tv = Television.new + + tv.power + tv.power + + assert !tv.on? + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_edge_case_on_off + tv = Television.new + + tv.power + tv.power + tv.power + + assert tv.on? + + tv.power + + assert !tv.on? + end + + # rubocop:enable Lint/UnneededCopDisableDirective + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_can_set_the_channel + tv = Television.new + + tv.channel = 11 + assert_equal 11, tv.channel + end +end diff --git a/2396/1/koans/about_regular_expressions.rb b/2396/1/koans/about_regular_expressions.rb new file mode 100644 index 000000000..8328d0d05 --- /dev/null +++ b/2396/1/koans/about_regular_expressions.rb @@ -0,0 +1,270 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This class smells of :reek:UncommunicativeModuleName +# This class smells of :reek:TooManyMethods +class AboutRegularExpressions < Neo::Koan + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_a_pattern_is_a_regular_expression + assert_equal Regexp, /pattern/.class + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_a_regexp_can_search_a_string_for_matching_content + assert_equal 'match', 'some matching content'[/match/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_a_failed_match_returns_nil + assert_equal nil, 'some matching content'[/missing/] + end + + # ------------------------------------------------------------------ + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_question_mark_means_optional + assert_equal 'ab', 'abbcccddddeeeee'[/ab?/] + assert_equal 'a', 'abbcccddddeeeee'[/az?/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_plus_means_one_or_more + assert_equal 'bccc', 'abbcccddddeeeee'[/bc+/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_asterisk_means_zero_or_more + assert_equal 'abb', 'abbcccddddeeeee'[/ab*/] + assert_equal 'a', 'abbcccddddeeeee'[/az*/] + assert_equal '', 'abbcccddddeeeee'[/z*/] + + # THINK ABOUT IT: + # + # When would * fail to match? + end + + # THINK ABOUT IT: + # + # We say that the repetition operators above are "greedy." + # + # Why? + + # ------------------------------------------------------------------ + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_the_left_most_match_wins + assert_equal 'a', 'abbccc az'[/az*/] + end + + # ------------------------------------------------------------------ + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_character_classes_give_options_for_a_character + animals = %w[cat bat rat zat] + assert_equal(%w[cat bat rat], animals.select { |a| a[/[cbr]at/] }) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_slash_d_is_a_shortcut_for_a_digit_character_class + assert_equal '42', 'the number is 42'[/[0123456789]+/] + assert_equal '42', 'the number is 42'[/\d+/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_character_classes_can_include_ranges + assert_equal '42', 'the number is 42'[/[0-9]+/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_slash_s_is_a_shortcut_for_a_whitespace_character_class + assert_equal " \t\n", "space: \t\n"[/\s+/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_slash_w_is_a_shortcut_for_a_word_character_class + # NOTE: This is more like how a programmer might define a word. + assert_equal 'variable_1', 'variable_1 = 42'[/[a-zA-Z0-9_]+/] + assert_equal 'variable_1', 'variable_1 = 42'[/\w+/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_period_is_a_shortcut_for_any_non_newline_character + assert_equal 'abc', "abc\n123"[/a.+/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_a_character_class_can_be_negated + assert_equal 'the number is ', 'the number is 42'[/[^0-9]+/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_shortcut_character_classes_are_negated_with_capitals + assert_equal 'the number is ', 'the number is 42'[/\D+/] + assert_equal 'space:', "space: \t\n"[/\S+/] + # ... a programmer would most likely do + assert_equal ' = ', 'variable_1 = 42'[/[^a-zA-Z0-9_]+/] + assert_equal ' = ', 'variable_1 = 42'[/\W+/] + end + + # ------------------------------------------------------------------ + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_slash_a_anchors_to_the_start_of_the_string + assert_equal 'start', 'start end'[/\Astart/] + assert_equal nil, 'start end'[/\Aend/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_slash_z_anchors_to_the_end_of_the_string + assert_equal 'end', 'start end'[/end\z/] + assert_equal nil, 'start end'[/start\z/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_caret_anchors_to_the_start_of_lines + assert_equal '2', "num 42\n2 lines"[/^\d+/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_dollar_sign_anchors_to_the_end_of_lines + assert_equal '42', "2 lines\nnum 42"[/\d+$/] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_slash_b_anchors_to_a_word_boundary + assert_equal 'vines', 'bovine vines'[/\bvine./] + end + + # ------------------------------------------------------------------ + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_parentheses_group_contents + assert_equal 'hahaha', 'ahahaha'[/(ha)+/] + end + + # ------------------------------------------------------------------ + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_parentheses_also_capture_matched_content_by_number + assert_equal 'Gray', 'Gray, James'[/(\w+), (\w+)/, 1] + assert_equal 'James', 'Gray, James'[/(\w+), (\w+)/, 2] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_variables_can_also_be_used_to_access_captures + assert_equal 'Gray, James', 'Name: Gray, James'[/(\w+), (\w+)/] + assert_equal 'Gray', Regexp.last_match(1) + assert_equal 'James', Regexp.last_match(2) + end + + # ------------------------------------------------------------------ + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_a_vertical_pipe_means_or + grays = /(James|Dana|Summer) Gray/ + assert_equal 'James Gray', 'James Gray'[grays] + assert_equal 'Summer', 'Summer Gray'[grays, 1] + assert_equal nil, 'Jim Gray'[grays, 1] + end + + # THINK ABOUT IT: + # + # Explain the difference between a character class ([...]) and alternation (|). + + # ------------------------------------------------------------------ + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_scan_is_like_find_all + assert_equal %w[one two three], 'one two-three'.scan(/\w+/) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_sub_is_like_find_and_replace + assert_equal 'one t-three', 'one two-three'.sub(/(t\w*)/) { Regexp.last_match(1)[0, 1] } + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_gsub_is_like_find_and_replace_all + assert_equal 'one t-t', 'one two-three'.gsub(/(t\w*)/) { Regexp.last_match(1)[0, 1] } + end +end diff --git a/2396/1/koans/about_sandwich_code.rb b/2396/1/koans/about_sandwich_code.rb new file mode 100644 index 000000000..491b12818 --- /dev/null +++ b/2396/1/koans/about_sandwich_code.rb @@ -0,0 +1,132 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# rubocop:disable Security/Open +# This class smells of :reek:UncommunicativeModuleName +# This class smells of :reek:RepeatedConditional +class AboutSandwichCode < Neo::Koan + # This method smells of :reek:FeatureEnvy + def count_lines(file_name) + file = open(file_name) + count = 0 + count += 1 while file.gets + count + ensure + file.close if file + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_counting_lines + assert_equal 4, count_lines('example_file.txt') + end + + # ------------------------------------------------------------------ + + # This method smells of :reek:FeatureEnvy + def find_line(file_name) + file = open(file_name) + while (line = file.gets) + return line if line =~ /e/ + end + ensure + file.close if file + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_finding_lines + assert_equal "test\n", find_line('example_file.txt') + end + + # ------------------------------------------------------------------ + # THINK ABOUT IT: + # + # The count_lines and find_line are similar, and yet different. + # They both follow the pattern of "sandwich code". + # + # Sandwich code is code that comes in three parts: (1) the top slice + # of bread, (2) the meat, and (3) the bottom slice of bread. The + # bread part of the sandwich almost always goes together, but + # the meat part changes all the time. + # + # Because the changing part of the sandwich code is in the middle, + # abstracting the top and bottom bread slices to a library can be + # difficult in many languages. + # + # (Aside for C++ programmers: The idiom of capturing allocated + # pointers in a smart pointer constructor is an attempt to deal with + # the problem of sandwich code for resource allocation.) + # + # Consider the following code: + # + + def file_sandwich(file_name) + file = open(file_name) + yield(file) + ensure + file.close if file + end + + # This class smells of :reek:UncommunicativeMethodName + # Now we write: + + def count_lines2(file_name) + file_sandwich(file_name) do |file| + count = 0 + count += 1 while file.gets + count + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_counting_lines2 + assert_equal 4, count_lines2('example_file.txt') + end + + # ------------------------------------------------------------------ + + # This class smells of :reek:UncommunicativeMethodName + def find_line2(file_name) + # Rewrite find_line using the file_sandwich library function. + file_sandwich(file_name) do |file| + while (line = file.gets) + return line if line =~ /e/ + end + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_finding_lines2 + assert_equal "test\n", find_line2('example_file.txt') + end + + # ------------------------------------------------------------------ + + # This class smells of :reek:UncommunicativeMethodName + def count_lines3(file_name) + open(file_name) do |file| + count = 0 + count += 1 while file.gets + count + end + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_open_handles_the_file_sandwich_when_given_a_block + assert_equal 4, count_lines3('example_file.txt') + end +end +# rubocop:enable Security/Open diff --git a/2396/1/koans/about_scope.rb b/2396/1/koans/about_scope.rb new file mode 100644 index 000000000..f8db4aa9f --- /dev/null +++ b/2396/1/koans/about_scope.rb @@ -0,0 +1,115 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# This class smells of :reek:UncommunicativeModuleName +class AboutScope < Neo::Koan + module Jims + class Dog + def identify + :jims_dog + end + end + end + + module Joes + class Dog + def identify + :joes_dog + end + end + end + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_dog_is_not_available_in_the_current_scope + assert_raise(NameError) do + Dog.new + end + end + + # This method smells of :reek:TooManyStatements + def test_you_can_reference_nested_classes_using_the_scope_operator + fido = Jims::Dog.new + rover = Joes::Dog.new + assert_equal :jims_dog, fido.identify + assert_equal :joes_dog, rover.identify + + assert_equal true, fido.class != rover.class + assert_equal true, Jims::Dog != Joes::Dog + end + + # ------------------------------------------------------------------ + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + class String + end + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_bare_bones_class_names_assume_the_current_scope + assert_equal true, AboutScope::String == String + end + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + + def test_nested_string_is_not_the_same_as_the_system_string + assert_equal false, String == 'HI'.class + end + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + + def test_use_the_prefix_scope_operator_to_force_the_global_scope + assert_equal true, ::String == 'HI'.class + end + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + # ------------------------------------------------------------------ + + PI = 3.1416 + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_constants_are_defined_with_an_initial_uppercase_letter + assert_equal 3.1416, PI + end + + # ------------------------------------------------------------------ + + MyString = ::String + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_class_names_are_just_constants + assert_equal true, MyString == ::String + assert_equal true, MyString == 'HI'.class + end + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + + def test_constants_can_be_looked_up_explicitly + assert_equal true, PI == AboutScope.const_get('PI') + assert_equal true, MyString == AboutScope.const_get('MyString') + end + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + + def test_you_can_get_a_list_of_constants_for_any_class_or_module + assert_equal %i[Dog], Jims.constants + assert Object.constants.size > 127 + end +end diff --git a/2396/1/koans/about_scoring_project.rb b/2396/1/koans/about_scoring_project.rb new file mode 100644 index 000000000..0c4bd31a1 --- /dev/null +++ b/2396/1/koans/about_scoring_project.rb @@ -0,0 +1,135 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# Greed is a dice game where you roll up to five dice to accumulate +# points. The following "score" function will be used to calculate the +# score of a single roll of the dice. +# +# A greed roll is scored as follows: +# +# * A set of three ones is 1000 points +# +# * A set of three numbers (other than ones) is worth 100 times the +# number. (e.g. three fives is 500 points). +# +# * A one (that is not part of a set of three) is worth 100 points. +# +# * A five (that is not part of a set of three) is worth 50 points. +# +# * Everything else is worth 0 points. +# +# +# Examples: +# +# score([1,1,1,5,1]) => 1150 points +# score([2,3,4,6,2]) => 0 points +# score([3,4,5,3,3]) => 350 points +# score([1,5,1,2,4]) => 250 points +# +# More scoring examples are given in the tests below: +# +# Your goal is to write the score method. + +# This method smells of :reek:UtilityFunction +# This method smells of :reek:TooManyStatements +class Rules + SET_SCORE = Hash.new do |_, key| + if key == 1 + 1000 + else + key * 100 + end + end + + SCORE_MULTIPLIER = Hash.new(0).merge!(1 => 100, 5 => 50) +end + +# This method smells of :reek:UtilityFunction +# This method smells of :reek:TooManyStatements +def score(dice) + score = 0 + freq = dice.each_with_object(Hash.new(0)) { |key, hash| hash[key] += 1 } + freq.keys.each do |val| + if freq[val] >= 3 + score += Rules::SET_SCORE[val] + freq[val] -= 3 + end + + score += freq[val] * Rules::SCORE_MULTIPLIER[val] + end + score +end + +# This class smells of :reek:UncommunicativeModuleName +class AboutScoringProject < Neo::Koan + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_score_of_an_empty_list_is_zero + assert_equal 0, score([]) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_score_of_a_single_roll_of_5_is_50 + assert_equal 50, score([5]) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_score_of_a_single_roll_of_1_is_100 + assert_equal 100, score([1]) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_score_of_multiple_1s_and_5s_is_the_sum_of_individual_scores + assert_equal 300, score([1, 5, 5, 1]) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_score_of_single_2s_3s_4s_and_6s_are_zero + assert_equal 0, score([2, 3, 4, 6]) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_score_of_a_triple_1_is_1000 + assert_equal 1000, score([1, 1, 1]) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_score_of_other_triples_is_100x + assert_equal 200, score([2, 2, 2]) + assert_equal 300, score([3, 3, 3]) + assert_equal 400, score([4, 4, 4]) + assert_equal 500, score([5, 5, 5]) + assert_equal 600, score([6, 6, 6]) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_score_of_mixed_is_sum + assert_equal 250, score([2, 5, 2, 2, 3]) + assert_equal 550, score([5, 5, 5, 5]) + assert_equal 1100, score([1, 1, 1, 1]) + assert_equal 1200, score([1, 1, 1, 1, 1]) + assert_equal 1150, score([1, 1, 1, 5, 1]) + end +end diff --git a/2396/1/koans/about_strings.rb b/2396/1/koans/about_strings.rb new file mode 100644 index 000000000..079aa08d1 --- /dev/null +++ b/2396/1/koans/about_strings.rb @@ -0,0 +1,319 @@ +require File.expand_path(File.dirname(__FILE__) + '/neo') + +# rubocop:disable Lint/UselessAssignment +# rubocop:disable Metrics/ClassLength +# This class smells of :reek:UncommunicativeModuleName +# This class smells of :reek:TooManyMethods +class AboutStrings < Neo::Koan + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_double_quoted_strings_are_strings + string = 'Hello, World' + assert_equal true, string.is_a?(String) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_single_quoted_strings_are_also_strings + string = 'Goodbye, World' + assert_equal true, string.is_a?(String) + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_use_single_quotes_to_create_string_with_double_quotes + string = 'He said, "Go Away."' + assert_equal 'He said, "Go Away."', string + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_use_double_quotes_to_create_strings_with_single_quotes + string = "Don't" + assert_equal "Don't", string + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_use_backslash_for_those_hard_cases + a = "He said, \"Don't\"" + b = 'He said, "Don\'t"' + assert_equal true, a == b + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_use_flexible_quoting_to_handle_really_hard_cases + a = %(flexible quotes can handle both ' and " characters) + b = %!flexible quotes can handle both ' and " characters! + c = %{flexible quotes can handle both ' and " characters} + assert_equal true, a == b + assert_equal true, a == c + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_flexible_quotes_can_handle_multiple_lines + long_string = %{ +It was the best of times, +It was the worst of times. +} + assert_equal 54, long_string.length + assert_equal 3, long_string.lines.count + assert_equal "\n", long_string[0, 1] + end + + # This method smells of :reek:UncommunicativeMethodName + # This method smells of :reek:UncommunicativeVariableName + # This method smells of :reek:TooManyStatements + # This method smells of :reek:FeatureEnvy + def test_here_documents_can_also_handle_multiple_lines + long_string = <