metavida-oauth 0.3.2

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 (84) hide show
  1. data/History.txt +66 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +83 -0
  4. data/README.rdoc +71 -0
  5. data/Rakefile +36 -0
  6. data/TODO +32 -0
  7. data/bin/oauth +5 -0
  8. data/examples/yql.rb +44 -0
  9. data/lib/oauth/cli.rb +300 -0
  10. data/lib/oauth/client/action_controller_request.rb +53 -0
  11. data/lib/oauth/client/helper.rb +71 -0
  12. data/lib/oauth/client/net_http.rb +78 -0
  13. data/lib/oauth/client.rb +4 -0
  14. data/lib/oauth/consumer.rb +296 -0
  15. data/lib/oauth/errors/error.rb +4 -0
  16. data/lib/oauth/errors/problem.rb +14 -0
  17. data/lib/oauth/errors/unauthorized.rb +12 -0
  18. data/lib/oauth/errors.rb +3 -0
  19. data/lib/oauth/helper.rb +55 -0
  20. data/lib/oauth/oauth.rb +7 -0
  21. data/lib/oauth/oauth_test_helper.rb +25 -0
  22. data/lib/oauth/request_proxy/action_controller_request.rb +68 -0
  23. data/lib/oauth/request_proxy/base.rb +157 -0
  24. data/lib/oauth/request_proxy/jabber_request.rb +41 -0
  25. data/lib/oauth/request_proxy/mock_request.rb +44 -0
  26. data/lib/oauth/request_proxy/net_http.rb +65 -0
  27. data/lib/oauth/request_proxy/rack_request.rb +40 -0
  28. data/lib/oauth/request_proxy.rb +24 -0
  29. data/lib/oauth/server.rb +66 -0
  30. data/lib/oauth/signature/base.rb +91 -0
  31. data/lib/oauth/signature/hmac/base.rb +12 -0
  32. data/lib/oauth/signature/hmac/md5.rb +9 -0
  33. data/lib/oauth/signature/hmac/rmd160.rb +9 -0
  34. data/lib/oauth/signature/hmac/sha1.rb +9 -0
  35. data/lib/oauth/signature/hmac/sha2.rb +9 -0
  36. data/lib/oauth/signature/md5.rb +13 -0
  37. data/lib/oauth/signature/plaintext.rb +23 -0
  38. data/lib/oauth/signature/rsa/sha1.rb +45 -0
  39. data/lib/oauth/signature/sha1.rb +13 -0
  40. data/lib/oauth/signature.rb +28 -0
  41. data/lib/oauth/token.rb +7 -0
  42. data/lib/oauth/tokens/access_token.rb +68 -0
  43. data/lib/oauth/tokens/consumer_token.rb +32 -0
  44. data/lib/oauth/tokens/request_token.rb +28 -0
  45. data/lib/oauth/tokens/server_token.rb +9 -0
  46. data/lib/oauth/tokens/token.rb +17 -0
  47. data/lib/oauth/version.rb +3 -0
  48. data/lib/oauth.rb +4 -0
  49. data/oauth.gemspec +49 -0
  50. data/script/destroy +14 -0
  51. data/script/generate +14 -0
  52. data/script/txt2html +74 -0
  53. data/setup.rb +1585 -0
  54. data/tasks/deployment.rake +34 -0
  55. data/tasks/environment.rake +7 -0
  56. data/tasks/website.rake +17 -0
  57. data/test/cases/oauth_case.rb +19 -0
  58. data/test/cases/spec/1_0-final/test_construct_request_url.rb +62 -0
  59. data/test/cases/spec/1_0-final/test_normalize_request_parameters.rb +88 -0
  60. data/test/cases/spec/1_0-final/test_parameter_encodings.rb +86 -0
  61. data/test/cases/spec/1_0-final/test_signature_base_strings.rb +77 -0
  62. data/test/keys/rsa.cert +11 -0
  63. data/test/keys/rsa.pem +16 -0
  64. data/test/test_access_token.rb +28 -0
  65. data/test/test_action_controller_request_proxy.rb +45 -0
  66. data/test/test_consumer.rb +327 -0
  67. data/test/test_helper.rb +11 -0
  68. data/test/test_hmac_sha1.rb +21 -0
  69. data/test/test_net_http_client.rb +169 -0
  70. data/test/test_net_http_request_proxy.rb +38 -0
  71. data/test/test_rack_request_proxy.rb +40 -0
  72. data/test/test_request_token.rb +53 -0
  73. data/test/test_rsa_sha1.rb +59 -0
  74. data/test/test_server.rb +40 -0
  75. data/test/test_signature.rb +11 -0
  76. data/test/test_signature_base.rb +32 -0
  77. data/test/test_signature_plain_text.rb +31 -0
  78. data/test/test_token.rb +14 -0
  79. data/website/index.html +87 -0
  80. data/website/index.txt +73 -0
  81. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  82. data/website/stylesheets/screen.css +138 -0
  83. data/website/template.rhtml +48 -0
  84. metadata +212 -0
@@ -0,0 +1,45 @@
1
+ require 'oauth/signature/base'
2
+ require 'openssl'
3
+
4
+ module OAuth::Signature::RSA
5
+ class SHA1 < OAuth::Signature::Base
6
+ implements 'rsa-sha1'
7
+
8
+ def ==(cmp_signature)
9
+ public_key.verify(OpenSSL::Digest::SHA1.new, Base64.decode64(cmp_signature.is_a?(Array) ? cmp_signature.first : cmp_signature), signature_base_string)
10
+ end
11
+
12
+ def public_key
13
+ if consumer_secret.is_a?(String)
14
+ decode_public_key
15
+ elsif consumer_secret.is_a?(OpenSSL::X509::Certificate)
16
+ consumer_secret.public_key
17
+ else
18
+ consumer_secret
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def decode_public_key
25
+ case consumer_secret
26
+ when /-----BEGIN CERTIFICATE-----/
27
+ OpenSSL::X509::Certificate.new( consumer_secret).public_key
28
+ else
29
+ OpenSSL::PKey::RSA.new( consumer_secret)
30
+ end
31
+ end
32
+
33
+ def digest
34
+ private_key = OpenSSL::PKey::RSA.new(
35
+ if options[:private_key_file]
36
+ IO.read(options[:private_key_file])
37
+ else
38
+ consumer_secret
39
+ end
40
+ )
41
+
42
+ private_key.sign(OpenSSL::Digest::SHA1.new, signature_base_string)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,13 @@
1
+ require 'oauth/signature/base'
2
+ require 'digest/sha1'
3
+
4
+ module OAuth::Signature
5
+ class SHA1 < Base
6
+ implements 'sha1'
7
+ digest_class Digest::SHA1
8
+
9
+ def signature_base_string
10
+ secret + super
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ module OAuth
2
+ module Signature
3
+ def self.available_methods
4
+ @available_methods ||= {}
5
+ end
6
+
7
+ def self.build(request, options = {}, &block)
8
+ request = OAuth::RequestProxy.proxy(request, options)
9
+ klass = available_methods[(request.signature_method || "").downcase]
10
+ raise UnknownSignatureMethod, request.signature_method unless klass
11
+ klass.new(request, options, &block)
12
+ end
13
+
14
+ def self.sign(request, options = {}, &block)
15
+ self.build(request, options, &block).signature
16
+ end
17
+
18
+ def self.verify(request, options = {}, &block)
19
+ self.build(request, options, &block).verify
20
+ end
21
+
22
+ def self.signature_base_string(request, options = {}, &block)
23
+ self.build(request, options, &block).signature_base_string
24
+ end
25
+
26
+ class UnknownSignatureMethod < Exception; end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ # this exists for backwards-compatibility
2
+
3
+ require 'oauth/tokens/token'
4
+ require 'oauth/tokens/server_token'
5
+ require 'oauth/tokens/consumer_token'
6
+ require 'oauth/tokens/request_token'
7
+ require 'oauth/tokens/access_token'
@@ -0,0 +1,68 @@
1
+ module OAuth
2
+ # The Access Token is used for the actual "real" web service calls that you perform against the server
3
+ class AccessToken < ConsumerToken
4
+ # The less intrusive way. Otherwise, if we are to do it correctly inside consumer,
5
+ # we need to restructure and touch more methods: request(), sign!(), etc.
6
+ def request(http_method, path, *arguments)
7
+ request_uri = URI.parse(path)
8
+ site_uri = consumer.uri
9
+ is_service_uri_different = (request_uri.absolute? && request_uri != site_uri)
10
+ consumer.uri(request_uri) if is_service_uri_different
11
+ @response = super(http_method, path, *arguments)
12
+ # NOTE: reset for wholesomeness? meaning that we admit only AccessToken service calls may use different URIs?
13
+ # so reset in case consumer is still used for other token-management tasks subsequently?
14
+ consumer.uri(site_uri) if is_service_uri_different
15
+ @response
16
+ end
17
+
18
+ # Make a regular GET request using AccessToken
19
+ #
20
+ # @response = @token.get('/people')
21
+ # @response = @token.get('/people', { 'Accept'=>'application/xml' })
22
+ #
23
+ def get(path, headers = {})
24
+ request(:get, path, headers)
25
+ end
26
+
27
+ # Make a regular HEAD request using AccessToken
28
+ #
29
+ # @response = @token.head('/people')
30
+ #
31
+ def head(path, headers = {})
32
+ request(:head, path, headers)
33
+ end
34
+
35
+ # Make a regular POST request using AccessToken
36
+ #
37
+ # @response = @token.post('/people')
38
+ # @response = @token.post('/people', { :name => 'Bob', :email => 'bob@mailinator.com' })
39
+ # @response = @token.post('/people', { :name => 'Bob', :email => 'bob@mailinator.com' }, { 'Accept' => 'application/xml' })
40
+ # @response = @token.post('/people', nil, {'Accept' => 'application/xml' })
41
+ # @response = @token.post('/people', @person.to_xml, { 'Accept'=>'application/xml', 'Content-Type' => 'application/xml' })
42
+ #
43
+ def post(path, body = '', headers = {})
44
+ request(:post, path, body, headers)
45
+ end
46
+
47
+ # Make a regular PUT request using AccessToken
48
+ #
49
+ # @response = @token.put('/people/123')
50
+ # @response = @token.put('/people/123', { :name => 'Bob', :email => 'bob@mailinator.com' })
51
+ # @response = @token.put('/people/123', { :name => 'Bob', :email => 'bob@mailinator.com' }, { 'Accept' => 'application/xml' })
52
+ # @response = @token.put('/people/123', nil, { 'Accept' => 'application/xml' })
53
+ # @response = @token.put('/people/123', @person.to_xml, { 'Accept' => 'application/xml', 'Content-Type' => 'application/xml' })
54
+ #
55
+ def put(path, body = '', headers = {})
56
+ request(:put, path, body, headers)
57
+ end
58
+
59
+ # Make a regular DELETE request using AccessToken
60
+ #
61
+ # @response = @token.delete('/people/123')
62
+ # @response = @token.delete('/people/123', { 'Accept' => 'application/xml' })
63
+ #
64
+ def delete(path, headers = {})
65
+ request(:delete, path, headers)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,32 @@
1
+ module OAuth
2
+ # Superclass for tokens used by OAuth Clients
3
+ class ConsumerToken < Token
4
+ attr_accessor :consumer, :params
5
+ attr_reader :response
6
+
7
+ def self.from_hash(consumer, hash)
8
+ token = self.new(consumer, hash[:oauth_token], hash[:oauth_token_secret])
9
+ token.params = hash
10
+ token
11
+ end
12
+
13
+ def initialize(consumer, token="", secret="")
14
+ super(token, secret)
15
+ @consumer = consumer
16
+ end
17
+
18
+ # Make a signed request using given http_method to the path
19
+ #
20
+ # @token.request(:get, '/people')
21
+ # @token.request(:post, '/people', @person.to_xml, { 'Content-Type' => 'application/xml' })
22
+ #
23
+ def request(http_method, path, *arguments)
24
+ @response = consumer.request(http_method, path, self, {}, *arguments)
25
+ end
26
+
27
+ # Sign a request generated elsewhere using Net:HTTP::Post.new or friends
28
+ def sign!(request, options = {})
29
+ consumer.sign!(request, self, options)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ module OAuth
2
+ # The RequestToken is used for the initial Request.
3
+ # This is normally created by the Consumer object.
4
+ class RequestToken < ConsumerToken
5
+
6
+ # Generate an authorization URL for user authorization
7
+ def authorize_url(params = nil)
8
+ params = (params || {}).merge(:oauth_token => self.token)
9
+ build_authorize_url(consumer.authorize_url, params)
10
+ end
11
+
12
+ # exchange for AccessToken on server
13
+ def get_access_token(options = {})
14
+ response = consumer.token_request(consumer.http_method, (consumer.access_token_url? ? consumer.access_token_url : consumer.access_token_path), self, options)
15
+ OAuth::AccessToken.from_hash(consumer, response)
16
+ end
17
+
18
+ protected
19
+
20
+ # construct an authorization url
21
+ def build_authorize_url(base_url, params)
22
+ uri = URI.parse(base_url.to_s)
23
+ # TODO doesn't handle array values correctly
24
+ uri.query = params.map { |k,v| [k, CGI.escape(v)] * "=" } * "&"
25
+ uri.to_s
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ module OAuth
2
+ # Used on the server for generating tokens
3
+ class ServerToken < Token
4
+
5
+ def initialize
6
+ super(generate_key(16), generate_key)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module OAuth
2
+ # Superclass for the various tokens used by OAuth
3
+ class Token
4
+ include OAuth::Helper
5
+
6
+ attr_accessor :token, :secret
7
+
8
+ def initialize(token, secret)
9
+ @token = token
10
+ @secret = secret
11
+ end
12
+
13
+ def to_query
14
+ "oauth_token=#{escape(token)}&oauth_secret=#{escape(secret)}"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module OAuth #:nodoc:
2
+ VERSION = '0.3.2'
3
+ end
data/lib/oauth.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'oauth/oauth'
2
+ require 'oauth/client/helper'
3
+ require 'oauth/signature/hmac/sha1'
4
+ require 'oauth/request_proxy/mock_request'
data/oauth.gemspec ADDED
@@ -0,0 +1,49 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{oauth}
5
+ s.version = "0.3.2"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Pelle Braendgaard", "Blaine Cook", "Larry Halff", "Jesse Clark", "Jon Crosby", "Seth Fitzsimmons"]
9
+ s.date = %q{2009-03-23}
10
+ s.default_executable = %q{oauth}
11
+ s.description = %q{OAuth Core Ruby implementation}
12
+ s.email = %q{oauth-ruby@googlegroups.com}
13
+ s.executables = ["oauth"]
14
+ s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "README.rdoc", "website/index.txt"]
15
+ s.files = ["History.txt", "License.txt", "Manifest.txt", "README.rdoc", "Rakefile", "TODO", "bin/oauth", "examples/yql.rb", "lib/oauth.rb", "lib/oauth/oauth.rb", "lib/oauth/cli.rb", "lib/oauth/client.rb", "lib/oauth/client/action_controller_request.rb", "lib/oauth/client/helper.rb", "lib/oauth/client/net_http.rb", "lib/oauth/consumer.rb", "lib/oauth/errors.rb", "lib/oauth/errors/error.rb", "lib/oauth/errors/problem.rb", "lib/oauth/errors/unauthorized.rb", "lib/oauth/helper.rb", "lib/oauth/oauth_test_helper.rb", "lib/oauth/request_proxy.rb", "lib/oauth/request_proxy/action_controller_request.rb", "lib/oauth/request_proxy/base.rb", "lib/oauth/request_proxy/jabber_request.rb", "lib/oauth/request_proxy/mock_request.rb", "lib/oauth/request_proxy/net_http.rb", "lib/oauth/request_proxy/rack_request.rb", "lib/oauth/server.rb", "lib/oauth/signature.rb", "lib/oauth/signature/base.rb", "lib/oauth/signature/hmac/base.rb", "lib/oauth/signature/hmac/md5.rb", "lib/oauth/signature/hmac/rmd160.rb", "lib/oauth/signature/hmac/sha1.rb", "lib/oauth/signature/hmac/sha2.rb", "lib/oauth/signature/md5.rb", "lib/oauth/signature/plaintext.rb", "lib/oauth/signature/rsa/sha1.rb", "lib/oauth/signature/sha1.rb", "lib/oauth/token.rb", "lib/oauth/tokens/access_token.rb", "lib/oauth/tokens/consumer_token.rb", "lib/oauth/tokens/request_token.rb", "lib/oauth/tokens/server_token.rb", "lib/oauth/tokens/token.rb", "lib/oauth/version.rb", "oauth.gemspec", "script/destroy", "script/generate", "script/txt2html", "setup.rb", "tasks/deployment.rake", "tasks/environment.rake", "tasks/website.rake", "test/cases/oauth_case.rb", "test/cases/spec/1_0-final/test_construct_request_url.rb", "test/cases/spec/1_0-final/test_normalize_request_parameters.rb", "test/cases/spec/1_0-final/test_parameter_encodings.rb", "test/cases/spec/1_0-final/test_signature_base_strings.rb", "test/keys/rsa.cert", "test/keys/rsa.pem", "test/test_access_token.rb", "test/test_action_controller_request_proxy.rb", "test/test_consumer.rb", "test/test_helper.rb", "test/test_hmac_sha1.rb", "test/test_net_http_client.rb", "test/test_net_http_request_proxy.rb", "test/test_rack_request_proxy.rb", "test/test_request_token.rb", "test/test_rsa_sha1.rb", "test/test_server.rb", "test/test_signature.rb", "test/test_signature_base.rb", "test/test_signature_plain_text.rb", "test/test_token.rb", "website/index.html", "website/index.txt", "website/javascripts/rounded_corners_lite.inc.js", "website/stylesheets/screen.css", "website/template.rhtml"]
16
+ s.has_rdoc = true
17
+ s.homepage = %q{http://oauth.rubyforge.org}
18
+ s.rdoc_options = ["--main", "README.rdoc"]
19
+ s.require_paths = ["lib"]
20
+ s.rubyforge_project = %q{oauth}
21
+ s.rubygems_version = %q{1.3.1}
22
+ s.summary = %q{OAuth Core Ruby implementation}
23
+ s.test_files = ["test/cases/spec/1_0-final/test_construct_request_url.rb", "test/cases/spec/1_0-final/test_normalize_request_parameters.rb", "test/cases/spec/1_0-final/test_parameter_encodings.rb", "test/cases/spec/1_0-final/test_signature_base_strings.rb", "test/test_access_token.rb", "test/test_action_controller_request_proxy.rb", "test/test_consumer.rb", "test/test_helper.rb", "test/test_hmac_sha1.rb", "test/test_net_http_client.rb", "test/test_net_http_request_proxy.rb", "test/test_rack_request_proxy.rb", "test/test_request_token.rb", "test/test_rsa_sha1.rb", "test/test_server.rb", "test/test_signature.rb", "test/test_signature_base.rb", "test/test_signature_plain_text.rb", "test/test_token.rb"]
24
+
25
+ if s.respond_to? :specification_version then
26
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
27
+ s.specification_version = 2
28
+
29
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
30
+ s.add_runtime_dependency(%q<ruby-hmac>, [">= 0.3.1"])
31
+ s.add_development_dependency(%q<newgem>, [">= 1.2.3"])
32
+ s.add_development_dependency(%q<actionpack>, [">= 0"])
33
+ s.add_development_dependency(%q<rack>, [">= 0"])
34
+ s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
35
+ else
36
+ s.add_dependency(%q<ruby-hmac>, [">= 0.3.1"])
37
+ s.add_dependency(%q<newgem>, [">= 1.2.3"])
38
+ s.add_dependency(%q<actionpack>, [">= 0"])
39
+ s.add_dependency(%q<rack>, [">= 0"])
40
+ s.add_dependency(%q<hoe>, [">= 1.8.0"])
41
+ end
42
+ else
43
+ s.add_dependency(%q<ruby-hmac>, [">= 0.3.1"])
44
+ s.add_dependency(%q<newgem>, [">= 1.2.3"])
45
+ s.add_dependency(%q<actionpack>, [">= 0"])
46
+ s.add_dependency(%q<rack>, [">= 0"])
47
+ s.add_dependency(%q<hoe>, [">= 1.8.0"])
48
+ end
49
+ end
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/script/txt2html ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'newgem'
6
+ rescue LoadError
7
+ puts "\n\nGenerating the website requires the newgem RubyGem"
8
+ puts "Install: gem install newgem\n\n"
9
+ exit(1)
10
+ end
11
+ require 'redcloth'
12
+ require 'syntax/convertors/html'
13
+ require 'erb'
14
+ require File.dirname(__FILE__) + '/../lib/oauth/version.rb'
15
+
16
+ version = OAuth::VERSION::STRING
17
+ download = 'http://rubyforge.org/projects/oauth'
18
+
19
+ class Fixnum
20
+ def ordinal
21
+ # teens
22
+ return 'th' if (10..19).include?(self % 100)
23
+ # others
24
+ case self % 10
25
+ when 1: return 'st'
26
+ when 2: return 'nd'
27
+ when 3: return 'rd'
28
+ else return 'th'
29
+ end
30
+ end
31
+ end
32
+
33
+ class Time
34
+ def pretty
35
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
36
+ end
37
+ end
38
+
39
+ def convert_syntax(syntax, source)
40
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
41
+ end
42
+
43
+ if ARGV.length >= 1
44
+ src, template = ARGV
45
+ template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
46
+
47
+ else
48
+ puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
49
+ exit!
50
+ end
51
+
52
+ template = ERB.new(File.open(template).read)
53
+
54
+ title = nil
55
+ body = nil
56
+ File.open(src) do |fsrc|
57
+ title_text = fsrc.readline
58
+ body_text = fsrc.read
59
+ syntax_items = []
60
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
61
+ ident = syntax_items.length
62
+ element, syntax, source = $1, $2, $3
63
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
64
+ "syntax-temp-#{ident}"
65
+ }
66
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
67
+ body = RedCloth.new(body_text).to_html
68
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
69
+ end
70
+ stat = File.stat(src)
71
+ created = stat.ctime
72
+ modified = stat.mtime
73
+
74
+ $stdout << template.result(binding)