expansions 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +3 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +4 -0
  4. data/Guardfile +5 -0
  5. data/README.md +61 -0
  6. data/Rakefile +1 -0
  7. data/bin/expansions +4 -0
  8. data/expansions.gemspec +29 -0
  9. data/lib/core/array.rb +8 -0
  10. data/lib/core/cli_interface.rb +39 -0
  11. data/lib/core/copy.rb +19 -0
  12. data/lib/core/copy_to_target.rb +11 -0
  13. data/lib/core/enumerable_extensions.rb +11 -0
  14. data/lib/core/erb_template_file.rb +31 -0
  15. data/lib/core/expansion.rb +79 -0
  16. data/lib/core/file.rb +32 -0
  17. data/lib/core/file_merge.rb +45 -0
  18. data/lib/core/kernel.rb +35 -0
  19. data/lib/core/log.rb +15 -0
  20. data/lib/core/mustache_template_file.rb +9 -0
  21. data/lib/core/shell.rb +7 -0
  22. data/lib/core/shell_action_against_file.rb +10 -0
  23. data/lib/core/string.rb +5 -0
  24. data/lib/core/template_processors.rb +22 -0
  25. data/lib/core/template_visitor.rb +18 -0
  26. data/lib/core/version.rb +3 -0
  27. data/lib/expansions.rb +24 -0
  28. data/spec/spec_helper.rb +50 -0
  29. data/spec/specs/array_spec.rb +20 -0
  30. data/spec/specs/copy_spec.rb +87 -0
  31. data/spec/specs/copy_to_target_spec.rb +22 -0
  32. data/spec/specs/enumerable_extensions_spec.rb +27 -0
  33. data/spec/specs/erb_template_file_spec.rb +63 -0
  34. data/spec/specs/expansion_spec.rb +107 -0
  35. data/spec/specs/file_merge_spec.rb +131 -0
  36. data/spec/specs/file_spec.rb +79 -0
  37. data/spec/specs/kernel_spec.rb +56 -0
  38. data/spec/specs/mustache_template_file_spec.rb +48 -0
  39. data/spec/specs/shell_action_against_file_spec.rb +22 -0
  40. data/spec/specs/string_spec.rb +11 -0
  41. data/spec/specs/template_processors_spec.rb +62 -0
  42. data/spec/specs/template_visitor_spec.rb +84 -0
  43. metadata +215 -0
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg
2
+ .bundle
3
+ Gemfile.lock
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.3@expander --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'rspec' do
2
+ watch(%r{^spec/specs/.+_spec\.rb$})
3
+ watch(%r{^lib/developwithpassion_expander/(.+)\.rb$}) { |m| "spec/specs/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ #developwithpassion_expander
2
+
3
+ This was one of the first projects I wrote when I was teaching myself ruby. A lot of the codebase definitely shows its age, but it is also what I use to this day to manage my [cross platform script configuration system].
4
+
5
+ It is really an extremely scaled down rake built to suit one specific purpose (supporting the expansions for the scripts I was managing!!)
6
+
7
+ #Usage
8
+
9
+ Create an ExpansionFile and place it in a folder that you want to use the expander in.
10
+
11
+ Here is the simplest possible ExpansionFile you could write:
12
+
13
+ ```ruby
14
+ expand do
15
+ puts "Hello"
16
+ end
17
+ ```
18
+
19
+ Along with regular ruby, within the context of an expand block you have access to the following methods:
20
+
21
+ -look_for_templates_in [pattern]
22
+ * This method allows you to specify glob style patterns for template files that will be used during the expansion process. Currently the system only supports Mustache and ERB templates and also expects the template files to end with their appropriate {mustache/erb} extension.
23
+ During the epansion process files that have been registered as templates will get expanded according to configuration information that has been provided to [configatron](https://github.com/markbates/configatron). My recommendation is to load configuration information near the start of your ExpansionFile. Here is an example of loading the configuration:
24
+
25
+ ```ruby
26
+ configs = {
27
+ :core =>
28
+ {
29
+ :home => ENV["HOME"] ,
30
+ :devtools_root => File.expand_path(File.dirname(__FILE__))
31
+ }
32
+ }
33
+ configatron.configure_from_hash configs
34
+ load "#{File.basename(`whoami`.chomp)}.settings"
35
+
36
+ platform_config_file = "expansions/config_#{platform}"
37
+ log "Loading config file #{platform_config_file}"
38
+ load "#{platform_config_file}"
39
+ ```
40
+ Notice in the example above I am populating configatron with information from one hash in the main ExpansionFile as well as supplementing that configuration information with configuration information from another file. Here is what is in the platform_config_file:
41
+
42
+ ```ruby
43
+ configatron.configure_from_hash :core =>{
44
+ :launcher => '',
45
+ :vim => 'mvim',
46
+ :editor => 'mvim -v -f -c "au VimLeave * !open -a Terminal"',
47
+ :is_windows => false,
48
+ :is_cygwin => false,
49
+ :is_mingw => false,
50
+ :is_osx => true,
51
+ :vi => "'mvim -v'",
52
+ },
53
+ :git => {
54
+ :autocrlf => 'input',
55
+ :merge_tool => 'vimdiff',
56
+ :diff_tool => 'vimdiff',
57
+ },
58
+ :vim => {
59
+ :environment => 'mac'
60
+ }
61
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/expansions ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'expansions'
4
+ Expansions::CLIInterface.new.run(ARGV)
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib",__FILE__)
3
+ require "core/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "expansions"
7
+ s.version = Expansions::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Develop With Passion®"]
10
+ s.email = ["open_source@developwithpassion.com"]
11
+ s.homepage = "http://www.developwithpassion.com"
12
+ s.summary = %q{Simple Expansion Automation Utility}
13
+ s.description = %q{Automation utitlity that I use to support cross platform file maintenance}
14
+ s.rubyforge_project = "expansions"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "guard"
23
+ s.add_development_dependency "guard-rspec"
24
+ s.add_development_dependency "fakes-rspec"
25
+ s.add_development_dependency "rb-notifu"
26
+ s.add_runtime_dependency 'configatron', '>=2.9.0'
27
+ s.add_runtime_dependency "mustache"
28
+ s.add_runtime_dependency "developwithpassion_arrays"
29
+ end
data/lib/core/array.rb ADDED
@@ -0,0 +1,8 @@
1
+ class Array
2
+ def as_home_files
3
+ self.map{|item| item.as_home_file}
4
+ end
5
+ def as_glob_pattern
6
+ File.join(self)
7
+ end
8
+ end
@@ -0,0 +1,39 @@
1
+ module Expansions
2
+ class CLIInterface
3
+ def startup
4
+ [
5
+ TemplateProcessors,
6
+ TemplateVisitor,
7
+ Expansion,
8
+ Shell
9
+ ].each{|item| item.send(:include,Singleton)}
10
+
11
+ TemplateProcessors.instance.register_processor(:erb,ERBTemplateFile.new)
12
+ TemplateProcessors.instance.register_processor(:mustache,MustacheTemplateFile.new)
13
+ end
14
+
15
+ def get_expansion_file_name(args)
16
+ return args.count > 0 ? args[0] : "Expansionfile"
17
+ end
18
+
19
+ def ensure_expansion_file_provided(file)
20
+ unless File.exist?(file)
21
+ print <<-prompt
22
+ dwp_expand aborted!
23
+ No Expansionfile found (looking for: Expansionfile)
24
+ prompt
25
+ exit
26
+ end
27
+ end
28
+
29
+ def run(args = [])
30
+ file_to_run = get_expansion_file_name(args)
31
+ ensure_expansion_file_provided file_to_run
32
+ startup
33
+ enable_logging
34
+ log "Running Expansions defined in file:#{file_to_run}"
35
+ load file_to_run
36
+ Expansion.instance.run
37
+ end
38
+ end
39
+ end
data/lib/core/copy.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Expansions
2
+ class Copy
3
+ def initialize(copy_target)
4
+ array :sources do|a|
5
+ a.read_and_write
6
+ a.mutator :folder do|item| register(item,true) end
7
+ a.mutator :contents do|item| register(item,false) end
8
+ a.mutator :all_contents_in do|set_of_folders| set_of_folders.each{|item| contents(item)} end
9
+ a.mutator :all_folders_in do|set_of_folders| set_of_folders.each{|item| folder(item)} end
10
+ a.process_using :run, copy_target
11
+ end
12
+ end
13
+
14
+ :private
15
+ def register(folder,copy_containing_folder = false)
16
+ @sources.push(copy_containing_folder ? folder : File.join(folder,'.'))
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ module Expansions
2
+ class CopyToTarget
3
+ def initialize(target)
4
+ @target = target
5
+ end
6
+
7
+ def run_using(source_item)
8
+ Shell.instance.run("cp -rf #{source_item} #{@target}")
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Enumerable
2
+ def process_all_items_using(visitor)
3
+ self.each{|item| visitor.run_using(item)}
4
+ end
5
+ end
6
+
7
+ class Hash
8
+ def process_all_values_using(&block)
9
+ self.values.each{|item| yield item}
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ module Expansions
2
+ class ERBTemplateFile
3
+ def prepare_template(template)
4
+ token_regex = /(@\w[\w\.\_]+\w@)/
5
+
6
+ hits = template.scan(token_regex)
7
+ tags = hits.map do |item|
8
+ item[0].gsub(/@/,'').squeeze
9
+ end
10
+ tags = tags.map{|tag| tag.to_sym}.uniq
11
+
12
+ tags.inject(template) do |text, tag|
13
+ text.gsub /@#{tag.to_s}@/, "<%= #{tag.to_s} %>"
14
+ end
15
+ end
16
+
17
+ def process_template(template,binding)
18
+ erb = ERB.new(template, 0, "%")
19
+ erb.result(binding)
20
+ end
21
+
22
+ def process(args)
23
+ template = prepare_template(File.read(args[:input]))
24
+ result = process_template(template,args[:binding])
25
+
26
+ File.open_for_write(args[:output]) do|file|
27
+ file.write(result)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,79 @@
1
+ module Expansions
2
+ class Expansion
3
+ attr_accessor :copies,:files_to_merge,:globber
4
+
5
+ def initialize
6
+ initialize_hashes :files_to_merge,:copies
7
+
8
+ @globber = lambda{|path| return glob(path)}
9
+
10
+ array :cleanup_items do|a|
11
+ a.readable
12
+ a.process_using :run_cleanup,:call
13
+ end
14
+
15
+ array :setup_items do|a|
16
+ a.readable
17
+ a.process_using :run_setup_items,:call
18
+ end
19
+
20
+ array :executable_files do|a|
21
+ a.mutator :register_executable
22
+ a.process_using :mark_files_executable,ShellActionAgainstFile.new("chmod +x")
23
+ end
24
+
25
+ array :files_with_line_endings_to_fix do|a|
26
+ a.mutator :fix_line_endings_for
27
+ a.process_using :fix_line_endings,ShellActionAgainstFile.new("d2u")
28
+ end
29
+
30
+ array :folders_to_purge do|a|
31
+ a.mutator :register_folder_to_purge
32
+ a.process_using :purge_targets,ShellActionAgainstFile.new("rm -rf")
33
+ end
34
+ end
35
+
36
+ def copy_to target,&block
37
+ hash_process(target,@copies,lambda{|key| return Copy.new(CopyToTarget.new(key))},&block)
38
+ end
39
+
40
+ def merge_to target,&block
41
+ hash_process(target,@files_to_merge,lambda{|key| return FileMerge.new(key)},&block)
42
+ end
43
+
44
+ def look_for_templates_in(path)
45
+ @globber.call(path).process_all_items_using(TemplateVisitor.instance)
46
+ end
47
+
48
+ def cleanup(&block)
49
+ @cleanup_items << block
50
+ end
51
+
52
+ def before(&block)
53
+ @setup_items << block
54
+ end
55
+
56
+ def run
57
+ run_setup_items
58
+ purge_targets
59
+ expand_items
60
+ merge_items
61
+ run_cleanup
62
+ fix_line_endings
63
+ mark_files_executable
64
+ end
65
+
66
+ :private
67
+ def hash_process(target,hash,factory,&block)
68
+ symbol = target.to_sym
69
+ hash[symbol] = factory.call(target) unless hash.has_key?(symbol)
70
+ hash[symbol].instance_eval(&block)
71
+ end
72
+ def expand_items
73
+ @copies.process_all_values_using{|copy| copy.run}
74
+ end
75
+ def merge_items
76
+ @files_to_merge.process_all_values_using{|merge| merge.run}
77
+ end
78
+ end
79
+ end
data/lib/core/file.rb ADDED
@@ -0,0 +1,32 @@
1
+ class File
2
+ def self.open_for_read(file)
3
+ File.open(file,'r').each do|line|
4
+ yield line
5
+ end
6
+ end
7
+
8
+ def self.read_all_text(file)
9
+ File.read_all_text_after_skipping_lines(file,0)
10
+ end
11
+
12
+ def self.read_all_text_after_skipping_lines(file,number_of_lines_to_skip)
13
+ index = 1
14
+ contents = ''
15
+
16
+ if File.exist?(file)
17
+ File.open_for_read(file) do |line|
18
+ contents += line if index > number_of_lines_to_skip
19
+ index+=1
20
+ end
21
+ end
22
+
23
+ return contents
24
+ end
25
+
26
+ def self.open_for_write(file)
27
+ File.open(file,'w') do|new_file|
28
+ yield new_file
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,45 @@
1
+ module Expansions
2
+ class FileMerge
3
+ attr_accessor :read_original_contents
4
+
5
+ def initialize(output_file)
6
+ array :before_files do|a|
7
+ a.read_and_write
8
+ a.mutator :add_before_original_contents do |file| add_merge_file(@before_files,file) end
9
+ a.mutator :add do|file| add_before_original_contents(file) end
10
+ end
11
+ array :after_files do|a|
12
+ a.read_and_write
13
+ a.mutator :add_after_original_contents do|file| add_merge_file(@after_files,file) end
14
+ end
15
+ @output_file = output_file
16
+ @read_original_contents = true
17
+ end
18
+
19
+ def dont_read_original_file_contents
20
+ @read_original_contents = false
21
+ end
22
+
23
+ def run
24
+ original_contents = File.read_all_text(@output_file)
25
+ FileUtils.rm_f @output_file
26
+ File.open_for_write(@output_file) do |file|
27
+ merge_files(@before_files,file)
28
+ file.write original_contents if @read_original_contents
29
+ merge_files(@after_files,file)
30
+ end
31
+ end
32
+
33
+ def merge_files(source_files,target)
34
+ source_files.each do|source|
35
+ target.write File.read_all_text(source)
36
+ end
37
+ end
38
+
39
+ :private
40
+ def add_merge_file(items,file)
41
+ return if items.include?(file)
42
+ items << file if File.exists?(file)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ module Kernel
2
+ def expand(title = "Expansion",&block)
3
+ DevelopWithPassion::Expander::Expansion.instance.instance_eval(&block)
4
+ end
5
+
6
+ def glob(path)
7
+ items = Dir.glob(path,File::FNM_DOTMATCH)
8
+ items.each{|item| yield item if block_given?}
9
+ return items
10
+ end
11
+
12
+ def disable_logging
13
+ DevelopWithPassion::Expander::Log.disable
14
+ end
15
+
16
+ def enable_logging
17
+ DevelopWithPassion::Expander::Log.enable
18
+ end
19
+
20
+ def delayed
21
+ Configatron::Delayed.new do
22
+ yield
23
+ end
24
+ end
25
+
26
+ def dynamic
27
+ Configatron::Dynamic.new do
28
+ yield
29
+ end
30
+ end
31
+
32
+ def log(message)
33
+ DevelopWithPassion::Expander::Log.message(message)
34
+ end
35
+ end
data/lib/core/log.rb ADDED
@@ -0,0 +1,15 @@
1
+ module Expansions
2
+ class Log
3
+ class << self
4
+ def message(item)
5
+ puts item if @enabled
6
+ end
7
+ def enable
8
+ @enabled = true
9
+ end
10
+ def disable
11
+ @enabled = false
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module Expansions
2
+ class MustacheTemplateFile
3
+ def process(args)
4
+ template = File.read_all_text(args[:input])
5
+
6
+ File.open_for_write(args[:output]){|file| file << Mustache.render(template,configatron.to_hash)}
7
+ end
8
+ end
9
+ end
data/lib/core/shell.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Expansions
2
+ class Shell
3
+ def run(cmd)
4
+ return `#{cmd}`
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module Expansions
2
+ class ShellActionAgainstFile
3
+ def initialize(cmd)
4
+ @cmd = cmd
5
+ end
6
+ def run_using(file_name)
7
+ Shell.instance.run("#{@cmd} #{file_name}")
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class String
2
+ def as_home_file
3
+ File.join(ENV["HOME"],self)
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ module Expansions
2
+ class TemplateProcessors
3
+ def initialize(processors = {})
4
+ @processors = processors
5
+ end
6
+
7
+ def get_processor_for(file_name)
8
+ template_type = File.extname(file_name).gsub(/\./,'').to_sym
9
+ raise "There is no processor for #{file_name}" unless processor_exists_for(template_type)
10
+ return @processors[template_type]
11
+ end
12
+
13
+ def register_processor(template_type,processor)
14
+ raise "The processor for the template already exists" if processor_exists_for(template_type)
15
+ @processors[template_type.to_sym] = processor
16
+ end
17
+
18
+ def processor_exists_for(template_type)
19
+ return @processors.has_key?(template_type.to_sym)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ module Expansions
2
+ class TemplateVisitor
3
+ def initialize(args = {})
4
+ @processor_registry = args.fetch(:processor_registry,TemplateProcessors.instance)
5
+ @file = args.fetch(:file,File)
6
+ end
7
+
8
+ def run_using(file_name)
9
+ processor = @processor_registry.get_processor_for(file_name)
10
+ generated_name = File.basename(file_name,File.extname(file_name))
11
+ generated_name = generated_name.gsub(/\.dotfile/,"")
12
+ generated_name = ".#{generated_name}" if (/\.dotfile/ =~ file_name)
13
+ output = File.join(File.dirname(file_name),generated_name)
14
+ @file.delete(output) if @file.exists?(output)
15
+ processor.process(:input => file_name,:output => output)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Expansions
2
+ VERSION = "0.0.6"
3
+ end
data/lib/expansions.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'configatron'
2
+ require 'fileutils'
3
+ require 'erb'
4
+ require 'singleton'
5
+ require 'mustache'
6
+ require 'developwithpassion_arrays'
7
+
8
+ require 'core/array'
9
+ require 'core/cli_interface'
10
+ require 'core/copy'
11
+ require 'core/copy_to_target'
12
+ require 'core/enumerable_extensions'
13
+ require 'core/erb_template_file'
14
+ require 'core/expansion'
15
+ require 'core/file'
16
+ require 'core/file_merge'
17
+ require 'core/kernel'
18
+ require 'core/log'
19
+ require 'core/mustache_template_file'
20
+ require 'core/shell'
21
+ require 'core/shell_action_against_file'
22
+ require 'core/string'
23
+ require 'core/template_processors'
24
+ require 'core/template_visitor'
@@ -0,0 +1,50 @@
1
+ require 'rspec'
2
+ require 'fileutils'
3
+ require 'fakes'
4
+ require 'fakes-rspec'
5
+
6
+ Dir.chdir(File.join(File.dirname(__FILE__),"..,lib".split(','))) do
7
+ require 'expansions.rb'
8
+ end
9
+
10
+ class RelativeFileSystem
11
+ class << self
12
+ def base_path
13
+ path = File.join(File.expand_path(File.dirname(__FILE__)),"spec_filesytem")
14
+
15
+ return path
16
+ end
17
+
18
+ def file_name(name)
19
+ item = File.join(base_path,name)
20
+ return item
21
+ end
22
+ end
23
+
24
+ def initialize
25
+ @files = []
26
+ FileUtils.mkdir_p(RelativeFileSystem.base_path)
27
+ end
28
+
29
+
30
+ def teardown
31
+ FileUtils.rm_rf(RelativeFileSystem.base_path)
32
+ end
33
+
34
+ def write_file(name,contents)
35
+ FileUtils.mkdir_p File.dirname(RelativeFileSystem.file_name(name))
36
+ File.open(RelativeFileSystem.file_name(name),'w') do |file|
37
+ file << contents
38
+ end
39
+ end
40
+ end
41
+
42
+ def catch_exception
43
+ begin
44
+ yield
45
+ rescue Exception => e
46
+ exception = e
47
+ end
48
+ exception
49
+ end
50
+
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ module Expansions
4
+ describe Array do
5
+ it "should be able to return the array contents as a glob pattern" do
6
+ items = %w[this is cool **/*.rb]
7
+ %w[this is cool **/*.rb].as_glob_pattern.should == File.join(items)
8
+ end
9
+
10
+ it "should map each item as a home item" do
11
+ items = %w[this is cool]
12
+ items.as_home_files.should == items.map{|item| item.as_home_file}
13
+ end
14
+
15
+ it "should be able to be converted to a glob pattern" do
16
+ %w[this is cool].as_glob_pattern.should == "this/is/cool"
17
+ end
18
+
19
+ end
20
+ end