hobix 0.4

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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/bin/hobix +90 -0
  3. data/lib/hobix/api.rb +91 -0
  4. data/lib/hobix/article.rb +22 -0
  5. data/lib/hobix/base.rb +477 -0
  6. data/lib/hobix/bixwik.rb +200 -0
  7. data/lib/hobix/commandline.rb +661 -0
  8. data/lib/hobix/comments.rb +99 -0
  9. data/lib/hobix/config.rb +39 -0
  10. data/lib/hobix/datamarsh.rb +110 -0
  11. data/lib/hobix/entry.rb +83 -0
  12. data/lib/hobix/facets/comments.rb +74 -0
  13. data/lib/hobix/facets/publisher.rb +314 -0
  14. data/lib/hobix/facets/trackbacks.rb +80 -0
  15. data/lib/hobix/linklist.rb +76 -0
  16. data/lib/hobix/out/atom.rb +92 -0
  17. data/lib/hobix/out/erb.rb +64 -0
  18. data/lib/hobix/out/okaynews.rb +55 -0
  19. data/lib/hobix/out/quick.rb +312 -0
  20. data/lib/hobix/out/rdf.rb +97 -0
  21. data/lib/hobix/out/redrum.rb +26 -0
  22. data/lib/hobix/out/rss.rb +115 -0
  23. data/lib/hobix/plugin/bloglines.rb +73 -0
  24. data/lib/hobix/plugin/calendar.rb +220 -0
  25. data/lib/hobix/plugin/flickr.rb +110 -0
  26. data/lib/hobix/plugin/recent_comments.rb +82 -0
  27. data/lib/hobix/plugin/sections.rb +91 -0
  28. data/lib/hobix/plugin/tags.rb +60 -0
  29. data/lib/hobix/publish/ping.rb +53 -0
  30. data/lib/hobix/publish/replicate.rb +283 -0
  31. data/lib/hobix/publisher.rb +18 -0
  32. data/lib/hobix/search/dictionary.rb +141 -0
  33. data/lib/hobix/search/porter_stemmer.rb +203 -0
  34. data/lib/hobix/search/simple.rb +209 -0
  35. data/lib/hobix/search/vector.rb +100 -0
  36. data/lib/hobix/storage/filesys.rb +398 -0
  37. data/lib/hobix/trackbacks.rb +94 -0
  38. data/lib/hobix/util/objedit.rb +193 -0
  39. data/lib/hobix/util/patcher.rb +155 -0
  40. data/lib/hobix/webapp/cli.rb +195 -0
  41. data/lib/hobix/webapp/htmlform.rb +107 -0
  42. data/lib/hobix/webapp/message.rb +177 -0
  43. data/lib/hobix/webapp/urigen.rb +141 -0
  44. data/lib/hobix/webapp/webrick-servlet.rb +90 -0
  45. data/lib/hobix/webapp.rb +723 -0
  46. data/lib/hobix/weblog.rb +860 -0
  47. data/lib/hobix.rb +223 -0
  48. metadata +87 -0
@@ -0,0 +1,94 @@
1
+ #
2
+ # = hobix/trackbacks.rb
3
+ #
4
+ # Hobix command-line weblog system, API for trackbacks.
5
+ #
6
+ # Copyright (c) 2003-2004 why the lucky stiff
7
+ #
8
+ # Written & maintained by why the lucky stiff <why@ruby-lang.org>
9
+ #
10
+ # This program is free software, released under a BSD license.
11
+ # See COPYING for details.
12
+ #
13
+ #--
14
+ # $Id$
15
+ #++
16
+
17
+ require 'hobix/facets/trackbacks'
18
+ require 'time'
19
+ require 'rexml/document'
20
+
21
+ module Hobix
22
+ module Out
23
+ class Quick
24
+ prepend_def :entry_title_erb, %{
25
+ <+ entry_trackback_rdf +>
26
+ }
27
+
28
+ def entry_trackback_rdf_erb; %{
29
+ <!--
30
+ <%= trackback_rdf_for( weblog, entry ) %>
31
+ -->
32
+ } end
33
+
34
+ append_def :entry_erb, %{
35
+ <% if entry and not defined? entries %><+ entry_trackback +><% end %>
36
+ }
37
+
38
+ def entry_trackback_erb; %{
39
+ <a name="trackbacks"></a>
40
+ <div id="trackbacks">
41
+ <% entry_id = entry.id %>
42
+ <% trackbacks = weblog.storage.load_attached( entry_id, "trackbacks") rescue [] %>
43
+ <% trackbacks.each do |trackback| %>
44
+ <div class="entry">
45
+ <div class="entryAttrib">
46
+ <div class="entryAuthor"><h3><%= trackback.blog_name %></h3></div>
47
+ <div class="entryTime">tracked back on <%= trackback.created.strftime("<nobr>%d %b %Y</nobr> at <nobr>%I:%M %p</nobr>" ) %></div>
48
+ </div>
49
+ <div class="entryContentOuter"><div class="entryContent">
50
+ <h3><a href="<%= trackback.url %>"><%= trackback.title %></a></h3>
51
+ <%= trackback.excerpt %>
52
+ </div></div>
53
+ </div>
54
+ <% end %>
55
+ </div>
56
+ } end
57
+
58
+ private
59
+ def trackback_rdf_for( weblog, entry )
60
+ trackback_link = '%s/control/trackback/%s' % [weblog.link, entry.id]
61
+ doc = REXML::Document.new
62
+ rdf = doc.add_element( "rdf:RDF" )
63
+ rdf.add_namespace( "rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#" )
64
+ rdf.add_namespace( "trackback", "http://madskills.com/public/xml/rss/module/trackback/" )
65
+ rdf.add_namespace( "dc", "http://purl.org/dc/elements/1.1/" )
66
+ desc = rdf.add_element( "rdf:Description" )
67
+ desc.add_attribute( "rdf:about", "")
68
+ desc.add_attribute( "trackback:ping", trackback_link )
69
+ desc.add_attribute( "dc:title", entry.title )
70
+ desc.add_attribute( "dc:identifier", entry.link )
71
+ ## i've dropped the following fields because i don't think they're used, and
72
+ ## dc:description in particular will potentially double the size of the
73
+ ## html pages. if they're actually useful to anyone, please re-add.
74
+ ##
75
+ ## desc.add_attribute( "dc:description", ( entry.summary || entry.content ).to_html )
76
+ ## desc.add_attribute( "dc:creator", entry.author )
77
+ ## desc.add_attribute( "dc:date", entry.created.xmlschema )
78
+ doc.to_s
79
+ end
80
+ end
81
+ end
82
+
83
+ class Trackback < BaseContent
84
+ _! "Trackback Information"
85
+ _ :blog_name, :edit_as => :text, :req => true
86
+ _ :url, :edit_as => :text, :req => true
87
+ _ :title, :edit_as => :text, :req => true
88
+ _ :excerpt , :edit_as => :text, :req => true
89
+ _ :created, :edit_as => :datetime
90
+ _ :ipaddress, :edit_as => :text
91
+
92
+ yaml_type "tag:hobix.com,2005:trackback"
93
+ end
94
+ end
@@ -0,0 +1,193 @@
1
+ #
2
+ # = hobix/util/objedit
3
+ #
4
+ # Hobix command-line weblog system.
5
+ #
6
+ # Copyright (c) 2003-2004 why the lucky stiff
7
+ #
8
+ # Written & maintained by why the lucky stiff <why@ruby-lang.org>
9
+ #
10
+ # This program is free software, released under a BSD license.
11
+ # See COPYING for details.
12
+ #
13
+ #--
14
+ # $Id$
15
+ #++
16
+ require 'ncurses'
17
+ require 'yaml'
18
+
19
+ module Hobix
20
+ module Util
21
+ # The ObjEdit class provides an ncurses-based editor for
22
+ # modifying Ruby objects. The ncurses library must be installed,
23
+ # which is available at http://ncurses-ruby.berlios.de/.
24
+ def self.ObjEdit( obj )
25
+ include Ncurses
26
+ include Ncurses::Form
27
+ # Initialize ncurses
28
+ scr = Ncurses.initscr
29
+ out_obj = nil
30
+ Ncurses.start_color
31
+ Ncurses.cbreak
32
+ Ncurses.keypad scr, true
33
+
34
+ # Initialize few color pairs
35
+ Ncurses.init_pair 1, COLOR_RED, COLOR_BLACK
36
+ Ncurses.init_pair 2, COLOR_WHITE, COLOR_BLACK
37
+ Ncurses.init_pair 3, COLOR_YELLOW, COLOR_BLACK
38
+ Ncurses.init_pair 4, COLOR_RED, COLOR_BLACK
39
+ scr.bkgd Ncurses.COLOR_PAIR(2)
40
+
41
+ # Initialize the fields
42
+ y = 0
43
+ labels = []
44
+ label_end = 12
45
+ ivars = []
46
+ fields =
47
+ obj.property_map.collect do |ivar, flag, edit_as|
48
+ ht, wt = 1, 60
49
+ case edit_as
50
+ when :text
51
+ field = FIELD.new ht, wt, y, 1, 0, 0
52
+ when :textarea
53
+ ht, wt = 5, 60
54
+ field = FIELD.new ht, wt, y, 1, 60, 0
55
+ end
56
+ if y + ht + 8 >= Ncurses.LINES
57
+ field.set_new_page TRUE
58
+ y = 0
59
+ end
60
+ labels << [y + 2, ivar, ht, wt]
61
+ ivars << ivar[1..-1]
62
+ label_end = ivar.length + 3 if label_end < ivar.length + 3
63
+ y += ht + 1
64
+
65
+ field.field_opts_off O_AUTOSKIP
66
+ field.set_field_back A_REVERSE
67
+ field.set_field_fore A_BOLD
68
+ field_write( field, obj.instance_variable_get( ivar ) )
69
+ field
70
+ end
71
+
72
+ # Create the form
73
+ my_form = FORM.new fields
74
+ my_form.user_object = "Editing #{ obj.class }"
75
+ rows, cols = [], []
76
+ my_form.scale_form rows, cols
77
+
78
+ # Create the window
79
+ my_win = WINDOW.new rows[0] + 3, cols[0] + 20, 0, 0
80
+ my_win.bkgd Ncurses.COLOR_PAIR( 3 )
81
+ my_win.keypad TRUE
82
+
83
+ # Attach
84
+ my_form.set_form_win my_win
85
+ my_form.set_form_sub my_win.derwin( rows[0], cols[0], 2, label_end )
86
+ my_form.form_opts_off O_NL_OVERLOAD
87
+ my_form.post_form
88
+ labels.each do |y, ivar, ht, wt|
89
+ my_win.mvaddstr y, 2, ivar
90
+ end
91
+ scr.mvprintw Ncurses.LINES - 2, 28, "Use TAB to switch between fields"
92
+ scr.mvprintw Ncurses.LINES - 1, 28, "F2 to save | F3 to cancel"
93
+ scr.refresh
94
+ my_win.wrefresh
95
+
96
+ # Loop through to get user requests
97
+ pressed = []
98
+ while((ch = my_win.getch()) != KEY_F2)
99
+ pressed << ch
100
+ case ch
101
+ when 16 # Ctrl + P
102
+ my_form.form_driver REQ_PREV_PAGE
103
+ my_form.form_driver REQ_FIRST_FIELD
104
+
105
+ when 14 # Ctrl + N
106
+ my_form.form_driver REQ_NEXT_PAGE
107
+ my_form.form_driver REQ_LAST_FIELD
108
+
109
+ when KEY_C3, ?\t
110
+ # Go to next field
111
+ my_form.form_driver REQ_NEXT_FIELD
112
+ # Go to the end of the present buffer
113
+ # Leaves nicely at the last character
114
+ my_form.form_driver REQ_END_LINE
115
+
116
+ when KEY_C1
117
+ # Go to previous field
118
+ my_form.form_driver REQ_PREV_FIELD
119
+ my_form.form_driver REQ_END_LINE
120
+
121
+ when KEY_UP
122
+ my_form.form_driver REQ_PREV_LINE
123
+
124
+ when KEY_DOWN
125
+ my_form.form_driver REQ_NEXT_LINE
126
+
127
+ when KEY_LEFT
128
+ # Go to previous character
129
+ my_form.form_driver REQ_PREV_CHAR
130
+
131
+ when KEY_RIGHT
132
+ # Go to previous field
133
+ my_form.form_driver REQ_NEXT_CHAR
134
+
135
+ when KEY_BACKSPACE, 010
136
+ my_form.form_driver REQ_DEL_PREV
137
+
138
+ when KEY_ENTER, ?\n, ?\r
139
+ my_form.form_driver REQ_NEW_LINE
140
+
141
+ when KEY_F3
142
+ return nil
143
+
144
+ else
145
+ # If this is a normal character, it gets Printed
146
+ my_form.form_driver ch
147
+ end
148
+ end
149
+ # Un post form and free the memory
150
+ my_form.form_driver REQ_NEXT_FIELD
151
+ my_form.unpost_form
152
+ my_form.free_form
153
+ obj_props = {}
154
+ fields.each do |f|
155
+ b = field_read(f)
156
+ f.free_field()
157
+ if String === b and b.empty?
158
+ b = nil
159
+ end
160
+ obj_props[ivars.shift] = b
161
+ end
162
+ out_obj = YAML::transfer( obj.to_yaml_type[1..-1], obj_props )
163
+ ensure
164
+ Ncurses.endwin
165
+ # p pressed
166
+ # p out_obj
167
+ end
168
+ def self.field_write( f, obj )
169
+ rows, cols, frow, fcol, nrow, nbuf = [], [], [], [], [], []
170
+ f.field_info( rows, cols, frow, fcol, nrow, nbuf )
171
+ if String === obj
172
+ obj = "#{ obj }"
173
+ end
174
+ str = obj.to_yaml( :BestWidth => cols[0] - 4 ).
175
+ sub( /^\-\-\-\s*(\>[0-9\-\+]*\n)?/, '' ).
176
+ gsub( /^([^\n]*)\n/ ) { |line| "%-#{cols}s" % [$1] }
177
+ f.set_field_buffer 0, str
178
+ end
179
+ def self.field_read( f )
180
+ rows, cols, frow, fcol, nrow, nbuf = [], [], [], [], [], []
181
+ f.field_info( rows, cols, frow, fcol, nrow, nbuf )
182
+ val = f.field_buffer(0).scan( /.{#{ cols[0] }}/ )
183
+ YAML::load(
184
+ if val.length > 1
185
+ "--- >\n " +
186
+ val.collect { |line| line.rstrip }.join( "\n " ).rstrip
187
+ else
188
+ "--- #{ val[0] }"
189
+ end
190
+ )
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,155 @@
1
+ #
2
+ # = hobix/util/patcher
3
+ #
4
+ # Hobix command-line weblog system.
5
+ #
6
+ # Copyright (c) 2003-2004 why the lucky stiff
7
+ #
8
+ # Written & maintained by why the lucky stiff <why@ruby-lang.org>
9
+ #
10
+ # This program is free software, released under a BSD license.
11
+ # See COPYING for details.
12
+ #
13
+ #--
14
+ # $Id$
15
+ #++
16
+ require 'fileutils'
17
+
18
+ module Hobix
19
+ module Util
20
+ # The Patcher class applies Hobix's own YAML patch format to a directory.
21
+ # These patches can create or append to existing plain-text files, as well
22
+ # as modifying YAML files using YPath.
23
+ #
24
+ # To apply your patch:
25
+ #
26
+ # patch_set = Hobix::Util::Patcher['1.patch', '2.patch']
27
+ # patch_set.apply('/dir/to/unaltered/code')
28
+ #
29
+ class PatchError < Exception; end
30
+ class Patcher
31
+ # Initialize the Patcher with a list of +paths+ to patches which
32
+ # must be applied in order.
33
+ #
34
+ # patch_set = Hobix::Util::Patcher.new('1.patch', '2.patch')
35
+ # patch_set.apply('/dir/to/unaltered/code')
36
+ #
37
+ def initialize( *paths )
38
+ @patches = {}
39
+ paths.each do |path|
40
+ YAML::load_file( path ).each do |k, v|
41
+ ( @patches[k] ||= [] ) << v
42
+ end
43
+ end
44
+ end
45
+
46
+ # Alias for Patcher.new.
47
+ #
48
+ # patch_set = Hobix::Util::Patcher['1.patch', '2.patch']
49
+ # patch_set.apply('/dir/to/unaltered/code')
50
+ #
51
+ def Patcher.[]( *paths )
52
+ Patcher.new( *paths )
53
+ end
54
+
55
+ # Apply the patches loaded into this class against a +path+ containing
56
+ # unaltered files.
57
+ #
58
+ # patch_set = Hobix::Util::Patcher['1.patch', '2.patch']
59
+ # patch_set.apply('/dir/to/unaltered/code')
60
+ #
61
+ def apply( path )
62
+ @patches.map do |fname, patchset|
63
+ fname = File.join( path, fname ) # .gsub( /^.*?[\/\\]/, '' ) )
64
+ ftarg = File.read( fname ) rescue ''
65
+ ftarg = YAML::load( ftarg ) if fname =~ /\.yaml$/
66
+
67
+ patchset.each_with_index do |(ptype, patch), patchno|
68
+ # apply the changes
69
+ puts "*** Applying patch ##{ patchno + 1 } for #{ fname } (#{ ptype })."
70
+ ftarg = method( ptype.gsub( /\W/, '_' ) ).call( ftarg, patch )
71
+ end
72
+
73
+ [fname, ftarg]
74
+ end.
75
+ each do |fname, ftext|
76
+ # save the files
77
+ if ftext == :remove
78
+ FileUtils.rm_rf fname
79
+ else
80
+ FileUtils.makedirs( File.dirname( fname ) )
81
+ ftext = ftext.to_yaml if fname =~ /\.yaml$/
82
+ File.open( fname, 'w+' ) { |f| f << ftext }
83
+ end
84
+ end
85
+ end
86
+
87
+ def file_remove( target, text )
88
+ :remove
89
+ end
90
+
91
+ def file_create( target, text )
92
+ text.to_s
93
+ end
94
+
95
+ def file_ensure( target, text )
96
+ target << text unless target.include? text
97
+ target
98
+ end
99
+
100
+ def yaml_merge( obj, merge )
101
+ obj = obj.value if obj.respond_to? :value
102
+ if obj.class != merge.class and merge.class != Hash
103
+ raise PatchError, "*** Patch failure since #{ obj.class } != #{ merge.class }."
104
+ end
105
+
106
+ case obj
107
+ when Hash
108
+ merge.each do |k, v|
109
+ if obj.has_key? k
110
+ yaml_merge obj[k], v
111
+ else
112
+ obj[k] = v
113
+ end
114
+ end
115
+ when Array
116
+ at = nil
117
+ merge.each do |v|
118
+ vat = obj.index( v )
119
+ if vat
120
+ at = vat if vat > at.to_i
121
+ else
122
+ if at
123
+ obj[at+=1,0] = v
124
+ else
125
+ obj << v
126
+ end
127
+ end
128
+ end
129
+ when String
130
+ obj.replace merge
131
+ else
132
+ merge.each do |k, v|
133
+ ivar = obj.instance_variable_get "@#{k}"
134
+ if ivar
135
+ yaml_merge ivar, v
136
+ else
137
+ obj.instance_variable_set "@#{k}", v
138
+ end
139
+ end
140
+ end
141
+
142
+ obj
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ YAML::add_domain_type( 'hobix.com,2004', 'patches/list' ) do |type, val|
149
+ val
150
+ end
151
+ ['yaml-merge', 'file-create', 'file-ensure', 'file-remove'].each do |ptype|
152
+ YAML::add_domain_type( 'hobix.com,2004', 'patches/' + ptype ) do |type, val|
153
+ [ptype, val]
154
+ end
155
+ end
@@ -0,0 +1,195 @@
1
+ # = webapp command line interface
2
+ #
3
+ # A web application using webapp has CLI (command line interface).
4
+ # You can invoke a webapp script from command line.
5
+ #
6
+ # xxx.cgi [options] [/path_info] [?query_string]
7
+ # -h, --help show this message
8
+ # -o, --output=FILE set output file
9
+ # --cern-meta output header as CERN httpd metafile
10
+ # --server-name=STRING set server name
11
+ # --server-port=INTEGER set server port number
12
+ # --script-name=STRING set script name
13
+ # --remote-addr=STRING set remote IP address
14
+ # --header=NAME:BODY set additional request header
15
+ #
16
+ # For example, hello.cgi, as follows, can be invoked from command line.
17
+ #
18
+ # % cat hello.cgi
19
+ # #!/usr/bin/env ruby
20
+ # require 'webapp'
21
+ # WebApp {|w| w.puts "Hello" }
22
+ # % ./hello.cgi
23
+ # Status: 200 OK
24
+ # Content-Type: text/plain
25
+ # Content-Length: 6
26
+ #
27
+ # Hello
28
+ #
29
+ # webapp.rb can be used in command line directly as follows.
30
+ # This document use the form to make examples short.
31
+ #
32
+ # % ruby -rwebapp -e 'WebApp {|w| w.puts "Hello" }'
33
+ # Status: 200 OK
34
+ # Content-Type: text/plain
35
+ # Content-Length: 6
36
+ #
37
+ # Hello
38
+ #
39
+ # The web application takes two optional argument: path info and query string.
40
+ # The optional first argument which begins with '/' is path info.
41
+ # The optional second argument which begins with '?' is query string.
42
+ # Since '?' is a shell meta character, it should be quoted.
43
+ #
44
+ # % ruby -rwebapp -e '
45
+ # WebApp {|w|
46
+ # w.puts w.path_info
47
+ # w.puts w.query_string
48
+ # }' /a '?q'
49
+ # Status: 200 OK
50
+ # Content-Type: text/plain
51
+ # Content-Length: 30
52
+ #
53
+ # /a
54
+ # #<WebApp::QueryString: ?q>
55
+ #
56
+ # If the option -o is specified, a response is generated on the specified file.
57
+ # Note that the format is suitable for Apache mod_asis.
58
+ #
59
+ # % ruby -rwebapp -e 'WebApp {|w| w.puts "Hello" }' -- -o ~/public_html/hello.asis
60
+ # % cat ~/public_html/hello.asis
61
+ # Status: 200 OK
62
+ # Content-Type: text/plain
63
+ # Content-Length: 6
64
+ #
65
+ # Hello
66
+ #
67
+ # If the option --cern-meta is specified addition to -o,
68
+ # The header in the response is stored in separated file.
69
+ # Note that the format is suitable for Apache mod_cern_meta.
70
+ #
71
+ # % ruby -rwebapp -e 'WebApp {|w| w.puts "Hello" }' -- --cern-meta -o ~/public_html/hello2.txt
72
+ # % cat ~/public_html/.web/hello2.txt.meta
73
+ # Content-Type: text/plain
74
+ # Content-Length: 6
75
+ # % cat ~/public_html/hello2.txt
76
+ # Hello
77
+ #
78
+ # The options --server-name, --server-port, --script-name and --remote-addr specifies
79
+ # information visible from web application.
80
+ # For example, WebApp#server_name returns a server name specified by --server-name.
81
+ #
82
+ # % ruby -rwebapp -e 'WebApp {|w| w.puts w.server_name }'
83
+ # Status: 200 OK
84
+ # Content-Type: text/plain
85
+ # Content-Length: 10
86
+ #
87
+ # localhost
88
+ # % ruby -rwebapp -e 'WebApp {|w| w.puts w.server_name }' -- --server-name=www.example.org
89
+ # Status: 200 OK
90
+ # Content-Type: text/plain
91
+ # Content-Length: 16
92
+ #
93
+ # www.example.org
94
+ #
95
+ # The option --header specifies an additional request header.
96
+ # For example, specifying "Accept-Encoding: gzip" makes output gzipped.
97
+ #
98
+ # % ruby -rwebapp -e 'WebApp {|w| w.puts "Hello"*100 }' -- --header='Accept-Encoding: gzip'|cat -v
99
+ # Status: 200 OK
100
+ # Content-Type: text/plain
101
+ # Content-Encoding: gzip
102
+ # Content-Length: 31
103
+ #
104
+ # ^_M-^K^H^@^O^VM-TA^@^CM-sHM-MM-IM-IM-w^X%F^RM-A^E^@ZTsDM-u^A^@^@
105
+
106
+ require 'optparse'
107
+
108
+ module Hobix
109
+ class WebApp
110
+ class Manager
111
+ # CLI (command line interface)
112
+ def run_cli
113
+ opt_output = '-'
114
+ opt_cern_meta = false
115
+ opt_server_name = 'localhost'
116
+ opt_server_port = 80
117
+ opt_script_name = "/#{File.basename($0)}"
118
+ opt_remote_addr = '127.0.0.1'
119
+ opt_headers = []
120
+ ARGV.options {|q|
121
+ q.banner = "#{File.basename $0} [options] [/path_info] [?query_string]"
122
+ q.def_option('-h', '--help', 'show this message') { puts q; exit(0) }
123
+ q.def_option('-o FILE', '--output=FILE', 'set output file') {|arg| opt_output = arg.untaint }
124
+ q.def_option('--cern-meta', 'output header as CERN httpd metafile') { opt_cern_meta = true }
125
+ q.def_option('--server-name=STRING', 'set server name') {|arg| opt_server_name = arg }
126
+ q.def_option('--server-port=INTEGER', 'set server port number') {|arg| opt_server_port = arg.to_i }
127
+ q.def_option('--script-name=STRING', 'set script name') {|arg| opt_script_name = arg }
128
+ q.def_option('--remote-addr=STRING', 'set remote IP address') {|arg| opt_remote_addr = arg }
129
+ q.def_option('--header=NAME:BODY', 'set additional request header') {|arg| opt_headers << arg.split(/:/, 2) }
130
+ q.parse!
131
+ }
132
+ if path_info = ARGV.shift
133
+ if %r{\A/} !~ path_info
134
+ ARGV.unshift path_info
135
+ path_info = nil
136
+ end
137
+ end
138
+ if query_string = ARGV.shift
139
+ if %r{\A\?} !~ query_string
140
+ ARGV.unshift query_string
141
+ query_string = nil
142
+ end
143
+ end
144
+ if !ARGV.empty?
145
+ raise "extra arguments: #{ARGV.inspect[1..-2]}"
146
+ end
147
+ path_info ||= ''
148
+ query_string ||= ''
149
+ setup_request = lambda {|req|
150
+ req.make_request_header_from_cgi_env({
151
+ 'REQUEST_METHOD' => 'GET',
152
+ 'SERVER_NAME' => opt_server_name,
153
+ 'SERVER_PORT' => opt_server_port,
154
+ 'SCRIPT_NAME' => opt_script_name,
155
+ 'PATH_INFO' => path_info,
156
+ 'QUERY_STRING' => query_string,
157
+ 'SERVER_PROTOCOL' => 'HTTP/1.0',
158
+ 'REMOTE_ADDR' => opt_remote_addr,
159
+ 'CONTENT_TYPE' => ''
160
+ })
161
+ opt_headers.each {|name, body|
162
+ req.header_object.add name, body
163
+ }
164
+ }
165
+ output_response = lambda {|res|
166
+ if opt_output == '-'
167
+ res.output_cgi_status_field($stdout)
168
+ res.output_message($stdout)
169
+ else
170
+ if opt_cern_meta
171
+ dir = "#{File.dirname(opt_output)}/.web"
172
+ begin
173
+ Dir.mkdir dir
174
+ rescue Errno::EEXIST
175
+ end
176
+ open("#{dir}/#{File.basename(opt_output)}.meta", 'w') {|f|
177
+ #res.output_cgi_status_field(f)
178
+ res.output_header(f)
179
+ }
180
+ open(opt_output, 'w') {|f|
181
+ res.output_body(f)
182
+ }
183
+ else
184
+ open(opt_output, 'w') {|f|
185
+ res.output_cgi_status_field(f)
186
+ res.output_message(f)
187
+ }
188
+ end
189
+ end
190
+ }
191
+ primitive_run(setup_request, output_response)
192
+ end
193
+ end
194
+ end
195
+ end