devver-germinate 1.0.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.
Files changed (86) hide show
  1. data/.gitignore +2 -0
  2. data/History.txt +4 -0
  3. data/README.rdoc +132 -0
  4. data/Rakefile +43 -0
  5. data/bin/germ +133 -0
  6. data/cucumber.yml +2 -0
  7. data/examples/basic.rb +118 -0
  8. data/examples/short.rb +17 -0
  9. data/features/author-formats-article.feature +108 -0
  10. data/features/author-lists-info.feature +45 -0
  11. data/features/author-publishes-article-source.feature +5 -0
  12. data/features/author-publishes-article.feature +5 -0
  13. data/features/author-republishes-article.feature +5 -0
  14. data/features/author-selects-hunks.feature +26 -0
  15. data/features/author-updates-article-source.feature +5 -0
  16. data/features/author-views-stuff.feature +48 -0
  17. data/features/bin/quoter +6 -0
  18. data/features/bin/sorter +4 -0
  19. data/features/example_articles/code_samples.rb +89 -0
  20. data/features/example_articles/escaping.txt +12 -0
  21. data/features/example_articles/hello.rb +9 -0
  22. data/features/example_articles/pipelines.txt +25 -0
  23. data/features/example_articles/regexen.rb +24 -0
  24. data/features/example_articles/sample_offsets.rb +18 -0
  25. data/features/example_articles/specials.rb +19 -0
  26. data/features/example_articles/wrapping.rb +8 -0
  27. data/features/example_output/code_samples.txt +186 -0
  28. data/features/example_output/escaping.out +5 -0
  29. data/features/example_output/hello.txt +1 -0
  30. data/features/example_output/pipelines.out +28 -0
  31. data/features/example_output/regexen.txt +22 -0
  32. data/features/example_output/sample_offsets.txt +15 -0
  33. data/features/example_output/specials.txt +36 -0
  34. data/features/example_output/wrapping.txt +3 -0
  35. data/features/step_definitions/germinate.rb +30 -0
  36. data/features/support/env.rb +18 -0
  37. data/germinate.gemspec +55 -0
  38. data/lib/germinate.rb +54 -0
  39. data/lib/germinate/application.rb +62 -0
  40. data/lib/germinate/article_editor.rb +20 -0
  41. data/lib/germinate/article_formatter.rb +75 -0
  42. data/lib/germinate/formatter.rb +119 -0
  43. data/lib/germinate/hunk.rb +149 -0
  44. data/lib/germinate/implicit_insertion.rb +9 -0
  45. data/lib/germinate/insertion.rb +15 -0
  46. data/lib/germinate/librarian.rb +179 -0
  47. data/lib/germinate/pipeline.rb +11 -0
  48. data/lib/germinate/process.rb +67 -0
  49. data/lib/germinate/reader.rb +212 -0
  50. data/lib/germinate/selector.rb +95 -0
  51. data/lib/germinate/shared_style_attributes.rb +23 -0
  52. data/lib/germinate/text_transforms.rb +90 -0
  53. data/spec/germinate/application_spec.rb +14 -0
  54. data/spec/germinate/article_editor_spec.rb +97 -0
  55. data/spec/germinate/article_formatter_spec.rb +153 -0
  56. data/spec/germinate/code_hunk_spec.rb +45 -0
  57. data/spec/germinate/formatter_spec.rb +160 -0
  58. data/spec/germinate/hunk_spec.rb +77 -0
  59. data/spec/germinate/implicit_insertion_spec.rb +33 -0
  60. data/spec/germinate/insertion_spec.rb +18 -0
  61. data/spec/germinate/librarian_spec.rb +336 -0
  62. data/spec/germinate/pipeline_spec.rb +24 -0
  63. data/spec/germinate/process_spec.rb +64 -0
  64. data/spec/germinate/reader_spec.rb +306 -0
  65. data/spec/germinate/selector_spec.rb +65 -0
  66. data/spec/germinate/text_hunk_spec.rb +53 -0
  67. data/spec/germinate/text_transforms_spec.rb +154 -0
  68. data/spec/germinate_spec.rb +8 -0
  69. data/spec/spec.opts +1 -0
  70. data/spec/spec_helper.rb +16 -0
  71. data/tasks/ann.rake +80 -0
  72. data/tasks/bones.rake +20 -0
  73. data/tasks/cucumber.rake +5 -0
  74. data/tasks/gem.rake +201 -0
  75. data/tasks/git.rake +40 -0
  76. data/tasks/notes.rake +27 -0
  77. data/tasks/post_load.rake +34 -0
  78. data/tasks/rdoc.rake +51 -0
  79. data/tasks/rubyforge.rake +55 -0
  80. data/tasks/setup.rb +292 -0
  81. data/tasks/spec.rake +54 -0
  82. data/tasks/svn.rake +47 -0
  83. data/tasks/test.rake +40 -0
  84. data/tasks/zentest.rake +36 -0
  85. data/test/test_germinate.rb +0 -0
  86. metadata +209 -0
data/lib/germinate.rb ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'fattr'
3
+ require 'logger'
4
+
5
+ module Germinate
6
+
7
+ # :stopdoc:
8
+ VERSION = '1.0.0'
9
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
10
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
11
+ # :startdoc:
12
+
13
+ # Returns the version string for the library.
14
+ #
15
+ def self.version
16
+ VERSION
17
+ end
18
+
19
+ # Returns the library path for the module. If any arguments are given,
20
+ # they will be joined to the end of the libray path using
21
+ # <tt>File.join</tt>.
22
+ #
23
+ def self.libpath( *args )
24
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
25
+ end
26
+
27
+ # Returns the lpath for the module. If any arguments are given,
28
+ # they will be joined to the end of the path using
29
+ # <tt>File.join</tt>.
30
+ #
31
+ def self.path( *args )
32
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
33
+ end
34
+
35
+ # Utility method used to require all files ending in .rb that lie in the
36
+ # directory below this file that has the same name as the filename passed
37
+ # in. Optionally, a specific _directory_ name can be passed in such that
38
+ # the _filename_ does not have to be equivalent to the directory.
39
+ #
40
+ def self.require_all_libs_relative_to( fname, dir = nil )
41
+ dir ||= ::File.basename(fname, '.*')
42
+ search_me = ::File.expand_path(
43
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
44
+
45
+ Dir.glob(search_me).sort.each {|rb| require rb}
46
+ end
47
+
48
+ Fattr(:logger) { Logger.new($stderr) }
49
+
50
+ end # module Germinate
51
+
52
+ Germinate.require_all_libs_relative_to(__FILE__)
53
+
54
+ # EOF
@@ -0,0 +1,62 @@
1
+ # The Application ties all the other componts together. It has public methods
2
+ # roughly corresponding commands that the 'germ' command-line tool supports.
3
+ class Germinate::Application
4
+ attr_writer :formatter
5
+
6
+ def format(source, output=$stdout, errors=$stderr)
7
+ librarian = load_librarian(source)
8
+ editor = Germinate::ArticleEditor.new(librarian)
9
+ formatter = Germinate::ArticleFormatter.new(output)
10
+
11
+ Germinate::SharedStyleAttributes.fattrs.each do
12
+ |style_attribute|
13
+ formatter.send(style_attribute, librarian.send(style_attribute))
14
+ end
15
+ formatter.start!
16
+ editor.each_hunk do |hunk|
17
+ formatter.format!(hunk)
18
+ end
19
+ formatter.finish!
20
+ end
21
+
22
+ def list(source, things_to_list, output=$stdout)
23
+ librarian = load_librarian(source)
24
+ if things_to_list.include?(:sections)
25
+ output.puts(librarian.section_names.join("\n"))
26
+ end
27
+ if things_to_list.include?(:samples)
28
+ output.puts(librarian.sample_names.join("\n"))
29
+ end
30
+ if things_to_list.include?(:processes)
31
+ output.puts(librarian.process_names.join("\n"))
32
+ end
33
+ end
34
+
35
+ def show(source, selection, output=$stdout)
36
+ librarian = load_librarian(source)
37
+ selection.fetch(:section, []).each do |section|
38
+ output.puts(*librarian.section(section))
39
+ end
40
+ selection.fetch(:sample, []).each do |sample|
41
+ output.puts(*librarian.sample(sample))
42
+ end
43
+ selection.fetch(:process, []).each do |process|
44
+ output.puts(*librarian.process(process).command)
45
+ end
46
+ end
47
+
48
+ def select(source, selector, output=$stdout)
49
+ librarian = load_librarian(source)
50
+ output.puts(*librarian[selector])
51
+ end
52
+ private
53
+
54
+ def load_librarian(source)
55
+ librarian = Germinate::Librarian.new
56
+ reader = Germinate::Reader.new(librarian)
57
+ source.each_line do |line|
58
+ reader << line
59
+ end
60
+ librarian
61
+ end
62
+ end
@@ -0,0 +1,20 @@
1
+ # An Editor is responsible for selecting hunks of text from a Librarian and
2
+ # assembling them into a list for formatting.
3
+ class Germinate::ArticleEditor
4
+ def initialize(librarian)
5
+ @librarian = librarian
6
+ end
7
+
8
+ def each_hunk(&block)
9
+ librarian.section_names.each do |section_name|
10
+ yield librarian.section(section_name).resolve_insertions
11
+ if librarian.has_sample?(section_name)
12
+ yield librarian.sample(section_name).resolve_insertions
13
+ end
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :librarian
20
+ end
@@ -0,0 +1,75 @@
1
+ require 'ick'
2
+ require 'fattr'
3
+ require File.expand_path("shared_style_attributes", File.dirname(__FILE__))
4
+
5
+ # A Formatter is responsible for taking content hunks received from an Editor
6
+ # and formatting them for display or publishing.
7
+ class Germinate::ArticleFormatter
8
+ Ick::Returning.belongs_to self
9
+ include Germinate::SharedStyleAttributes
10
+
11
+ fattr :join_lines => true
12
+ fattr :strip_blanks => true
13
+ fattr :rstrip_newlines => true
14
+ fattr :uncomment => true
15
+ fattr :rstrip_lines => true
16
+
17
+ def initialize(output_stream=$stdout)
18
+ @output_stream = output_stream
19
+ @first_output = true
20
+ end
21
+
22
+ def start!
23
+ end
24
+
25
+ def finish!
26
+ end
27
+
28
+ def format!(hunk)
29
+ @output_stream.puts unless first_output?
30
+ hunk.format_with(self)
31
+ @first_output = false if first_output?
32
+ end
33
+
34
+ def format_text!(hunk, comment_prefix=nil)
35
+ text_transforms.inject(hunk) do |hunk, transform|
36
+ transform.call(hunk)
37
+ end.each do |line|
38
+ @output_stream.puts(line)
39
+ end
40
+ end
41
+
42
+ def format_code!(hunk, comment_prefix=nil)
43
+ code_transforms.inject(hunk) do |hunk, transform|
44
+ transform.call(hunk)
45
+ end.each do |line|
46
+ @output_stream.puts(line)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def first_output?
53
+ @first_output
54
+ end
55
+
56
+ def text_transforms
57
+ returning([]) do |transforms|
58
+ transforms << Germinate::TextTransforms.strip_blanks if strip_blanks?
59
+ if uncomment?
60
+ transforms << Germinate::TextTransforms.uncomment(comment_prefix)
61
+ end
62
+ transforms << Germinate::TextTransforms.join_lines if join_lines?
63
+ transforms << Germinate::TextTransforms.rstrip_lines if rstrip_lines?
64
+ end
65
+ end
66
+
67
+ def code_transforms
68
+ returning([]) do |transforms|
69
+ transforms << Germinate::TextTransforms.strip_blanks if strip_blanks?
70
+ transforms << Germinate::TextTransforms.rstrip_lines if rstrip_lines?
71
+ transforms <<
72
+ Germinate::TextTransforms.bracket
73
+ end
74
+ end
75
+ end
@@ -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,149 @@
1
+ require 'ick'
2
+ require File.expand_path("shared_style_attributes", File.dirname(__FILE__))
3
+
4
+ # A Hunk represents a chunk of content. There are different types of Hunk, like
5
+ # Code or Text, which may be formatted differently by the Formatter. At its
6
+ # most basic a Hunk is just a list of Strings, each one representing a single
7
+ # line.
8
+ class Germinate::Hunk < ::Array
9
+ include Germinate::SharedStyleAttributes
10
+ Ick::Returning.belongs_to(self)
11
+
12
+ def initialize(contents=[], template = {})
13
+ super(contents)
14
+ if Germinate::SharedStyleAttributes === template
15
+ copy_shared_style_attrubutes_from(template)
16
+ else
17
+ template.each_pair do |key, value|
18
+ send(key, value)
19
+ end
20
+ end
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_attrubutes_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_attrubutes_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
+ private
81
+
82
+ def resolved?
83
+ !unresolved_hunks?
84
+ end
85
+
86
+ def unresolved_hunks?
87
+ any?{|line| line.respond_to?(:resolve)}
88
+ end
89
+
90
+ def nested?
91
+ unresolved_hunks? || nested_hunks?
92
+ end
93
+
94
+ def nested_hunks?
95
+ any?{|line| line.respond_to?(:format_with)}
96
+ end
97
+
98
+ def group_hunks
99
+ return self unless nested?
100
+ groups = inject([empty_dup]) { |hunks, line_or_hunk|
101
+ if line_or_hunk.respond_to?(:format_with)
102
+ hunks << line_or_hunk
103
+ hunks << empty_dup
104
+ else
105
+ hunks.last << line_or_hunk
106
+ end
107
+ hunks
108
+ }
109
+ groups.delete_if{|g| g.empty?}
110
+ groups
111
+ end
112
+
113
+ # An empty duplicate retains metadata but has no lines
114
+ def empty_dup
115
+ returning(dup) do |duplicate|
116
+ duplicate.clear
117
+ end
118
+ end
119
+
120
+ end
121
+
122
+ class Germinate::TextHunk < Germinate::Hunk
123
+ def format_with(formatter)
124
+ super(formatter) do |formatter|
125
+ formatter.format_text!(self, comment_prefix)
126
+ end
127
+ end
128
+ end
129
+
130
+ class Germinate::CodeHunk < Germinate::Hunk
131
+ def code_open_bracket=(new_value)
132
+ super
133
+ end
134
+
135
+ def code_close_bracket=(new_value)
136
+ super
137
+ end
138
+
139
+
140
+ def format_with(formatter)
141
+ super(formatter) do |formatter|
142
+ formatter.format_code!(self, comment_prefix)
143
+ end
144
+ end
145
+ end
146
+
147
+ class Germinate::NullHunk < Germinate::Hunk
148
+ end
149
+