nesta 0.9.11 → 0.9.13

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.
@@ -50,10 +50,28 @@ module Nesta
50
50
  end
51
51
  end
52
52
 
53
+ class Edit
54
+ include Command
55
+
56
+ def initialize(*args)
57
+ @filename = Nesta::Config.page_path(args.shift)
58
+ end
59
+
60
+ def execute
61
+ editor = ENV.fetch('EDITOR')
62
+ rescue IndexError
63
+ $stderr.puts "No editor: set EDITOR environment variable"
64
+ else
65
+ system(editor, @filename)
66
+ end
67
+ end
68
+
53
69
  class New
54
70
  include Command
55
71
 
56
- def initialize(path, options = {})
72
+ def initialize(*args)
73
+ path = args.shift
74
+ options = args.shift || {}
57
75
  path.nil? && (raise UsageError.new('path not specified'))
58
76
  if File.exist?(path)
59
77
  raise RuntimeError.new("#{path} already exists")
@@ -104,7 +122,7 @@ module Nesta
104
122
  class Content
105
123
  include Command
106
124
 
107
- def initialize
125
+ def initialize(*args)
108
126
  @dir = 'content-demo'
109
127
  end
110
128
 
@@ -135,7 +153,8 @@ module Nesta
135
153
 
136
154
  module Plugin
137
155
  class Create
138
- def initialize(name)
156
+ def initialize(*args)
157
+ name = args.shift
139
158
  name.nil? && (raise UsageError.new('name not specified'))
140
159
  @name = name
141
160
  @gem_name = "nesta-plugin-#{name}"
@@ -210,7 +229,9 @@ end
210
229
  class Create
211
230
  include Command
212
231
 
213
- def initialize(name, options = {})
232
+ def initialize(*args)
233
+ name = args.shift
234
+ options = args.shift || {}
214
235
  name.nil? && (raise UsageError.new('name not specified'))
215
236
  @name = name
216
237
  @theme_path = Nesta::Path.themes(@name)
@@ -234,7 +255,9 @@ end
234
255
  class Install
235
256
  include Command
236
257
 
237
- def initialize(url, options = {})
258
+ def initialize(*args)
259
+ url = args.shift
260
+ options = args.shift || {}
238
261
  url.nil? && (raise UsageError.new('URL not specified'))
239
262
  @url = url
240
263
  @name = File.basename(url, '.git').sub(/nesta-theme-/, '')
@@ -243,14 +266,20 @@ end
243
266
  def execute
244
267
  system('git', 'clone', @url, "themes/#{@name}")
245
268
  FileUtils.rm_r(File.join("themes/#{@name}", '.git'))
246
- enable(@name)
269
+ enable
270
+ end
271
+
272
+ def enable
273
+ Enable.new(@name).execute
247
274
  end
248
275
  end
249
276
 
250
277
  class Enable
251
278
  include Command
252
279
 
253
- def initialize(name, options = {})
280
+ def initialize(*args)
281
+ name = args.shift
282
+ options = args.shift || {}
254
283
  name.nil? && (raise UsageError.new('name not specified'))
255
284
  @name = name
256
285
  end
@@ -1,7 +1,4 @@
1
- require "yaml"
2
-
3
- require "rubygems"
4
- require "sinatra"
1
+ require 'yaml'
5
2
 
6
3
  module Nesta
7
4
  class Config
@@ -68,7 +65,7 @@ module Nesta
68
65
 
69
66
  def self.from_yaml(setting)
70
67
  if can_use_yaml?
71
- self.yaml_conf ||= YAML::load(IO.read(yaml_path))
68
+ self.yaml_conf ||= YAML::load(ERB.new(IO.read(yaml_path)).result)
72
69
  rack_env_conf = self.yaml_conf[Nesta::App.environment.to_s]
73
70
  (rack_env_conf && rack_env_conf[setting]) || self.yaml_conf[setting]
74
71
  end
@@ -0,0 +1,74 @@
1
+ module Nesta
2
+ module View
3
+ module Helpers
4
+ def set_from_config(*variables)
5
+ variables.each do |var|
6
+ instance_variable_set("@#{var}", Nesta::Config.send(var))
7
+ end
8
+ end
9
+
10
+ def set_from_page(*variables)
11
+ variables.each do |var|
12
+ instance_variable_set("@#{var}", @page.send(var))
13
+ end
14
+ end
15
+
16
+ def no_widow(text)
17
+ text.split[0...-1].join(" ") + " #{text.split[-1]}"
18
+ end
19
+
20
+ def set_common_variables
21
+ @menu_items = Nesta::Menu.for_path('/')
22
+ @site_title = Nesta::Config.title
23
+ set_from_config(:title, :subtitle, :google_analytics_code)
24
+ @heading = @title
25
+ end
26
+
27
+ def absolute_urls(text)
28
+ text.gsub!(/(<a href=['"])\//, '\1' + url('/'))
29
+ text
30
+ end
31
+
32
+ def nesta_atom_id_for_page(page)
33
+ published = page.date.strftime('%Y-%m-%d')
34
+ "tag:#{request.host},#{published}:#{page.abspath}"
35
+ end
36
+
37
+ def atom_id(page = nil)
38
+ if page
39
+ page.atom_id || nesta_atom_id_for_page(page)
40
+ else
41
+ "tag:#{request.host},2009:/"
42
+ end
43
+ end
44
+
45
+ def format_date(date)
46
+ date.strftime("%d %B %Y")
47
+ end
48
+
49
+ def local_stylesheet?
50
+ Nesta.deprecated('local_stylesheet?', 'use local_stylesheet_link_tag')
51
+ File.exist?(File.expand_path('views/local.sass', Nesta::App.root))
52
+ end
53
+
54
+ def local_stylesheet_link_tag(name)
55
+ pattern = File.expand_path("views/#{name}.s{a,c}ss", Nesta::App.root)
56
+ if Dir.glob(pattern).size > 0
57
+ haml_tag :link, :href => url("/css/#{name}.css"), :rel => "stylesheet"
58
+ end
59
+ end
60
+
61
+ def latest_articles(count = 8)
62
+ Nesta::Page.find_articles[0..count - 1]
63
+ end
64
+
65
+ def article_summaries(articles)
66
+ haml(:summaries, :layout => false, :locals => { :pages => articles })
67
+ end
68
+
69
+ def articles_heading
70
+ @page.metadata('articles heading') || "Articles on #{@page.heading}"
71
+ end
72
+ end
73
+ end
74
+ end
@@ -7,6 +7,8 @@ Tilt.register Tilt::RDiscountTemplate, 'mdown'
7
7
  Tilt.register Tilt::RedcarpetTemplate, 'mdown'
8
8
 
9
9
  module Nesta
10
+ class MetadataParseError < RuntimeError; end
11
+
10
12
  class FileModel
11
13
  FORMATS = [:mdown, :haml, :textile]
12
14
  @@cache = {}
@@ -123,33 +125,49 @@ module Nesta
123
125
  flags && flags.split(',').map { |name| name.strip }.include?(flag)
124
126
  end
125
127
 
128
+ def parse_metadata(first_paragraph)
129
+ is_metadata = first_paragraph.split("\n").first =~ /^[\w ]+:/
130
+ raise MetadataParseError unless is_metadata
131
+ metadata = CaseInsensitiveHash.new
132
+ first_paragraph.split("\n").each do |line|
133
+ key, value = line.split(/\s*:\s*/, 2)
134
+ next if value.nil?
135
+ metadata[key.downcase] = value.chomp
136
+ end
137
+ metadata
138
+ end
139
+
126
140
  private
127
141
  def markup
128
142
  @markup
129
143
  end
130
144
 
131
- def metadata?(text)
132
- text.split("\n").first =~ /^[\w ]+:/
133
- end
134
-
135
145
  def parse_file
136
146
  contents = File.open(@filename).read
137
147
  rescue Errno::ENOENT
138
148
  raise Sinatra::NotFound
139
149
  else
140
150
  first_paragraph, remaining = contents.split(/\r?\n\r?\n/, 2)
141
- metadata = CaseInsensitiveHash.new
142
- if metadata?(first_paragraph)
143
- first_paragraph.split("\n").each do |line|
144
- key, value = line.split(/\s*:\s*/, 2)
145
- metadata[key.downcase] = value.chomp
151
+ begin
152
+ return parse_metadata(first_paragraph), remaining
153
+ rescue MetadataParseError
154
+ return {}, contents
155
+ end
156
+ end
157
+
158
+ def tag_lines_of_haml(text)
159
+ tagged = (text =~ /^\s*%/)
160
+ if tagged
161
+ text
162
+ else
163
+ text.split(/\r?\n/).inject("") do |accumulator, line|
164
+ accumulator << "%p #{line}\n"
146
165
  end
147
166
  end
148
- markup = metadata?(first_paragraph) ? remaining : contents
149
- return metadata, markup
150
167
  end
151
168
 
152
169
  def convert_to_html(format, scope, text)
170
+ text = tag_lines_of_haml(text) if @format == :haml
153
171
  template = Tilt[format].new { text }
154
172
  template.render(scope)
155
173
  end
@@ -237,16 +255,19 @@ module Nesta
237
255
  end
238
256
  end
239
257
 
240
- def body(scope = nil)
241
- body_text = case @format
258
+ def body_markup
259
+ case @format
242
260
  when :mdown
243
261
  markup.sub(/^#[^#].*$\r?\n(\r?\n)?/, '')
244
262
  when :haml
245
263
  markup.sub(/^\s*%h1\s+.*$\r?\n(\r?\n)?/, '')
246
264
  when :textile
247
265
  markup.sub(/^\s*h1\.\s+.*$\r?\n(\r?\n)?/, '')
248
- end
249
- convert_to_html(@format, scope, body_text)
266
+ end
267
+ end
268
+
269
+ def body(scope = nil)
270
+ convert_to_html(@format, scope, body_markup)
250
271
  end
251
272
 
252
273
  def categories
@@ -21,9 +21,9 @@ module Nesta
21
21
  end
22
22
  end
23
23
  else
24
- html_class = (request.path == item.abspath) ? "current" : nil
24
+ html_class = current_item?(item) ? "current" : nil
25
25
  haml_tag :li, :class => html_class do
26
- haml_tag :a, :<, :href => item.abspath do
26
+ haml_tag :a, :<, :href => url(item.abspath) do
27
27
  haml_concat item.heading
28
28
  end
29
29
  end
@@ -44,7 +44,7 @@ module Nesta
44
44
  haml_tag :ul, :class => options[:class] do
45
45
  breadcrumb_ancestors[0...-1].each do |page|
46
46
  haml_tag :li do
47
- haml_tag :a, :<, :href => page.abspath do
47
+ haml_tag :a, :<, :href => url(page.abspath) do
48
48
  haml_concat breadcrumb_label(page)
49
49
  end
50
50
  end
@@ -56,6 +56,10 @@ module Nesta
56
56
  def breadcrumb_label(page)
57
57
  (page.abspath == '/') ? 'Home' : page.heading
58
58
  end
59
+
60
+ def current_item?(item)
61
+ request.path == item.abspath
62
+ end
59
63
  end
60
64
  end
61
65
  end
@@ -6,6 +6,11 @@ module Nesta
6
6
  super(template, defaults.merge(options), locals)
7
7
  end
8
8
 
9
+ def erb(template, options = {}, locals = {})
10
+ defaults, engine = Overrides.render_options(template, :erb)
11
+ super(template, defaults.merge(options), locals)
12
+ end
13
+
9
14
  def scss(template, options = {}, locals = {})
10
15
  defaults, engine = Overrides.render_options(template, :scss)
11
16
  super(template, defaults.merge(options), locals)
@@ -17,17 +17,19 @@ module Nesta
17
17
  end
18
18
 
19
19
  def self.load_local_plugins
20
+ # This approach is deprecated; plugins should now be distributed
21
+ # as gems. See http://nestacms.com/docs/plugins/writing-plugins
20
22
  plugins = Dir.glob(File.expand_path('../plugins/*', File.dirname(__FILE__)))
21
23
  plugins.each { |path| require_local_plugin(path) }
22
24
  end
23
25
 
24
- private
25
- def self.require_local_plugin(path)
26
- Nesta.deprecated(
27
- 'loading plugins from ./plugins', "convert #{path} to a gem")
28
- require File.join(path, 'lib', File.basename(path))
29
- rescue LoadError => e
30
- $stderr.write("Couldn't load plugins/#{File.basename(path)}: #{e}\n")
31
- end
26
+ def self.require_local_plugin(path)
27
+ Nesta.deprecated(
28
+ 'loading plugins from ./plugins', "convert #{path} to a gem")
29
+ require File.join(path, 'lib', File.basename(path))
30
+ rescue LoadError => e
31
+ $stderr.write("Couldn't load plugins/#{File.basename(path)}: #{e}\n")
32
+ end
33
+ private_class_method :require_local_plugin
32
34
  end
33
35
  end
@@ -1,3 +1,3 @@
1
1
  module Nesta
2
- VERSION = '0.9.11'
2
+ VERSION = '0.9.13'
3
3
  end
@@ -35,7 +35,8 @@ EOF
35
35
  s.add_dependency('sass', '~> 3.1')
36
36
  s.add_dependency('rdiscount', '~> 1.6')
37
37
  s.add_dependency('RedCloth', '~> 4.2')
38
- s.add_dependency('sinatra', '1.2.6')
38
+ s.add_dependency('sinatra', '~> 1.3')
39
+ s.add_dependency('rack', '~> 1.1')
39
40
 
40
41
  # Useful in development
41
42
  s.add_dependency('shotgun', '>= 0.8')
@@ -32,7 +32,7 @@ describe "atom feed" do
32
32
  end
33
33
 
34
34
  it "should have an alternate link element" do
35
- body.should have_tag("/feed/link[@rel=alternate][@href='http://example.org']")
35
+ body.should have_tag("/feed/link[@rel=alternate][@href='http://example.org/']")
36
36
  end
37
37
 
38
38
  it "should have a self link element" do
@@ -167,6 +167,29 @@ describe "nesta" do
167
167
  end
168
168
  end
169
169
 
170
+ describe "edit" do
171
+ before(:each) do
172
+ Nesta::Config.stub!(:content_path).and_return('content')
173
+ @page_path = 'path/to/page.mdown'
174
+ @command = Nesta::Commands::Edit.new(@page_path)
175
+ @command.stub!(:system)
176
+ end
177
+
178
+ it "should launch the editor" do
179
+ ENV['EDITOR'] = 'vi'
180
+ full_path = File.join('content/pages', @page_path)
181
+ @command.should_receive(:system).with(ENV['EDITOR'], full_path)
182
+ @command.execute
183
+ end
184
+
185
+ it "should not try and launch an editor if environment not setup" do
186
+ ENV.delete('EDITOR')
187
+ @command.should_not_receive(:system)
188
+ $stderr.stub!(:puts)
189
+ @command.execute
190
+ end
191
+ end
192
+
170
193
  describe "plugin:create" do
171
194
  before(:each) do
172
195
  @name = 'my-feature'
@@ -247,6 +270,7 @@ describe "nesta" do
247
270
  @theme_dir = 'themes/mine'
248
271
  FileUtils.mkdir_p(File.join(@theme_dir, '.git'))
249
272
  @command = Nesta::Commands::Theme::Install.new(@repo_url)
273
+ @command.stub!(:enable)
250
274
  @command.stub!(:system)
251
275
  end
252
276
 
@@ -267,16 +291,17 @@ describe "nesta" do
267
291
  end
268
292
 
269
293
  it "should enable the freshly installed theme" do
270
- @command.should_receive(:enable).with('mine')
294
+ @command.should_receive(:enable)
271
295
  @command.execute
272
296
  end
273
297
 
274
- describe "when theme URL doesn't match recommendation" do
298
+ describe "when theme URL doesn't match recommended pattern" do
275
299
  before(:each) do
276
300
  @repo_url = 'git://foobar.com/path/to/mytheme.git'
277
301
  @other_theme_dir = 'themes/mytheme'
278
302
  FileUtils.mkdir_p(File.join(@other_theme_dir, '.git'))
279
303
  @command = Nesta::Commands::Theme::Install.new(@repo_url)
304
+ @command.stub!(:enable)
280
305
  end
281
306
 
282
307
  after(:each) do
@@ -58,6 +58,24 @@ describe "Page", :shared => true do
58
58
  Nesta::Page.find_by_path('banana').heading.should == 'Banana'
59
59
  end
60
60
 
61
+ it "should respond to #parse_metadata, returning hash of key/value" do
62
+ page = create_page(:heading => 'Banana', :path => 'banana')
63
+ metadata = page.parse_metadata('My key: some value')
64
+ metadata['my key'].should == 'some value'
65
+ end
66
+
67
+ it "should be parseable if metadata is invalid" do
68
+ dodgy_metadata = "Key: value\nKey without value\nAnother key: value"
69
+ create_page(:heading => 'Banana', :path => 'banana') do |path|
70
+ text = File.read(path)
71
+ File.open(path, 'w') do |file|
72
+ file.puts(dodgy_metadata)
73
+ file.write(text)
74
+ end
75
+ end
76
+ Nesta::Page.find_by_path('banana')
77
+ end
78
+
61
79
  describe "for home page" do
62
80
  it "should set title to heading and site title" do
63
81
  create_page(:heading => 'Home', :path => 'index')
@@ -396,6 +414,10 @@ describe "Page", :shared => true do
396
414
  it "should not include metadata in the HTML" do
397
415
  @article.to_html.should_not have_tag("p", /^Date/)
398
416
  end
417
+
418
+ it "should not include heading in body markup" do
419
+ @article.body_markup.should_not include("My article")
420
+ end
399
421
 
400
422
  it "should not include heading in body" do
401
423
  @article.body.should_not have_tag("h1", "My article")
@@ -501,12 +523,31 @@ describe "Haml page" do
501
523
  it_should_behave_like "Page"
502
524
 
503
525
  it "should set heading from first h1 tag" do
504
- create_page(
526
+ page = create_page(
505
527
  :path => "a-page",
506
528
  :heading => "First heading",
507
529
  :content => "%h1 Second heading"
508
530
  )
509
- Nesta::Page.find_by_path("a-page").heading.should == "First heading"
531
+ page.heading.should == "First heading"
532
+ end
533
+
534
+ it "should wrap <p> tags around one line summary text" do
535
+ page = create_page(
536
+ :path => "a-page",
537
+ :heading => "First para",
538
+ :metadata => { "Summary" => "Wrap me" }
539
+ )
540
+ page.summary.should include("<p>Wrap me</p>")
541
+ end
542
+
543
+ it "should wrap <p> tags around multiple lines of summary text" do
544
+ page = create_page(
545
+ :path => "a-page",
546
+ :heading => "First para",
547
+ :metadata => { "Summary" => 'Wrap me\nIn paragraph tags' }
548
+ )
549
+ page.summary.should include("<p>Wrap me</p>")
550
+ page.summary.should include("<p>In paragraph tags</p>")
510
551
  end
511
552
  end
512
553
 
@@ -518,12 +559,12 @@ describe "Textile page" do
518
559
  it_should_behave_like "Page"
519
560
 
520
561
  it "should set heading from first h1 tag" do
521
- create_page(
562
+ page = create_page(
522
563
  :path => "a-page",
523
564
  :heading => "First heading",
524
565
  :content => "h1. Second heading"
525
566
  )
526
- Nesta::Page.find_by_path("a-page").heading.should == "First heading"
567
+ page.heading.should == "First heading"
527
568
  end
528
569
  end
529
570