germinate 1.2.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/.gitignore +2 -0
- data/History.txt +26 -0
- data/README.rdoc +152 -0
- data/Rakefile +43 -0
- data/TODO +140 -0
- data/bin/germ +260 -0
- data/cucumber.yml +2 -0
- data/examples/basic.rb +123 -0
- data/examples/short.rb +19 -0
- data/features/author-formats-article.feature +111 -0
- data/features/author-lists-info.pending_feature +48 -0
- data/features/author-publishes-article-source.feature +5 -0
- data/features/author-publishes-article.feature +57 -0
- data/features/author-republishes-article.feature +5 -0
- data/features/author-selects-hunks.feature +26 -0
- data/features/author-sets-variables.feature +88 -0
- data/features/author-updates-article-source.feature +5 -0
- data/features/author-views-stuff.pending_feature +52 -0
- data/features/bin/quoter +6 -0
- data/features/bin/sorter +4 -0
- data/features/example_articles/bracketing.rb +27 -0
- data/features/example_articles/escaping.txt +13 -0
- data/features/example_articles/excerpt_output.rb +16 -0
- data/features/example_articles/hello.rb +9 -0
- data/features/example_articles/pipelines.txt +25 -0
- data/features/example_articles/regexen.rb +24 -0
- data/features/example_articles/sample_offsets.rb +18 -0
- data/features/example_articles/specials.rb +19 -0
- data/features/example_articles/stderr.rb +10 -0
- data/features/example_articles/wrapping.rb +8 -0
- data/features/example_output/bracketing.out +23 -0
- data/features/example_output/code_samples.txt +186 -0
- data/features/example_output/escaping.out +5 -0
- data/features/example_output/excerpt_output.out +6 -0
- data/features/example_output/hello.txt +1 -0
- data/features/example_output/pipelines.out +28 -0
- data/features/example_output/regexen.txt +22 -0
- data/features/example_output/sample_offsets.txt +15 -0
- data/features/example_output/specials.txt +40 -0
- data/features/example_output/stderr.out +3 -0
- data/features/example_output/wrapping.txt +3 -0
- data/features/step_definitions/germinate.rb +42 -0
- data/features/support/env.rb +20 -0
- data/germinate.gemspec +55 -0
- data/lib/germinate.rb +54 -0
- data/lib/germinate/application.rb +113 -0
- data/lib/germinate/article_editor.rb +20 -0
- data/lib/germinate/formatter.rb +119 -0
- data/lib/germinate/hunk.rb +183 -0
- data/lib/germinate/implicit_insertion.rb +9 -0
- data/lib/germinate/insertion.rb +29 -0
- data/lib/germinate/librarian.rb +293 -0
- data/lib/germinate/origin.rb +5 -0
- data/lib/germinate/pipeline.rb +13 -0
- data/lib/germinate/publisher.rb +57 -0
- data/lib/germinate/reader.rb +266 -0
- data/lib/germinate/selector.rb +136 -0
- data/lib/germinate/shared_style_attributes.rb +54 -0
- data/lib/germinate/shell_process.rb +94 -0
- data/lib/germinate/shell_publisher.rb +19 -0
- data/lib/germinate/simple_publisher.rb +7 -0
- data/lib/germinate/source_file.rb +41 -0
- data/lib/germinate/text_transforms.rb +119 -0
- data/lib/germinate/transform_process.rb +25 -0
- data/lib/germinate/variable.rb +23 -0
- data/sample.rb +14 -0
- data/spec/germinate/application_spec.rb +31 -0
- data/spec/germinate/article_editor_spec.rb +97 -0
- data/spec/germinate/code_hunk_spec.rb +73 -0
- data/spec/germinate/file_hunk_spec.rb +28 -0
- data/spec/germinate/formatter_spec.rb +160 -0
- data/spec/germinate/hunk_spec.rb +84 -0
- data/spec/germinate/implicit_insertion_spec.rb +33 -0
- data/spec/germinate/insertion_spec.rb +19 -0
- data/spec/germinate/librarian_spec.rb +555 -0
- data/spec/germinate/pipeline_spec.rb +34 -0
- data/spec/germinate/process_spec.rb +105 -0
- data/spec/germinate/publisher_spec.rb +130 -0
- data/spec/germinate/reader_spec.rb +385 -0
- data/spec/germinate/selector_spec.rb +121 -0
- data/spec/germinate/shell_publisher_spec.rb +61 -0
- data/spec/germinate/source_file_spec.rb +99 -0
- data/spec/germinate/text_hunk_spec.rb +98 -0
- data/spec/germinate/text_transforms_spec.rb +242 -0
- data/spec/germinate/transform_process_spec.rb +50 -0
- data/spec/germinate/variable_spec.rb +14 -0
- data/spec/germinate_spec.rb +8 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/cucumber.rake +5 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test_germinate.rb +0 -0
- metadata +228 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'alter_ego'
|
2
|
+
|
3
|
+
# Obsolete
|
4
|
+
class Germinate::Formatter
|
5
|
+
include AlterEgo
|
6
|
+
|
7
|
+
attr_accessor :comment_prefix
|
8
|
+
|
9
|
+
def initialize(output=$stdio)
|
10
|
+
@output = output
|
11
|
+
end
|
12
|
+
|
13
|
+
state :initial, :default => true do
|
14
|
+
transition :to => :code, :on => :start!
|
15
|
+
end
|
16
|
+
|
17
|
+
state :code do
|
18
|
+
handle :add_line!, :add_code_line!
|
19
|
+
|
20
|
+
transition :to => :paragraph, :on => :paragraph!
|
21
|
+
transition :to => :finished, :on => :finish!
|
22
|
+
end
|
23
|
+
|
24
|
+
state :paragraph do
|
25
|
+
handle :add_line!, :add_paragraph_line!
|
26
|
+
|
27
|
+
transition :to => :linebreak, :on => :linebreak!
|
28
|
+
transition :to => :code, :on => :code!
|
29
|
+
transition :to => :finished, :on => :finish!
|
30
|
+
|
31
|
+
on_exit do
|
32
|
+
flush_paragraph!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
state :linebreak do
|
37
|
+
handle :add_line!, :add_linebreak_line!
|
38
|
+
|
39
|
+
transition :to => :paragraph, :on => :paragraph! do
|
40
|
+
emit!("\n")
|
41
|
+
end
|
42
|
+
|
43
|
+
transition :to => :code, :on => :code!
|
44
|
+
transition :to => :finished, :on => :finish!
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
state :finished do
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
def add_code_line!(line)
|
55
|
+
case line
|
56
|
+
when /\s*(\S+)?\s*:TEXT:/ then
|
57
|
+
self.comment_prefix = $1
|
58
|
+
paragraph!
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_paragraph_line!(line)
|
63
|
+
case line
|
64
|
+
when /:CUT:/
|
65
|
+
code!
|
66
|
+
when text_pattern
|
67
|
+
paragraph_buffer << $1.chomp
|
68
|
+
when whitespace_pattern
|
69
|
+
linebreak!
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_linebreak_line!(line)
|
74
|
+
case line
|
75
|
+
when /:CUT:/
|
76
|
+
code!
|
77
|
+
when text_pattern
|
78
|
+
paragraph_buffer << $1.chomp
|
79
|
+
paragraph!
|
80
|
+
else
|
81
|
+
# NOOP
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
attr_reader :output
|
88
|
+
|
89
|
+
def text_pattern
|
90
|
+
if comment_prefix
|
91
|
+
/^\s*#{comment_prefix}+\s*(\S+.*)$/
|
92
|
+
else
|
93
|
+
/^\s*(\S+.*)\s*$/
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def whitespace_pattern
|
98
|
+
if comment_prefix
|
99
|
+
/^\s*#{comment_prefix}*\s*$/
|
100
|
+
else
|
101
|
+
/^\s*$/
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def paragraph_buffer
|
106
|
+
@paragraph_buffer ||= []
|
107
|
+
end
|
108
|
+
|
109
|
+
def flush_paragraph!
|
110
|
+
emit!(paragraph_buffer.join(" "))
|
111
|
+
paragraph_buffer.clear
|
112
|
+
end
|
113
|
+
|
114
|
+
def emit!(text)
|
115
|
+
unless text.empty?
|
116
|
+
output.puts(text.chomp)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'ick'
|
3
|
+
require 'fattr'
|
4
|
+
require File.expand_path("shared_style_attributes", File.dirname(__FILE__))
|
5
|
+
|
6
|
+
# A Hunk represents a chunk of content. There are different types of Hunk, like
|
7
|
+
# Code or Text, which may be formatted differently by the Formatter. At its
|
8
|
+
# most basic a Hunk is just a list of Strings, each one representing a single
|
9
|
+
# line.
|
10
|
+
class Germinate::Hunk < ::Array
|
11
|
+
include Germinate::SharedStyleAttributes
|
12
|
+
Ick::Returning.belongs_to(self)
|
13
|
+
|
14
|
+
def initialize(contents=[], template = {})
|
15
|
+
super(contents)
|
16
|
+
copy_shared_style_attributes_from(template)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"#{self.class.name}[#{origin}](#{self.size} lines)"
|
21
|
+
end
|
22
|
+
|
23
|
+
# return a copy with leading and trailing whitespace lines removed
|
24
|
+
def strip
|
25
|
+
Germinate::TextTransforms.strip_blanks.call(self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def resolve_insertions
|
29
|
+
dup.map!{ |line_or_insertion|
|
30
|
+
if line_or_insertion.respond_to?(:resolve)
|
31
|
+
line_or_insertion.resolve
|
32
|
+
else
|
33
|
+
line_or_insertion
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def format_with(formatter)
|
39
|
+
raise "Unresolved hunk cannot be formatted!" unless resolved?
|
40
|
+
if nested_hunks?
|
41
|
+
group_hunks.each do |hunk|
|
42
|
+
hunk.format_with(formatter)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
yield formatter
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
attrs = Germinate::SharedStyleAttributes.fattrs.inject({}) {|attrs, key|
|
51
|
+
attrs[key] = send(key)
|
52
|
+
attrs
|
53
|
+
}
|
54
|
+
"#{self.class}:#{super}:#{attrs.inspect}:#{object_id}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def [](*args)
|
58
|
+
returning(super) do |slice|
|
59
|
+
if slice.kind_of?(Germinate::Hunk)
|
60
|
+
slice.copy_shared_style_attributes_from(self)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def slice(*args)
|
66
|
+
returning(super) do |slice|
|
67
|
+
if slice.kind_of?(Germinate::Hunk)
|
68
|
+
slice.copy_shared_style_attributes_from(self)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def index_matching(pattern, start_index=0)
|
74
|
+
(start_index...(size)).each { |i|
|
75
|
+
return i if pattern === self[i]
|
76
|
+
}
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def whole_file?
|
81
|
+
false
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def resolved?
|
87
|
+
!unresolved_hunks?
|
88
|
+
end
|
89
|
+
|
90
|
+
def unresolved_hunks?
|
91
|
+
any?{|line| line.respond_to?(:resolve)}
|
92
|
+
end
|
93
|
+
|
94
|
+
def nested?
|
95
|
+
unresolved_hunks? || nested_hunks?
|
96
|
+
end
|
97
|
+
|
98
|
+
def nested_hunks?
|
99
|
+
any?{|line| line.respond_to?(:format_with)}
|
100
|
+
end
|
101
|
+
|
102
|
+
def group_hunks
|
103
|
+
return self unless nested?
|
104
|
+
groups = inject([empty_dup]) { |hunks, line_or_hunk|
|
105
|
+
if line_or_hunk.respond_to?(:format_with)
|
106
|
+
hunks << line_or_hunk
|
107
|
+
hunks << empty_dup
|
108
|
+
else
|
109
|
+
hunks.last << line_or_hunk
|
110
|
+
end
|
111
|
+
hunks
|
112
|
+
}
|
113
|
+
groups.delete_if{|g| g.empty?}
|
114
|
+
groups
|
115
|
+
end
|
116
|
+
|
117
|
+
# An empty duplicate retains metadata but has no lines
|
118
|
+
def empty_dup
|
119
|
+
returning(dup) do |duplicate|
|
120
|
+
duplicate.clear
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
# Represents a hunk of article text
|
127
|
+
class Germinate::TextHunk < Germinate::Hunk
|
128
|
+
def initialize(contents = [], template = {})
|
129
|
+
self.join_lines = true
|
130
|
+
self.strip_blanks = true
|
131
|
+
self.rstrip_lines = true
|
132
|
+
self.uncomment = true
|
133
|
+
self.expand_insertions = true
|
134
|
+
self.flatten_nested = true
|
135
|
+
super
|
136
|
+
end
|
137
|
+
|
138
|
+
def format_with(formatter)
|
139
|
+
super(formatter) do |formatter|
|
140
|
+
formatter.format_text!(self, comment_prefix)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Represents a hunk of source code
|
146
|
+
class Germinate::CodeHunk < Germinate::Hunk
|
147
|
+
def initialize(contents = [], template = {})
|
148
|
+
self.strip_blanks = true
|
149
|
+
self.bracket = true
|
150
|
+
super
|
151
|
+
end
|
152
|
+
|
153
|
+
def code_open_bracket=(new_value)
|
154
|
+
super
|
155
|
+
end
|
156
|
+
|
157
|
+
def code_close_bracket=(new_value)
|
158
|
+
super
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
def format_with(formatter)
|
163
|
+
super(formatter) do |formatter|
|
164
|
+
formatter.format_code!(self, comment_prefix)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class Germinate::NullHunk < Germinate::Hunk
|
170
|
+
end
|
171
|
+
|
172
|
+
# Represents a the entire text of a file on disk
|
173
|
+
class Germinate::FileHunk < Germinate::CodeHunk
|
174
|
+
def initialize(lines, template)
|
175
|
+
super(lines, template)
|
176
|
+
raise ArgumentError, "Path required" if source_path.nil?
|
177
|
+
end
|
178
|
+
|
179
|
+
def whole_file?
|
180
|
+
true
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'fattr'
|
2
|
+
require 'ick'
|
3
|
+
require File.expand_path("shared_style_attributes", File.dirname(__FILE__))
|
4
|
+
|
5
|
+
class Germinate::Insertion
|
6
|
+
include Germinate::SharedStyleAttributes
|
7
|
+
Ick::Returning.belongs_to(self)
|
8
|
+
|
9
|
+
attr_reader :library
|
10
|
+
attr_reader :selector
|
11
|
+
|
12
|
+
fattr(:log) { Germinate.logger }
|
13
|
+
|
14
|
+
def initialize(selector, library, template={})
|
15
|
+
copy_shared_style_attributes_from(template)
|
16
|
+
@selector = selector
|
17
|
+
@library = library
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"Insertion[#{selector}]"
|
22
|
+
end
|
23
|
+
|
24
|
+
def resolve
|
25
|
+
returning(library[selector, self, self]) do |hunk|
|
26
|
+
log.debug "Resolved #{self} to #{hunk}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,293 @@
|
|
1
|
+
require 'orderedhash'
|
2
|
+
require 'fattr'
|
3
|
+
require 'ick'
|
4
|
+
require File.expand_path("shared_style_attributes", File.dirname(__FILE__))
|
5
|
+
|
6
|
+
# The Librarian is responsible for organizing all the chunks of content derived
|
7
|
+
# from reading a source file and making them available for later re-assembly and
|
8
|
+
# formatting.
|
9
|
+
class Germinate::Librarian
|
10
|
+
include Germinate::SharedStyleAttributes
|
11
|
+
Ick::Returning.belongs_to(self)
|
12
|
+
|
13
|
+
class VariableStore < OrderedHash
|
14
|
+
def initialize(librarian)
|
15
|
+
super()
|
16
|
+
@librarian = librarian
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(key, value)
|
20
|
+
if key?(key)
|
21
|
+
variable = fetch(key)
|
22
|
+
variable.replace(value.to_s)
|
23
|
+
variable.update_source_line!(@librarian.comment_prefix)
|
24
|
+
else
|
25
|
+
variable =
|
26
|
+
case value
|
27
|
+
when Germinate::Variable
|
28
|
+
value
|
29
|
+
else
|
30
|
+
line_number = @librarian.lines.length + 1
|
31
|
+
line = ""
|
32
|
+
Germinate::Variable.new(
|
33
|
+
key, value, line, @librarian.source_path, line_number)
|
34
|
+
end
|
35
|
+
variable.update_source_line!(@librarian.comment_prefix)
|
36
|
+
store(key, variable)
|
37
|
+
@librarian.log.debug "Appending #{variable.line.inspect} to lines"
|
38
|
+
@librarian.lines << variable.line
|
39
|
+
end
|
40
|
+
@librarian.updated = true
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_reader :lines
|
46
|
+
attr_reader :text_lines
|
47
|
+
attr_reader :code_lines
|
48
|
+
attr_reader :front_matter_lines
|
49
|
+
|
50
|
+
fattr :source_path => nil
|
51
|
+
|
52
|
+
fattr(:log) { Germinate.logger }
|
53
|
+
fattr(:variables) { VariableStore.new(self) }
|
54
|
+
fattr(:updated) { false }
|
55
|
+
fattr(:source_file) { Germinate::SourceFile.new(source_path) }
|
56
|
+
|
57
|
+
def initialize
|
58
|
+
@lines = []
|
59
|
+
@text_lines = []
|
60
|
+
@code_lines = []
|
61
|
+
@front_matter_lines = []
|
62
|
+
@sections = OrderedHash.new do |hash, key|
|
63
|
+
hash[key] = Germinate::TextHunk.new([], shared_style_attributes)
|
64
|
+
end
|
65
|
+
@samples = OrderedHash.new do |hash, key|
|
66
|
+
hash[key] = Germinate::CodeHunk.new([], shared_style_attributes)
|
67
|
+
end
|
68
|
+
@processes = {'_transform' => Germinate::TransformProcess.new}
|
69
|
+
@publishers = OrderedHash.new
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_front_matter!(line)
|
73
|
+
add_line!(line)
|
74
|
+
@front_matter_lines << line
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_control!(line)
|
78
|
+
add_line!(line)
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_text!(section, line)
|
82
|
+
add_line!(line)
|
83
|
+
@text_lines << line
|
84
|
+
@sections[section] << line
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_code!(sample, line)
|
88
|
+
add_line!(line)
|
89
|
+
@code_lines << line
|
90
|
+
@samples[sample] << line
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_insertion!(section, selector, attributes)
|
94
|
+
insertion = Germinate::Insertion.new(selector, self, attributes)
|
95
|
+
@sections[section] << insertion
|
96
|
+
@text_lines << insertion
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_code_attributes!(sample, attributes)
|
100
|
+
attributes.each_pair do |key, value|
|
101
|
+
@samples[sample].send(key, value) unless value.nil?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def add_process!(process_name, command)
|
106
|
+
@processes[process_name] =
|
107
|
+
Germinate::ShellProcess.new(process_name, command, variables)
|
108
|
+
end
|
109
|
+
|
110
|
+
def add_publisher!(name, identifier, options)
|
111
|
+
@publishers[name] = Germinate::Publisher.make(name, identifier, self, options)
|
112
|
+
end
|
113
|
+
|
114
|
+
def store_changes!
|
115
|
+
source_file.write!(lines)
|
116
|
+
end
|
117
|
+
|
118
|
+
def set_variable!(line, line_number, name, value)
|
119
|
+
variables.store(name,Germinate::Variable.new(name, value, line, source_path, line_number))
|
120
|
+
end
|
121
|
+
|
122
|
+
def comment_prefix_known?
|
123
|
+
!comment_prefix.nil?
|
124
|
+
end
|
125
|
+
|
126
|
+
def section(section_name)
|
127
|
+
unless has_section?(section_name)
|
128
|
+
raise IndexError,
|
129
|
+
"No text section named '#{section_name}'. "\
|
130
|
+
"Known sections: #{@sections.keys.join(', ')}"
|
131
|
+
end
|
132
|
+
Array(@sections[section_name])
|
133
|
+
end
|
134
|
+
|
135
|
+
def has_section?(section_name)
|
136
|
+
@sections.key?(section_name)
|
137
|
+
end
|
138
|
+
|
139
|
+
def sample(sample_name)
|
140
|
+
unless has_sample?(sample_name)
|
141
|
+
raise IndexError,
|
142
|
+
"No code sample named '#{sample_name}'. "\
|
143
|
+
"Known samples: #{@samples.keys.join(', ')}"
|
144
|
+
end
|
145
|
+
Array(@samples[sample_name])
|
146
|
+
end
|
147
|
+
|
148
|
+
# Fetch a process by name
|
149
|
+
def process(process_name)
|
150
|
+
@processes.fetch(process_name)
|
151
|
+
rescue IndexError => error
|
152
|
+
raise error.exception("Unknown process #{process_name.inspect}")
|
153
|
+
end
|
154
|
+
|
155
|
+
def process_names
|
156
|
+
@processes.keys
|
157
|
+
end
|
158
|
+
|
159
|
+
def publisher_names
|
160
|
+
@publishers.keys
|
161
|
+
end
|
162
|
+
|
163
|
+
# fetch a publisher by name
|
164
|
+
def publisher(publisher_name)
|
165
|
+
@publishers.fetch(publisher_name)
|
166
|
+
rescue IndexError => error
|
167
|
+
raise error.exception("Unknown publisher #{publisher_name.inspect}")
|
168
|
+
end
|
169
|
+
|
170
|
+
def has_sample?(sample_name)
|
171
|
+
@samples.key?(sample_name)
|
172
|
+
end
|
173
|
+
|
174
|
+
# TODO Too big, refactor.
|
175
|
+
def [](selector, origin="<Unknown>", template={})
|
176
|
+
log.debug "Selecting #{selector}, from #{origin}"
|
177
|
+
selector = case selector
|
178
|
+
when Germinate::Selector then selector
|
179
|
+
else Germinate::Selector.new(selector, "SECTION0", origin)
|
180
|
+
end
|
181
|
+
sample =
|
182
|
+
case selector.selector_type
|
183
|
+
when :code then
|
184
|
+
sample(selector.key)
|
185
|
+
when :special then
|
186
|
+
case selector.key
|
187
|
+
when "SOURCE"
|
188
|
+
source_hunk =
|
189
|
+
if selector.whole?
|
190
|
+
Germinate::FileHunk.new(lines, self)
|
191
|
+
else
|
192
|
+
Germinate::CodeHunk.new(lines, self)
|
193
|
+
end
|
194
|
+
source_hunk.disable_all_transforms!
|
195
|
+
source_hunk
|
196
|
+
when "CODE" then Germinate::CodeHunk.new(code_lines, self)
|
197
|
+
when "TEXT" then Germinate::TextHunk.new(text_lines, self)
|
198
|
+
else raise "Unknown special section '$#{selector.key}'"
|
199
|
+
end
|
200
|
+
else
|
201
|
+
raise Exception,
|
202
|
+
"Unknown selector type #{selector.selector_type.inspect}"
|
203
|
+
end
|
204
|
+
|
205
|
+
sample.copy_shared_style_attributes_from(template)
|
206
|
+
sample.origin.source_path ||= source_path
|
207
|
+
sample.origin.selector ||= selector
|
208
|
+
|
209
|
+
sample = if selector.excerpt_output?
|
210
|
+
excerpt(execute_pipeline(sample, selector.pipeline), selector)
|
211
|
+
else
|
212
|
+
execute_pipeline(excerpt(sample, selector), selector.pipeline)
|
213
|
+
end
|
214
|
+
sample
|
215
|
+
end
|
216
|
+
|
217
|
+
def section_names
|
218
|
+
@sections.keys
|
219
|
+
end
|
220
|
+
|
221
|
+
def sample_names
|
222
|
+
@samples.keys
|
223
|
+
end
|
224
|
+
|
225
|
+
# Given a list of process names or a '|'-delimited string, return a Pipeline
|
226
|
+
# object representing a super-process of all the named processes chained
|
227
|
+
# together.
|
228
|
+
def make_pipeline(process_names_or_string)
|
229
|
+
names =
|
230
|
+
if process_names_or_string.kind_of?(String)
|
231
|
+
process_names_or_string.split("|")
|
232
|
+
else
|
233
|
+
process_names_or_string
|
234
|
+
end
|
235
|
+
processes = names.map{|n| process(n)}
|
236
|
+
Germinate::Pipeline.new(processes)
|
237
|
+
end
|
238
|
+
|
239
|
+
private
|
240
|
+
|
241
|
+
def excerpt(sample, selector)
|
242
|
+
# TODO make excerpting just another TextTransform
|
243
|
+
start_offset = start_offset(sample, selector)
|
244
|
+
end_offset = end_offset(sample, selector, start_offset)
|
245
|
+
case selector.delimiter
|
246
|
+
when '..' then sample[start_offset..end_offset]
|
247
|
+
when '...' then sample[start_offset...end_offset]
|
248
|
+
when ',' then sample[start_offset, selector.length]
|
249
|
+
when nil then sample.dup.replace([sample[start_offset]])
|
250
|
+
else raise "Don't understand delimiter #{selector.delimiter.inspect}"
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def add_line!(line)
|
255
|
+
line.chomp!
|
256
|
+
line << "\n"
|
257
|
+
@lines << line
|
258
|
+
end
|
259
|
+
|
260
|
+
def start_offset(hunk, selector)
|
261
|
+
offset = selector.start_offset_for_slice
|
262
|
+
case offset
|
263
|
+
when Integer then offset
|
264
|
+
when Regexp then
|
265
|
+
returning(hunk.index_matching(offset)) do |index|
|
266
|
+
if index.nil?
|
267
|
+
raise "Cannot find line matching #{offset.inspect} in #{selector}"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
else
|
271
|
+
raise "Don't know how to use #{offset.inspect} as an offset"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def end_offset(hunk, selector, start_offset)
|
276
|
+
offset = selector.end_offset_for_slice
|
277
|
+
case offset
|
278
|
+
when Integer, nil then offset
|
279
|
+
when Regexp then
|
280
|
+
returning(hunk.index_matching(offset, start_offset)) do |index|
|
281
|
+
if index.nil?
|
282
|
+
raise "Cannot find line matching #{offset.inspect} in #{selector}"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
else
|
286
|
+
raise "Don't know how to use #{offset.inspect} as an offset"
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def execute_pipeline(hunk, names)
|
291
|
+
make_pipeline(names).call(hunk)
|
292
|
+
end
|
293
|
+
end
|