ruby_slippers 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.rvmrc +2 -0
  2. data/Gemfile +16 -0
  3. data/Gemfile.lock +45 -0
  4. data/Guardfile +16 -0
  5. data/LICENSE +20 -0
  6. data/README.md +122 -0
  7. data/Rakefile +5 -0
  8. data/TODO +32 -0
  9. data/VERSION +1 -0
  10. data/lib/ext/ext.rb +164 -0
  11. data/lib/ruby_slippers.rb +28 -0
  12. data/lib/ruby_slippers/app.rb +47 -0
  13. data/lib/ruby_slippers/archives.rb +22 -0
  14. data/lib/ruby_slippers/article.rb +104 -0
  15. data/lib/ruby_slippers/config.rb +37 -0
  16. data/lib/ruby_slippers/context.rb +38 -0
  17. data/lib/ruby_slippers/engine.rb +21 -0
  18. data/lib/ruby_slippers/repo.rb +21 -0
  19. data/lib/ruby_slippers/site.rb +115 -0
  20. data/lib/ruby_slippers/template.rb +28 -0
  21. data/lib/tasks/gemspec.rake +24 -0
  22. data/lib/tasks/test.rake +17 -0
  23. data/test/fixtures/articles/2010-05-17-the-wonderful-wizard-of-oz.txt +6 -0
  24. data/test/fixtures/articles/2010-05-18-the-marvelous-land-of-oz.txt +7 -0
  25. data/test/fixtures/articles/2010-05-20-dorothy-and-the-wizard-of-oz.txt +6 -0
  26. data/test/fixtures/articles/2011-05-18-ozma-of-oz.txt +10 -0
  27. data/test/fixtures/images/ozma.png +0 -0
  28. data/test/fixtures/pages/about.html.erb +1 -0
  29. data/test/fixtures/pages/archives.html.erb +23 -0
  30. data/test/fixtures/pages/article.html.erb +28 -0
  31. data/test/fixtures/pages/index.html.erb +27 -0
  32. data/test/fixtures/pages/sitemap.html.erb +0 -0
  33. data/test/fixtures/pages/tagged.html.erb +9 -0
  34. data/test/fixtures/templates/index.builder +21 -0
  35. data/test/fixtures/templates/layout.html.erb +4 -0
  36. data/test/fixtures/templates/repo.html.erb +1 -0
  37. data/test/fixtures/templates/sitemap.builder +25 -0
  38. data/test/integration/about_test.rb +23 -0
  39. data/test/integration/archives_test.rb +34 -0
  40. data/test/integration/articles_test.rb +56 -0
  41. data/test/integration/atom_test.rb +24 -0
  42. data/test/integration/invalid_route_test.rb +31 -0
  43. data/test/integration/tags_test.rb +24 -0
  44. data/test/support/test_helper.rb +47 -0
  45. data/test/unit/app_test.rb +14 -0
  46. data/test/unit/archives_test.rb +10 -0
  47. data/test/unit/article_test.rb +145 -0
  48. data/test/unit/date_patch_test.rb +10 -0
  49. data/test/unit/engine_test.rb +71 -0
  50. data/test/unit/site_test.rb +52 -0
  51. metadata +270 -0
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ rvm_gemset_create_on_use_flag=1
2
+ rvm use 1.9.2@slippers
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rake'
4
+ gem 'rack'
5
+ gem 'hpricot'
6
+ gem 'builder'
7
+ gem 'rdiscount'
8
+ gem 'maruku'
9
+
10
+ group :test, :development do
11
+ gem 'guard'
12
+ gem 'guard-bundler'
13
+ gem 'guard-test'
14
+ gem 'riot'
15
+ gem 'jeweler'
16
+ end
@@ -0,0 +1,45 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ builder (3.0.0)
5
+ git (1.2.5)
6
+ guard (0.4.2)
7
+ thor (~> 0.14.6)
8
+ guard-bundler (0.1.3)
9
+ bundler (>= 1.0.0)
10
+ guard (>= 0.2.2)
11
+ guard-test (0.3.0)
12
+ guard (>= 0.2.2)
13
+ test-unit (~> 2.2)
14
+ hpricot (0.8.4)
15
+ jeweler (1.6.2)
16
+ bundler (~> 1.0)
17
+ git (>= 1.2.5)
18
+ rake
19
+ maruku (0.6.0)
20
+ syntax (>= 1.0.0)
21
+ rack (1.3.0)
22
+ rake (0.9.2)
23
+ rdiscount (1.6.8)
24
+ riot (0.12.4)
25
+ rr
26
+ rr (1.0.2)
27
+ syntax (1.0.0)
28
+ test-unit (2.3.0)
29
+ thor (0.14.6)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ builder
36
+ guard
37
+ guard-bundler
38
+ guard-test
39
+ hpricot
40
+ jeweler
41
+ maruku
42
+ rack
43
+ rake
44
+ rdiscount
45
+ riot
@@ -0,0 +1,16 @@
1
+ guard 'bundler' do
2
+ watch('Gemfile')
3
+ watch('Gemfile.lock')
4
+ end
5
+
6
+ guard 'test' do
7
+ watch(%r{^test/fixtures/articles/(.+)\.txt$})
8
+ watch(%r{^test/fixtures/templates/(.+)$})
9
+ watch(%r{^test/fixtures/pages/(.+)$})
10
+
11
+ watch(%r{^lib/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
12
+ watch(%r{^lib/ruby_slippers/(.+)\.rb$}) { |m| ["test/unit/#{m[1]}_test.rb", "test/integration/#{m[1]}_test.rb"] }
13
+ watch(%r{^test/integration/.+_test\.rb$})
14
+ watch(%r{^test/unit/.+_test\.rb$})
15
+ watch(%r{^test/support/(.+)\.rb$}) { "test" }
16
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 dreamr
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.
@@ -0,0 +1,122 @@
1
+ README
2
+ ======
3
+
4
+ RubySlippers, the smartest blogging engine in all of Oz!
5
+ --------------------------------------------------------
6
+
7
+ [website](http://ruby-slippers.heroku.com)
8
+
9
+ Introduction
10
+ ------------
11
+
12
+ ### To set up a new blog
13
+
14
+ $ git clone git://github.com/dreamr/ruby-slippers.git myblog
15
+ $ cd myblog
16
+ $ gem install bundler
17
+ $ bundle
18
+ $ rake install
19
+ $ -> Blog name: My new blog
20
+ $ Installing your blog to my-new-blog
21
+ $ Blog installed!
22
+
23
+
24
+ ### To create an article
25
+
26
+ $ rake new
27
+ $ -> Title: My new blog post!
28
+ $ Creating and opening my-new-blog-post (opens in your text editor!)
29
+ $ rake publish (commits, pushes, publishes then opens in your browser!)
30
+
31
+
32
+ Philosophy
33
+ ----------
34
+
35
+ RubySlippers is based on [Toto](http://github.com/cloudhead/toto) and aims to achieve their goals as well as our own. Hosting a ruby based free blog shouldn't be hard. We want to take that a step further and say it should be easy. Easy as pie. Easy as my best friend's Mom. Easy as a 1 click installer.
36
+
37
+ Oh yeah, MRI, bytecode whatever. If it is Ruby, it should run.
38
+
39
+ How it works
40
+ ------------
41
+
42
+ - Article management is done with a text editor and git
43
+ * stored as _.txt_ files, with embeded metadata (in yaml format).
44
+ * processed through a markdown converter (rdiscount) by default.
45
+ * can have tags
46
+ * can have images
47
+ * can be browsed by date, or tags
48
+ * comments are handled by [disqus](http://disqus.com)
49
+ - built for easy use with _ERB_.
50
+ - built right on top of _Rack_.
51
+ - built to take advantage of _HTTP caching_.
52
+ - built with _heroku_ in mind.
53
+
54
+
55
+ RubySlippers comes with a basic default theme for you to mangle. I hope to release more themes shortly and will accept your submitted themes.
56
+
57
+ Deployment
58
+ ==========
59
+
60
+ #### On heroku
61
+
62
+ $ git add .
63
+ $ git commit -m 'updated blog'
64
+ $ git push heroku
65
+
66
+ #### On your own server or locally
67
+
68
+ Once you have created the remote git repo, and pushed your changes to it, you can run RubySlippers with any Rack compliant web server, such as **thin**, **mongrel** or **unicorn**.
69
+
70
+ I like to use shotgun as it reloads while I work
71
+
72
+ $ cd myblog
73
+ $ bundle
74
+ $ shotgun
75
+
76
+ With thin, you would do something like:
77
+
78
+ $ thin start -R config.ru
79
+
80
+ With unicorn, you can just do:
81
+
82
+ $ unicorn
83
+
84
+
85
+ ### Configuration
86
+
87
+ You can configure ruby-slippers, by modifying the _config.ru_ file. For example, if you want to set the blog author to 'John Galt',
88
+ you could add `set :author, 'John Galt'` inside the `RubySlippers::Engine::App.new` block. Here are the defaults, to get you started:
89
+
90
+ #
91
+ # Add your settings here
92
+ # set [:setting], [value]
93
+ #
94
+ set :author, "Dreamr" # blog author
95
+ set :title, "RubySlippers, the smartest blog engine in all of Oz!" # site title
96
+ # set :root, "index" # page to load on /
97
+ set :date, lambda {|now| now.strftime("%m/%d/%Y") } # date format for articles
98
+ # set :markdown, :smart # use markdown + smart-mode
99
+ # set :disqus, false # disqus id, or false
100
+ set :summary, :max => 300, :delim => /~/ # length of article summary and delimiter
101
+ # set :ext, 'txt' # file extension for articles
102
+ # set :cache, 28800 # cache duration, in seconds
103
+ set :tag_separator, ', ' # tag separator for articles
104
+ set :date, lambda {|now| now.strftime("%B #{now.day.ordinal} %Y") }
105
+ # set this to your local port. I use shotgun, so 9393.
106
+ set :url, "http://localhost:9393" if ENV['RACK_ENV'] == 'development'
107
+
108
+ # to use haml, add the gem to your Gemfile and bundle, then uncomment this
109
+ # and redo your templates using haml and renamed to html.haml
110
+ # set :to_html, lambda { |path, page, binding|
111
+ # Haml::Engine.new(File.read("#{path}/#{page}.html.haml"),
112
+ # :attr_wrapper => '"',
113
+ # :filename => path ).render(binding)
114
+ # }
115
+
116
+ Thanks
117
+ ------
118
+
119
+ * To heroku for making this easy as pie.
120
+ * To the developers of [Toto](http://github.com/cloudhead/toto), for making such an awesome minimal blog engine in Ruby.
121
+
122
+ Copyright (c) 2011 dreamr. See LICENSE for details.
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ Dir.glob('lib/tasks/*.rake').each { |file| import file }
4
+
5
+ task :default => 'test:all'
data/TODO ADDED
@@ -0,0 +1,32 @@
1
+ TODO
2
+ ====
3
+
4
+ Site features
5
+ -------------
6
+ Contact form (email)
7
+ * reverse captcha
8
+ * must be free service
9
+
10
+
11
+ Blog features
12
+ -----------------------------
13
+
14
+ * Most popular posts (hits)
15
+ * Most commented posts (comments)
16
+ * Related posts (by tag)
17
+
18
+
19
+
20
+ Full stack test/deployment scheme
21
+ -------------------------------------------
22
+
23
+ * Engine
24
+ [*] Unit Tests
25
+ [*] Integration Tests
26
+
27
+ * Staged (deployed)
28
+ [*] Integration Tests
29
+ [ ] Client Rake Tests
30
+
31
+
32
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,164 @@
1
+ require 'enumerator'
2
+
3
+ class Array
4
+ # Splits or iterates over the array in groups of size +number+,
5
+ # padding any remaining slots with +fill_with+ unless it is +false+.
6
+ #
7
+ # %w(1 2 3 4 5 6 7).in_groups_of(3) {|group| p group}
8
+ # ["1", "2", "3"]
9
+ # ["4", "5", "6"]
10
+ # ["7", nil, nil]
11
+ #
12
+ # %w(1 2 3).in_groups_of(2, ' ') {|group| p group}
13
+ # ["1", "2"]
14
+ # ["3", " "]
15
+ #
16
+ # %w(1 2 3).in_groups_of(2, false) {|group| p group}
17
+ # ["1", "2"]
18
+ # ["3"]
19
+ def in_groups_of(number, fill_with = nil)
20
+ if fill_with == false
21
+ collection = self
22
+ else
23
+ # size % number gives how many extra we have;
24
+ # subtracting from number gives how many to add;
25
+ # modulo number ensures we don't add group of just fill.
26
+ padding = (number - size % number) % number
27
+ collection = dup.concat([fill_with] * padding)
28
+ end
29
+
30
+ if block_given?
31
+ collection.each_slice(number) { |slice| yield(slice) }
32
+ else
33
+ groups = []
34
+ collection.each_slice(number) { |group| groups << group }
35
+ groups
36
+ end
37
+ end
38
+
39
+ # Splits or iterates over the array in +number+ of groups, padding any
40
+ # remaining slots with +fill_with+ unless it is +false+.
41
+ #
42
+ # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
43
+ # ["1", "2", "3", "4"]
44
+ # ["5", "6", "7", nil]
45
+ # ["8", "9", "10", nil]
46
+ #
47
+ # %w(1 2 3 4 5 6 7).in_groups(3, '&nbsp;') {|group| p group}
48
+ # ["1", "2", "3"]
49
+ # ["4", "5", "&nbsp;"]
50
+ # ["6", "7", "&nbsp;"]
51
+ #
52
+ # %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
53
+ # ["1", "2", "3"]
54
+ # ["4", "5"]
55
+ # ["6", "7"]
56
+ def in_groups(number, fill_with = nil)
57
+ # size / number gives minor group size;
58
+ # size % number gives how many objects need extra accommodation;
59
+ # each group hold either division or division + 1 items.
60
+ division = size / number
61
+ modulo = size % number
62
+
63
+ # create a new array avoiding dup
64
+ groups = []
65
+ start = 0
66
+
67
+ number.times do |index|
68
+ length = division + (modulo > 0 && modulo > index ? 1 : 0)
69
+ padding = fill_with != false &&
70
+ modulo > 0 && length == division ? 1 : 0
71
+ groups << slice(start, length).concat([fill_with] * padding)
72
+ start += length
73
+ end
74
+
75
+ if block_given?
76
+ groups.each { |g| yield(g) }
77
+ else
78
+ groups
79
+ end
80
+ end
81
+
82
+ # Divides the array into one or more subarrays based on a delimiting +value+
83
+ # or the result of an optional block.
84
+ #
85
+ # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
86
+ # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
87
+ def split(value = nil)
88
+ using_block = block_given?
89
+
90
+ inject([[]]) do |results, element|
91
+ if (using_block && yield(element)) || (value == element)
92
+ results << []
93
+ else
94
+ results.last << element
95
+ end
96
+
97
+ results
98
+ end
99
+ end
100
+ end
101
+
102
+ class Object
103
+ def meta_def name, &blk
104
+ (class << self; self; end).instance_eval do
105
+ define_method(name, &blk)
106
+ end
107
+ end
108
+ end
109
+
110
+ class String
111
+ def slugize
112
+ self.downcase.gsub(/&/, 'and').gsub(/\s+/, '-').gsub(/[^a-z0-9-]/, '')
113
+ end
114
+
115
+ def humanize
116
+ self.capitalize.gsub(/[-_]+/, ' ')
117
+ end
118
+ end
119
+
120
+ class Fixnum
121
+ def ordinal
122
+ # 1 => 1st
123
+ # 2 => 2nd
124
+ # 3 => 3rd
125
+ # ...
126
+ case self % 100
127
+ when 11..13; "#{self}th"
128
+ else
129
+ case self % 10
130
+ when 1; "#{self}st"
131
+ when 2; "#{self}nd"
132
+ when 3; "#{self}rd"
133
+ else "#{self}th"
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ class Object
140
+ def blank?
141
+ self.nil? || self == ""
142
+ end
143
+ end
144
+
145
+ class Date
146
+ # This check is for people running RubySlippers::Enginewith ActiveSupport, avoid a collision
147
+ unless respond_to? :iso8601
148
+ # Return the date as a String formatted according to ISO 8601.
149
+ def iso8601
150
+ ::Time.utc(year, month, day, 0, 0, 0, 0).iso8601
151
+ end
152
+ end
153
+ end
154
+
155
+ module Kernel
156
+ def silence_stream(stream)
157
+ old_stream = stream.dup
158
+ stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
159
+ stream.sync = true
160
+ yield
161
+ ensure
162
+ stream.reopen(old_stream)
163
+ end
164
+ end
@@ -0,0 +1,28 @@
1
+ require 'yaml'
2
+ require 'date'
3
+ require 'erb'
4
+ require 'rack'
5
+ require 'digest'
6
+ require 'open-uri'
7
+
8
+ if RUBY_PLATFORM =~ /win32/
9
+ require 'maruku'
10
+ Markdown = Maruku
11
+ else
12
+ require 'rdiscount'
13
+ end
14
+
15
+ require 'builder'
16
+
17
+ $:.unshift File.dirname(__FILE__)
18
+
19
+ require 'ext/ext'
20
+ require 'ruby_slippers/app'
21
+ require 'ruby_slippers/site'
22
+ require 'ruby_slippers/engine'
23
+ require 'ruby_slippers/config'
24
+ require 'ruby_slippers/template'
25
+ require 'ruby_slippers/context'
26
+ require 'ruby_slippers/article'
27
+ require 'ruby_slippers/archives'
28
+ require 'ruby_slippers/repo'