nesta 0.9.0

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 (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