Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

documentation and a bit of tidying up of the code

  • Loading branch information...
commit 245a65a97f003120c45c955ec2dde13856488dd0 1 parent b751db4
@RicSwirrl RicSwirrl authored
View
181 README
@@ -1 +1,180 @@
-== Semantic Journal Blog Engine ==
+Contents of this README file
+============================
+
+1. Overview
+2. Installing
+ 2.1 Pre-requisites
+ 2.2 Getting Started
+3. Storing Assets
+4. Configuration
+5. Deployment
+6. Tests
+7. Caching
+8. Setting the Domain(s) for a blog
+9. How the data is organized
+ 9.1 The semanticjournal database
+ 9.2 9.2 The individual blog databases
+10. Themes
+11. Feeds
+
+1. Overview
+===========
+
+SemanticJournal (or 'semjo' for short) is a simple Rails-based blog-engine which uses CouchDB for storage. It includes some Semantic Web features, such as helpers to aid with marking up elements on public-facing pages with RDFa (such as the date, author, title of articles etc).
+
+Current Features
+----------------
+* CouchDB Storage. Each blog has its own CouchDB database.
+* Helpers for marking up elements with RDFa
+* Host multiple blogs using the same rails app
+* Write articles with Textile markup
+* Design the theme used for each blog, using HTML and ERB
+* Caching, using Rack/Cache
+* Just provides the blog engine only but...
+* Easy to extend with 3rd party plugins (e.g. Disqus for comments, ShareThis for social bookmarking, Google for search etc, Gists for Code/Syntax highlighting, S3 for storing other assets used on your blog).
+
+TODOs/Coming soon
+-----------------
+* Smarten up the admin interface.
+* Tagging/Categories for articles.
+* Uploading assets to S3.
+* Add a way to clear the cache for a blog from the admin interface.
+* Finish rights/roles stuff.
+* Better documentation & comments in the code to help others use/modify/contribute.
+
+
+2. Installing
+=============
+
+2.1 Pre-requisites
+-------------------
+* CouchDB (for storage)
+* Rails 2.3.5
+* Passenger (for running rails)
+* Rack/Cache (for caching)
+* CouchRest gem (data adapter layer)
+* RedCloth gem (for textile markup)
+* FactoryGirl gem (for testing)
+
+
+2.2 Getting Started
+-------------------
+1. Clone the repo from github.
+
+2. Run the create_semjo_db rake task to create the main 'semanticjournal' CouchDB.
+e.g. rake semjo:create_semjo_db
+
+3. Run the create_new_blog rake task to make the CouchDB for your blog. (Each blog needs its own database). The BLOG_HOST parameter is the domain under which you want to appear, and BLOG_NAME is just an internal identifier for the blog (see 'How the data is organized' section below for more details). If you don't specify a BLOG_HOST, the blog will be available at #{BLOG_NAME}.semanticjournal.com
+e.g. rake semjo:create_new_blog BLOG_NAME='mynewblog' BLOG_HOST='http://myblogsdomain.com' COUCH_SERVER='http://127.0.0.1:5984'
+
+4. Run the create_new_user rake task to create a user for the blog. If an account already exists with the ACCOUNT_NAME specified, it will use that one. The PERSONAL_URI should be an identifier for the user on the web (that ideally, returns rdf).
+e.g. rake couchdb:create_user ACCOUNT_NAME=ric DISPLAY_NAME='ric roberts' PERSONAL_URI='http://swirrl.com/ric.rdf#me' PASSWORD='pwd1' EMAIL='hello@ricroberts.com' BLOG_NAME='ricroberts' COUCH_SERVER='http://127.0.0.1:5984'
+
+4. Set up an apache virtual host for the new domain. For running in development mode, you'll also need to set up an apache alias and /etc/hosts entry. (The simplest way to do this is with the 'ghost' gem, and the Passenger Pref pane for OS X).
+
+5. Make a new folder in the views/themes/custom folder of the project (at the same level as the ricroberts folder).
+
+6. Inside the new folder you made in the previous step, create 2 ERB templates: home.erb and show.erb, for your blog's home page and for individual posts, respectively. (These can use partials to help organise the code).
+
+* In home.erb, an instance variable called @articles is available which contains a page's worth of articles. At the moment, the @page_size defaults to 8, but you can change it in ArticlesController#home.
+
+* In show.erb, an instance variable called @article is available, which is article being shown.
+
+Note: I've included in the repository all the theme templates for my own blog (http://ricroberts.com), in the ricroberts folder, which you can use as a reference or for ideas. Note: the test, and test2 folders are there for the unit tests and you can just ignore them.
+
+7. In your web browser, navigate to the host you specified in step 2. If your template is valid, you'll see your home page. To log in go to /admin, and enter the email and password you specified in step 3.
+
+
+3. Storing Assets
+=================
+
+Right now, SemanticJournal doesn't provide a means for uploading and storing assets for your blog (such as images, CSS, javascript files etc). I plan to add a user interface for uploading files to S3 soon, but for the moment, you can just stick them on S3 (or your preferred file store), and reference those files from your ERB templates.
+
+Note: Tempting as it may be, I recommend that you DON'T just put these kinds of things in the public folder. This is because if you're hosting several blogs on your SemanticJournal instance, users (including bots) be able to access these public files from any of your blog's urls, leading to confusion (and possibly embarrassment, depending on the different blog's topics).
+
+
+4. Configuration
+================
+
+The only configuration you really need to worry about is the location of the CouchDB for your production environment. This can be set in config/config.yml
+
+
+5. Deployment
+=============
+
+Note: I'm assuming you're running Rails on Passenger. SemanticJournal is not tested on other systems (e.g. Webrick, Mongrel etc).
+
+There's a stub capistrano deploy.rb in the config folder which should get you started. Points to note:
+
+* In CouchDB, there's no such thing as migrations, so the deploy:cold task has been overridden not to try to run any migrations. (Don't try to run deploy:migrate, or deploy:migrations - they wont work).
+
+* By default, CouchRest updates all the design docs on a database every time you restart the process (which causes a reindex of that kind of document in the database: a potentially time consuming process). The SemanticJournalCouchRest::DeferUpdateDesignDocsInProduction module that extends all my CouchRest document types prevents this happening in production mode as it's often the case that nothing has changed. So, if you update your CouchRest document code, you'll need to manually initiate the re-indexing. This can be done by using the rake tasks that I've added in the "couchdb" namespace. e.g.
+
+"rake couchdb:refresh_design_docs" will refresh all the design docs on the local server.
+
+Taks a look at lib/tasks/design_docs.rake for more focused tasks.
+
+
+6. Tests
+========
+
+The default rake task runs all the unit and funcitonal tests, as you'd expect for a Rails project. The tests will create 2 databases: semanticjournal_test and semanticjournal_blog_test. If these bother you, you can delete them (they will be re-created the next time the tests run).
+
+A couple of the functional tests rely on the test and test2 blog themes existing, but you can delete these in production if you like.
+
+
+7. Caching
+==========
+
+SemanticJournal uses Rack/Cache. By default this is set up to cache in the tmp/cache folder. If you're changing code, and things aren't changing in your browser, try deleting this (it will automatically rebuild the cache on subsequent requests).
+
+The home page is considered to be stale if the most recently updated article has changed since the last request.
+
+An article page is considered to be stale if it has been updated since the last request.
+
+
+8. Setting the Domain(s) for a blog
+===================================
+
+The hosts property of the Blog documents in the semanticjournal CouchDB is an array of all the hosts that correspond to a blog. If SemanticJournal receives a request at o one of these hosts it will render the blog in which it appears. If that host is not the first item in that blog's hosts array, it will redirect to the first host in the array. This is that blog's 'canonical url'.
+
+
+9. How the data is organized
+============================
+
+As I mentioned in the Getting Started Section above, each blog in Semantic Journal has its own CouchDB, and there's also a 'central' database called semanticjournal.
+
+9.1 The semanticjournal database
+--------------------------------
+The semanticjournal database stores 2 types of document:
+
+* Blogs running on your instance, and their hosts/domains (the Blog couchrest document type). See the section above for how to set up domains.
+
+* Accounts in the system (the Account document type). An account is a user/person in the system. Accounts can be given log-in access to one or more blogs running on the system.
+
+9.2 The individual blog databases
+---------------------------------
+* All the articles for an individual blog are stored in a database with the name of the blog (Article documents).
+
+* This database also stores information about which accounts have access to that blog (BlogUsers). The BlogUser documents will store the role for that account on this blog (when I get around to coding it - at the moment there's only one role, which can do everything).
+
+
+10. Themes
+==========
+
+I've included in the repository all the theme templates for my own blog (http://ricroberts.com), in the views/themes/ricroberts folder, which you can use as a reference or for ideas. Note: the test, and test2 folders are there for the unit tests and you can just ignore them (see the Testing section of this README).
+
+10.1 Semantic Helpers
+---------------------
+In your theme, you can use the helper methods in app/helpers/articles_helper, to add semantic markup to your blog post. Check out the comments in the code and see my blog theme for examples of use.
+
+* rdfa_article_tag
+* rdfa_title_tag
+* rdfa_author_tag
+* rdfa_date_tag
+
+
+11. RSS Feed
+============
+
+SemanticJournal generates a feed of the most recently updated 10 articles at /feed.rss.
View
2  Rakefile
@@ -3,8 +3,6 @@
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
-COUCH_DB_LOCATION = 'http://127.0.0.1:5984'
-
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
View
2  app/controllers/application_controller.rb
@@ -39,7 +39,7 @@ def set_current_account
return false # quit filter chain
end
- svr = CouchRest::Server.new(COUCH_DB_LOCATION)
+ svr = CouchRest::Server.new(APP_CONFIG['couch_db_location'])
couch_db = CouchRest::Database.new(svr, @blog.name)
Thread.current[:blog_db] = couch_db
View
14 app/controllers/articles_controller.rb
@@ -16,12 +16,14 @@ def show
end
def home
- if stale?(:last_modified => @most_recently_updated_article.updated_at, :public => true)
- @page = params[:page].to_i || 1
- @page = 1 if @page < 1
- @per_page = 8
- @articles = Article.get_paginated_articles({:page => @page, :per_page => @per_page})
- @total_articles = Article.by_published_at(:reduce => true)["rows"][0]["value"]
+ if @most_recently_updated_article
+ if stale?(:last_modified => @most_recently_updated_article.updated_at, :public => true)
+ @page = params[:page].to_i || 1
+ @page = 1 if @page < 1
+ @per_page = 8
+ @articles = Article.get_paginated_articles({:page => @page, :per_page => @per_page})
+ @total_articles = Article.by_published_at(:reduce => true)["rows"][0]["value"]
+ end
end
end
View
3  app/helpers/articles_helper.rb
@@ -37,18 +37,21 @@ def rdfa_article_tag(tag_name, html_options = {}, about = nil, &block)
content_tag(tag_name, article_options, {}, &block)
end
+ # make a title tag (e.g. :h2), and the content to go in the title.
def rdfa_title_tag(tag_name, title_content)
content_tag(tag_name, {"property" => "dc:title"}) do
title_content
end
end
+ # make an author tag (e.g. :div), and the personal uri and display name of the article's author
def rdfa_author_tag(tag_name, personal_uri, display_name)
content_tag(tag_name, {"rel" => "dc:creator", "resource" => personal_uri} ) do
h(display_name)
end
end
+ # make a date tag with the name passed in (e.g. :div), and the date passed.
def rdfa_date_tag(tag_name, date, time_format = "%d %b %Y")
content_tag(tag_name, {"property" => "dc:date", "datatype"=>"xsd:dateTime", "content" => date.xmlschema} ) do
date.strftime(time_format)
View
2  app/models/account.rb
@@ -5,7 +5,7 @@ class Account < CouchRest::ExtendedDocument
include CouchRest::Validation
def self.database
- CouchRest.database("#{COUCH_DB_LOCATION}/semanticjournal") # the central db
+ CouchRest.database("#{APP_CONFIG['couch_db_location']}/semanticjournal") # the central db
end
# WE USE THE ACCOUNT NAME AS THE ID.
View
2  app/models/blog.rb
@@ -3,7 +3,7 @@ class Blog < CouchRest::ExtendedDocument
extend SemanticJournalCouchRest::DeferUpdateDesignDocsInProduction
def self.database
- CouchRest.database("#{COUCH_DB_LOCATION}/semanticjournal") # the central db
+ CouchRest.database("#{APP_CONFIG['couch_db_location']}/semanticjournal") # the central db
end
property :name
View
2  app/models/role.rb
@@ -3,7 +3,7 @@ class Role < CouchRest::ExtendedDocument
extend SemanticJournalCouchRest::DeferUpdateDesignDocsInProduction
def self.database
- CouchRest.database("#{COUCH_DB_LOCATION}/semanticjournal") # the central db
+ CouchRest.database("#{APP_CONFIG['couch_db_location']}/semanticjournal") # the central db
end
# list of constants representing the different roles
View
10 app/views/themes/custom/ricroberts/articles/home.erb
@@ -32,10 +32,12 @@
<div id="content">
<div class="grid_8 main">
- <% @articles.each do |article|%>
- <%= render :partial => 'post_preview', :locals => {:article => article}%>
- <% end %>
- <%= pagination_links() %>
+ <%if @articles%>
+ <% @articles.each do |article|%>
+ <%= render :partial => 'post_preview', :locals => {:article => article}%>
+ <% end %>
+ <%= pagination_links() %>
+ <%end%>
</div>
<%= render :partial => 'sidebar' %>
</div>
View
9 config/config.yml
@@ -0,0 +1,9 @@
+development:
+ couch_db_location: 'http://127.0.0.1:5984'
+
+test:
+ couch_db_location: 'http://127.0.0.1:5984'
+
+production:
+ couch_db_location: 'http://127.0.0.1:5984'
+# couch_db_location: 'http://my.production.server:5984'
View
2  config/environments/development.rb
@@ -15,5 +15,3 @@
# Don't care if the mailer can't send
config.action_mailer.raise_delivery_errors = false
-
-COUCH_DB_LOCATION = 'http://127.0.0.1:5984'
View
2  config/environments/production.rb
@@ -26,5 +26,3 @@
# Enable threaded mode
# config.threadsafe!
-
-COUCH_DB_LOCATION = 'http://my.production.server:5984'
View
2  config/environments/test.rb
@@ -26,5 +26,3 @@
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
-
-COUCH_DB_LOCATION = 'http://127.0.0.1:5984'
View
1  config/initializers/load_config.rb
@@ -0,0 +1 @@
+APP_CONFIG = YAML.load_file("#{RAILS_ROOT}/config/config.yml")[RAILS_ENV]
View
1  lib/semantic_journal_couch_rest/defer_update_design_docs_in_production.rb
@@ -5,6 +5,7 @@ module SemanticJournalCouchRest::DeferUpdateDesignDocsInProduction
def update_design_doc(design_doc, db = database)
saved = db.get(design_doc['_id']) rescue nil
if saved
+
# If the design doc is already there, don't update it in prod mode. We'll do it manually.
unless Thread.current[:env] && Thread.current[:env] == :production
design_doc['views'].each do |name, view|
View
2  lib/tasks/setup.rake
@@ -43,7 +43,7 @@ namespace :semjo do
desc "creates an admin user in the specifed blog, with the specified acct name, first_name, last_name, uri, pwd and email
e.g. rake couchdb:create_user ACCOUNT_NAME=ric DISPLAY_NAME='ric roberts' PERSONAL_URI='http://swirrl.com/ric.rdf#me'
- PASSWORD='pwd1' EMAIL='ric@swirrl.com' BLOG_NAME='ricroberts' COUCH_SERVER='http://127.0.0.1:5984'
+ PASSWORD='pwd1' EMAIL='hello@ricroberts.com' BLOG_NAME='ricroberts' COUCH_SERVER='http://127.0.0.1:5984'
Note: COUCH_SERVER defaults to local server"
task (:create_user => :environment) do |t, args|
Please sign in to comment.
Something went wrong with that request. Please try again.