village 2.1.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 (49) hide show
  1. data/Gemfile +29 -0
  2. data/Guardfile +9 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +255 -0
  5. data/Rakefile +29 -0
  6. data/app/controllers/village/articles_controller.rb +27 -0
  7. data/app/controllers/village/pages_controller.rb +16 -0
  8. data/app/views/layouts/village.html.haml +22 -0
  9. data/app/views/village/articles/_addthis.html.haml +8 -0
  10. data/app/views/village/articles/_analytics.html.haml +13 -0
  11. data/app/views/village/articles/_article.html.haml +25 -0
  12. data/app/views/village/articles/_comments.html.haml +8 -0
  13. data/app/views/village/articles/_feed_link.html.haml +2 -0
  14. data/app/views/village/articles/_sidebar.html.haml +13 -0
  15. data/app/views/village/articles/index.atom.builder +26 -0
  16. data/app/views/village/articles/index.html.haml +11 -0
  17. data/app/views/village/articles/index.rss.builder +31 -0
  18. data/app/views/village/articles/show.html.haml +9 -0
  19. data/app/views/village/pages/show.html.haml +1 -0
  20. data/lib/generators/base.rb +178 -0
  21. data/lib/generators/village/articles/USAGE +6 -0
  22. data/lib/generators/village/articles/articles_generator.rb +50 -0
  23. data/lib/generators/village/setup/USAGE +5 -0
  24. data/lib/generators/village/setup/setup_generator.rb +67 -0
  25. data/lib/generators/village/setup/templates/2001-01-01-example-article.markdown +8 -0
  26. data/lib/generators/village/setup/templates/example-page.markdown +10 -0
  27. data/lib/generators/village/setup/templates/views/articles/_addthis.html.haml +8 -0
  28. data/lib/generators/village/setup/templates/views/articles/_analytics.html.haml +13 -0
  29. data/lib/generators/village/setup/templates/views/articles/_article.html.haml +25 -0
  30. data/lib/generators/village/setup/templates/views/articles/_comments.html.haml +8 -0
  31. data/lib/generators/village/setup/templates/views/articles/_feed_link.html.haml +2 -0
  32. data/lib/generators/village/setup/templates/views/articles/_sidebar.html.haml +13 -0
  33. data/lib/generators/village/setup/templates/views/articles/index.atom.builder +26 -0
  34. data/lib/generators/village/setup/templates/views/articles/index.html.haml +11 -0
  35. data/lib/generators/village/setup/templates/views/articles/index.rss.builder +31 -0
  36. data/lib/generators/village/setup/templates/views/articles/show.html.haml +9 -0
  37. data/lib/generators/village/setup/templates/views/pages/show.html.haml +1 -0
  38. data/lib/generators/village/setup/templates/views/village.css +72 -0
  39. data/lib/generators/village/setup/templates/views/village.html.haml +22 -0
  40. data/lib/generators/village/setup/templates/village_config.yml +81 -0
  41. data/lib/village.rb +4 -0
  42. data/lib/village/article.rb +125 -0
  43. data/lib/village/attributes.rb +21 -0
  44. data/lib/village/config.rb +53 -0
  45. data/lib/village/engine.rb +11 -0
  46. data/lib/village/file_model.rb +83 -0
  47. data/lib/village/page.rb +10 -0
  48. data/lib/village/routes.rb +16 -0
  49. metadata +158 -0
@@ -0,0 +1,8 @@
1
+ / AddThis Button BEGIN
2
+ .addthis_toolbox.addthis_default_style
3
+ %a.addthis_button_facebook_like{"fb:like:layout" => "button_count"}
4
+ %a.addthis_button_tweet
5
+ %a.addthis_button_google_plusone{"g:plusone:size" => "medium"}
6
+ %a.addthis_counter.addthis_pill_style
7
+ %script{:src => "http://s7.addthis.com/js/250/addthis_widget.js#pubid=ra-4eade9351b25746b", :type => "text/javascript"}
8
+ / AddThis Button END
@@ -0,0 +1,13 @@
1
+ - content_for :head do
2
+ - if Village::Config.google_analytics_code?
3
+ :plain
4
+ <script type="text/javascript">
5
+ var _gaq = _gaq || [];
6
+ _gaq.push(['_setAccount', '#{Village::Config.google_analytics_code}']);
7
+ _gaq.push(['_trackPageview']);
8
+ (function() {
9
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
10
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
11
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
12
+ })();
13
+ </script>
@@ -0,0 +1,25 @@
1
+ = content_tag_for :article, article do
2
+ %header
3
+ %h1= link_to article.title, article
4
+ = image_tag article.gravatar_url(:size => 32, :default => 'identicon'), :height => 32, :width => 32
5
+ .meta
6
+ %time{:pubdate => 'true', :datetime => article.timestamp.iso8601}
7
+ Posted on #{article.date.strftime '%e %B %Y'}
8
+ %span
9
+ by
10
+ = article.author[:name]
11
+ %p
12
+ - if article.categories?
13
+ Categories on:
14
+ - article.categories.each do |category|
15
+ = link_to category, village_articles_path(:category => category)
16
+ %p
17
+ - if article.tags?
18
+ Tags on:
19
+ - article.tags.each do |tag|
20
+ = link_to tag, village_articles_path(:tag => tag)
21
+ - if summary
22
+ = article.summary_html
23
+ %p.more= link_to 'Read more&hellip;'.html_safe, article
24
+ - else
25
+ ~ article.content_html
@@ -0,0 +1,8 @@
1
+ - if Village::Config.disqus_short_name?
2
+ #disqus_thread
3
+ - if ENV['RAILS_ENV'] == :development
4
+ :javascript
5
+ var disqus_developer = true;
6
+ %script{:type => 'text/javascript', :src => "http://#{Village::Config.disqus_short_name}.disqus.com/embed.js", :async => true}
7
+ %noscript
8
+ %a(href="http://#{Village::Config.disqus_short_name}.disqus.com/embed.js?url=ref") View comments.
@@ -0,0 +1,2 @@
1
+ - content_for :head do
2
+ %link{:href => village_articles_path(:format => :atom), :type => 'application/rss+xml', :rel => :alternate, :title => Village::Config.title}
@@ -0,0 +1,13 @@
1
+ %h4
2
+ Categories
3
+ - unless Village::Article.categories.nil?
4
+ %ul
5
+ - Village::Article.categories.each do |category|
6
+ %li= link_to "#{category[0]} (#{category[1]})", village_articles_path(:category => category[0])
7
+
8
+ %h4
9
+ Tags
10
+ - unless Village::Article.tags.nil?
11
+ %ul
12
+ - Village::Article.tags.each do |tag|
13
+ %li= link_to "#{tag[0]} (#{tag[1]})", village_articles_path(:tag => tag[0])
@@ -0,0 +1,26 @@
1
+ xml.instruct!
2
+ xml.feed :xmlns => 'http://www.w3.org/2005/Atom' do
3
+ xml.title Village::Config.title
4
+ xml.link village_articles_url(:format => :atom)
5
+ xml.id village_articles_url
6
+ xml.updated Village::Article.feed_last_modified.xmlschema
7
+
8
+ Village::Article.feed.each do |article|
9
+ xml.entry do
10
+ xml.title article.title, :type => :text
11
+ xml.link :href => village_article_url(article), :rel => :alternate, :type => 'text/html'
12
+ xml.published article.timestamp.xmlschema
13
+ xml.updated article.last_modified.xmlschema
14
+ if article.author?
15
+ xml.author do
16
+ xml.name article.author[:name]
17
+ xml.email article.author[:email]
18
+ end
19
+ end
20
+ xml.id village_article_url(article)
21
+ xml.content :type => :html, 'xml:base' => village_article_url(article) do
22
+ xml.cdata! article.content_html
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ = render 'analytics'
2
+ = render 'feed_link'
3
+ %section#sidebar
4
+ = render 'sidebar'
5
+
6
+ %section#articles
7
+ - if collection.present?
8
+ = render :partial => 'article', :collection => collection, :locals => { :summary => true }
9
+ = paginate collection
10
+ - else
11
+ No articles found.
@@ -0,0 +1,31 @@
1
+ xml.instruct! :xml, :version => "1.0"
2
+ xml.rss :version => "2.0" do
3
+ xml.channel do
4
+ xml.title Village::Config.title
5
+ xml.description Village::Config.subtitle
6
+ xml.link village_articles_url(:format => :rss)
7
+ xml.generator village_articles_url(:format => :rss)
8
+ xml.lastBuildDate Village::Article.feed_last_modified
9
+
10
+ for article in Village::Article.feed
11
+ xml.item do
12
+ xml.title article.title
13
+ xml.description :type => :html, 'xml:base' => village_article_url(article) do
14
+ xml.cdata! article.summary_html
15
+ end
16
+ xml.pubDate article.timestamp.xmlschema
17
+ xml.link :href => village_article_url(article), :rel => :alternate, :type => 'text/html'
18
+ xml.guid village_article_url(article)
19
+ if article.author.present?
20
+ xml.author do
21
+ xml.name article.author[:name]
22
+ xml.email article.author[:email]
23
+ end
24
+ end
25
+ if article.tags?
26
+ xml.category article.tags.join(",")
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,9 @@
1
+ = render 'feed_link'
2
+ %section#sidebar
3
+ = render 'sidebar'
4
+
5
+ = render :partial => 'article', :object => resource, :locals => { :summary => false }
6
+
7
+ = render 'addthis'
8
+
9
+ = render 'comments'
@@ -0,0 +1,72 @@
1
+ /* -- Reset ----------------------------------------------------------------- */
2
+ body,div,dl,dt,dd,ul,ol,li,h1,h2,h4,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0}
3
+ table{border-collapse:collapse;border-spacing:0}
4
+ fieldset,img{border:0}
5
+ address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit}
6
+ del,ins{text-decoration:none}
7
+ li{list-style:none}
8
+ caption,th{text-align:left}
9
+ h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}
10
+ q:before,q:after{content:''}
11
+ abbr,acronym{border:0;font-variant:normal}
12
+ legend{color:#000}
13
+ input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit}
14
+ input,button,textarea,select{font-size:100%}
15
+ sup,sub{vertical-align:baseline}
16
+ header, sidebar, section, article, footer{display:block;}
17
+
18
+ /* -- Global ---------------------------------------------------------------- */
19
+ html{background:#F3F3F3;text-shadow:0px 1px 0px #fff;}
20
+ body{width:760px;margin:40px auto;font:14px/140% 'Helvetica Neue', Arial, Helvetica, Sans-Serif;}
21
+ a{color:#222;}
22
+
23
+ h1, h2, h3, h4, h5, h6{font-weight:bold;}
24
+ h1{font-size:26px;margin-bottom:5px;}
25
+ h1 a{text-decoration:none;}
26
+ h2{font-size:24px;padding-top:10px;margin:30px 0 20px;border-top:2px solid #ccc;}
27
+ h3{font-size:20px;margin-bottom:20px;}
28
+ h4{font-size:18px;margin-bottom:10px;}
29
+ h5, h6{font-size:16px;margin-bottom:5px;}
30
+
31
+ p{margin-bottom:20px;}
32
+ em{font-style:italic;}
33
+ sup{font-size:12px;vertical-align:super;}
34
+ sub{font-size:12px;vertical-align:sub;}
35
+ del{color:#666;font-style:line-through;}
36
+ abbr{border-bottom:1px dotted #bbb;color:#666;text-transform:uppercase;}
37
+ code{background:#eee;padding:2px 5px;font-family:Monospace;}
38
+ small{color:#999;font-size:12px;}
39
+ strong{font-weight:bold;}
40
+ blockquote{color:#666;font-size:16px;margin-left:20px;font-family:Georgia, Serif;}
41
+
42
+ /* code */
43
+ pre{background:#eee;margin-bottom:20px;padding:10px;border:1px solid #ccc;border-width:1px 0;}
44
+
45
+ /* lists */
46
+ ol, ul{margin:0 0 20px 20px;}
47
+ ol li{list-style:decimal inside;}
48
+ ul li{list-style:disc inside;}
49
+
50
+ /* -- Header ---------------------------------------------------------------- */
51
+ header#header {margin-bottom:40px;}
52
+ header#header h1 {font-size:42px;margin-bottom:20px;font-family:Georgia, Serif;font-weight:normal;}
53
+ header#header p {color:#999;}
54
+
55
+ /* -- Content --------------------------------------------------------------- */
56
+ section#content{}
57
+
58
+ section#content section article p{margin-bottom:10px;}
59
+
60
+ section#content article{margin-bottom:40px;}
61
+ section#content article header{overflow:hidden;}
62
+ section#content article header h1{}
63
+ section#content article header img{display:none;}
64
+ section#content article header .meta{color:#999;font-size:12px;}
65
+
66
+ /* -- Sidebar --------------------------------------------------------------- */
67
+ section#sidebar{position:absolute;top:10;right:0;margin-right:100px;}
68
+ section#sidebar ul{margin:0 0 3px 3px}
69
+ section#sidebar ul li{list-style:none inside;}
70
+
71
+ /* -- Footer ---------------------------------------------------------------- */
72
+ footer{margin-bottom:20px;}
@@ -0,0 +1,22 @@
1
+ !!! 5
2
+ %html
3
+ %head
4
+ %title= Village::Config.title
5
+ = stylesheet_link_tag 'village'
6
+ = yield :head
7
+ %body
8
+ %header#header
9
+ %h1= link_to Village::Config.title, '/'
10
+ %p
11
+ = Village::Config.subtitle
12
+ %section#content
13
+ = yield
14
+ %footer
15
+ %small
16
+ - if Village::Config.author?
17
+ A #{link_to 'village', Village::Config.author[:url]} blog
18
+ = "by #{Village::Config.author[:name]}"
19
+ = "Copyright &copy; #{Date.today.year}".html_safe
20
+ = link_to 'rss', village_articles_path(:rss)
21
+ |
22
+ = link_to 'atom', village_articles_path(:atom)
@@ -0,0 +1,81 @@
1
+ # title & subtitle
2
+ # Title and subheading for your site. Used on the layout page and
3
+ # will be overriden in every page titles.
4
+ #
5
+ title: Your Site
6
+ subtitle: (change this text in config/village.yml)
7
+
8
+ # author
9
+ # You should really specify your content's author when generating an
10
+ # Atom feed. Specify at least one of name, uri or email, and Village will
11
+ # include it in your feed. See the Atom spec for more info:
12
+ #
13
+ # http://www.atomenabled.org/developers/syndication/atom-format-spec.php#element.feed
14
+ #
15
+ author:
16
+ name: Your Name
17
+ uri: http://yourhomepage.com
18
+ email: your@email.com
19
+
20
+ # layout
21
+ # The name of the layout file to be used by Village specified in "app/views/layouts",
22
+ # village will look for every pages / articles layouts for every request.
23
+ #
24
+ layout: "village"
25
+
26
+ # page_size
27
+ # the page size / PER_PAGE used by Kaminari for paginate your articles,
28
+ # the default is 5
29
+ #
30
+ # page_size: 5
31
+
32
+ # disqus_short_name
33
+ # If you want to use the Disqus service (http://disqus.com) to display
34
+ # comments on your site, register a Disqus account and then specify your
35
+ # site's short name here. A comment form will automatically be added to
36
+ # the bottom of your pages.
37
+ #
38
+ # disqus_short_name: mysite
39
+
40
+ # google_analytics_code
41
+ # Set this if you want Google Analytics to track traffic on your site.
42
+ # Probably best not to set a default value, but to set it in production.
43
+ #
44
+ # The production settings are used if you're deploying to Heroku, so
45
+ # scroll down a bit to set it in production even if you're not deploying
46
+ # to your own server.
47
+ #
48
+ # google_analytics_code: "UA-???????-?"
49
+
50
+ # -------------------------------
51
+ # ======== DEFAULT CONFIGURATIONS
52
+ # -------------------------------
53
+ #
54
+ # permalink_format
55
+ # Set this if you want change the default permalink_format
56
+ # the options are :
57
+ #
58
+ # "day" URL: http://localhost:3000/articles/2011/01/01/test-post
59
+ # "month" URL: http://localhost:3000/articles/2011/01/test-post
60
+ # "year" URL: http://localhost:3000/articles/2011/test-post
61
+ # "slug" URL: http://localhost:3000/articles/test-post
62
+ #
63
+ # the default village:articles will use permalink_format "day"
64
+ # You can also customize this throug route options
65
+ #
66
+ # permalink_format: day
67
+
68
+ # file_extensions
69
+ # Set this file_extensions which engine you want to use
70
+ # Village will accepts these formats :
71
+ #
72
+ # [ "erb", "rhtml", "erubis", "haml", "builder", "liquid",
73
+ # "markdown", "mkd", "md", "textile", "rdoc", "radius",
74
+ # "mab", "wiki", "mediawiki", "mw", "creole", "yajl" ]
75
+ #
76
+ # remember you have to set this settings with String
77
+ # inside an [Array]
78
+ # by default Village will accepts :markdown, :erb and :haml
79
+ #
80
+ # file_extensions: ["markdown", "erb", "haml"]
81
+
@@ -0,0 +1,4 @@
1
+ if defined?(Rails)
2
+ require 'village/engine'
3
+ require 'village/routes'
4
+ end
@@ -0,0 +1,125 @@
1
+ require 'gravtastic'
2
+ require 'nokogiri'
3
+
4
+ module Village
5
+ class Article < FileModel
6
+ include Gravtastic
7
+ is_gravtastic
8
+
9
+ CONTENT_PATH = "app/views/articles"
10
+
11
+ delegate :year, :month, :day, :to => :date
12
+
13
+ class Sanitizer < HTML::WhiteListSanitizer; self.allowed_tags -= %w(img a); end
14
+
15
+ TagHelper = Class.new.extend ActionView::Helpers::TagHelper
16
+
17
+ self.superclass.create_class_methods_on(Village::Article)
18
+
19
+ class << self
20
+ def order
21
+ all.select(&:visible?).sort_by(&:date).reverse
22
+ end
23
+
24
+ def tags
25
+ @tags ||= all.map do |article|
26
+ article.tags if article.attributes[:tags].present?
27
+ end.compact.flatten.inject(Hash.new(0)) do |h,e|
28
+ h[e] += 1; h
29
+ end.inject({}) do |r, e|
30
+ r[e.first] = e.last; r
31
+ end
32
+ end
33
+
34
+ def categories
35
+ @categories ||= all.map do |article|
36
+ article.categories if article.attributes[:categories].present?
37
+ end.compact.flatten.inject(Hash.new(0)) do |h,e|
38
+ h[e] += 1; h
39
+ end.inject({}) do |r, e|
40
+ r[e.first] = e.last; r
41
+ end
42
+ end
43
+
44
+ def where(conditions = {})
45
+ conditions = conditions.symbolize_keys
46
+ conditions.assert_valid_keys :year, :month, :day, :slug, :to_param, :tag, :category
47
+ [:year, :month, :day].each do |key|
48
+ conditions[key] = conditions[key].to_i if conditions[key].present?
49
+ end
50
+ order.select do |article|
51
+ conditions.all? do |key, value|
52
+ case key
53
+ when :tag
54
+ article.tags.include?(value) if article.attributes[:tags].present?
55
+ when :category
56
+ article.categories.include?(value) if article.attributes[:categories].present?
57
+ else
58
+ article.send(key) == value
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def feed
65
+ order.first(Village::Config.page_size)
66
+ end
67
+
68
+ def feed_last_modified
69
+ feed.first.try(:last_modified) || Time.now.utc
70
+ end
71
+
72
+ def reset!
73
+ @files = nil
74
+ @tags = nil
75
+ @categories = nil
76
+ end
77
+ end
78
+
79
+ def initialize(path)
80
+ super path
81
+ attributes[:permalink_format] = Village::Config.permalink_format
82
+ @date_str, @slug = File.basename(path).match(/^(\d+-\d+-\d+)-(.*)(\.[^.]+)$/).captures
83
+ end
84
+
85
+ def date
86
+ @date ||= Time.zone.parse(attributes[:date] || @date_str).to_date
87
+ end
88
+
89
+ def email
90
+ if attributes[:author] && attributes[:author][:email]
91
+ attributes[:author][:email]
92
+ end
93
+ end
94
+
95
+ def timestamp
96
+ date.to_time_in_current_zone
97
+ end
98
+ alias_method :last_modified, :timestamp
99
+
100
+ def visible?
101
+ timestamp <= Time.zone.now
102
+ end
103
+
104
+ def summary_html
105
+ if attributes[:summary].present?
106
+ TagHelper.content_tag :p, summary
107
+ else
108
+ html = Sanitizer.new.sanitize(content_html)
109
+ doc = Nokogiri::HTML.fragment(html)
110
+ para = doc.search('p').detect { |p| p.text.present? }
111
+ para.try(:to_html).try(:html_safe)
112
+ end
113
+ end
114
+
115
+ def to_param
116
+ case attributes[:permalink_format]
117
+ when :day then "%04d/%02d/%02d/%s" % [year, month, day, slug]
118
+ when :month then "%04d/%02d/%s" % [year, month, slug]
119
+ when :year then "%04d/%s" % [year, slug]
120
+ when :slug then slug
121
+ end
122
+ end
123
+
124
+ end
125
+ end