devver-germinate 1.1.0 → 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.
Files changed (57) hide show
  1. data/History.txt +12 -0
  2. data/README.rdoc +26 -4
  3. data/TODO +80 -8
  4. data/bin/germ +162 -38
  5. data/examples/basic.rb +14 -9
  6. data/examples/short.rb +2 -0
  7. data/features/author-formats-article.feature +3 -3
  8. data/features/{author-lists-info.feature → author-lists-info.pending_feature} +3 -0
  9. data/features/author-publishes-article.feature +52 -0
  10. data/features/author-selects-hunks.feature +1 -1
  11. data/features/author-sets-variables.feature +88 -0
  12. data/features/{author-views-stuff.feature → author-views-stuff.pending_feature} +4 -0
  13. data/features/example_articles/escaping.txt +1 -0
  14. data/features/example_articles/specials.rb +3 -3
  15. data/features/example_output/specials.txt +9 -5
  16. data/features/step_definitions/germinate.rb +9 -0
  17. data/germinate.gemspec +3 -3
  18. data/lib/germinate.rb +1 -1
  19. data/lib/germinate/application.rb +82 -31
  20. data/lib/germinate/hunk.rb +20 -0
  21. data/lib/germinate/insertion.rb +10 -2
  22. data/lib/germinate/librarian.rb +129 -31
  23. data/lib/germinate/origin.rb +5 -0
  24. data/lib/germinate/pipeline.rb +2 -0
  25. data/lib/germinate/publisher.rb +57 -0
  26. data/lib/germinate/reader.rb +51 -8
  27. data/lib/germinate/selector.rb +18 -6
  28. data/lib/germinate/shared_style_attributes.rb +18 -1
  29. data/lib/germinate/{process.rb → shell_process.rb} +27 -8
  30. data/lib/germinate/shell_publisher.rb +19 -0
  31. data/lib/germinate/simple_publisher.rb +7 -0
  32. data/lib/germinate/source_file.rb +41 -0
  33. data/lib/germinate/text_transforms.rb +38 -9
  34. data/lib/germinate/transform_process.rb +25 -0
  35. data/lib/germinate/variable.rb +23 -0
  36. data/sample.rb +14 -0
  37. data/spec/germinate/application_spec.rb +18 -1
  38. data/spec/germinate/article_editor_spec.rb +3 -3
  39. data/spec/germinate/code_hunk_spec.rb +28 -0
  40. data/spec/germinate/file_hunk_spec.rb +1 -0
  41. data/spec/germinate/hunk_spec.rb +1 -0
  42. data/spec/germinate/insertion_spec.rb +2 -1
  43. data/spec/germinate/librarian_spec.rb +280 -85
  44. data/spec/germinate/pipeline_spec.rb +10 -0
  45. data/spec/germinate/process_spec.rb +31 -6
  46. data/spec/germinate/publisher_spec.rb +130 -0
  47. data/spec/germinate/reader_spec.rb +58 -2
  48. data/spec/germinate/selector_spec.rb +34 -14
  49. data/spec/germinate/shell_publisher_spec.rb +61 -0
  50. data/spec/germinate/source_file_spec.rb +99 -0
  51. data/spec/germinate/text_hunk_spec.rb +45 -0
  52. data/spec/germinate/text_transforms_spec.rb +90 -2
  53. data/spec/germinate/transform_process_spec.rb +50 -0
  54. data/spec/germinate/variable_spec.rb +14 -0
  55. metadata +19 -7
  56. data/lib/germinate/article_formatter.rb +0 -75
  57. data/spec/germinate/article_formatter_spec.rb +0 -153
@@ -1,3 +1,15 @@
1
+ == 1.2.0 / 2009-07-21
2
+
3
+ * 4 major enhancements
4
+ * Added "publish" command and pluggable publishers
5
+ * Added builtin publisher "shell" for publishing via shell command
6
+ * Added variables, settable with the :SET: directive
7
+ * Added the ability for commands to write changes back to the source file
8
+ (with backups)
9
+ * 2 minor enhancements
10
+ * Refactored standard text transforms to be just another process
11
+ * Made the list/show/select command arguments easier to use
12
+
1
13
  == 1.1.0 / 2009-07-12
2
14
 
3
15
  * 1 major enhancement
@@ -74,6 +74,24 @@ Development of Germinate is graciously sponsored by Devver, purveyor of fine
74
74
  cloud-based services to busy Ruby developers. If you like this tool please
75
75
  check them out at http://devver.net.
76
76
 
77
+ == WHAT GERMINATE IS NOT
78
+
79
+ Germinate is not a markup language like HTML, Textile, or Markdown. It is
80
+ completely markup-agnostic, although it can be configured to automatically
81
+ pre-process your articles with markup tools before publishing. Germinate
82
+ concerns itslf strictly with bringing together your words and your code, and
83
+ with automating the process of publishing the finished product.
84
+
85
+ Germinate is not a code documentation tool. While it facilitates the process of
86
+ writing *about* code, it has no understanding of the code itself. Germinate is
87
+ orthogonal to tools such as RDoc or Doxygen.
88
+
89
+ Germinate is not a blogging engine. It is designed to integrate with your
90
+ favorite blogging engine through publisher plugins.
91
+
92
+ Germinate is none of these things, but it is designed to complement all of
93
+ them.
94
+
77
95
  == FEATURES
78
96
 
79
97
  * Language and markup agnostic
@@ -81,20 +99,22 @@ check them out at http://devver.net.
81
99
  * Define arbitrary command pipelines to preprocess excerpts
82
100
  * Article text is reformatted to be more compatible with popular blogging
83
101
  engines, e.g. WordPress.
102
+ * Extensable with plugins for publishing to Gist, WordPress, etc.
84
103
  * Introspection commands make it easy to experiment
85
- * Fully tested
86
104
 
87
105
  == KNOWN ISSUES
88
106
 
89
107
  * Directive syntax conflicts with RDoc. Doh!
108
+ * Focus is currently on features. Documentation is lacking. Have a look at the
109
+ features/ directory to get a better idea of various Germinate features.
90
110
 
91
111
  == FUTURE
92
112
 
93
- * Integration with blogging platforms, e.g. WordPress
94
- * Integration with Github's Gist
113
+ * See the TODO file
95
114
 
96
115
  == REQUIREMENTS
97
116
 
117
+ * Ruby 1.8
98
118
  * main
99
119
  * fattr
100
120
  * ick
@@ -105,12 +125,14 @@ check them out at http://devver.net.
105
125
  == INSTALL:
106
126
 
107
127
  gem install --source http://gems.rubyforge.org devver-germinate
128
+ gem install --source http://gems.rubyforge.org devver-germinate-gist
129
+ gem install --source http://gems.rubyforge.org devver-germinate-atompub
108
130
 
109
131
  == LICENSE:
110
132
 
111
133
  (The MIT License)
112
134
 
113
- Copyright (c) 2008
135
+ Copyright (c) 2009
114
136
 
115
137
  Permission is hereby granted, free of charge, to any person obtaining
116
138
  a copy of this software and associated documentation files (the
data/TODO CHANGED
@@ -11,13 +11,33 @@
11
11
  * DONE Make $SOURCE use the actual source file path in processes
12
12
  CLOSED: [2009-07-12 Sun 19:46]
13
13
 
14
- * TODO Improve error message for non-matching regexen in selectors :usability:
15
-
14
+ * DONE Improve error message for non-matching regexen in selectors :usability:
15
+ CLOSED: [2009-07-13 Mon 02:17]
16
+ * DONE Make text transforms just another process for pipelines :techdebt:
17
+ CLOSED: [2009-07-19 Sun 16:33]
18
+ This should help make [[*Make%20it%20possible%20for%20publishers%20to%20use%20formatted%20text%20as%20input][this item]] possible.
19
+ * DONE Add standard "select" option to publishers
20
+ CLOSED: [2009-07-19 Sun 16:33]
21
+ The select option would define a selector to be used to get the input for the
22
+ publisher. Another step towards [[*Make%20it%20possible%20for%20publishers%20to%20use%20formatted%20text%20as%20input][this]].
23
+
24
+ This MAY make the current "pipeline" option redundant.
25
+ * DONE Make it possible for publishers to use formatted text as input :feature:
26
+ CLOSED: [2009-07-19 Sun 16:33]
27
+ Pretty useless without this...
28
+
29
+ * TODO Refactor format command into a null publisher :techdebt:
30
+ This just needs one last step of pre-loading a "stdout publisher" and then we
31
+ can do e.g.:
32
+
33
+ : germ publish stdout my_article.c
16
34
  * TODO Output line numbers for Reader errors :usability:
17
35
 
18
- * TODO Gist uploads :feature:
36
+ * DONE Gist uploads :feature:
37
+ CLOSED: [2009-07-21 Tue 10:54]
19
38
 
20
- * TODO Publishing to WordPress blogs (Atompub) :feature:
39
+ * DONE Publishing to WordPress blogs (Atompub) :feature:
40
+ CLOSED: [2009-07-21 Tue 10:54]
21
41
 
22
42
  * TODO Alternate directive syntax :usability:
23
43
  one that doesn't conflict with RDoc
@@ -37,6 +57,7 @@
37
57
  * TODO Format Pipelines :feature:
38
58
  Define a pipeline to be run on the entire article after formatting
39
59
 
60
+ Or maybe just demote "format" to be a built-in publisher.
40
61
  * TODO ERB Substitution :feature:
41
62
  Should be run on TEXT sections.
42
63
 
@@ -44,8 +65,6 @@
44
65
  A helper for ERB subtitution which inserts the line# of a given selector.
45
66
 
46
67
  * TODO Plugin API :feature:
47
- Should use gems to discover plugins.
48
-
49
68
  * TODO User-global preference file :feature:
50
69
 
51
70
  * TODO Multiple file support :feature:
@@ -60,12 +79,65 @@
60
79
 
61
80
  Workaround: Authors can just include 2>&1 in their process definitions.
62
81
 
63
- * DONE Syntax for post-process excerpting
82
+ * DONE Syntax for post-process excerpting :feature:
64
83
  CLOSED: [2009-07-12 Sun 20:50]
65
84
  E.g. "$SOURCE|ruby:/---/../---/"
66
85
 
67
86
  This would cause the process to be run on $SOURCE and then a subset of the
68
87
  output to be excerpted.
69
88
 
70
- * TODO Named Styles
89
+ * TODO Named Styles :feature:
71
90
  A way to group together a bunch of style attributes in a reusable way.
91
+ * DONE A way to set arbitrary variables for later substitution :feature:
92
+ CLOSED: [2009-07-16 Thu 11:51]
93
+ * DONE Standard "pipeline" option for publishers :feature:
94
+ CLOSED: [2009-07-15 Wed 01:32]
95
+ Defines a pipeline which will be run on $SOURCE before publishing.
96
+ * TODO Plugin discovery :feature:
97
+ Should use gems to discover plugins.
98
+
99
+ * TODO Line continuations for directives :usability:
100
+ After some thought I think what I really want is a second syntax for long-form
101
+ directives, wherein the YAML arguments go in between directive opening and
102
+ closing lines. E.g.
103
+
104
+ : # :PUBLISHER: ---
105
+ : # - source
106
+ : # - shell
107
+ : # -
108
+ : # command: cat
109
+ : # pipeline: markdown
110
+ : # :END:
111
+ * DONE Variables
112
+ CLOSED: [2009-07-16 Thu 19:47]
113
+ Syntax:
114
+ : :SET: foo, 123
115
+ *** Reader records what line they were set on
116
+ *** SourceFile class which can set and re-set variables
117
+ - Backs the file up first
118
+ - Verifies the backup
119
+ - Locks the file and the backup
120
+ - Removes the old line
121
+ - Replaces it with a new :SET: line
122
+ - Or adds a new :SET: line to the end
123
+ - Re-reads and verifies value
124
+ - Restores the backup if there is a problem
125
+
126
+ *** Variables are available to processes as environment vars
127
+ * TODO Add processing/style options to TEXT sections :feature:
128
+ * TODO Add console modes :feature:
129
+ *** TODO Shell console
130
+ In this version we just exec $SHELL with some custom environment. A
131
+ GERMINATE_SOURCE variable should point to the source file, and all germ
132
+ commands should use it if no explicit source is specified.
133
+
134
+ In addition, aliases for all the common germ modes should be installed -
135
+ publish, format, list, etc.
136
+ *** TODO Debug console
137
+ An IRB session with Germinate preloaded.
138
+
139
+ * TODO Clean up output during tests :techdebt:
140
+ * TODO Add a dry-run mode :feature:
141
+ * TODO Add a TITLE variable
142
+ Should be used by publishers.
143
+ * TODO Make publishers more configurable
data/bin/germ CHANGED
@@ -3,6 +3,7 @@
3
3
  require File.expand_path(
4
4
  File.join(File.dirname(__FILE__), %w[.. lib germinate]))
5
5
 
6
+ require 'English'
6
7
  require 'main'
7
8
 
8
9
  Main do
@@ -39,78 +40,93 @@ Main do
39
40
  end
40
41
  end
41
42
 
43
+ option(:debug, :d) do
44
+ cast :bool
45
+ end
46
+
47
+ def run
48
+ help!
49
+ end
50
+
42
51
  mode :format do
43
52
  description "Format an article for publishing"
44
53
  source_argument
45
54
  def run
46
- Germinate.logger = self
47
55
  with_source_file do |source, path|
48
- application = Germinate::Application.new
49
- application.format(source, path, $stdout, $stderr)
56
+ @application.format(source, path)
50
57
  end
51
58
  end
52
59
  end
53
60
 
54
61
  mode :list do
55
- description "List various article components"
56
- source_argument
57
- COLLECTIONS = [:sections, :samples, :processes]
58
- COLLECTIONS.each do |collection|
59
- option collection do
60
- description "List all #{collection}"
61
- cast :bool
62
- end
62
+ description "List info about the source file"
63
+ argument :collection do
64
+ arity 1
65
+ required
66
+ argument_required
67
+ description "One of: sections, samples, processes, publishers, variables"
63
68
  end
69
+ source_argument
64
70
 
65
71
  def run
66
- things_to_list = []
67
- COLLECTIONS.each do |collection|
68
- things_to_list << collection if params[collection].value
69
- end
70
72
  with_source_file do |source, path|
71
- application = Germinate::Application.new
72
- application.list(source, path, things_to_list, $stdout)
73
+ @application.list(source, path, params[:collection].value)
73
74
  end
74
75
  end
75
76
  end
76
77
 
77
78
  mode :show do
78
79
  description "Show details about various article components"
79
- source_argument
80
- TYPES = [:section, :sample, :process]
81
- TYPES.each do |type|
82
- option type do
83
- description "Show details about a #{type}"
84
- argument_required
85
- end
80
+ argument :type do
81
+ arity 1
82
+ required
83
+ argument_required
84
+ description "One of: section, sample, process, publisher, variable"
86
85
  end
86
+ argument :item do
87
+ arity 1
88
+ required
89
+ argument_required
90
+ description "The specific item to show details about"
91
+ end
92
+ source_argument
87
93
 
88
94
  def run
89
- selection = TYPES.inject({}) {|sel, type|
90
- sel[type] = params[type].values
91
- sel
92
- }
93
95
  with_source_file do |source, path|
94
- application = Germinate::Application.new
95
- application.show(source, path, selection, $stdout)
96
+ @application.show(source, path, params['type'].value, params['item'].value)
96
97
  end
97
98
  end
98
99
  end
99
100
 
100
101
  mode :select do
101
102
  description "Test out a selector"
102
- source_argument
103
- option(:selector, :s) do
103
+ argument :selector do
104
104
  arity 1
105
105
  required
106
106
  argument_required
107
- description "The selector to search for"
107
+ description "The selector to retrieve"
108
+ end
109
+ source_argument
110
+
111
+ Germinate::TextTransforms.singleton_methods.each do |transform|
112
+ option(transform) do
113
+ description "Enable/disable the '#{transform}' text transform"
114
+ cast :bool
115
+ end
108
116
  end
109
117
 
110
118
  def run
119
+ options = Germinate::TextTransforms.singleton_methods.inject({}) do
120
+ |opts, transform|
121
+ if params[transform].given?
122
+ opts[transform] = params[transform].value
123
+ logger.info "Text transform '#{transform}' " +
124
+ (params[transform].value ? "enabled" : "disabled")
125
+ end
126
+ opts
127
+ end
111
128
  with_source_file do |source, path|
112
- application = Germinate::Application.new
113
- application.select(source, path, params[:selector].value, $stdout)
129
+ @application.select(source, path, params[:selector].value, options)
114
130
  end
115
131
  end
116
132
  end
@@ -125,12 +141,120 @@ Main do
125
141
  end
126
142
  end
127
143
 
144
+ mode :publish do
145
+ description "Publish the article using the named publisher"
146
+ argument :publisher
147
+ source_argument
148
+ option 'publish-options' do
149
+ arity 1
150
+ end
151
+
152
+ def run
153
+ with_source_file do |source, path|
154
+ options = YAML.load(params['publish-options'].value || "{}")
155
+ @application.publish(source, path, params[:publisher].value, options)
156
+ end
157
+ end
158
+ end
159
+
160
+ mode :set do
161
+ description "Set a named value in the article"
162
+ argument :name do
163
+ description "Variable name"
164
+ cast :string
165
+ end
166
+ argument :value do
167
+ description "Variable value"
168
+ cast :string
169
+ end
170
+ source_argument
171
+
172
+ def run
173
+ with_source_file do |source, path|
174
+ @application.set(
175
+ source,
176
+ path,
177
+ params[:name].value,
178
+ params[:value].value)
179
+ end
180
+ end
181
+ end
182
+
183
+ def initialize
184
+ @command = "#{$PROGRAM_NAME} #{ARGV.join(' ')}"
185
+ Germinate.logger = logger
186
+ logger.progname = "germinate"
187
+ logger.formatter = lambda { |severity, time, progname, message|
188
+ lines = case message
189
+ when ::String then message.split("\n")
190
+ when ::Exception then
191
+ Array(message.message) + Array(message.backtrace)
192
+ else
193
+ message.inspect
194
+ end
195
+ lines.map{|l| "#{severity} -- #{progname}: #{l}"}.join("\n") + "\n"
196
+ }
197
+ @application = Germinate::Application.new(stdout, stderr)
198
+ @application.load_plugins!
199
+ end
200
+
201
+ def pre_parse_parameters
202
+ end
203
+
204
+ def pre_run
205
+ logger.level = params['debug'].value ? Logger::DEBUG : Logger::INFO
206
+ end
207
+
128
208
  def with_source_file
129
- path = params['source'].value
130
- File.open(path) do |file|
131
- yield file, path
209
+ rescue_errors do
210
+ path = params['source'].value
211
+ File.open(path) do |file|
212
+ yield file, path
213
+ end
132
214
  end
133
215
  end
216
+
217
+ def rescue_errors
218
+ yield
219
+ rescue RuntimeError => error
220
+ raise if params['debug'].value
221
+ log_user_error(error)
222
+ rescue Exception => error
223
+ raise if params['debug'].value
224
+ log_program_error(error)
225
+ end
226
+
227
+ def log_user_error(error)
228
+ log_fatal_error <<-END
229
+ Germinate could not complete your command.
230
+ Please check your command and article for proper syntax.
231
+ #{command_report}
232
+ #{error_report(error)}
233
+ For more information, re-run the command with the --debug flag.
234
+ END
235
+ end
236
+
237
+ def log_program_error(error)
238
+ log_fatal_error <<-END
239
+ Germinate encountered an error while executing your command.
240
+ #{command_report}
241
+ #{error_report(error)}
242
+ Please re-run the command with the --debug flag, and file a problem report at
243
+ http://github.com/devver/germinate/
244
+ END
245
+ end
246
+
247
+ def command_report
248
+ "The command was: '#{@command}'"
249
+ end
250
+
251
+ def error_report(error)
252
+ "The error was: '#{error.message}'"
253
+ end
254
+
255
+ def log_fatal_error(error)
256
+ logger.fatal(error)
257
+ end
134
258
  end
135
259
 
136
260
  # EOF