Skip to content
This repository has been archived by the owner on Apr 6, 2021. It is now read-only.

Commit

Permalink
Use TaskEnv to pass around data to tasks, add config.require support.
Browse files Browse the repository at this point in the history
We want the graph to be lazily loaded, so I've added TaskEnv, a monad
style class that encapsulates the loading of the config, graph and other
things. This is the only object we pass to tasks other than the options,
so they can use this to get the graph and config.

In addition, the first config option called "require" has been added.
This is a config option that takes a list of files, folders or
shell-globs that should be required when Shanty is loaded. This is used
to load custom build-scripts or project types from within a repo that
aren't proper packaged plugins or the like.

Tasks have been renamed to TaskSets to better distinguish the difference
between the classes that have tasks in them, and the tasks themselves.

Finally, I have upgraded the Ruby version on Travis to 2.2.
  • Loading branch information
nathankleyn committed Jan 8, 2015
1 parent bdf32b6 commit 6419458
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 140 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.DS_Store
*.gem
coverage
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: ruby
rvm:
- 2.1.4
- 2.2.0
notifications:
webhooks: https://hubot-shantytown.rhcloud.com/hubot/travis?room=#shantytow n
script: bundle exec ./bin/shanty --trace test
25 changes: 5 additions & 20 deletions lib/shanty.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
require 'shanty/cli'
require 'shanty/discoverers/shantyfile'
require 'shanty/graph'
require 'shanty/global'
require 'shanty/mutators/git'
require 'shanty/tasks/basic'
require 'shanty/task_env'
require 'shanty/task_sets/basic'

module Shanty
# Main shanty class
Expand All @@ -16,11 +16,10 @@ class Shanty

def start!
setup_i18n
Cli.new(graph).run
end

def graph
@graph ||= construct_project_graph
task_env = TaskEnv.new
task_env.load!
Cli.new(task_env).run
end

private
Expand All @@ -29,19 +28,5 @@ def setup_i18n
I18n.enforce_available_locales = true
I18n.load_path = Dir[File.join(GEM_ROOT, 'translations', '*.yml')]
end

def construct_project_graph
project_templates = Dir.chdir(Global.root) do
Discoverer.new.discover_all
end

projects = project_templates.map do |project_template|
project_template.type.new(project_template)
end

graph = Graph.new(projects)

Mutator.new.apply_mutations(graph)
end
end
end
46 changes: 29 additions & 17 deletions lib/shanty/cli.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
require 'commander'
require 'i18n'
require 'shanty/task'
require 'shanty/task_set'

module Shanty
# Public: Handle the CLI interface between the user and the registered tasks
# and plugins.
class Cli
include Commander::Methods

def initialize(graph)
@graph = graph
def initialize(task_env)
@task_env = task_env
end

def tasks
Task.tasks.reduce({}) do |acc, task|
acc.merge(task.task_definitions)
TaskSet.task_sets.reduce({}) do |acc, task_set|
acc.merge(task_set.tasks)
end
end

Expand All @@ -36,28 +36,40 @@ def setup_tasks
end

def setup_task(name, task)
command name do |c|
command(name) do |c|
c.description = I18n.t(task[:desc], default: task[:desc])

task[:options].each do |option_name, option|
c.option(syntax_for_option(option_name, option), I18n.t(option[:desc], default: option[:desc]))
end

add_options_to_command(task, c)
c.action do |args, options|
execute_task(name, args, options)
task = tasks[name]
options.default(Hash[defaults_for_options(task)])
execute_task(name, task, args, options)
end
end
end

def execute_task(name, args, options)
task = tasks[name]
def add_options_to_command(task, command)
task[:options].each do |option_name, option|
command.option(syntax_for_option(option_name, option), I18n.t(option[:desc], default: option[:desc]))
end
end

def execute_task(name, task, args, options)
# We use allocate here beccause we do not want this to blow up because the class defines a constructor.
# We cannot and do not support taskset classes needing constructors.
klass = task[:klass].allocate
arity = klass.method(name).arity

default_option_pairs = task[:options].map do |option_name, option|
args.unshift(@task_env) if arity >= 2
args.unshift(options) if arity >= 1

klass.send(name, *args)
end

def defaults_for_options(task)
task[:options].map do |option_name, option|
[option_name, default_for_type(option)]
end
options.default(Hash[default_option_pairs])

task[:klass].new.send(name, options, @graph, *args)
end

def syntax_for_option(name, option)
Expand Down
49 changes: 0 additions & 49 deletions lib/shanty/global.rb

This file was deleted.

45 changes: 0 additions & 45 deletions lib/shanty/task.rb

This file was deleted.

67 changes: 67 additions & 0 deletions lib/shanty/task_env.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
require 'deep_merge'
require 'yaml'

module Shanty
#
class TaskEnv
CONFIG_FILE = '.shanty.yml'
DEFAULT_CONFIG = {}

def load!
(config['require'] || {}).each do |requirement|
requirement = "#{requirement}/**/*.rb" unless requirement.include?('.rb')
Dir[requirement].each { |f| require f }
end
end

def graph
@graph ||= construct_project_graph
end

private

def environment
@environment = ENV['SHANTY_ENV'] || 'local'
end

def root
@root ||= find_root
end

def config
return @config unless @config.nil?

file_config = YAML.load_file("#{root}/#{CONFIG_FILE}") || {}
@config = DEFAULT_CONFIG.deep_merge!(file_config[environment])
end

def find_root
if root_dir.nil?
fail "Could not find a #{CONFIG_FILE} file in this or any parent directories. \
Please run `shanty init` in the directory you want to be the root of your project structure."
end

root_dir
end

def root_dir
Pathname.new(Dir.pwd).ascend do |d|
return d if d.join(CONFIG_FILE).exist?
end
end

def construct_project_graph
project_templates = Dir.chdir(root) do
Discoverer.new.discover_all
end

projects = project_templates.map do |project_template|
project_template.type.new(project_template)
end

graph = Graph.new(projects)

Mutator.new.apply_mutations(graph)
end
end
end
44 changes: 44 additions & 0 deletions lib/shanty/task_set.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module Shanty
# Public: Discover shanty tasks
class TaskSet
class << self
attr_reader :task_sets, :tasks, :partial_task
end

# This method is auto-triggered by Ruby whenever a class inherits from
# Shanty::TaskSet. This means we can build up a list of all the tasks
# without requiring them to register with us - neat!
def self.inherited(task_set)
@task_sets ||= []
@task_sets << task_set
end

def self.desc(desc)
partial_task[:desc] = desc
end

def self.param(name, options = {})
partial_task[:params][name] = options
end

def self.option(name, options = {})
partial_task[:options][name] = options
end

def self.method_added(name)
@tasks ||= {}
@tasks[name] = partial_task.merge(klass: self)

# Now reset the task definition.
@partial_task = {}
end

def self.partial_task
@partial_task ||= {}
@partial_task[:params] ||= {}
@partial_task[:options] ||= {}

@partial_task
end
end
end
21 changes: 13 additions & 8 deletions lib/shanty/tasks/basic.rb → lib/shanty/task_sets/basic.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
require 'i18n'
require 'shanty/task'
require 'shanty/task_set'
require 'shanty/util'

module Shanty
# Public: A set of basic tasks that can be applied to all projects and that
# ship with the core of Shanty.
class BasicTasks < Shanty::Task
class BasicTasks < Shanty::TaskSet
desc 'tasks.init.desc'
def init
# FIXME
end

desc 'tasks.projects.desc'
option :changed, type: :boolean, desc: 'tasks.common.options.changed'
option :types, type: :array, desc: 'tasks.common.options.types'
def projects(options, graph)
graph.projects.each do |project|
def projects(options, task_env)
task_env.graph.projects.each do |project|
next if options.changed? && !project.changed?
puts project
end
Expand All @@ -20,8 +25,8 @@ def projects(options, graph)
option :changed, type: :boolean, desc: 'tasks.common.options.changed'
option :watch, type: :boolean, desc: 'tasks.common.options.watch'
option :types, type: :array, desc: 'tasks.common.options.types'
def build(options, graph)
graph.projects.each do |project|
def build(options, task_env)
task_env.graph.projects.each do |project|
next if options.changed? && !project.changed?
fail I18n.t('tasks.build.failed', project: project) unless project.publish(:build)
end
Expand All @@ -31,8 +36,8 @@ def build(options, graph)
option :changed, type: :boolean, desc: 'tasks.common.options.changed'
option :watch, type: :boolean, desc: 'tasks.common.options.watch'
option :types, type: :array, desc: 'tasks.common.options.types'
def test(options, graph)
graph.projects.each do |project|
def test(options, task_env)
task_env.graph.projects.each do |project|
next if options.changed? && !project.changed?
fail I18n.t('tasks.test.failed', project: project) unless project.publish(:test)
end
Expand Down

0 comments on commit 6419458

Please sign in to comment.