nesta 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +13 -0
  2. data/Gemfile +6 -0
  3. data/Gemfile.lock +58 -0
  4. data/LICENSE +19 -0
  5. data/README.md +45 -0
  6. data/Rakefile +12 -0
  7. data/bin/nesta +67 -0
  8. data/config.ru +9 -0
  9. data/config/config.yml.sample +73 -0
  10. data/config/deploy.rb.sample +62 -0
  11. data/lib/nesta/app.rb +199 -0
  12. data/lib/nesta/cache.rb +139 -0
  13. data/lib/nesta/commands.rb +135 -0
  14. data/lib/nesta/config.rb +87 -0
  15. data/lib/nesta/models.rb +313 -0
  16. data/lib/nesta/nesta.rb +0 -0
  17. data/lib/nesta/overrides.rb +59 -0
  18. data/lib/nesta/path.rb +11 -0
  19. data/lib/nesta/plugins.rb +15 -0
  20. data/lib/nesta/version.rb +3 -0
  21. data/nesta.gemspec +49 -0
  22. data/scripts/import-from-mephisto +207 -0
  23. data/spec/atom_spec.rb +138 -0
  24. data/spec/commands_spec.rb +220 -0
  25. data/spec/config_spec.rb +69 -0
  26. data/spec/model_factory.rb +94 -0
  27. data/spec/models_spec.rb +445 -0
  28. data/spec/overrides_spec.rb +113 -0
  29. data/spec/page_spec.rb +428 -0
  30. data/spec/path_spec.rb +28 -0
  31. data/spec/sitemap_spec.rb +102 -0
  32. data/spec/spec.opts +1 -0
  33. data/spec/spec_helper.rb +72 -0
  34. data/templates/Gemfile +8 -0
  35. data/templates/Rakefile +35 -0
  36. data/templates/config.ru +9 -0
  37. data/templates/config/config.yml +73 -0
  38. data/templates/config/deploy.rb +47 -0
  39. data/views/analytics.haml +12 -0
  40. data/views/atom.builder +28 -0
  41. data/views/categories.haml +3 -0
  42. data/views/comments.haml +8 -0
  43. data/views/error.haml +13 -0
  44. data/views/feed.haml +3 -0
  45. data/views/index.haml +5 -0
  46. data/views/layout.haml +27 -0
  47. data/views/master.sass +246 -0
  48. data/views/not_found.haml +13 -0
  49. data/views/page.haml +29 -0
  50. data/views/sidebar.haml +3 -0
  51. data/views/sitemap.builder +15 -0
  52. data/views/summaries.haml +14 -0
  53. metadata +302 -0
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ *.gem
2
+ ._*
3
+ .*.swp
4
+ .bundle
5
+ .DS_Store
6
+ .rvmrc
7
+ .sass-cache
8
+ config/config.yml
9
+ config/deploy.rb
10
+ db/*.db
11
+ pkg/*
12
+ plugins
13
+ public/cache
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in nesta.gemspec
4
+ gemspec
5
+
6
+ # gem (RUBY_VERSION =~ /^1.9/) ? 'ruby-debug19': 'ruby-debug'
data/Gemfile.lock ADDED
@@ -0,0 +1,58 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ nesta (0.9.0)
5
+ RedCloth (= 4.2.2)
6
+ builder (= 2.1.2)
7
+ haml (= 3.0.12)
8
+ maruku (= 0.6.0)
9
+ shotgun (>= 0.8)
10
+ sinatra (= 1.1.0)
11
+
12
+ GEM
13
+ remote: http://rubygems.org/
14
+ specs:
15
+ RedCloth (4.2.2)
16
+ builder (2.1.2)
17
+ haml (3.0.12)
18
+ hoe (2.6.2)
19
+ rake (>= 0.8.7)
20
+ rubyforge (>= 2.0.4)
21
+ hpricot (0.8.2)
22
+ json_pure (1.4.6)
23
+ maruku (0.6.0)
24
+ syntax (>= 1.0.0)
25
+ rack (1.2.1)
26
+ rack-test (0.5.3)
27
+ rack (>= 1.0)
28
+ rake (0.8.7)
29
+ rspec (1.3.0)
30
+ rspec_hpricot_matchers (1.0)
31
+ rubyforge (2.0.4)
32
+ json_pure (>= 1.1.7)
33
+ shotgun (0.8)
34
+ rack (>= 1.0)
35
+ sinatra (1.1.0)
36
+ rack (~> 1.1)
37
+ tilt (~> 1.1)
38
+ syntax (1.0.0)
39
+ test-unit (1.2.3)
40
+ hoe (>= 1.5.1)
41
+ tilt (1.1)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ RedCloth (= 4.2.2)
48
+ builder (= 2.1.2)
49
+ haml (= 3.0.12)
50
+ hpricot (= 0.8.2)
51
+ maruku (= 0.6.0)
52
+ nesta!
53
+ rack-test (= 0.5.3)
54
+ rspec (= 1.3.0)
55
+ rspec_hpricot_matchers (= 1.0)
56
+ shotgun (>= 0.8)
57
+ sinatra (= 1.1.0)
58
+ test-unit (= 1.2.3)
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008-2010 Graham Ashton
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Nesta - a CMS for Ruby Developers
2
+
3
+ A CMS for small web sites and blogs, written in
4
+ [Sinatra](http://www.sinatrarb.com/ "Sinatra").
5
+
6
+ Content can be written in
7
+ [Markdown](http://daringfireball.net/projects/markdown/ "Daring Fireball:
8
+ Markdown") or [Textile](http://textism.com/tools/textile/) and stored in text
9
+ files (though you can also use Haml if you need to add some HTML to your
10
+ pages). There's no database; write your content in your editor. Publish by
11
+ pushing to a git repository.
12
+
13
+ ## Installation
14
+
15
+ Begin by installing the gem:
16
+
17
+ $ gem install nesta
18
+
19
+ Then use the `nesta` command to generate a new site:
20
+
21
+ $ nesta new mysite.com
22
+
23
+ If you intend to deploy to Heroku, you'll also want the Heroku rake
24
+ tasks, so run this version instead:
25
+
26
+ $ nesta new --heroku mysite.com
27
+
28
+ Install a few dependencies, and you're away:
29
+
30
+ $ cd mysite.com
31
+ $ bundle install
32
+
33
+ You'll find basic configuration options for your site in
34
+ `config/config.yml`. The defaults will work, but you'll want to tweak it
35
+ before you go very far.
36
+
37
+ That's it - you can launch a local web server in development mode using
38
+ shotgun...
39
+
40
+ $ bundle exec shotgun config.ru
41
+
42
+ ...then point your web browser at http://localhost:9393. Start editing
43
+ the files in `content/pages` (see [Creating Your
44
+ Content](http://effectif.com/nesta/creating-content) for full
45
+ instructions).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ Bundler.require(:default, :test)
5
+
6
+ require "spec/rake/spectask"
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ desc "Run all specs in spec directory"
10
+ Spec::Rake::SpecTask.new(:spec) do |t|
11
+ t.spec_files = FileList["spec/*_spec.rb"]
12
+ end
data/bin/nesta ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'getoptlong'
4
+
5
+ require File.expand_path('../lib/nesta/commands', File.dirname(__FILE__))
6
+
7
+ module Nesta
8
+ class Cli
9
+ def self.usage
10
+ puts <<EOF
11
+ USAGE: #{File.basename($0)} [GLOBAL OPTIONS] <command> [COMMAND OPTIONS]
12
+
13
+ GLOBAL OPTIONS
14
+ --help, -h Display this message.
15
+
16
+ COMMANDS
17
+ new <path> Create a new Nesta project.
18
+ theme:install <url> Install a theme from a git repository.
19
+ theme:enable <name> Make the theme active, updating config.yml.
20
+ theme:create <name> Makes a template for a new theme in ./themes.
21
+
22
+ OPTIONS FOR new
23
+ --heroku Include the heroku:config rake task.
24
+ --vlad Include config/deploy.rb.
25
+
26
+ EOF
27
+ exit 0
28
+ end
29
+
30
+ def self.parse_command_line
31
+ opts = GetoptLong.new(
32
+ ['--help', '-h', GetoptLong::NO_ARGUMENT],
33
+ ['--heroku', GetoptLong::NO_ARGUMENT],
34
+ ['--vlad', GetoptLong::NO_ARGUMENT]
35
+ )
36
+ options = {}
37
+ opts.each do |opt, arg|
38
+ case opt
39
+ when '--help'
40
+ usage
41
+ else
42
+ options[opt.sub(/^--/, '')] = arg
43
+ end
44
+ end
45
+ options
46
+ end
47
+
48
+ def self.main(options)
49
+ command = ARGV.shift
50
+ command.nil? && usage
51
+ case command
52
+ when 'new'
53
+ Nesta::Commands::New.new(ARGV[0], options).execute
54
+ when /^theme:(create|enable|install)$/
55
+ command_cls = Nesta::Commands::Theme.const_get($1.capitalize.to_sym)
56
+ command_cls.new(ARGV[0], options).execute
57
+ else
58
+ usage
59
+ end
60
+ rescue Nesta::Commands::UsageError => e
61
+ $stderr.puts "ERROR: #{e}"
62
+ usage
63
+ end
64
+ end
65
+ end
66
+
67
+ Nesta::Cli.main(Nesta::Cli.parse_command_line)
data/config.ru ADDED
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ Bundler.require(:default)
5
+
6
+ $LOAD_PATH.unshift(::File.expand_path('lib', ::File.dirname(__FILE__)))
7
+ require 'nesta/app'
8
+
9
+ run Nesta::App
@@ -0,0 +1,73 @@
1
+ # Title and subheading for your site. Used on the home page and in page
2
+ # titles.
3
+ #
4
+ title: "My Site"
5
+ subtitle: "(change this text in config/config.yml)"
6
+
7
+ # If you want to set the descrition or keywords meta tags on your site's
8
+ # home page, do it here.
9
+ #
10
+ # description: "Set this to something that describes your home page"
11
+ # keywords: "enter 3 or 4, comma separated, keywords"
12
+
13
+ # You should really specify your content's author when generating an
14
+ # Atom feed. Specify at least one of name, uri or email, and Nesta will
15
+ # include it in your feed. See the Atom spec for more info:
16
+ #
17
+ # http://www.atomenabled.org/developers/syndication/atom-format-spec.php#element.feed
18
+ #
19
+ # author:
20
+ # name: Your Name
21
+ # uri: http://yourhomepage.com
22
+ # email: you@yourdomain.com
23
+
24
+ # You can stick with the default look and feel, or use a theme. Themes are
25
+ # easy to create or install, and live inside the themes directory. You
26
+ # can also use scripts/theme to install them.
27
+ #
28
+ # theme: name-of-theme
29
+
30
+ # If you want to use the Disqus service (http://disqus.com) to display
31
+ # comments on your site, register a Disqus account and then specify your
32
+ # site's short name here. A comment form will automatically be added to
33
+ # the bottom of your pages.
34
+ #
35
+ # disqus_short_name: mysite
36
+
37
+ # cache
38
+ # Set it to true if you'd like Nesta to cache your pages in ./public.
39
+ # Useful if you're deploying Nesta with a proxy server such as Nginx,
40
+ # but not in the least bit helpful if your pages are dynamic, or you're
41
+ # deploying Nesta to Heroku.
42
+ #
43
+ cache: false
44
+
45
+ # content
46
+ # The root directory where nesta will look for your article files.
47
+ # Should contain "pages" and "attachments" subdirectories that contain
48
+ # your actual content and the (optional) menu.txt file that links to your
49
+ # main category pages.
50
+ #
51
+ content: content
52
+
53
+ # google_analytics_code
54
+ # Set this if you want Google Analytics to track traffic on your site.
55
+ # Probably best not to set a default value, but to set it in production.
56
+ #
57
+ # The production settings are used if you're deploying to Heroku, so
58
+ # scroll down a bit to set it in production even if you're not deploying
59
+ # to your own server.
60
+ #
61
+ # google_analytics_code: "UA-???????-?"
62
+
63
+ # Overriding "cache" and "content" in production is recommended if you're
64
+ # deploying Nesta to your own server (but see the deployment documentation
65
+ # on the Nesta site). Setting google_analytics_code in production is
66
+ # recommended regardless of how you're deploying (if you have a GA account!).
67
+ #
68
+ # Don't forget to uncomment the "production:" line too...
69
+
70
+ # production:
71
+ # cache: true
72
+ # content: /var/apps/nesta/shared/content
73
+ # google_analytics_code: "UA-???????-?"
@@ -0,0 +1,62 @@
1
+ set :application, "nesta"
2
+ set :repository, "git://github.com/gma/nesta.git"
3
+
4
+ # Set :user if you want to connect (via ssh) to your server using a
5
+ # different username. You will also need to include the user in :domain
6
+ # (see below).
7
+ #
8
+ #set :user, "deploy"
9
+ #set :domain, "#{user}@example.com"
10
+ set :domain, "example.com"
11
+
12
+ set :deploy_to, "/var/apps/#{application}"
13
+
14
+ # ============================================================================
15
+ # You probably don't need to worry about anything beneath this point...
16
+ # ============================================================================
17
+
18
+ require "tempfile"
19
+ require "vlad"
20
+
21
+ namespace :vlad do
22
+ remote_task :config_yml do
23
+ put "#{shared_path}/config.yml", "vlad.config.yml" do
24
+ File.open(File.join(File.dirname(__FILE__), "config.yml")).read
25
+ end
26
+ end
27
+
28
+ task :setup do
29
+ Rake::Task["vlad:config_yml"].invoke
30
+ end
31
+
32
+ remote_task :symlink_config_yml do
33
+ run "ln -s #{shared_path}/config.yml #{current_path}/config/config.yml"
34
+ end
35
+
36
+ remote_task :symlink_attachments do
37
+ run "ln -s #{shared_path}/content/attachments #{current_path}/public/attachments"
38
+ end
39
+
40
+ task :update do
41
+ Rake::Task["vlad:symlink_config_yml"].invoke
42
+ Rake::Task["vlad:symlink_attachments"].invoke
43
+ end
44
+
45
+ remote_task :bundle do
46
+ run "cd #{current_path} && sudo bundle install --without development test"
47
+ end
48
+
49
+ # Depending on how you host Nesta, you might want to swap :start_app
50
+ # with :start below. The :start_app task will tell your application
51
+ # server (e.g. Passenger) to restart once your new code is deployed by
52
+ # :update. Passenger is the default app server; tell Vlad that you're
53
+ # using a different app server in the call to Vlad.load in Rakefile.
54
+ #
55
+ desc "Deploy the code and restart the server"
56
+ task :deploy => [:update, :start_app]
57
+
58
+ # If you use bundler to manage the installation of gems on your server
59
+ # you can use this definition of the deploy task instead:
60
+ #
61
+ # task :deploy => [:update, :bundle, :start_app]
62
+ end
data/lib/nesta/app.rb ADDED
@@ -0,0 +1,199 @@
1
+ require "sinatra/base"
2
+ require "builder"
3
+ require "haml"
4
+ require "sass"
5
+
6
+ require File.expand_path('cache', File.dirname(__FILE__))
7
+ require File.expand_path('config', File.dirname(__FILE__))
8
+ require File.expand_path('models', File.dirname(__FILE__))
9
+ require File.expand_path('path', File.dirname(__FILE__))
10
+ require File.expand_path('plugins', File.dirname(__FILE__))
11
+ require File.expand_path('overrides', File.dirname(__FILE__))
12
+
13
+ Nesta::Plugins.load_local_plugins
14
+
15
+ module Nesta
16
+ class App < Sinatra::Base
17
+ register Sinatra::Cache
18
+
19
+ set :views, File.expand_path('../../views', File.dirname(__FILE__))
20
+ set :cache_enabled, Config.cache
21
+
22
+ helpers Overrides::Renderers
23
+
24
+ helpers do
25
+ def set_from_config(*variables)
26
+ variables.each do |var|
27
+ instance_variable_set("@#{var}", Nesta::Config.send(var))
28
+ end
29
+ end
30
+
31
+ def set_from_page(*variables)
32
+ variables.each do |var|
33
+ instance_variable_set("@#{var}", @page.send(var))
34
+ end
35
+ end
36
+
37
+ def set_title(page)
38
+ if page.respond_to?(:parent) && page.parent
39
+ @title = "#{page.heading} - #{page.parent.heading}"
40
+ else
41
+ @title = "#{page.heading} - #{Nesta::Config.title}"
42
+ end
43
+ end
44
+
45
+ def display_menu(menu, options = {})
46
+ defaults = { :class => nil, :levels => 2 }
47
+ options = defaults.merge(options)
48
+ if options[:levels] > 0
49
+ haml_tag :ul, :class => options[:class] do
50
+ menu.each do |item|
51
+ haml_tag :li do
52
+ if item.respond_to?(:each)
53
+ display_menu(item, :levels => (options[:levels] - 1))
54
+ else
55
+ haml_tag :a, :href => item.abspath do
56
+ haml_concat item.heading
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def no_widow(text)
66
+ text.split[0...-1].join(" ") + "&nbsp;#{text.split[-1]}"
67
+ end
68
+
69
+ def set_common_variables
70
+ @menu_items = Nesta::Menu.for_path('/')
71
+ @site_title = Nesta::Config.title
72
+ set_from_config(:title, :subtitle, :google_analytics_code)
73
+ @heading = @title
74
+ end
75
+
76
+ def url_for(page)
77
+ File.join(base_url, page.path)
78
+ end
79
+
80
+ def base_url
81
+ url = "http://#{request.host}"
82
+ request.port == 80 ? url : url + ":#{request.port}"
83
+ end
84
+
85
+ def absolute_urls(text)
86
+ text.gsub!(/(<a href=['"])\//, '\1' + base_url + '/')
87
+ text
88
+ end
89
+
90
+ def nesta_atom_id_for_page(page)
91
+ published = page.date.strftime('%Y-%m-%d')
92
+ "tag:#{request.host},#{published}:#{page.abspath}"
93
+ end
94
+
95
+ def atom_id(page = nil)
96
+ if page
97
+ page.atom_id || nesta_atom_id_for_page(page)
98
+ else
99
+ "tag:#{request.host},2009:/"
100
+ end
101
+ end
102
+
103
+ def format_date(date)
104
+ date.strftime("%d %B %Y")
105
+ end
106
+
107
+ def local_stylesheet?
108
+ # Checks for the existence of views/local.sass. Useful for
109
+ # themes that want to give the user the option to add their own
110
+ # CSS rules.
111
+ File.exist?(File.expand_path('views/local.sass', Nesta::App.root))
112
+ end
113
+ end
114
+
115
+ not_found do
116
+ set_common_variables
117
+ haml(:not_found)
118
+ end
119
+
120
+ error do
121
+ set_common_variables
122
+ haml(:error)
123
+ end unless Nesta::App.environment == :development
124
+
125
+ # If you want to change Nesta's behaviour, you have two options:
126
+ #
127
+ # 1. Create an app.rb file in your project's root directory.
128
+ # 2. Make a theme or a plugin, and put all your code in there.
129
+ #
130
+ # You can add new routes, or modify the behaviour of any of the
131
+ # default objects in app.rb, or replace any of the default view
132
+ # templates by creating replacements of the same name in a ./views
133
+ # folder situated in the root directory of the project for your
134
+ # site.
135
+ #
136
+ # Your ./views folder gets searched first when rendering a template
137
+ # or Sass file, then the currently configured theme is searched, and
138
+ # finally Nesta will check if the template exists in the views
139
+ # folder in the Nesta gem (which is where the default look and feel
140
+ # is defined).
141
+ #
142
+ Overrides.load_local_app
143
+ Overrides.load_theme_app
144
+
145
+ get '/robots.txt' do
146
+ content_type 'text/plain', :charset => 'utf-8'
147
+ <<-EOF
148
+ # robots.txt
149
+ # See http://en.wikipedia.org/wiki/Robots_exclusion_standard
150
+ EOF
151
+ end
152
+
153
+ get '/css/:sheet.css' do
154
+ content_type 'text/css', :charset => 'utf-8'
155
+ cache sass(params[:sheet].to_sym)
156
+ end
157
+
158
+ get '/' do
159
+ set_common_variables
160
+ set_from_config(:title, :subtitle, :description, :keywords)
161
+ @heading = @title
162
+ @title = "#{@title} - #{@subtitle}"
163
+ @articles = Page.find_articles[0..7]
164
+ @body_class = 'home'
165
+ cache haml(:index)
166
+ end
167
+
168
+ get %r{/attachments/([\w/.-]+)} do
169
+ file = File.join(Nesta::Config.attachment_path, params[:captures].first)
170
+ send_file(file, :disposition => nil)
171
+ end
172
+
173
+ get '/articles.xml' do
174
+ content_type :xml, :charset => 'utf-8'
175
+ set_from_config(:title, :subtitle, :author)
176
+ @articles = Page.find_articles.select { |a| a.date }[0..9]
177
+ cache builder(:atom)
178
+ end
179
+
180
+ get '/sitemap.xml' do
181
+ content_type :xml, :charset => 'utf-8'
182
+ @pages = Page.find_all
183
+ @last = @pages.map { |page| page.last_modified }.inject do |latest, page|
184
+ (page > latest) ? page : latest
185
+ end
186
+ cache builder(:sitemap)
187
+ end
188
+
189
+ get '*' do
190
+ set_common_variables
191
+ parts = params[:splat].map { |p| p.sub(/\/$/, '') }
192
+ @page = Nesta::Page.find_by_path(File.join(parts))
193
+ raise Sinatra::NotFound if @page.nil?
194
+ set_title(@page)
195
+ set_from_page(:description, :keywords)
196
+ cache haml(@page.template, :layout => @page.layout)
197
+ end
198
+ end
199
+ end