hobix 0.4

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