Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def initialize(track:, metadata:)
# Doesn't update the version information.
class GenerateTests < ImplementationDelegator
def call
create_tests_file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this 👍

build_tests
end
end

Expand All @@ -24,7 +24,7 @@ class UpdateVersionAndGenerateTests < ImplementationDelegator
def call
update_tests_version
update_example_solution
create_tests_file
build_tests
end
end
end
10 changes: 7 additions & 3 deletions lib/generator/command_line.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def parse(args)
attr_reader :paths

def generators
implementations.map { |slug| generator(implementation(slug)) }
implementations.map { |slug| generator(implementation(exercise(slug))) }
end

def implementations
Expand All @@ -35,9 +35,13 @@ def freeze?
@options[:freeze] || @options[:all]
end

def implementation(slug)
def exercise(slug)
Exercise.new(slug: slug)
end

def implementation(exercise)
LoggingImplementation.new(
implementation: Implementation.new(paths: paths, slug: slug),
implementation: Implementation.new(paths: paths, exercise: exercise),
logger: logger
)
end
Expand Down
19 changes: 19 additions & 0 deletions lib/generator/exercise.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Generator
class Exercise
using Generator::Underscore

attr_reader :slug

def initialize(slug:)
@slug = slug
end

def name
@name ||= slug.underscore
end

def case_class
slug.camel_case + 'Case'
end
end
end
17 changes: 5 additions & 12 deletions lib/generator/files/generator_cases.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ module Files
module GeneratorCases
class << self
def available(track_path)
cases_filepaths(track_path).map { |filepath| slugify(filepath) }.sort
Copy link
Copy Markdown
Contributor

@Insti Insti May 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly disagree with this change.

It is true that available does not need to know exactly how a test case is generated, and you could achieve this by abstracting out the availability criteria, but since that is all this class does that seems excessive.

because that might go away or get changed.

This is a case of YAGNI and we can worry about that when it happens.

See also: #646 which aims to give new generator implementers helpful advice.

Request: Omit this commit.

end

def class_name(exercise_name_or_slug)
filename(exercise_name_or_slug).split('_').map(&:capitalize).join
filepaths(track_path).map do |filepath|
%r{#{track_path}/exercises/([-a-z]+)/}.match(filepath)[1]
end.sort
end

def source_filepath(track_path, slug)
Expand All @@ -18,13 +16,8 @@ def source_filepath(track_path, slug)

private

def cases_filepaths(track_path)
generator_glob = File.join(meta_generator_path(track_path, '*'), '*_case.rb')
Dir.glob(generator_glob, File::FNM_DOTMATCH)
end

def slugify(filepath)
File.basename(filepath, '_case.rb').tr('_', '-')
def filepaths(track_path)
Dir.glob(meta_generator_path(track_path, '*'))
end

def filename(exercise_name_or_slug)
Expand Down
2 changes: 1 addition & 1 deletion lib/generator/files/metadata_files.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def canonical_data
private

def exercise_metadata_path
File.join(paths.metadata, 'exercises', slug)
File.join(paths.metadata, 'exercises', exercise.slug)
end
end

Expand Down
6 changes: 3 additions & 3 deletions lib/generator/files/track_files.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def tests_template
private

def exercise_path
File.join(paths.track, 'exercises', slug)
File.join(paths.track, 'exercises', exercise.slug)
end

def meta_path
Expand All @@ -34,15 +34,15 @@ def solutions_path
end

def minitest_tests_filename
"#{exercise_name}_test.rb"
"#{exercise.name}_test.rb"
end

def version_filename
'.version'
end

def example_filename
"#{exercise_name}.rb"
"#{exercise.name}.rb"
end

def tests_template_absolute_filename
Expand Down
18 changes: 7 additions & 11 deletions lib/generator/implementation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ class Implementation
include Files::MetadataFiles
include TemplateValuesFactory

def initialize(paths:, slug:)
def initialize(paths:, exercise:)
@paths = paths
@slug = slug
@exercise = exercise
end

attr_reader :paths, :slug
attr_reader :paths, :exercise

def version
tests_version.to_i
Expand All @@ -25,16 +25,12 @@ def update_example_solution
example_solution.update_version(version)
end

def create_tests_file
def build_tests
minitest_tests.generate(
template: tests_template.to_s,
values: template_values
)
end

def exercise_name
@exercise_name ||= slug.tr('-', '_')
end
end

# This exists to give us a clue as to what we are delegating to.
Expand All @@ -58,9 +54,9 @@ def update_example_solution
@logger.debug "Updated version in example solution to #{version}"
end

def create_tests_file
@implementation.create_tests_file
@logger.info "Generated #{slug} tests version #{version}"
def build_tests
@implementation.build_tests
@logger.info "Generated #{exercise.slug} tests version #{version}"
end
end
end
42 changes: 25 additions & 17 deletions lib/generator/template_values.rb
Original file line number Diff line number Diff line change
@@ -1,55 +1,63 @@
module Generator
# Contains methods accessible to the ERB template
class TemplateValues
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original intention of TemplateValues was that it it should be a dumb container and would only contain the template values available to the erb file. The initialize method provided one place where it was possible to see exactly all the properties that would be available to the erb.

It is the responsibility of the TemplateValuesFactory to construct the necessary values.

If that is the goal, then there is probably a nicer way of implementing this but I'm not sure this commit is heading in that direction.

attr_reader :abbreviated_commit_hash, :version, :exercise_name, :test_cases, :canonical_data_version
using Underscore

def initialize(abbreviated_commit_hash:, version:, exercise_name:, test_cases:, canonical_data_version: nil)
@abbreviated_commit_hash = abbreviated_commit_hash
attr_reader :exercise, :version, :canonical_data, :test_cases

def initialize(exercise:, version:, canonical_data:, test_cases:)
@exercise = exercise
@version = version
@exercise_name = exercise_name
@canonical_data = canonical_data
@test_cases = test_cases
@canonical_data_version = canonical_data_version
end

def get_binding
binding
end

def abbreviated_commit_hash
canonical_data.abbreviated_commit_hash
end

def canonical_data_version
canonical_data.version
end

def exercise_name
exercise.name
end

def exercise_name_camel
exercise_name.split('_').map(&:capitalize).join
exercise.name.camel_case
Copy link
Copy Markdown
Contributor

@Insti Insti May 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

slug is the canonical value for problem identity.
This should be exercise.slug.camel_case
But either way it's a Demeter violation, so should be just exercise.test_class_name

exercise_name_camel is a slightly misleading method name here given it's usage is to determine the test class name used in the template. But this should be a separate PR/refactoring. Issue: #651

end
end

module TemplateValuesFactory
def template_values
TemplateValues.new(
abbreviated_commit_hash: canonical_data.abbreviated_commit_hash,
canonical_data_version: canonical_data.version,
exercise: exercise,
version: version,
exercise_name: slug_underscore,
canonical_data: canonical_data,
test_cases: extract
)
end

private

def slug_underscore
slug ? slug.tr('-_', '_') : ''
end

def extract
load cases_load_name
extractor.cases(canonical_data.to_s)
end

def extractor
CaseValues::Extractor.new(
case_class: Object.const_get(Files::GeneratorCases.class_name(slug))
)
CaseValues::Extractor.new(
case_class: Object.const_get(exercise.case_class)
)
end

def cases_load_name
Files::GeneratorCases.source_filepath(paths.track, slug)
Files::GeneratorCases.source_filepath(paths.track, exercise.slug)
end
end
end
4 changes: 4 additions & 0 deletions lib/generator/underscore.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ module Underscore
def underscore
downcase.gsub(/[- ]/, '_').gsub(/[^\w?]/, '')
end

def camel_case
underscore.split('_').map(&:capitalize).join
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I'm sceptical of any change that is an "add" as part of a 'refactor' commit.
  • This change makes the class name Underscore inaccurate.

end
end
end
6 changes: 3 additions & 3 deletions test/fixtures/xruby/lib/generator/test_template.erb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/env ruby
require 'minitest/autorun'
require_relative 'acronym'
require_relative '<%= exercise_name %>'

# Common test data version: <%= abbreviated_commit_hash %>
class AcronymTest < Minitest::Test
# Common test data version: <%= canonical_data_version %> <%= abbreviated_commit_hash %>
class <%= exercise_name_camel %>Test < Minitest::Test
<% test_cases.each_with_index do |test_case, idx| %>
def <%= test_case.name %>
<%= test_case.skipped(idx) %>
Expand Down
20 changes: 20 additions & 0 deletions test/generator/exercise_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require_relative '../test_helper'

module Generator
class ExerciseTest < Minitest::Test
def test_slug
exercise = Exercise.new(slug: 'alpha')
assert_equal 'alpha', exercise.slug
end

def test_name
exercise = Exercise.new(slug: 'alpha-beta')
assert_equal 'alpha_beta', exercise.name
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yay for tests.
I'm not sure name is the right name for this property, but it is consistent with what we've been using.

end

def test_case_class
exercise = Exercise.new(slug: 'alpha-beta')
assert_equal 'AlphaBetaCase', exercise.case_class
end
end
end
12 changes: 4 additions & 8 deletions test/generator/files/generate_cases_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,23 @@ module Files
class GeneratorCasesTest < Minitest::Test
def test_available
track_path = '/track'
fake_filenames = %w(/track/zzz/alpha_case.rb /track/aaa/hy_phen_ated_case.rb)
Dir.stub :glob, fake_filenames do
fake_filepaths = %w(/track/exercises/alpha/zzz /track/exercises/hy-phen-ated/yyy)
Dir.stub :glob, fake_filepaths do
assert_equal %w(alpha hy-phen-ated), GeneratorCases.available(track_path)
end
end

def test_available_calls_glob_with_the_right_arguments
track_path = '/track'
expected_glob = "#{track_path}/exercises/*/.meta/generator/*_case.rb"
expected_glob = "#{track_path}/exercises/*/.meta/generator"
mock_glob_call = Minitest::Mock.new
mock_glob_call.expect :call, [], [expected_glob, File::FNM_DOTMATCH]
mock_glob_call.expect :call, [], [expected_glob]
Dir.stub :glob, mock_glob_call do
GeneratorCases.available(track_path)
end
mock_glob_call.verify
end

def test_class_name
assert_equal 'TwoParterCase', GeneratorCases.class_name('two-parter')
end

def test_source_filepath
track_path = '/track'
slug = 'slug'
Expand Down
4 changes: 2 additions & 2 deletions test/generator/files/metadata_files_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ class MetadataFilesTest < Minitest::Test
class TestMetadataFiles
def initialize
@paths = FixturePaths
@slug = 'alpha'
@exercise = Exercise.new(slug: 'alpha')
end
attr_reader :paths, :slug
attr_reader :paths, :exercise
include MetadataFiles
end

Expand Down
9 changes: 4 additions & 5 deletions test/generator/files/track_files_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ class TrackFilesTest < Minitest::Test
class TestTrackFiles
def initialize
@paths = FixturePaths
@slug = 'alpha-beta'
@exercise_name = 'alpha_beta'
@exercise = Exercise.new(slug: 'alpha-beta')
end
attr_accessor :paths, :slug, :exercise_name
attr_reader :paths, :exercise
include TrackFiles
end

Expand Down Expand Up @@ -44,9 +43,9 @@ def test_tests_template
class TestTrackFilesUseDefault
def initialize
@paths = FixturePaths
@slug = 'notemplate'
@exercise = Exercise.new(slug: 'notemplate')
end
attr_reader :paths, :slug
attr_reader :paths, :exercise
include TrackFiles
end

Expand Down
Loading