sourcery 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.ruby ADDED
@@ -0,0 +1,44 @@
1
+ ---
2
+ source:
3
+ - Profile
4
+ authors:
5
+ - name: Trans
6
+ email: transfire@gmail.com
7
+ copyrights:
8
+ - holder: Rubyworks
9
+ year: '2009'
10
+ license: BSD-2-Clause
11
+ replacements: []
12
+ alternatives: []
13
+ requirements:
14
+ - name: detroit
15
+ groups:
16
+ - build
17
+ development: true
18
+ dependencies: []
19
+ conflicts: []
20
+ repositories:
21
+ - uri: git://github.com/rubyworks/sourcery.git
22
+ scm: git
23
+ name: upstream
24
+ resources:
25
+ home: https://github.com/rubyworks/sourcery
26
+ code: https://github.com/rubyworks/sourcery
27
+ bugs: https://github.com/rubyworks/sourcery/issues
28
+ mail: https://groups.google.com/groups/rubyworks-mailinglist
29
+ extra: {}
30
+ load_path:
31
+ - lib
32
+ revision: 0
33
+ name: sourcery
34
+ title: Sourcery
35
+ version: 0.1.0
36
+ summary: New Age Ruby, Coding in the Astrological Plain.
37
+ created: '2009-07-15'
38
+ description: ! 'Sourcery puts a spell-casting layer between
39
+
40
+ the code you write and the code you ship. Using ERB templating
41
+
42
+ in this layer lets you write highly meta-magical code.'
43
+ organization: rubyworks
44
+ date: '2012-02-06'
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'sourcery'
3
+ Sourcery.cli(*ARGV)
4
+
@@ -0,0 +1,16 @@
1
+ module Sourcery
2
+
3
+ def self.metadata
4
+ @metadata ||= (
5
+ require 'yaml'
6
+ YAML.load_file(File.dirname(__FILE__) + '/sourcery.yml')
7
+ )
8
+ end
9
+
10
+ def self.const_missing(name)
11
+ metadata[name.to_s.downcase] || super(name)
12
+ end
13
+
14
+ end
15
+
16
+ require 'sourcery/cli'
@@ -0,0 +1,187 @@
1
+ module Sourcery
2
+
3
+ require 'sourcery/context'
4
+ require 'fileutils'
5
+ require 'erb'
6
+
7
+ # Spell Caster renders `src/` files to `lib/`.
8
+ #
9
+ class Caster
10
+
11
+ # Source directory, defaults to `src`.
12
+ attr :source
13
+
14
+ # Target directory, defaults to `lib`.
15
+ attr :target
16
+
17
+ #
18
+ attr :files
19
+
20
+ #
21
+ attr_accessor :ask
22
+
23
+ #
24
+ attr_accessor :skip
25
+
26
+ #
27
+ attr_accessor :stdout
28
+
29
+ #
30
+ #attr_accessor :delete
31
+
32
+ #
33
+ def initialize(options={})
34
+ @source = options[:source] || 'src'
35
+ @target = options[:target] || 'lib'
36
+ @force = options[:ask]
37
+ @skip = options[:skip]
38
+ @stdout = options[:stdout]
39
+ #@delete = options[:delete]
40
+
41
+ if options[:files]
42
+ @files = options[:files]
43
+ else
44
+ @files = collect_files(source)
45
+ end
46
+ end
47
+
48
+ # Collect all files from source except those starting with an `_` or `.`.
49
+ def collect_files(dir)
50
+ Dir[File.join(dir,'**/*')].reject do |f|
51
+ basename = File.basename(f)
52
+ basename.start_with?('_') or basename.start_with?('.')
53
+ end
54
+ end
55
+
56
+ #
57
+ def ask?
58
+ @ask
59
+ end
60
+
61
+ #
62
+ def skip?
63
+ @skip
64
+ end
65
+
66
+ #
67
+ #def delete? ; @delete ; end
68
+
69
+ #
70
+ def debug?
71
+ $DEBUG
72
+ end
73
+
74
+ #
75
+ def trial?
76
+ $TRIAL
77
+ end
78
+
79
+ #
80
+ def call
81
+ ensure_target_directory
82
+
83
+ copy_map = {}
84
+
85
+ files.each do |file|
86
+ output = target_file(file)
87
+ if output == file
88
+ raise "output and source file are identical -- #{file}."
89
+ end
90
+ copy_map[file] = output
91
+ end
92
+
93
+ copy_map.each do |file, output|
94
+ render(file, output)
95
+ end
96
+ end
97
+
98
+ # Render and save ERB template `file` to `output` file.
99
+ def render(file, output)
100
+ if File.file?(output) && skip?
101
+ print_line('SKIP', output)
102
+ else
103
+ template = ERB.new(File.read(file))
104
+ result = template.result(context.__binding__)
105
+ if stdout
106
+ puts result
107
+ else
108
+ save(result, output, file)
109
+ end
110
+ end
111
+ end
112
+
113
+ # Determine output file name given source `file`.
114
+ def target_file(file)
115
+ name = file.sub(source+'/', '')
116
+ File.join(target, name)
117
+ end
118
+
119
+ #
120
+ def context
121
+ @context ||= Context.new
122
+ end
123
+
124
+ #
125
+ def save(text, output, source_file)
126
+ name = output # relative_path(output)
127
+ if trial?
128
+ puts " CAST #{name} (dryrun)"
129
+ else
130
+ save = false
131
+ if File.exist?(output)
132
+ if FileUtils.uptodate?(output, [source_file])
133
+ print_line('UNCHANGED', name)
134
+ elsif ask?
135
+ case ask("%11s %s?" % ['OVERWRITE', name])
136
+ when 'y', 'yes'
137
+ save = true
138
+ end
139
+ else
140
+ save = true
141
+ end
142
+ else
143
+ save = true
144
+ end
145
+
146
+ if save
147
+ save_file(output, text)
148
+ puts " CAST #{name}"
149
+ end
150
+ end
151
+ end
152
+
153
+ #
154
+ def print_line(label, string)
155
+ "%11s %s" % [label, string]
156
+ end
157
+
158
+ # Save file and make it read-only.
159
+ def save_file(file, text)
160
+ #File.open(file, 'w'){ |f| << text }
161
+ mode = nil
162
+ if File.exist?(file)
163
+ mode = File.stat(file).mode
164
+ mask = mode | 0000200
165
+ File.chmod(mask, file) # make writeable
166
+ end
167
+ File.open(file, 'w'){ |f| f << text }
168
+ mode = mode || File.stat(file).mode
169
+ mask = mode & (mode ^ 0000222)
170
+ File.chmod(mask, file) # make read-only
171
+ end
172
+
173
+ #
174
+ def ask(prompt=nil)
175
+ $stdout << "#{prompt}"
176
+ $stdout.flush
177
+ $stdin.gets.chomp!
178
+ end
179
+
180
+ #
181
+ def ensure_target_directory
182
+ FileUtils.mkdir(target) unless File.directory?(target)
183
+ end
184
+
185
+ end
186
+
187
+ end
@@ -0,0 +1,56 @@
1
+ module Sourcery
2
+
3
+ require 'optparse'
4
+ require 'sourcery/caster'
5
+
6
+ #
7
+ def self.cli(*argv)
8
+ options = {}
9
+
10
+ usage = OptionParser.new do |use|
11
+ use.banner = 'Usage: sourcery [OPTIONS] [FILE1 FILE2 ...]'
12
+
13
+ #use.on('--delete', 'delete templates when finished') do
14
+ # options[:delete] = true
15
+ #end
16
+
17
+ use.on('-a', '--ask', 'prompt user before overwrites') do
18
+ options[:ask] = true
19
+ end
20
+
21
+ use.on('-s', '--skip', 'automatically skip overwrites') do
22
+ options[:skip] = true
23
+ end
24
+
25
+ use.on('-o', '--stdout', 'dump output to stdout instead of saving') do
26
+ options[:stdout] = true
27
+ end
28
+
29
+ use.on('-t', '--trial', 'run in trial mode (no actual disk write)') do
30
+ $TRIAL = true
31
+ end
32
+
33
+ use.on('--debug', 'run in debug mode') do
34
+ $DEBUG = true
35
+ end
36
+
37
+ use.on_tail('-h', '--help', 'display this help information') do
38
+ puts use
39
+ exit
40
+ end
41
+
42
+ #use['<DIR>', 'directory to till; default is working directory']
43
+ end
44
+
45
+ usage.parse!(argv)
46
+
47
+ if !argv.empty?
48
+ options[:files] = argv
49
+ end
50
+
51
+ caster = Caster.new(options)
52
+
53
+ caster.call
54
+ end
55
+
56
+ end
@@ -0,0 +1,45 @@
1
+ module Sourcery
2
+
3
+ require 'sourcery/metadata'
4
+
5
+ #
6
+ class Context
7
+
8
+ #
9
+ attr :metadata
10
+
11
+ #
12
+ def initialize(dir=nil)
13
+ @metadata = Metadata.new(dir)
14
+ end
15
+
16
+ #
17
+ def method_missing(s)
18
+ @metadata.send(s)
19
+ end
20
+
21
+ #
22
+ def root
23
+ metadata.root
24
+ end
25
+
26
+ #
27
+ def to_h
28
+ @metadata.to_h
29
+ end
30
+
31
+ # Processes through erb.
32
+ #def erb(text)
33
+ # erb = ERB.new(text)
34
+ # erb.result(binding)
35
+ #end
36
+
37
+ # Get a clean binding.
38
+ def __binding__
39
+ binding
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
@@ -0,0 +1,136 @@
1
+ module Sourcery
2
+
3
+ #
4
+ class Metadata
5
+
6
+ #
7
+ # Project root pathname.
8
+ #
9
+ attr :root
10
+
11
+ #
12
+ # Initialize new Metadata object.
13
+ #
14
+ def initialize(root=nil)
15
+ #@root = self.class.root(root) || Dir.pwd
16
+ @root = root || Dir.pwd
17
+ @cache = {}
18
+
19
+ raise "not a directory -- #{root}" unless File.directory?(root)
20
+
21
+ load_metadata
22
+ end
23
+
24
+ #
25
+ # If method is missing see if there is a metadata entry for it.
26
+ #
27
+ def method_missing(s, *a)
28
+ m = s.to_s
29
+ case m
30
+ when /=$/
31
+ raise ArgumentError if a.size != 1
32
+ @cache[s.to_s] = a.first
33
+ else
34
+ super(s, *a) unless a.empty?
35
+ @cache[s.to_s]
36
+ end
37
+ end
38
+
39
+ #
40
+ # Return copy of metadata store.
41
+ #
42
+ def to_h
43
+ @cache.dup
44
+ end
45
+
46
+ private
47
+
48
+ #
49
+ # Load metadata.
50
+ #
51
+ def load_metadata
52
+ load_metadata_from_directory
53
+ load_metadata_from_dotruby
54
+ end
55
+
56
+ #
57
+ # Load metadata from metadata directory.
58
+ #
59
+ def load_metadata_from_directory
60
+ entries = Dir.glob(File.join(meta_dir, '*'))
61
+ entries.each do |f|
62
+ val = File.read(f).strip
63
+ val = YAML.load(val) if val =~ /\A---/
64
+ @cache[File.basename(f)] = val
65
+ end
66
+ end
67
+
68
+ #
69
+ # Load metadata from .ruby file.
70
+ #
71
+ def load_metadata_from_dotruby
72
+ file = Dir[File.join(root, '.ruby')].first
73
+ if file
74
+ @cache.update(YAML.load_file(file))
75
+ end
76
+ end
77
+
78
+ #
79
+ # Locate the project's metadata directory. This is either
80
+ # `meta` or `.meta` or `var`.
81
+ #
82
+ def meta_dir
83
+ @meta_dir ||= Dir[File.join(root, '{meta,.meta,var}/')].first || '.meta/'
84
+ end
85
+
86
+ #
87
+ #def load_value(name)
88
+ # file = File.join(metadir, name)
89
+ # file = Dir[file].first
90
+ # if file && File.file?(file)
91
+ # #return erb(file).strip
92
+ # return File.read(file).strip
93
+ # end
94
+ #end
95
+
96
+ public
97
+
98
+ #
99
+ # Root directory is indicated by the presence of a +src/+ directory.
100
+ #
101
+ ROOT_INDICATORS = ['.ruby,var/,meta/,.meta/,.git,.hg,_darcs']
102
+
103
+ #
104
+ # Locate the project's root directory. This is determined
105
+ # by ascending up the directory tree from the current position
106
+ # until the ROOT_INDICATORS is matched. Returns +nil+ if not found.
107
+ #
108
+ def self.root(local=Dir.pwd)
109
+ local ||= Dir.pwd
110
+ Dir.chdir(local) do
111
+ dir = nil
112
+ ROOT_INDICATORS.find do |i|
113
+ dir = locate_root_at(i)
114
+ end
115
+ dir ? Pathname.new(File.dirname(dir)) : nil
116
+ end
117
+ end
118
+
119
+ #
120
+ # Helper method for `Metadata.root()`.
121
+ #
122
+ def self.locate_root_at(indicator)
123
+ root = nil
124
+ dir = Dir.pwd
125
+ while !root && dir != '/'
126
+ find = File.join(dir, indicator)
127
+ root = Dir.glob(find, File::FNM_CASEFOLD).first
128
+ #break if root
129
+ dir = File.dirname(dir)
130
+ end
131
+ root ? Pathname.new(root) : nil
132
+ end
133
+
134
+ end
135
+
136
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sourcery
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Trans
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: detroit
16
+ requirement: &14325640 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *14325640
25
+ description: ! 'Sourcery puts a spell-casting layer between
26
+
27
+ the code you write and the code you ship. Using ERB templating
28
+
29
+ in this layer lets you write highly meta-magical code.'
30
+ email:
31
+ - transfire@gmail.com
32
+ executables:
33
+ - sourcery
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .ruby
38
+ - bin/sourcery
39
+ - lib/sourcery/caster.rb
40
+ - lib/sourcery/cli.rb
41
+ - lib/sourcery/context.rb
42
+ - lib/sourcery/metadata.rb
43
+ - lib/sourcery.rb
44
+ homepage: https://github.com/rubyworks/sourcery
45
+ licenses:
46
+ - BSD-2-Clause
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 1.8.11
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: New Age Ruby, Coding in the Astrological Plain.
69
+ test_files: []