devver-germinate 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+