mirrored 0.1.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 (47) hide show
  1. data/History.txt +2 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +47 -0
  4. data/README.txt +1 -0
  5. data/Rakefile +4 -0
  6. data/config/hoe.rb +71 -0
  7. data/config/requirements.rb +17 -0
  8. data/lib/mirrored/base.rb +50 -0
  9. data/lib/mirrored/connection.rb +39 -0
  10. data/lib/mirrored/date.rb +28 -0
  11. data/lib/mirrored/post.rb +120 -0
  12. data/lib/mirrored/tag.rb +34 -0
  13. data/lib/mirrored/update.rb +16 -0
  14. data/lib/mirrored/version.rb +9 -0
  15. data/lib/mirrored.rb +25 -0
  16. data/script/destroy +14 -0
  17. data/script/generate +14 -0
  18. data/script/txt2html +74 -0
  19. data/setup.rb +1585 -0
  20. data/tasks/deployment.rake +27 -0
  21. data/tasks/environment.rake +7 -0
  22. data/tasks/website.rake +17 -0
  23. data/test/fixtures/xml/add_post_done.xml +2 -0
  24. data/test/fixtures/xml/all_posts.xml +2919 -0
  25. data/test/fixtures/xml/all_posts_by_tag.xml +30 -0
  26. data/test/fixtures/xml/dates.xml +739 -0
  27. data/test/fixtures/xml/dates_for_tag.xml +270 -0
  28. data/test/fixtures/xml/delete_post.xml +2 -0
  29. data/test/fixtures/xml/delete_post_failed.xml +2 -0
  30. data/test/fixtures/xml/posts.xml +6 -0
  31. data/test/fixtures/xml/posts_by_date.xml +4 -0
  32. data/test/fixtures/xml/posts_by_tag.xml +4 -0
  33. data/test/fixtures/xml/posts_by_url.xml +4 -0
  34. data/test/fixtures/xml/recent_posts.xml +19 -0
  35. data/test/fixtures/xml/recent_posts_by_tag.xml +19 -0
  36. data/test/fixtures/xml/recent_posts_by_tag_and_count.xml +8 -0
  37. data/test/fixtures/xml/tag_rename.xml +2 -0
  38. data/test/fixtures/xml/tags.xml +1368 -0
  39. data/test/test_base.rb +33 -0
  40. data/test/test_date.rb +26 -0
  41. data/test/test_helper.rb +12 -0
  42. data/test/test_post.rb +135 -0
  43. data/test/test_tag.rb +23 -0
  44. data/test/test_update.rb +18 -0
  45. data/website/css/common.css +47 -0
  46. data/website/index.html +92 -0
  47. metadata +109 -0
data/History.txt ADDED
@@ -0,0 +1,2 @@
1
+ 0.1.0 - Oct 6, 2007
2
+ * initial release
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 John Nunemaker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,47 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ config/hoe.rb
7
+ config/requirements.rb
8
+ lib/mirrored.rb
9
+ lib/mirrored/base.rb
10
+ lib/mirrored/connection.rb
11
+ lib/mirrored/date.rb
12
+ lib/mirrored/post.rb
13
+ lib/mirrored/tag.rb
14
+ lib/mirrored/update.rb
15
+ lib/mirrored/version.rb
16
+ script/destroy
17
+ script/generate
18
+ script/txt2html
19
+ setup.rb
20
+ tasks/deployment.rake
21
+ tasks/environment.rake
22
+ tasks/website.rake
23
+ test/test_base.rb
24
+ test/test_date.rb
25
+ test/test_helper.rb
26
+ test/test_post.rb
27
+ test/test_tag.rb
28
+ test/test_update.rb
29
+ test/fixtures/xml/add_post_done.xml
30
+ test/fixtures/xml/all_posts.xml
31
+ test/fixtures/xml/all_posts_by_tag.xml
32
+ test/fixtures/xml/dates.xml
33
+ test/fixtures/xml/dates_for_tag.xml
34
+ test/fixtures/xml/delete_post.xml
35
+ test/fixtures/xml/delete_post_failed.xml
36
+ test/fixtures/xml/posts.xml
37
+ test/fixtures/xml/posts_by_date.xml
38
+ test/fixtures/xml/posts_by_tag.xml
39
+ test/fixtures/xml/posts_by_url.xml
40
+ test/fixtures/xml/recent_posts.xml
41
+ test/fixtures/xml/recent_posts_by_tag.xml
42
+ test/fixtures/xml/recent_posts_by_tag_and_count.xml
43
+ test/fixtures/xml/tag_rename.xml
44
+ test/fixtures/xml/tags.xml
45
+ website/css/common.css
46
+ website/images
47
+ website/index.html
data/README.txt ADDED
@@ -0,0 +1 @@
1
+ README
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/config/hoe.rb ADDED
@@ -0,0 +1,71 @@
1
+ require 'mirrored/version'
2
+
3
+ AUTHOR = 'John Nunemaker' # can also be an array of Authors
4
+ EMAIL = "nunemaker@gmail.com"
5
+ DESCRIPTION = "Wrapper for delicious and magnolia mirrored apis"
6
+ GEM_NAME = 'mirrored' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'mirrored' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+
11
+ @config_file = "~/.rubyforge/user-config.yml"
12
+ @config = nil
13
+ RUBYFORGE_USERNAME = "jnunemaker"
14
+
15
+ def rubyforge_username
16
+ unless @config
17
+ begin
18
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
19
+ rescue
20
+ puts <<-EOS
21
+ ERROR: No rubyforge config file found: #{@config_file}
22
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
23
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
24
+ EOS
25
+ exit
26
+ end
27
+ end
28
+ RUBYFORGE_USERNAME.replace @config["username"]
29
+ end
30
+
31
+
32
+ REV = nil
33
+ # UNCOMMENT IF REQUIRED:
34
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
35
+ VERS = Mirrored::VERSION::STRING + (REV ? ".#{REV}" : "")
36
+ RDOC_OPTS = ['--quiet', '--title', 'mirrored documentation',
37
+ "--opname", "index.html",
38
+ "--line-numbers",
39
+ "--main", "README",
40
+ "--inline-source"]
41
+
42
+ class Hoe
43
+ def extra_deps
44
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
45
+ @extra_deps
46
+ end
47
+ end
48
+
49
+ # Generate all the Rake tasks
50
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
51
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
52
+ p.author = AUTHOR
53
+ p.description = DESCRIPTION
54
+ p.email = EMAIL
55
+ p.summary = DESCRIPTION
56
+ p.url = HOMEPATH
57
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
58
+ p.test_globs = ["test/**/test_*.rb"]
59
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
60
+
61
+ # == Optional
62
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\\n\\n")
63
+ p.extra_deps = [['hpricot', '0.5']] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
64
+
65
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
66
+
67
+ end
68
+
69
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
70
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
71
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
16
+
17
+ require 'mirrored'
@@ -0,0 +1,50 @@
1
+ module Mirrored
2
+
3
+ class InvalidService < Exception; end
4
+ class ConnectionNotSet < Exception; end
5
+
6
+ API_URL = {:delicious => 'https://api.del.icio.us/v1/', :magnolia => 'https://ma.gnolia.com/api/mirrord/v1/'}
7
+
8
+ class Base
9
+ class << self
10
+ # Sets up the login information for either magnolia or delicious.
11
+ #
12
+ # Usage:
13
+ # Mirrored::Base.establish_connection(:delicious, 'jnunemaker', 'password')
14
+ # Mirrored::Base.establish_connection(:magnolia, 'jnunemaker', 'password')
15
+ def establish_connection(s, u, p)
16
+ remove_connection
17
+ raise InvalidService unless valid_service?(s)
18
+ @@service = s
19
+ @@connection = Connection.new(api_url, :username => u, :password => p)
20
+ end
21
+
22
+ # Removes the current connection information
23
+ def remove_connection
24
+ @@service, @@connection = nil, nil
25
+ end
26
+
27
+ def valid_service?(s) #:nodoc:
28
+ s = s.nil? ? '' : s
29
+ API_URL.keys.include?(s)
30
+ end
31
+
32
+ def api_url
33
+ API_URL[@@service]
34
+ end
35
+
36
+ def service
37
+ @@service
38
+ end
39
+
40
+ def connection
41
+ @@connection
42
+ end
43
+ end
44
+
45
+ private
46
+ def ensure_connection_set #:nodoc:
47
+ raise ConnectionNotSet if self.class.connection.nil? || self.class.service.nil?
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,39 @@
1
+ require 'net/https'
2
+
3
+ module Mirrored
4
+ class Connection
5
+ def initialize(base_url, args = {})
6
+ @base_url = base_url
7
+ @username = args[:username]
8
+ @password = args[:password]
9
+ end
10
+
11
+ def get(resource, args = nil)
12
+ request(resource, "get", args)
13
+ end
14
+
15
+ def post(resource, args = nil)
16
+ request(resource, "post", args)
17
+ end
18
+
19
+ def request(resource, method = "get", args = nil)
20
+ url = URI.join(@base_url, resource)
21
+ url.query = args.map { |k,v| "%s=%s" % [URI.encode(k.to_s), URI.encode(v.to_s)] }.join("&") if args
22
+
23
+ case method
24
+ when "get"
25
+ req = Net::HTTP::Get.new(url.request_uri)
26
+ when "post"
27
+ req = Net::HTTP::Post.new(url.request_uri)
28
+ end
29
+
30
+ req.basic_auth(@username, @password) if @username && @password
31
+
32
+ http = Net::HTTP.new(url.host, url.port)
33
+ http.use_ssl = (url.port == 443)
34
+
35
+ res = http.start() { |conn| conn.request(req) }
36
+ res.body
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,28 @@
1
+ module Mirrored
2
+ class Date < Base
3
+ attr_accessor :count, :date
4
+
5
+ class << self
6
+ def new_from_xml(xml) #:nodoc:
7
+ d = Date.new
8
+ d.date = ::Date.parse((xml)['date'])
9
+ d.count = (xml)['count']
10
+ d
11
+ end
12
+
13
+ # Does all the hard work finding the dates you have posted and how many posts on those days.
14
+ #
15
+ # Usage:
16
+ # Mirrored::Date.find(:all) # => finds all dates you have posted with counts for each day
17
+ # Mirrored::Date.find(:all, :tag => 'ruby') # => finds all dates you have posted something tagged ruby with counts for each day
18
+ def find(*args)
19
+ raise ArgumentError, "First argument must be symbol (:all)" unless args.first.kind_of?(Symbol)
20
+ params = args.extract_options!
21
+ params = params == {} ? nil : params
22
+
23
+ doc = Hpricot::XML(connection.get("posts/dates", params))
24
+ (doc/:date).inject([]) { |elements, el| elements << new_from_xml(el); elements }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,120 @@
1
+ module Mirrored
2
+ class Post < Base
3
+ attr_accessor :href, :description, :extended, :hash, :others, :tags, :time, :code, :share
4
+
5
+ alias :url :href
6
+ alias :url= :href=
7
+ alias :dt :time
8
+ alias :dt= :time=
9
+
10
+ class << self
11
+ # Creates a new post from an hpricot post instance.
12
+ def new_from_xml(xml) #:nodoc:
13
+ p = Post.new
14
+ p.href = (xml)['href']
15
+ p.description = (xml)['description']
16
+ p.extended = (xml)['extended']
17
+ p.hash = (xml)['hash']
18
+ p.others = (xml)['others']
19
+ p.tags = (xml)['tag']
20
+ p.time = Time.parse((xml)['time'])
21
+ p
22
+ end
23
+
24
+ # Does all the hard work finding your posts by various filters and such.
25
+ #
26
+ # Usage:
27
+ #
28
+ # Valid hash options for :get are :tag, :dt and :url. All are optional and can be used in any combination.
29
+ # Mirrored::Post.find(:get) # => posts
30
+ # Mirrored::Post.find(:get, :tag => 'ruby') # => posts tagged ruby
31
+ # Mirrored::Post.find(:get, :tag => 'ruby', :dt => '2007-10-05')# => posts tagged ruby on oct. 5, 2007
32
+ # Mirrored::Post.find(:get, :url => 'http://addictedtonew') # => posts with url http://addictedtonew
33
+ #
34
+ # Valid hash option for :all is :tag. All are optional and can be used in any combination.
35
+ # Use sparingly according to magnolia and delicious.
36
+ # Mirrored::Post.find(:all) # => all posts
37
+ # Mirrored::Post.find(:all, :tag => 'ruby') # => all posts for tag ruby
38
+ #
39
+ # Valid hash options for :recent are :tag and :count. All are optional and can be used in any combination.
40
+ # Mirrored::Post.find(:recent) # most recent posts
41
+ # Mirrored::Post.find(:recent, :tag => 'ruby') # => most recent posts for tag ruby
42
+ # Mirrored::Post.find(:recent, :tag => 'ruby', :count => '5') # => 5 most recent posts for tag ruby
43
+ def find(*args)
44
+ raise ArgumentError, "First argument must be symbol (:all or :recent)" unless args.first.kind_of?(Symbol)
45
+ params = args.extract_options!
46
+ params = params == {} ? nil : params
47
+
48
+ filter = case args.first
49
+ when :recent
50
+ 'recent'
51
+ when :all
52
+ 'all'
53
+ else
54
+ 'get'
55
+ end
56
+
57
+ doc = Hpricot::XML(connection.get("posts/#{filter}", params))
58
+ (doc/:post).inject([]) { |elements, el| elements << Post.new_from_xml(el); elements }
59
+ end
60
+
61
+ # Destroys a post by url.
62
+ #
63
+ # Usage:
64
+ # Mirrored::Post.destroy('http://microsoft.com')
65
+ # Mirrored::Post.delete('http://microsoft.com') # => aliased version
66
+ def destroy(url)
67
+ doc = Hpricot::XML(connection.get('posts/delete', :url => url))
68
+ code = doc.at('result')['code']
69
+ code == 'done' ? true : false
70
+ end
71
+ alias :delete :destroy
72
+ end
73
+
74
+ # Saves a post to delicious or magnolia.
75
+ #
76
+ # Usage:
77
+ # p = Mirrored::Post.new
78
+ # p.url = 'http://addictedtonew.com'
79
+ # p.description = 'Addicted to New by John Nuneamker'
80
+ # p.extended = 'Really cool dude'
81
+ # p.tags = %w(cool dude ruby)
82
+ # p.save # => do not replace if post already exists
83
+ # p.save(true) # => replace post if it already exists
84
+ def save(replace=false)
85
+ attrs = to_h.merge((replace) ? {:replace => 'yes'} : {:replace => 'no'})
86
+ puts attrs.inspect
87
+ doc = Hpricot::XML(self.class.connection.get('posts/add', attrs))
88
+ @code = doc.at('result')['code']
89
+ (@code == 'done') ? true : false
90
+ end
91
+
92
+ def to_h #:nodoc:
93
+ {
94
+ :url => url,
95
+ :description => description,
96
+ :extended => extended,
97
+ :tags => (tags ? tags.map { |t| t.gsub(' ', '_') }.join(' ') : ''),
98
+ :share => share
99
+ }
100
+ end
101
+
102
+ # Defaults share to yes
103
+ def share
104
+ @share ? @share : 'yes'
105
+ end
106
+
107
+ # Makes it so that tags= can take a string or array.
108
+ #
109
+ # Usage:
110
+ # Mirrored::Post.new.tags = %w(ruby rails sweet) # => %w(ruby rails sweet)
111
+ # Mirrored::Post.new.tags = 'ruby rails sweet' # => %w(ruby rails sweet)
112
+ def tags=(string_or_array)
113
+ if string_or_array.kind_of?(Array)
114
+ @tags = string_or_array
115
+ else
116
+ @tags = string_or_array.split(' ').map { |t| t.gsub('_', ' ') }
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,34 @@
1
+ module Mirrored
2
+ class Tag < Base
3
+ attr_accessor :count, :name
4
+
5
+ class << self
6
+ def new_from_xml(xml) #:nodoc:
7
+ t = Tag.new
8
+ t.count = (xml)['count']
9
+ t.name = (xml)['tag']
10
+ t
11
+ end
12
+
13
+ # Does all the hard work finding your tags and how much you've used them.
14
+ #
15
+ # Usage:
16
+ # Mirrored::Tag.find(:get)
17
+ def find(*args)
18
+ raise ArgumentError, "Only takes a symbol as the argument (:get, :all)" unless args.first.kind_of?(Symbol)
19
+ doc = Hpricot::XML(connection.get('tags/get'))
20
+ (doc/:tag).inject([]) { |elements, el| elements << Tag.new_from_xml(el); elements }
21
+ end
22
+
23
+ # Renames a tag from the old name to a new one.
24
+ #
25
+ # Usage:
26
+ # Mirrored::Tag.rename('microsoft', 'suckfest')
27
+ def rename(old_name, new_name)
28
+ doc = Hpricot::XML(connection.get('tags/rename', :old => old_name, :new => new_name))
29
+ result = (doc).at(:result)
30
+ (result && result.inner_html == 'done') ? true : false
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ module Mirrored
2
+ class Update < Base
3
+ class << self
4
+ # Returns a ruby time object that is equal to the last time you posted something. If you haven't posted anything it returns an empty string (probably).
5
+ #
6
+ # Usage:
7
+ # Mirrored::Update.last # => a ruby time object in UTC
8
+ def last
9
+ result = connection.get('posts/update')
10
+ doc = Hpricot::XML(result)
11
+ update = doc.at('update')
12
+ update ? Time.parse(update['time']) : ''
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ module Mirrored #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/mirrored.rb ADDED
@@ -0,0 +1,25 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ class Array
4
+ # Extract options from a set of arguments. Removes and returns the last element in the array if it's a hash, otherwise returns a blank hash.
5
+ #
6
+ # def options(*args)
7
+ # args.extract_options!
8
+ # end
9
+ #
10
+ # options(1, 2) # => {}
11
+ # options(1, 2, :a => :b) # => {:a=>:b}
12
+ def extract_options!
13
+ last.is_a?(::Hash) ? pop : {}
14
+ end
15
+ end
16
+
17
+ require 'rubygems'
18
+ require 'hpricot'
19
+
20
+ require 'mirrored/base'
21
+ require 'mirrored/connection'
22
+ require 'mirrored/date'
23
+ require 'mirrored/post'
24
+ require 'mirrored/tag'
25
+ require 'mirrored/update'
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]
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]
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/mirrored/version.rb'
15
+
16
+ version = Mirrored::VERSION::STRING
17
+ download = 'http://rubyforge.org/projects/mirrored'
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)