Skip to content
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
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
14 changes: 11 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,13 +35,21 @@ 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(exercise: exercise, repository: repository(exercise)),
logger: logger
)
end

def repository(exercise)
Repository.new(paths: paths, exercise: exercise)
end

def logger
logger = Logger.new($stdout)
logger.formatter = proc { |_severity, _datetime, _progname, msg| "#{msg}\n" }
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
31 changes: 5 additions & 26 deletions lib/generator/files/generator_cases.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,15 @@ module Files
module GeneratorCases
class << self
def available(track_path)
cases_filepaths(track_path).map { |filepath| slugify(filepath) }.sort
end

def class_name(exercise_name_or_slug)
filename(exercise_name_or_slug).split('_').map(&:capitalize).join
end

def source_filepath(track_path, slug)
path = meta_generator_path(track_path, slug)
filename = filename(slug) + '.rb'
File.join(path, filename)
filepaths(track_path).map do |filepath|
%r{#{track_path}/exercises/([-a-z]+)/}.match(filepath)[1]
end.sort
end

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('_', '-')
end

def filename(exercise_name_or_slug)
"#{exercise_name_or_slug.tr('-', '_')}_case"
end

def meta_generator_path(track_path, slug)
File.join(track_path, 'exercises', slug, '.meta', 'generator')
def filepaths(track_path)
Dir.glob(File.join(track_path, 'exercises', '*', '.meta', 'generator'))
end
end
end
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
21 changes: 18 additions & 3 deletions lib/generator/files/track_files.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,18 @@ def example_solution
ExampleSolutionFile.new(filename: File.join(solutions_path, example_filename))
end

def test_case
TestCaseFile.new(filename: File.join(generator_path, case_filename))
end

def tests_template
TestsTemplateFile.new(filename: tests_template_absolute_filename)
end

private

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

def meta_path
Expand All @@ -33,16 +37,24 @@ def solutions_path
File.join(meta_path, 'solutions')
end

def generator_path
File.join(meta_path, 'generator')
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 case_filename
"#{exercise.name}_case.rb"
end

def tests_template_absolute_filename
Expand Down Expand Up @@ -89,5 +101,8 @@ def generate(template:, values:)

class TestsTemplateFile < Readable
end

class TestCaseFile < Readable
end
end
end
26 changes: 12 additions & 14 deletions lib/generator/implementation.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
require 'delegate'
require 'forwardable'

module Generator
class Implementation
include Files::TrackFiles
include Files::MetadataFiles
include TemplateValuesFactory
extend Forwardable

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

attr_reader :paths, :slug
attr_reader :exercise, :repository
def_delegators :@repository, :tests_version, :example_solution, :minitest_tests,
:tests_template, :canonical_data, :test_case

def version
tests_version.to_i
Expand All @@ -25,16 +27,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 +56,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
14 changes: 14 additions & 0 deletions lib/generator/repository.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'delegate'

module Generator
class Repository
include Files::TrackFiles
include Files::MetadataFiles

def initialize(paths:, exercise:)
@paths = paths
@exercise = exercise
end
attr_reader :paths, :exercise
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
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
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)
test_case.filename
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
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
end

def test_case_class
exercise = Exercise.new(slug: 'alpha-beta')
assert_equal 'AlphaBetaCase', exercise.case_class
end
end
end
Loading