parchemin 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/parchemin ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'haml'
4
+ require 'fileutils'
5
+
6
+ #############################################
7
+ # create the parchemin-based blog directory #
8
+ #############################################
9
+
10
+ # The blog must be given a name
11
+ unless ARGV.size == 2
12
+ puts "Usage: parchemin BLOG_NAME AUTHOR_NAME"
13
+ exit
14
+ end
15
+
16
+ blog_name = ARGV[0]
17
+ author_name = ARGV[1]
18
+
19
+ # Create the directory structure
20
+ Dir.mkdir(blog_name)
21
+ Dir.mkdir("#{blog_name}/articles")
22
+ Dir.mkdir("#{blog_name}/statics")
23
+
24
+ # create the statics
25
+ FileUtils.cp File.join(File.dirname(__FILE__), "templates/about.markdown"), "#{blog_name}/statics/about.markdown"
26
+ FileUtils.cp File.join(File.dirname(__FILE__), "templates/abstract.markdown"), "#{blog_name}/statics/abstract.markdown"
27
+
28
+ # Create the rack file
29
+ template = File.read(File.join(File.dirname(__FILE__), "templates/config.ru.haml"))
30
+ haml_engine = Haml::Engine.new(template)
31
+ File.open("#{blog_name}/config.ru", 'w') do |f|
32
+ f.write(haml_engine.render(Object.new, :blog_name => blog_name, :author_name => author_name, :dirname => `pwd`))
33
+ end
@@ -0,0 +1,3 @@
1
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
2
+
3
+ Curae molestie ullamco felis proin tempor proident, nostra rutrum vitae varius in sapien. Facilisis nullam nam consectetuer netus blandit laboris nonummy nam dolor et, gravida ea ex volutpat non lectus imperdiet nascetur stevirota sociis. Fames velit nunc cupidatat viverra, imperdiet magnis nullam adipiscing imperdiet mi morbi. Vivamus nascetur litora dictumst ad proident facilisi, nullam eros ligula blandit elementum fugiat mollis leo semper. Incididunt etiam interdum eiusmod dolore non lobortis dictum magna odio est, suspendisse laoreet ex sint volutpat bibendum hac reprehenderit.
@@ -0,0 +1 @@
1
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
@@ -0,0 +1,9 @@
1
+ require 'parchemin'
2
+
3
+ Parchemin::Config.blog_name = "#{blog_name}"
4
+ Parchemin::Config.author_name = "#{author_name}"
5
+ Parchemin::Config.host = "www.#{blog_name}.com"
6
+ Parchemin::Config.project_path = "#{dirname.strip}/#{blog_name}"
7
+ Parchemin::Config.db_name = "#{blog_name}"
8
+
9
+ run Parchemin::Application
@@ -0,0 +1,73 @@
1
+ # coding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), "config/config")
4
+
5
+ module Parchemin
6
+ class Application < Sinatra::Base
7
+
8
+ set :public, File.expand_path(File.join(File.dirname(__FILE__), "../../app/public"))
9
+ set :views, File.expand_path(File.join(File.dirname(__FILE__), "../../app/views"))
10
+
11
+ require File.join(File.dirname(__FILE__), "config/environment")
12
+
13
+ before do
14
+ @blog_name = Parchemin::Config.blog_name
15
+ @author_name = Parchemin::Config.author_name
16
+ @abstract = Parchemin::Static.new('abstract')
17
+ end
18
+
19
+ get '/:name.css' do
20
+ content_type 'text/css', :charset => 'utf-8'
21
+ sass(:"#{params[:name]}", Compass.sass_engine_options )
22
+ end
23
+
24
+ get '/' do
25
+ @articles = Parchemin::Article.all
26
+ haml :index
27
+ end
28
+
29
+ get '/articles/:article' do |filename|
30
+ @article = Parchemin::Article.new("#{Parchemin::Config.articles_path}/#{filename}.markdown")
31
+ @comments = @article.comments
32
+ @comment = Parchemin::Comment.new
33
+ haml :show
34
+ end
35
+
36
+ get '/tags/:tag' do |tag|
37
+ @tag = tag
38
+ @articles = Parchemin::Article.all(:tag => tag)
39
+ haml :index
40
+ end
41
+
42
+ get '/search' do
43
+ @search = params[:search]
44
+ @articles = Parchemin::Article.search(params[:search])
45
+ haml :index
46
+ end
47
+
48
+ get '/statics/:static' do |static|
49
+ @static = Parchemin::Static.new(params[:static])
50
+ haml :static
51
+ end
52
+
53
+ post '/comment' do
54
+ @article = Parchemin::Article.new("#{params[:comment][:article]}.markdown")
55
+ @comments = @article.comments
56
+
57
+ @comment = Parchemin::Comment.new(params[:comment])
58
+ if @comment.save
59
+ flash[:notice] = "Commentaire ajouté avec succès. Merci."
60
+ redirect "/articles/#{@article.id}"
61
+ else
62
+ flash[:error] = "Données non valides. Verifiez le formulaire svp."
63
+ haml :show
64
+ end
65
+ end
66
+
67
+ get '/rss' do
68
+ @host = Parchemin::Config.host
69
+ @articles = Parchemin::Article.all
70
+ builder :rss
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,15 @@
1
+ # coding: utf-8
2
+
3
+ module Parchemin
4
+ class Application
5
+ configure do
6
+ Compass.configuration do |config|
7
+ config.project_path = File.dirname(__FILE__)
8
+ config.sass_dir = 'views'
9
+ end
10
+
11
+ set :haml, { :format => :html5 }
12
+ set :sass, Compass.sass_engine_options
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,65 @@
1
+ # coding: utf-8
2
+
3
+ module Parchemin
4
+ module Config
5
+
6
+ def self.author_name=(name)
7
+ @author_name = name
8
+ end
9
+
10
+ def self.author_name
11
+ raise "The author_name configuration variable has not been set" if @author_name.nil?
12
+ @author_name
13
+ end
14
+
15
+ def self.blog_name=(name)
16
+ @blog_name = name
17
+ end
18
+
19
+ def self.blog_name
20
+ raise "The blog_name configuration variable has not been set" if @blog_name.nil?
21
+ @blog_name
22
+ end
23
+
24
+ def self.host=(host)
25
+ @host = host
26
+ end
27
+
28
+ def self.host
29
+ raise "The host configuration variable has not been set" if @host.nil?
30
+ @host
31
+ end
32
+
33
+ def self.project_path=(path)
34
+ @project_path = path
35
+ end
36
+
37
+ def self.project_path
38
+ raise "The project_path configuration variable has not been set" if @project_path.nil?
39
+ @project_path
40
+ end
41
+
42
+ def self.articles_path=(path)
43
+ @articles_path = path
44
+ end
45
+
46
+ def self.articles_path
47
+ @articles_path ||= "#{project_path}/articles"
48
+ end
49
+
50
+ def self.statics_path=(path)
51
+ @statics_path = path
52
+ end
53
+
54
+ def self.statics_path
55
+ @statics_path ||= "#{project_path}/statics"
56
+ end
57
+
58
+ def self.db_name=(name)
59
+ Mongoid.configure do |config|
60
+ config.allow_dynamic_fields = false
61
+ config.master = Mongo::Connection.new.db(name)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,55 @@
1
+ # coding: utf-8
2
+ module Parchemin
3
+ class Application
4
+ # Rails-style request params hash
5
+ before do
6
+ new_params = {}
7
+ params.each_pair do |full_key, value|
8
+ this_param = new_params
9
+ split_keys = full_key.split(/\]\[|\]|\[/)
10
+ split_keys.each_index do |index|
11
+ break if split_keys.length == index + 1
12
+ this_param[split_keys[index]] ||= {}
13
+ this_param = this_param[split_keys[index]]
14
+ end
15
+ this_param[split_keys.last] = value
16
+ end
17
+ request.params.replace new_params
18
+ end
19
+
20
+ # Partial implementation
21
+ helpers do
22
+ def partial(template, *args)
23
+ options = args.last.is_a?(::Hash) ? args.pop : {}
24
+ options.merge!(:layout => false)
25
+ if collection = options.delete(:collection) then
26
+ collection.inject([]) do |buffer, member|
27
+ buffer << haml(template, options.merge(:layout => false, :locals => {template.to_sym => member}))
28
+ end.join("\n")
29
+ else
30
+ haml(template, options)
31
+ end
32
+ end
33
+ end
34
+
35
+ # Flash implementation
36
+ use Rack::Session::Cookie
37
+
38
+ helpers do
39
+ def flash
40
+ @_flash ||= {}
41
+ end
42
+
43
+ def redirect(uri, *args)
44
+ session[:_flash] = flash unless flash.empty?
45
+ status 302
46
+ response['Location'] = uri
47
+ halt(*args)
48
+ end
49
+ end
50
+
51
+ before do
52
+ @_flash, session[:_flash] = session[:_flash], nil if session[:_flash]
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,95 @@
1
+ # coding: utf-8
2
+
3
+ require 'date'
4
+
5
+ module Parchemin
6
+ class Article
7
+ attr_reader :title, :created_at, :abstract, :body, :filename, :tags
8
+
9
+ MONTHS = %w(janv fevr mars avr mai juin juill aout sept oct nov dec)
10
+
11
+ def initialize(filename)
12
+ @filename = filename.split('.').first
13
+ parse_attributes(filename)
14
+ end
15
+
16
+ def parse_attributes(filename)
17
+ body = ""
18
+ File.open(filename) do |f|
19
+ f.lines.each do |line|
20
+ if line.match /<!--\s(.*)\s-->/
21
+ $1.match /(.*)\s:\s(.*)/
22
+ eval "@#{$1} = #{$2}"
23
+ else
24
+ body += line
25
+ end
26
+ end
27
+ end
28
+
29
+ @body = RDiscount.new(body).to_html
30
+ end
31
+
32
+ def id
33
+ File.basename(filename)
34
+ end
35
+
36
+ def month
37
+ MONTHS[@created_at.month-1]
38
+ end
39
+
40
+ def day
41
+ @created_at.day
42
+ end
43
+
44
+ def year
45
+ @created_at.year
46
+ end
47
+
48
+ def date
49
+ "#{day} #{month.capitalize} #{year}"
50
+ end
51
+
52
+ def <=>(article)
53
+ created_at <=> article.created_at
54
+ end
55
+
56
+ def self.all(conditions={})
57
+ articles = []
58
+ Dir.foreach(Parchemin::Config.articles_path) do |entry|
59
+ next if entry.match /^\./
60
+ article = Article.new("#{Parchemin::Config.articles_path}/#{entry}")
61
+
62
+ if conditions.count == 0
63
+ articles << article
64
+ else
65
+ articles << article if not filtered?(article, conditions)
66
+ end
67
+ end
68
+ articles.sort.reverse
69
+ end
70
+
71
+ def self.search(search)
72
+ output = `grep -Hi '#{search}' #{Parchemin::Config.articles_path}/* | cut -d: -f1`
73
+ articles = output.split("\n").uniq.map { |filename| Article.new(filename) }
74
+ articles.sort.reverse
75
+ end
76
+
77
+ def comments
78
+ Comment.where(:article => @filename)
79
+ end
80
+
81
+ protected
82
+
83
+ def self.filtered?(article, conditions)
84
+ filtered = true
85
+ conditions.each do |attribute, value|
86
+ if attribute.to_s == 'tag'
87
+ filtered &&= !(article.tags.include?(value))
88
+ else
89
+ filtered &&= !(article.send(attribute) == value)
90
+ end
91
+ end
92
+ filtered
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+
3
+ module Parchemin
4
+ class Comment
5
+ include Mongoid::Document
6
+ include Mongoid::Timestamps
7
+
8
+ field :author
9
+ field :email
10
+ field :website
11
+ field :body
12
+ field :article
13
+ # field :created_at, :type => DateTime
14
+ # field :updated_at, :type => DateTime
15
+
16
+ validates_presence_of :author, :body, :article
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+
3
+ module Parchemin
4
+ class Static
5
+ attr_reader :filename, :id
6
+
7
+ def initialize(id)
8
+ @id = id
9
+ @filename = "#{Parchemin::Config.statics_path}/#{@id}.markdown"
10
+ end
11
+
12
+ def body(interpret_markdown=true)
13
+ raise "#{@filename} doesn't exist." if not File.exist?(@filename)
14
+ if interpret_markdown
15
+ @body ||= RDiscount.new(File.read(@filename)).to_html
16
+ else
17
+ @body ||= File.read(@filename)
18
+ end
19
+ @body
20
+ end
21
+ end
22
+ end
data/lib/parchemin.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'sinatra/base'
2
+ require 'yaml'
3
+
4
+ require 'mongoid'
5
+ require 'rdiscount'
6
+
7
+ require 'haml'
8
+ require 'sass'
9
+ require 'compass'
10
+ require 'susy'
11
+
12
+ require File.join(File.dirname(__FILE__), "parchemin/models/article")
13
+ require File.join(File.dirname(__FILE__), "parchemin/models/static")
14
+ require File.join(File.dirname(__FILE__), "parchemin/models/comment")
15
+ require File.join(File.dirname(__FILE__), "parchemin/application")
16
+
@@ -0,0 +1,139 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Parchemin" do
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ @app ||= Parchemin::Application
8
+ end
9
+
10
+ before :all do
11
+ Parchemin::Comment.delete_all
12
+ end
13
+
14
+ it "renders the stylesheet" do
15
+ get '/layout.css'
16
+ last_response.should be_ok
17
+ end
18
+
19
+ it "renders the home page" do
20
+ get '/'
21
+
22
+ doc = Nokogiri::HTML(last_response.body)
23
+ doc.css('.abstract').count.should == 4
24
+ doc.css('.details').count.should == 4
25
+
26
+ last_response.should be_ok
27
+ end
28
+
29
+ it "renders an article" do
30
+ article = Parchemin::Article.new(File.expand_path(File.dirname(__FILE__) + '/bin/articles/lorem.markdown'))
31
+ Parchemin::Comment.create(:article => article.filename, :author => 'Rawane', :body => 'test')
32
+ Parchemin::Comment.create(:article => article.filename, :author => 'Rawane', :body => 'test')
33
+ Parchemin::Comment.create(:article => article.filename, :author => 'Rawane', :body => 'test')
34
+
35
+ get 'articles/lorem'
36
+
37
+ doc = Nokogiri::HTML(last_response.body)
38
+ doc.css('.comment').count.should == 3
39
+ doc.css('.add').count.should == 1
40
+
41
+ last_response.should be_ok
42
+ end
43
+
44
+ it "filters articles by tag" do
45
+ get '/tags/tutorial'
46
+
47
+ doc = Nokogiri::HTML(last_response.body)
48
+ doc.css('.abstract').count.should == 2
49
+
50
+ last_response.should be_ok
51
+ end
52
+
53
+ it "performs accurate searches" do
54
+ get '/search', {:search => 'lorem ipsum'}
55
+
56
+ doc = Nokogiri::HTML(last_response.body)
57
+ doc.css('.abstract').count.should == 2
58
+
59
+ last_response.should be_ok
60
+ end
61
+
62
+ it "creates articles comments with valid attributes" do
63
+ article = Parchemin::Article.new(File.expand_path(File.dirname(__FILE__) + '/bin/articles/azerty.markdown'))
64
+
65
+ post '/comment', {:comment => {:article => article.filename, :author => 'Rawane', :body => 'test'}}
66
+ follow_redirect!
67
+
68
+ last_request.url.should == "http://example.org/articles/#{article.id}"
69
+ doc = Nokogiri::HTML(last_response.body)
70
+ doc.css('.comment').count.should == 1
71
+
72
+ article.comments.count.should == 1
73
+ article.comments.first.author.should == 'Rawane'
74
+ article.comments.first.body.should == 'test'
75
+
76
+ last_response.should be_ok
77
+ end
78
+
79
+ it "does not create an article'comment with invalid attributes" do
80
+ article = Parchemin::Article.new(File.expand_path(File.dirname(__FILE__) + '/bin/articles/tellus.markdown'))
81
+
82
+ post '/comment', {:comment => {:article => article.filename, :author => 'Rawane'}}
83
+
84
+ article.comments.count.should == 0
85
+
86
+ last_response.should be_ok
87
+ end
88
+
89
+ it "renders the feeds" do
90
+ get '/rss'
91
+
92
+ doc = Nokogiri::XML(last_response.body)
93
+ doc.css('item').count.should == 4
94
+
95
+ last_response.should be_ok
96
+ end
97
+
98
+ it "display the application and author's names" do
99
+ get '/'
100
+ last_response.should be_ok
101
+ doc = Nokogiri::HTML(last_response.body)
102
+ doc.css('title').first.content.strip.should == 'Parchemin Test'
103
+ doc.css('.brand').first.content.strip.should == 'Parchemin Test'
104
+ doc.css('.tagline').first.content.strip.should == 'par Rawane ZOSSOU'
105
+
106
+ get 'articles/lorem'
107
+ last_response.should be_ok
108
+ doc = Nokogiri::HTML(last_response.body)
109
+ doc.css('title').first.content.strip.should == 'Parchemin Test'
110
+ doc.css('.brand').first.content.strip.should == 'Parchemin Test'
111
+ doc.css('.tagline').first.content.strip.should == 'par Rawane ZOSSOU'
112
+
113
+ get '/tags/tutorial'
114
+ last_response.should be_ok
115
+ doc = Nokogiri::HTML(last_response.body)
116
+ doc.css('title').first.content.strip.should == 'Parchemin Test'
117
+ doc.css('.brand').first.content.strip.should == 'Parchemin Test'
118
+ doc.css('.tagline').first.content.strip.should == 'par Rawane ZOSSOU'
119
+
120
+ get '/search', {:search => 'lorem ipsum'}
121
+ last_response.should be_ok
122
+ doc = Nokogiri::HTML(last_response.body)
123
+ doc.css('title').first.content.strip.should == 'Parchemin Test'
124
+ doc.css('.brand').first.content.strip.should == 'Parchemin Test'
125
+ doc.css('.tagline').first.content.strip.should == 'par Rawane ZOSSOU'
126
+ end
127
+
128
+ it "renders statics" do
129
+ get '/statics/about'
130
+
131
+ doc = Nokogiri::HTML(last_response.body)
132
+ doc.css('title').first.content.strip.should == 'Parchemin Test'
133
+ doc.css('.brand').first.content.strip.should == 'Parchemin Test'
134
+ doc.css('.tagline').first.content.strip.should == 'par Rawane ZOSSOU'
135
+ doc.css('p').count.should == 2
136
+
137
+ last_response.should be_ok
138
+ end
139
+ end
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'parchemin'
4
+ require 'rspec/core'
5
+ require 'rspec/autorun'
6
+ require 'rack/test'
7
+ require 'nokogiri'
8
+
9
+ module Parchemin
10
+ class Application
11
+ set :environment, :test
12
+ set :run, false
13
+ set :raise_errors, true
14
+ set :logging, false
15
+ end
16
+ end
17
+
18
+ Parchemin::Config.blog_name = "Parchemin Test"
19
+ Parchemin::Config.author_name = "Rawane ZOSSOU"
20
+ Parchemin::Config.host = "http://example.com"
21
+ Parchemin::Config.project_path = File.join(File.dirname(__FILE__), 'bin')
22
+ Parchemin::Config.db_name = 'parchemin_test'