radiant-snippets-extension 1.0.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.
- data/README.md +7 -0
- data/Rakefile +109 -0
- data/app/controllers/admin/snippets_controller.rb +8 -0
- data/app/helpers/admin/snippets_helper.rb +41 -0
- data/app/models/snippet.rb +23 -0
- data/app/views/admin/snippets/_form.html.haml +33 -0
- data/app/views/admin/snippets/edit.html.haml +9 -0
- data/app/views/admin/snippets/index.html.haml +33 -0
- data/app/views/admin/snippets/new.html.haml +7 -0
- data/app/views/admin/snippets/remove.html.haml +17 -0
- data/config/initializers/radiant_config.rb +3 -0
- data/config/locales/en.yml +16 -0
- data/config/routes.rb +5 -0
- data/cucumber.yml +1 -0
- data/features/admin/configuration.feature +9 -0
- data/features/admin/pagination.feature +27 -0
- data/features/admin/snippets_management.feature +82 -0
- data/features/step_definitions/admin/snippet_steps.rb +25 -0
- data/features/support/env.rb +11 -0
- data/features/support/paths.rb +22 -0
- data/lib/radiant-snippets-extension.rb +8 -0
- data/lib/snippet_tags.rb +80 -0
- data/lib/tasks/snippets_extension_tasks.rake +47 -0
- data/radiant-snippets-extension.gemspec +24 -0
- data/snippets_extension.rb +52 -0
- data/spec/ci/before_script +23 -0
- data/spec/ci/script +2 -0
- data/spec/controllers/admin/snippets_controller_spec.rb +110 -0
- data/spec/datasets/snippets_dataset.rb +38 -0
- data/spec/lib/radiant/admin_ui_spec.rb +33 -0
- data/spec/models/page_spec.rb +80 -0
- data/spec/models/snippet_spec.rb +59 -0
- data/spec/models/user_action_observer_spec.rb +28 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +36 -0
- metadata +104 -0
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# Determine where the RSpec plugin is by loading the boot
|
2
|
+
unless defined? RADIANT_ROOT
|
3
|
+
ENV["RAILS_ENV"] = "test"
|
4
|
+
case
|
5
|
+
when ENV["RADIANT_ENV_FILE"]
|
6
|
+
require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
|
7
|
+
when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
|
8
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
|
9
|
+
else
|
10
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rake'
|
15
|
+
require 'rdoc/task'
|
16
|
+
require 'rake/testtask'
|
17
|
+
|
18
|
+
rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
|
19
|
+
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
20
|
+
require 'spec/rake/spectask'
|
21
|
+
require 'cucumber'
|
22
|
+
require 'cucumber/rake/task'
|
23
|
+
|
24
|
+
# Cleanup the RADIANT_ROOT constant so specs will load the environment
|
25
|
+
Object.send(:remove_const, :RADIANT_ROOT)
|
26
|
+
|
27
|
+
extension_root = File.expand_path(File.dirname(__FILE__))
|
28
|
+
|
29
|
+
task :default => [:spec, :features]
|
30
|
+
task :stats => "spec:statsetup"
|
31
|
+
|
32
|
+
desc "Run all specs in spec directory"
|
33
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
34
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
35
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
36
|
+
end
|
37
|
+
|
38
|
+
task :features => 'spec:integration'
|
39
|
+
|
40
|
+
namespace :spec do
|
41
|
+
desc "Run all specs in spec directory with RCov"
|
42
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
43
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
44
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
45
|
+
t.rcov = true
|
46
|
+
t.rcov_opts = ['--exclude', 'spec', '--rails']
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Print Specdoc for all specs"
|
50
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
51
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
52
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
53
|
+
end
|
54
|
+
|
55
|
+
[:models, :controllers, :views, :helpers].each do |sub|
|
56
|
+
desc "Run the specs under spec/#{sub}"
|
57
|
+
Spec::Rake::SpecTask.new(sub) do |t|
|
58
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
59
|
+
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "Run the Cucumber features"
|
64
|
+
Cucumber::Rake::Task.new(:integration) do |t|
|
65
|
+
t.fork = true
|
66
|
+
t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'pretty')]
|
67
|
+
# t.feature_pattern = "#{extension_root}/features/**/*.feature"
|
68
|
+
t.profile = "default"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Setup specs for stats
|
72
|
+
task :statsetup do
|
73
|
+
require 'code_statistics'
|
74
|
+
::STATS_DIRECTORIES << %w(Model\ specs spec/models)
|
75
|
+
::STATS_DIRECTORIES << %w(View\ specs spec/views)
|
76
|
+
::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
|
77
|
+
::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
|
78
|
+
::CodeStatistics::TEST_TYPES << "Model specs"
|
79
|
+
::CodeStatistics::TEST_TYPES << "View specs"
|
80
|
+
::CodeStatistics::TEST_TYPES << "Controller specs"
|
81
|
+
::CodeStatistics::TEST_TYPES << "Helper specs"
|
82
|
+
::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
|
83
|
+
end
|
84
|
+
|
85
|
+
namespace :db do
|
86
|
+
namespace :fixtures do
|
87
|
+
desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
|
88
|
+
task :load => :environment do
|
89
|
+
require 'active_record/fixtures'
|
90
|
+
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
|
91
|
+
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
|
92
|
+
Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
desc 'Generate documentation for the snippets extension.'
|
100
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
101
|
+
rdoc.rdoc_dir = 'rdoc'
|
102
|
+
rdoc.title = 'SnippetsExtension'
|
103
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
104
|
+
rdoc.rdoc_files.include('README')
|
105
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
106
|
+
end
|
107
|
+
|
108
|
+
# Load any custom rakefiles for extension
|
109
|
+
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class Admin::SnippetsController < Admin::ResourceController
|
2
|
+
paginate_models
|
3
|
+
only_allow_access_to :index, :show, :new, :create, :edit, :update, :remove, :destroy,
|
4
|
+
:when => [:designer, :admin],
|
5
|
+
:denied_url => { :controller => 'admin/pages', :action => 'index' },
|
6
|
+
:denied_message => 'You must have designer privileges to perform this action.'
|
7
|
+
|
8
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Admin::SnippetsHelper
|
2
|
+
def snippet_edit_javascripts
|
3
|
+
<<-CODE
|
4
|
+
|
5
|
+
var tagReferenceWindows = {};
|
6
|
+
function loadTagReference() {
|
7
|
+
var pageType = 'Page';
|
8
|
+
if (!tagReferenceWindows[pageType])
|
9
|
+
tagReferenceWindows[pageType] = new Popup.AjaxWindow("#{admin_reference_path('tags')}?class_name=" + encodeURIComponent(pageType), {reload: false});
|
10
|
+
var window = tagReferenceWindows[pageType];
|
11
|
+
if('Page' != pageType) {
|
12
|
+
$('tag_reference_link').highlight();
|
13
|
+
window.show();
|
14
|
+
} else {
|
15
|
+
window.toggle();
|
16
|
+
}
|
17
|
+
lastPageType = pageType;
|
18
|
+
return false;
|
19
|
+
}
|
20
|
+
|
21
|
+
var lastFilter = '#{@snippet.filter_id}';
|
22
|
+
var filterWindows = {};
|
23
|
+
function loadFilterReference() {
|
24
|
+
var filter = $F("snippet_filter_id");
|
25
|
+
if (filter != "") {
|
26
|
+
if (!filterWindows[filter]) filterWindows[filter] = new Popup.AjaxWindow("#{admin_reference_path('filters')}?filter_name="+encodeURIComponent(filter), {reload: false});
|
27
|
+
var window = filterWindows[filter];
|
28
|
+
if(lastFilter != filter) {
|
29
|
+
window.show();
|
30
|
+
} else {
|
31
|
+
window.toggle();
|
32
|
+
}
|
33
|
+
lastFilter = filter;
|
34
|
+
} else {
|
35
|
+
alert('No documentation for filter.');
|
36
|
+
}
|
37
|
+
return false;
|
38
|
+
}
|
39
|
+
CODE
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Snippet < ActiveRecord::Base
|
2
|
+
|
3
|
+
# Default Order
|
4
|
+
default_scope :order => 'name'
|
5
|
+
|
6
|
+
# Associations
|
7
|
+
belongs_to :created_by, :class_name => 'User'
|
8
|
+
belongs_to :updated_by, :class_name => 'User'
|
9
|
+
|
10
|
+
# Validations
|
11
|
+
validates_presence_of :name
|
12
|
+
validates_length_of :name, :maximum => 100
|
13
|
+
validates_length_of :filter_id, :maximum => 25, :allow_nil => true
|
14
|
+
validates_format_of :name, :with => %r{^\S*$}
|
15
|
+
validates_uniqueness_of :name
|
16
|
+
|
17
|
+
object_id_attr :filter, TextFilter
|
18
|
+
|
19
|
+
def after_initialize
|
20
|
+
self.filter_id ||= Radiant::Config['defaults.snippet.filter'] if new_record?
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
- form_for [:admin, @snippet], :html => {'data-onsubmit_status' => onsubmit_status(@snippet)} do |f|
|
2
|
+
= f.hidden_field :lock_version
|
3
|
+
= render_region :form_top, :locals => {:f => f}
|
4
|
+
.form_area
|
5
|
+
- render_region :form, :locals => {:f => f} do |form|
|
6
|
+
- form.edit_title do
|
7
|
+
%p.title
|
8
|
+
= f.label :name, t('name')
|
9
|
+
= f.text_field :name, :class => 'textbox activate', :maxlength => 100
|
10
|
+
- form.edit_content do
|
11
|
+
%p.content
|
12
|
+
= f.label :content, t('body')
|
13
|
+
~ f.text_area :content, :class => "textarea large", :style => "width: 100%"
|
14
|
+
- form.edit_filter do
|
15
|
+
.set
|
16
|
+
%p
|
17
|
+
= f.label :filter_id, t('filter')
|
18
|
+
= f.select :filter_id, filter_options_for_select(f.object.filter_id), :id => 'snippet_filter'
|
19
|
+
%span.reference_links
|
20
|
+
== #{t('reference')}:
|
21
|
+
%span{:id => "filter_reference_link"}
|
22
|
+
= link_to_function t('filter'), "loadFilterReference();"
|
23
|
+
%span{:id => "tag_reference_link"}
|
24
|
+
= link_to_function t('available_tags'), "loadTagReference('Page');"
|
25
|
+
- render_region :form_bottom, :locals => {:f => f} do |form_bottom|
|
26
|
+
- form_bottom.edit_buttons do
|
27
|
+
.buttons{:style=>"clear: left"}
|
28
|
+
= save_model_button(@snippet)
|
29
|
+
= save_model_and_continue_editing_button(@snippet)
|
30
|
+
= t('or')
|
31
|
+
= link_to t('cancel'), admin_snippets_url
|
32
|
+
- form_bottom.edit_timestamp do
|
33
|
+
= updated_stamp @snippet
|
@@ -0,0 +1,9 @@
|
|
1
|
+
- @page_title = @snippet.name + ' ' + t('snippet') + ' - ' + default_page_title
|
2
|
+
|
3
|
+
- render_region :main do |main|
|
4
|
+
- main.edit_header do
|
5
|
+
%h1= t('edit_snippet')
|
6
|
+
- main.edit_form do
|
7
|
+
= render :partial => 'form'
|
8
|
+
|
9
|
+
- content_for 'page_scripts', snippet_edit_javascripts
|
@@ -0,0 +1,33 @@
|
|
1
|
+
- @page_title = t('snippets') + ' - ' + default_page_title
|
2
|
+
|
3
|
+
.outset
|
4
|
+
= render_region :top
|
5
|
+
%table.index#snippets
|
6
|
+
%thead
|
7
|
+
%tr
|
8
|
+
- render_region :thead do |thead|
|
9
|
+
- thead.title_header do
|
10
|
+
%th.name= t('snippet')
|
11
|
+
- thead.actions_header do
|
12
|
+
%th.actions{:style=>"width:9em"}= t('modify')
|
13
|
+
%tbody
|
14
|
+
- if @snippets.any?
|
15
|
+
- @snippets.each do |snippet|
|
16
|
+
%tr[snippet]
|
17
|
+
- render_region :tbody, :locals => {:snippet => snippet} do |tbody|
|
18
|
+
- tbody.title_cell do
|
19
|
+
%td.name
|
20
|
+
= link_to image('snippet', :alt => '') + ' ' + snippet.name, edit_admin_snippet_url(snippet)
|
21
|
+
- tbody.actions_cell do
|
22
|
+
%td.actions
|
23
|
+
= link_to image('minus') + ' ' + t('remove'), remove_admin_snippet_url(snippet), :class => "action"
|
24
|
+
- else
|
25
|
+
%tr
|
26
|
+
%td.empty{:colspan => admin.snippet.index.tbody.length}= t('no_snippets')
|
27
|
+
|
28
|
+
- render_region :bottom do |bottom|
|
29
|
+
- bottom.new_button do
|
30
|
+
#actions
|
31
|
+
= pagination_for(@snippets)
|
32
|
+
%ul
|
33
|
+
%li= link_to image('plus') + " " + t('new_snippet'), new_admin_snippet_url, :class => 'action_button'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
%h1= t('remove_snippet')
|
2
|
+
|
3
|
+
%p
|
4
|
+
= t('text.snippets.remove_warning')
|
5
|
+
|
6
|
+
%table.index#snippets
|
7
|
+
%tbody
|
8
|
+
%tr.node.level_1
|
9
|
+
%td.snippet
|
10
|
+
= image('snippet', :alt => "")
|
11
|
+
%span.title= @snippet.name
|
12
|
+
|
13
|
+
- form_for [:admin, @snippet], :html => {:method => :delete, 'data-onsubmit_status'=>"Removing snippet…"} do
|
14
|
+
.buttons
|
15
|
+
%input.button{:type=>"submit", :value => t('delete_snippet')}/
|
16
|
+
= t('or')
|
17
|
+
= link_to t('cancel'), admin_snippets_url
|
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
en:
|
3
|
+
snippet: 'Snippet'
|
4
|
+
snippets: 'Snippets'
|
5
|
+
config:
|
6
|
+
defaults:
|
7
|
+
snippet:
|
8
|
+
filter: "snippet filter"
|
9
|
+
delete_snippet: 'Delete Snippet'
|
10
|
+
edit_snippet: 'Edit Snippet'
|
11
|
+
new_snippet: 'New Snippet'
|
12
|
+
no_snippets: 'No Snippets'
|
13
|
+
remove_snippet: 'Remove Snippet'
|
14
|
+
text:
|
15
|
+
snippets:
|
16
|
+
remove_warning: 'Are you sure you want to <strong class="warning">permanently remove</strong> the following snippet?'
|
data/config/routes.rb
ADDED
data/cucumber.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
default: --format progress features --tags ~@proposed,~@in_progress
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Feature: Rich configuration
|
2
|
+
In order to control site delivery
|
3
|
+
an administrator
|
4
|
+
wants to change the configuration of the site
|
5
|
+
|
6
|
+
Scenario: Reviewing configuration
|
7
|
+
Given I am logged in as "admin"
|
8
|
+
And I go to the "configuration" admin page
|
9
|
+
Then I should see "Snippet Filter"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Feature: Paginating admin views
|
2
|
+
In order to reduce load time and page bulk
|
3
|
+
a content editor
|
4
|
+
wants longer resource lists to be paginated
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given There are many snippets
|
8
|
+
And I am logged in as "designer"
|
9
|
+
And I go to the "snippets" admin page
|
10
|
+
|
11
|
+
Scenario: More snippets than we want to show on one page
|
12
|
+
Then I should see "first"
|
13
|
+
And I should not see "snippet_50"
|
14
|
+
And I should see page 1 of the results
|
15
|
+
And I should see pagination controls
|
16
|
+
And I should see a depagination link
|
17
|
+
|
18
|
+
When I follow "2" within "div.pagination"
|
19
|
+
Then I should see page 2 of the results
|
20
|
+
And I should see "snippet_50"
|
21
|
+
|
22
|
+
When I follow "show all"
|
23
|
+
# Then I should mention the request parameters
|
24
|
+
Then I should see all the snippets
|
25
|
+
And I should not see pagination controls
|
26
|
+
And I should not see a depagination link
|
27
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
Feature: Managing snippets
|
2
|
+
In order to share content between layouts and pages, as a designer I want to
|
3
|
+
manage a collection of snippets
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given I am logged in as "designer"
|
7
|
+
|
8
|
+
Scenario: List snippets
|
9
|
+
When I follow "Design" within "#navigation"
|
10
|
+
And I follow "Snippets"
|
11
|
+
Then I should see "first"
|
12
|
+
And I should see "another"
|
13
|
+
And I should see "markdown"
|
14
|
+
# And a host of others
|
15
|
+
|
16
|
+
Scenario: Create a snippet
|
17
|
+
When I follow "Design" within "#navigation"
|
18
|
+
And I follow "Snippets"
|
19
|
+
And I follow "New Snippet"
|
20
|
+
And I fill in "Name" with "Mine"
|
21
|
+
And I fill in "Body" with "My snippet"
|
22
|
+
And I press "Create Snippet"
|
23
|
+
Then I should be on the snippets list
|
24
|
+
And I should see "Mine"
|
25
|
+
|
26
|
+
Scenario: Display form errors
|
27
|
+
When I follow "Design" within "#navigation"
|
28
|
+
And I follow "Snippets"
|
29
|
+
And I follow "New Snippet"
|
30
|
+
And I fill in "Body" with "My snippet"
|
31
|
+
And I press "Create Snippet"
|
32
|
+
Then I should see an error message
|
33
|
+
And I should see the form
|
34
|
+
|
35
|
+
Scenario: Continue editing
|
36
|
+
When I follow "Design" within "#navigation"
|
37
|
+
And I follow "Snippets"
|
38
|
+
And I follow "New Snippet"
|
39
|
+
And I fill in "Name" with "Mine"
|
40
|
+
And I fill in "Body" with "My snippet"
|
41
|
+
And I press "Save and Continue Editing"
|
42
|
+
Then I should see "Edit Snippet"
|
43
|
+
And I should see the form
|
44
|
+
|
45
|
+
Scenario: View a snippet
|
46
|
+
When I view a filtered snippet
|
47
|
+
Then I should see "Edit Snippet"
|
48
|
+
And I should see "Markdown"
|
49
|
+
|
50
|
+
Scenario: Delete a snippet with confirmation
|
51
|
+
When I follow "Design" within "#navigation"
|
52
|
+
And I follow "Snippets"
|
53
|
+
And I follow "Remove"
|
54
|
+
Then I should see "permanently remove"
|
55
|
+
And I should see "another"
|
56
|
+
When I press "Delete Snippet"
|
57
|
+
Then I should not see "another"
|
58
|
+
|
59
|
+
Scenario Outline: Admins and designers can see and edit snippets
|
60
|
+
Given I am logged in as "<username>"
|
61
|
+
And I should see "Design"
|
62
|
+
When I follow "Design" within "#navigation"
|
63
|
+
And I follow "Snippets"
|
64
|
+
And I should not see "You must have designer privileges"
|
65
|
+
And I follow "first"
|
66
|
+
Then I should see "Edit Snippet"
|
67
|
+
|
68
|
+
Examples:
|
69
|
+
| username |
|
70
|
+
| admin |
|
71
|
+
| designer |
|
72
|
+
|
73
|
+
Scenario Outline: Ordinary users cannot edit snippets
|
74
|
+
Given I am logged in as "<username>"
|
75
|
+
And I should not see "Design"
|
76
|
+
When I go to the "snippets" admin page
|
77
|
+
Then I should see "You must have designer privileges"
|
78
|
+
|
79
|
+
Examples:
|
80
|
+
| username |
|
81
|
+
| existing |
|
82
|
+
| another |
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Given /^There are many snippets$/ do
|
2
|
+
100.times do |i|
|
3
|
+
Snippet.create(:name => "snippet_#{i}", :content => "This is snippet #{i}")
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
Given /^There are few snippets$/ do
|
8
|
+
#
|
9
|
+
end
|
10
|
+
|
11
|
+
Then /^I should see all the snippets$/ do
|
12
|
+
Snippet.all.each do |snippet|
|
13
|
+
response.body.should have_tag('tr.snippet') do
|
14
|
+
with_tag("a", :text => snippet.name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
When /^I view a snippet$/ do
|
20
|
+
visit "/admin/snippets/#{snippets(:first).id}"
|
21
|
+
end
|
22
|
+
|
23
|
+
When /^I view a filtered snippet$/ do
|
24
|
+
visit "/admin/snippets/#{snippets(:markdown).id}"
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Sets up the Rails environment for Cucumber
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
# Extension root
|
4
|
+
extension_env = File.expand_path(File.dirname(__FILE__) + '/../../../../../config/environment')
|
5
|
+
require extension_env+'.rb'
|
6
|
+
|
7
|
+
Dir.glob(File.join(RADIANT_ROOT, "features", "**", "*.rb")).each {|step| require step unless step =~ /datasets_loader\.rb$/}
|
8
|
+
|
9
|
+
Cucumber::Rails::World.class_eval do
|
10
|
+
dataset :snippets
|
11
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module NavigationHelpers
|
2
|
+
|
3
|
+
# Extend the standard PathMatchers with your own paths
|
4
|
+
# to be used in your features.
|
5
|
+
#
|
6
|
+
# The keys and values here may be used in your standard web steps
|
7
|
+
# Using:
|
8
|
+
#
|
9
|
+
# When I go to the "snippets" admin page
|
10
|
+
#
|
11
|
+
# would direct the request to the path you provide in the value:
|
12
|
+
#
|
13
|
+
# admin_snippets_path
|
14
|
+
#
|
15
|
+
PathMatchers = {} unless defined?(PathMatchers)
|
16
|
+
PathMatchers.merge!({
|
17
|
+
/snippets/i => 'admin_snippets_path'
|
18
|
+
})
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
World(NavigationHelpers)
|
data/lib/snippet_tags.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module SnippetTags
|
2
|
+
include Radiant::Taggable
|
3
|
+
|
4
|
+
class TagError < StandardError; end
|
5
|
+
|
6
|
+
desc %{
|
7
|
+
Renders the snippet specified in the @name@ attribute within the context of a page.
|
8
|
+
|
9
|
+
*Usage:*
|
10
|
+
|
11
|
+
<pre><code><r:snippet name="snippet_name" /></code></pre>
|
12
|
+
|
13
|
+
When used as a double tag, the part in between both tags may be used within the
|
14
|
+
snippet itself, being substituted in place of @<r:yield/>@.
|
15
|
+
|
16
|
+
*Usage:*
|
17
|
+
|
18
|
+
<pre><code><r:snippet name="snippet_name">Lorem ipsum dolor...</r:snippet></code></pre>
|
19
|
+
}
|
20
|
+
tag 'snippet' do |tag|
|
21
|
+
required_attr(tag, 'name')
|
22
|
+
name = tag['name']
|
23
|
+
|
24
|
+
snippet = snippet_cache(name.strip)
|
25
|
+
|
26
|
+
if snippet
|
27
|
+
tag.locals.yield = tag.expand if tag.double?
|
28
|
+
tag.globals.page.render_snippet(snippet)
|
29
|
+
else
|
30
|
+
raise TagError.new("snippet '#{name}' not found")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def snippet_cache(name)
|
35
|
+
@snippet_cache ||= {}
|
36
|
+
|
37
|
+
snippet = @snippet_cache[name]
|
38
|
+
unless snippet
|
39
|
+
snippet = Snippet.find_by_name(name)
|
40
|
+
@snippet_cache[name] = snippet
|
41
|
+
end
|
42
|
+
snippet
|
43
|
+
end
|
44
|
+
private :snippet_cache
|
45
|
+
|
46
|
+
desc %{
|
47
|
+
Used within a snippet as a placeholder for substitution of child content, when
|
48
|
+
the snippet is called as a double tag.
|
49
|
+
|
50
|
+
*Usage (within a snippet):*
|
51
|
+
|
52
|
+
<pre><code>
|
53
|
+
<div id="outer">
|
54
|
+
<p>before</p>
|
55
|
+
<r:yield/>
|
56
|
+
<p>after</p>
|
57
|
+
</div>
|
58
|
+
</code></pre>
|
59
|
+
|
60
|
+
If the above snippet was named "yielding", you could call it from any Page,
|
61
|
+
Layout or Snippet as follows:
|
62
|
+
|
63
|
+
<pre><code><r:snippet name="yielding">Content within</r:snippet></code></pre>
|
64
|
+
|
65
|
+
Which would output the following:
|
66
|
+
|
67
|
+
<pre><code>
|
68
|
+
<div id="outer">
|
69
|
+
<p>before</p>
|
70
|
+
Content within
|
71
|
+
<p>after</p>
|
72
|
+
</div>
|
73
|
+
</code></pre>
|
74
|
+
|
75
|
+
When called in the context of a Page or a Layout, @<r:yield/>@ outputs nothing.
|
76
|
+
}
|
77
|
+
tag 'yield' do |tag|
|
78
|
+
tag.locals.yield
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
namespace :radiant do
|
2
|
+
namespace :extensions do
|
3
|
+
namespace :snippets do
|
4
|
+
|
5
|
+
desc "Runs the migration of the Snippets extension"
|
6
|
+
task :migrate => :environment do
|
7
|
+
require 'radiant/extension_migrator'
|
8
|
+
if ENV["VERSION"]
|
9
|
+
SnippetsExtension.migrator.migrate(ENV["VERSION"].to_i)
|
10
|
+
Rake::Task['db:schema:dump'].invoke
|
11
|
+
else
|
12
|
+
SnippetsExtension.migrator.migrate
|
13
|
+
Rake::Task['db:schema:dump'].invoke
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Copies public assets of the Snippets to the instance public/ directory."
|
18
|
+
task :update => :environment do
|
19
|
+
is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
|
20
|
+
puts "Copying assets from SnippetsExtension"
|
21
|
+
Dir[SnippetsExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
|
22
|
+
path = file.sub(SnippetsExtension.root, '')
|
23
|
+
directory = File.dirname(path)
|
24
|
+
mkdir_p RAILS_ROOT + directory, :verbose => false
|
25
|
+
cp file, RAILS_ROOT + path, :verbose => false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Syncs all available translations for this ext to the English ext master"
|
30
|
+
task :sync => :environment do
|
31
|
+
# The main translation root, basically where English is kept
|
32
|
+
language_root = SnippetsExtension.root + "/config/locales"
|
33
|
+
words = TranslationSupport.get_translation_keys(language_root)
|
34
|
+
|
35
|
+
Dir["#{language_root}/*.yml"].each do |filename|
|
36
|
+
next if filename.match('_available_tags')
|
37
|
+
basename = File.basename(filename, '.yml')
|
38
|
+
puts "Syncing #{basename}"
|
39
|
+
(comments, other) = TranslationSupport.read_file(filename, basename)
|
40
|
+
words.each { |k,v| other[k] ||= words[k] } # Initializing hash variable as empty if it does not exist
|
41
|
+
other.delete_if { |k,v| !words[k] } # Remove if not defined in en.yml
|
42
|
+
TranslationSupport.write_file(filename, basename, comments, other)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "radiant-snippets-extension"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "radiant-snippets-extension"
|
7
|
+
s.version = RadiantSnippetsExtension::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = RadiantSnippetsExtension::AUTHORS
|
10
|
+
s.email = RadiantSnippetsExtension::EMAIL
|
11
|
+
s.homepage = RadiantSnippetsExtension::URL
|
12
|
+
s.summary = RadiantSnippetsExtension::SUMMARY
|
13
|
+
s.description = RadiantSnippetsExtension::DESCRIPTION
|
14
|
+
|
15
|
+
ignores = if File.exist?('.gitignore')
|
16
|
+
File.read('.gitignore').split("\n").inject([]) {|a,p| a + Dir[p] }
|
17
|
+
else
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
s.files = Dir['**/*'] - ignores
|
21
|
+
s.test_files = Dir['test/**/*','spec/**/*','features/**/*'] - ignores
|
22
|
+
# s.executables = Dir['bin/*'] - ignores
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Uncomment this if you reference any of your controllers in activate
|
2
|
+
# require_dependency "application_controller"
|
3
|
+
require "radiant-snippets-extension"
|
4
|
+
|
5
|
+
class SnippetsExtension < Radiant::Extension
|
6
|
+
version RadiantSnippetsExtension::VERSION
|
7
|
+
description RadiantSnippetsExtension::DESCRIPTION
|
8
|
+
url RadiantSnippetsExtension::URL
|
9
|
+
|
10
|
+
def activate
|
11
|
+
|
12
|
+
if defined?(Radiant::Exporter)
|
13
|
+
Radiant::Exporter.exportable_models << Snippet
|
14
|
+
Radiant::Exporter.template_models << Snippet
|
15
|
+
end
|
16
|
+
|
17
|
+
Page.class_eval do
|
18
|
+
include SnippetTags
|
19
|
+
end
|
20
|
+
|
21
|
+
Radiant::AdminUI.class_eval do
|
22
|
+
attr_accessor :snippet
|
23
|
+
alias_method :snippets, :snippet
|
24
|
+
|
25
|
+
def load_default_snippet_regions
|
26
|
+
OpenStruct.new.tap do |snippet|
|
27
|
+
snippet.edit = Radiant::AdminUI::RegionSet.new do |edit|
|
28
|
+
edit.main.concat %w{edit_header edit_form}
|
29
|
+
edit.form.concat %w{edit_title edit_content edit_filter}
|
30
|
+
edit.form_bottom.concat %w{edit_buttons edit_timestamp}
|
31
|
+
end
|
32
|
+
snippet.index = Radiant::AdminUI::RegionSet.new do |index|
|
33
|
+
index.top.concat %w{}
|
34
|
+
index.thead.concat %w{title_header actions_header}
|
35
|
+
index.tbody.concat %w{title_cell actions_cell}
|
36
|
+
index.bottom.concat %w{new_button}
|
37
|
+
end
|
38
|
+
snippet.new = snippet.edit
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
admin.snippet ||= Radiant::AdminUI.load_default_snippet_regions
|
44
|
+
|
45
|
+
UserActionObserver.instance.send :add_observer!, Snippet
|
46
|
+
|
47
|
+
tab 'Design' do
|
48
|
+
add_item "Snippets", "/admin/snippets"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
cd ~
|
2
|
+
git clone git://github.com/radiant/radiant.git
|
3
|
+
cd ~/radiant
|
4
|
+
if [[ $RADIANT_VERSION != "master" ]]
|
5
|
+
then
|
6
|
+
git checkout -b $RADIANT_VERSION $RADIANT_VERSION
|
7
|
+
fi
|
8
|
+
cp -r ~/builds/*/radiant-snippets-extension vendor/extensions/snippets
|
9
|
+
gem install bundler --pre
|
10
|
+
echo 'gem "radiant-snippets-extension", :path => "vendor/extensions/snippets"' >> Gemfile
|
11
|
+
bundle install
|
12
|
+
|
13
|
+
case $DB in
|
14
|
+
"mysql" )
|
15
|
+
mysql -e 'create database radiant_test;'
|
16
|
+
cp spec/ci/database.mysql.yml config/database.yml;;
|
17
|
+
"postgres" )
|
18
|
+
psql -c 'create database radiant_test;' -U postgres
|
19
|
+
cp spec/ci/database.postgresql.yml config/database.yml;;
|
20
|
+
esac
|
21
|
+
|
22
|
+
bundle exec rake db:migrate
|
23
|
+
bundle exec rake db:migrate:extensions
|
data/spec/ci/script
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../spec_helper"
|
2
|
+
|
3
|
+
describe Admin::SnippetsController do
|
4
|
+
dataset :users, :snippets
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
ActionController::Routing::Routes.reload
|
8
|
+
login_as :designer
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should be an ResourceController" do
|
12
|
+
controller.should be_kind_of(Admin::ResourceController)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should handle Snippets" do
|
16
|
+
controller.class.model_class.should == Snippet
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
describe "show" do
|
21
|
+
it "should redirect to the edit action" do
|
22
|
+
get :show, :id => 1
|
23
|
+
response.should redirect_to(edit_admin_snippet_path(params[:id]))
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should show xml when format is xml" do
|
27
|
+
snippet = Snippet.first
|
28
|
+
get :show, :id => snippet.id, :format => "xml"
|
29
|
+
response.body.should == snippet.to_xml
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "with invalid snippet id" do
|
34
|
+
[:edit, :remove].each do |action|
|
35
|
+
before do
|
36
|
+
@parameters = {:id => 999}
|
37
|
+
end
|
38
|
+
it "should redirect the #{action} action to the index action" do
|
39
|
+
get action, @parameters
|
40
|
+
response.should redirect_to(admin_snippets_path)
|
41
|
+
end
|
42
|
+
it "should say that the 'Snippet could not be found.' after the #{action} action" do
|
43
|
+
get action, @parameters
|
44
|
+
flash[:notice].should == 'Snippet could not be found.'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
it 'should redirect the update action to the index action' do
|
48
|
+
put :update, @parameters
|
49
|
+
response.should redirect_to(admin_snippets_path)
|
50
|
+
end
|
51
|
+
it "should say that the 'Snippet could not be found.' after the update action" do
|
52
|
+
put :update, @parameters
|
53
|
+
flash[:notice].should == 'Snippet could not be found.'
|
54
|
+
end
|
55
|
+
it 'should redirect the destroy action to the index action' do
|
56
|
+
delete :destroy, @parameters
|
57
|
+
response.should redirect_to(admin_snippets_path)
|
58
|
+
end
|
59
|
+
it "should say that the 'Snippet could not be found.' after the destroy action" do
|
60
|
+
delete :destroy, @parameters
|
61
|
+
flash[:notice].should == 'Snippet could not be found.'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
{:get => [:index, :show, :new, :edit, :remove],
|
66
|
+
:post => [:create],
|
67
|
+
:put => [:update],
|
68
|
+
:delete => [:destroy]}.each do |method, actions|
|
69
|
+
actions.each do |action|
|
70
|
+
it "should require login to access the #{action} action" do
|
71
|
+
logout
|
72
|
+
lambda { send(method, action, :id => snippet_id(:first)) }.should require_login
|
73
|
+
end
|
74
|
+
|
75
|
+
if action == :show
|
76
|
+
it "should request authentication for API access on show" do
|
77
|
+
logout
|
78
|
+
send(method, action, :id => snippet_id(:first), :format => "xml")
|
79
|
+
response.response_code.should == 401
|
80
|
+
end
|
81
|
+
else
|
82
|
+
it "should allow access to designers for the #{action} action" do
|
83
|
+
lambda {
|
84
|
+
send(method, action, :id => snippet_id(:first))
|
85
|
+
}.should restrict_access(:allow => [users(:designer)],
|
86
|
+
:url => '/admin/pages')
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should allow access to admins for the #{action} action" do
|
90
|
+
lambda {
|
91
|
+
send(method, action, :id => snippet_id(:first))
|
92
|
+
}.should restrict_access(:allow => [users(:designer)],
|
93
|
+
:url => '/admin/pages')
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should deny non-designers and non-admins for the #{action} action" do
|
97
|
+
lambda {
|
98
|
+
send(method, action, :id => Snippet.first.id)
|
99
|
+
}.should restrict_access(:deny => [users(:non_admin), users(:existing)],
|
100
|
+
:url => '/admin/pages')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should clear the page cache when saved" do
|
107
|
+
Radiant::Cache.should_receive(:clear)
|
108
|
+
put :update, :id => snippet_id(:first), :snippet => {:content => "Foobar."}
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class SnippetsDataset < Dataset::Base
|
2
|
+
|
3
|
+
def load
|
4
|
+
create_snippet "first", :content => "test"
|
5
|
+
create_snippet "another", :content => "another test"
|
6
|
+
create_snippet "markdown", :filter_id => "Markdown", :content => "**markdown**"
|
7
|
+
create_snippet "radius", :content => "<r:title />"
|
8
|
+
create_snippet "global_page_cascade", :content => "<r:children:each><r:page:title /> </r:children:each>"
|
9
|
+
create_snippet "recursive", :content => "<r:children:each><r:snippet name='recursive' /></r:children:each><r:title />"
|
10
|
+
create_snippet "yielding", :content => "Before...<r:yield/>...and after"
|
11
|
+
create_snippet "div_wrap", :content => "<div><r:yield/></div>"
|
12
|
+
create_snippet "nested_yields", :content => '<snippet name="div_wrap">above <r:yield/> below</snippet>'
|
13
|
+
create_snippet "yielding_often", :content => '<r:yield/> is <r:yield/>er than <r:yield/>'
|
14
|
+
end
|
15
|
+
|
16
|
+
helpers do
|
17
|
+
def create_snippet(name, attributes={})
|
18
|
+
create_record :snippet, name.symbolize, snippet_params(attributes.reverse_merge(:name => name))
|
19
|
+
end
|
20
|
+
|
21
|
+
def snippet_params(attributes={})
|
22
|
+
name = attributes[:name] || unique_snippet_name
|
23
|
+
{
|
24
|
+
:name => name,
|
25
|
+
:content => "<r:content />"
|
26
|
+
}.merge(attributes)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def unique_snippet_name
|
32
|
+
@@unique_snippet_name_call_count ||= 0
|
33
|
+
@@unique_snippet_name_call_count += 1
|
34
|
+
"snippet-#{@@unique_snippet_name_call_count}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../spec_helper"
|
2
|
+
|
3
|
+
describe Radiant::AdminUI do
|
4
|
+
let(:admin) do
|
5
|
+
@admin = Radiant::AdminUI.new
|
6
|
+
@admin.snippet = @admin.load_default_snippet_regions
|
7
|
+
@admin
|
8
|
+
end
|
9
|
+
|
10
|
+
subject{ admin }
|
11
|
+
its(:snippet){ should_not be_nil }
|
12
|
+
its(:snippets){ should_not be_nil }
|
13
|
+
|
14
|
+
context 'edit Region' do
|
15
|
+
subject{ admin.snippet.edit }
|
16
|
+
its(:main){ should == %w{ edit_header edit_form } }
|
17
|
+
its(:form){ should == %w{ edit_title edit_content edit_filter } }
|
18
|
+
its(:form_bottom){ should == %w{ edit_buttons edit_timestamp } }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'new Region' do
|
22
|
+
subject{ admin.snippet.new }
|
23
|
+
its(:main){ should == %w{ edit_header edit_form } }
|
24
|
+
its(:form){ should == %w{ edit_title edit_content edit_filter } }
|
25
|
+
its(:form_bottom){ should == %w{ edit_buttons edit_timestamp } }
|
26
|
+
end
|
27
|
+
|
28
|
+
subject{ admin.snippet.index }
|
29
|
+
its(:top){ should == [] }
|
30
|
+
its(:thead){ should == %w{ title_header actions_header } }
|
31
|
+
its(:tbody){ should == %w{ title_cell actions_cell } }
|
32
|
+
its(:bottom){ should == %w{ new_button } }
|
33
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "Page rendering snippets" do
|
4
|
+
dataset :pages, :snippets
|
5
|
+
|
6
|
+
test_helper :render
|
7
|
+
|
8
|
+
let(:page){ pages(:home) }
|
9
|
+
|
10
|
+
it 'should render a snippet' do
|
11
|
+
page.render_snippet(snippets(:first)).should == 'test'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should render a snippet with a filter' do
|
15
|
+
page.render_snippet(snippets(:markdown)).should match(%r{<p><strong>markdown</strong></p>})
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should render a snippet with a tag' do
|
19
|
+
page.render_snippet(snippets(:radius)).should == 'Home'
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "<r:snippet>" do
|
23
|
+
it "should render the contents of the specified snippet" do
|
24
|
+
page.should render('<r:snippet name="first" />').as('test')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should render an error when the snippet does not exist" do
|
28
|
+
page.should render('<r:snippet name="non-existant" />').with_error("snippet 'non-existant' not found")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should render an error when not given a 'name' attribute" do
|
32
|
+
page.should render('<r:snippet />').with_error("`snippet' tag must contain a `name' attribute.")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should filter the snippet with its assigned filter" do
|
36
|
+
page.should render('<r:page><r:snippet name="markdown" /></r:page>').matching(%r{<p><strong>markdown</strong></p>})
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should maintain the global page inside the snippet" do
|
40
|
+
pages(:parent).should render('<r:snippet name="global_page_cascade" />').as("#{page.title} " * page.children.count)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should maintain the global page when the snippet renders recursively" do
|
44
|
+
pages(:child).should render('<r:snippet name="recursive" />').as("Great GrandchildGrandchildChild")
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should render the specified snippet when called as an empty double-tag" do
|
48
|
+
page.should render('<r:snippet name="first"></r:snippet>').as('test')
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should capture contents of a double tag, substituting for <r:yield/> in snippet" do
|
52
|
+
page.should render('<r:snippet name="yielding">inner</r:snippet>').
|
53
|
+
as('Before...inner...and after')
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should do nothing with contents of double tag when snippet doesn't yield" do
|
57
|
+
page.should render('<r:snippet name="first">content disappears!</r:snippet>').
|
58
|
+
as('test')
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should render nested yielding snippets" do
|
62
|
+
page.should render('<r:snippet name="div_wrap"><r:snippet name="yielding">Hello, World!</r:snippet></r:snippet>').
|
63
|
+
as('<div>Before...Hello, World!...and after</div>')
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should render double-tag snippets called from within a snippet" do
|
67
|
+
page.should render('<r:snippet name="nested_yields">the content</r:snippet>').
|
68
|
+
as('<snippet name="div_wrap">above the content below</snippet>')
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should render contents each time yield is called" do
|
72
|
+
page.should render('<r:snippet name="yielding_often">French</r:snippet>').
|
73
|
+
as('French is Frencher than French')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should do nothing when called from page body" do
|
78
|
+
page.should render('<r:yield/>').as("")
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Snippet do
|
4
|
+
dataset :snippets
|
5
|
+
test_helper :validations
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
@original_filter = Radiant::Config['defaults.snippet.filter']
|
9
|
+
@snippet = @model = Snippet.new(snippet_params)
|
10
|
+
end
|
11
|
+
|
12
|
+
after :each do
|
13
|
+
Radiant::Config['defaults.snippet.filter'] = @original_filter
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should take the filter from the default filter" do
|
17
|
+
Radiant::Config['defaults.snippet.filter'] = "Textile"
|
18
|
+
snippet = Snippet.new :name => 'new-snippet'
|
19
|
+
snippet.filter_id.should == "Textile"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "shouldn't override existing snippets filters with the default filter" do
|
23
|
+
snippet = Snippet.find(:first, :conditions => {:filter_id => nil})
|
24
|
+
Radiant::Config['defaults.snippet.filter'] = "Textile"
|
25
|
+
snippet.reload
|
26
|
+
snippet.filter_id.should_not == "Textile"
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should validate length of' do
|
30
|
+
{
|
31
|
+
:name => 100,
|
32
|
+
:filter_id => 25
|
33
|
+
}.each do |field, max|
|
34
|
+
assert_invalid field, ('this must not be longer than %d characters' % max), 'x' * (max + 1)
|
35
|
+
assert_valid field, 'x' * max
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should validate presence of' do
|
40
|
+
[:name].each do |field|
|
41
|
+
assert_invalid field, 'this must not be blank', '', ' ', nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should validate uniqueness of' do
|
46
|
+
assert_invalid :name, 'this name is already in use', 'first', 'another', 'markdown'
|
47
|
+
assert_valid :name, 'just-a-test'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should validate format of name' do
|
51
|
+
assert_valid :name, 'abc', 'abcd-efg', 'abcd_efg', 'abc.html', '/', '123'
|
52
|
+
assert_invalid :name, 'cannot contain spaces or tabs'
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should allow the filter to be specified' do
|
56
|
+
@snippet = snippets(:markdown)
|
57
|
+
@snippet.filter.should be_kind_of(MarkdownFilter)
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe UserActionObserver do
|
4
|
+
dataset :users, :pages_with_layouts, :snippets
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@user = users(:existing)
|
8
|
+
UserActionObserver.current_user = @user
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should observe create' do
|
12
|
+
[
|
13
|
+
Snippet.create!(snippet_params)
|
14
|
+
].each do |model|
|
15
|
+
model.created_by.should == @user
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should observe update' do
|
20
|
+
[
|
21
|
+
snippets(:first)
|
22
|
+
].each do |model|
|
23
|
+
model.attributes = model.attributes.dup
|
24
|
+
model.save.should == true
|
25
|
+
model.updated_by.should == @user
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
unless defined? RADIANT_ROOT
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
case
|
4
|
+
when ENV["RADIANT_ENV_FILE"]
|
5
|
+
require ENV["RADIANT_ENV_FILE"]
|
6
|
+
when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
|
7
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../../")}/config/environment"
|
8
|
+
else
|
9
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../")}/config/environment"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
require "#{RADIANT_ROOT}/spec/spec_helper"
|
13
|
+
|
14
|
+
Dataset::Resolver.default << (File.dirname(__FILE__) + "/datasets")
|
15
|
+
|
16
|
+
if File.directory?(File.dirname(__FILE__) + "/matchers")
|
17
|
+
Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file }
|
18
|
+
end
|
19
|
+
|
20
|
+
Spec::Runner.configure do |config|
|
21
|
+
# config.use_transactional_fixtures = true
|
22
|
+
# config.use_instantiated_fixtures = false
|
23
|
+
# config.fixture_path = RAILS_ROOT + '/spec/fixtures'
|
24
|
+
|
25
|
+
# You can declare fixtures for each behaviour like this:
|
26
|
+
# describe "...." do
|
27
|
+
# fixtures :table_a, :table_b
|
28
|
+
#
|
29
|
+
# Alternatively, if you prefer to declare them only once, you can
|
30
|
+
# do so here, like so ...
|
31
|
+
#
|
32
|
+
# config.global_fixtures = :table_a, :table_b
|
33
|
+
#
|
34
|
+
# If you declare global fixtures, be aware that they will be declared
|
35
|
+
# for all of your examples, even those that don't use them.
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: radiant-snippets-extension
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jim Gay
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2012-03-05 00:00:00 Z
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Makes Radiant better by adding snippets!
|
17
|
+
email:
|
18
|
+
- radiant@radiantcms.org
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- cucumber.yml
|
27
|
+
- radiant-snippets-extension.gemspec
|
28
|
+
- Rakefile
|
29
|
+
- README.md
|
30
|
+
- snippets_extension.rb
|
31
|
+
- app/controllers/admin/snippets_controller.rb
|
32
|
+
- app/helpers/admin/snippets_helper.rb
|
33
|
+
- app/models/snippet.rb
|
34
|
+
- app/views/admin/snippets/_form.html.haml
|
35
|
+
- app/views/admin/snippets/edit.html.haml
|
36
|
+
- app/views/admin/snippets/index.html.haml
|
37
|
+
- app/views/admin/snippets/new.html.haml
|
38
|
+
- app/views/admin/snippets/remove.html.haml
|
39
|
+
- config/routes.rb
|
40
|
+
- config/initializers/radiant_config.rb
|
41
|
+
- config/locales/en.yml
|
42
|
+
- features/admin/configuration.feature
|
43
|
+
- features/admin/pagination.feature
|
44
|
+
- features/admin/snippets_management.feature
|
45
|
+
- features/step_definitions/admin/snippet_steps.rb
|
46
|
+
- features/support/env.rb
|
47
|
+
- features/support/paths.rb
|
48
|
+
- lib/radiant-snippets-extension.rb
|
49
|
+
- lib/snippet_tags.rb
|
50
|
+
- lib/tasks/snippets_extension_tasks.rake
|
51
|
+
- spec/spec.opts
|
52
|
+
- spec/spec_helper.rb
|
53
|
+
- spec/ci/before_script
|
54
|
+
- spec/ci/script
|
55
|
+
- spec/controllers/admin/snippets_controller_spec.rb
|
56
|
+
- spec/datasets/snippets_dataset.rb
|
57
|
+
- spec/lib/radiant/admin_ui_spec.rb
|
58
|
+
- spec/models/page_spec.rb
|
59
|
+
- spec/models/snippet_spec.rb
|
60
|
+
- spec/models/user_action_observer_spec.rb
|
61
|
+
homepage: http://github.com/radiant
|
62
|
+
licenses: []
|
63
|
+
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: "0"
|
81
|
+
requirements: []
|
82
|
+
|
83
|
+
rubyforge_project:
|
84
|
+
rubygems_version: 1.8.9
|
85
|
+
signing_key:
|
86
|
+
specification_version: 3
|
87
|
+
summary: Snippets for Radiant CMS
|
88
|
+
test_files:
|
89
|
+
- spec/spec.opts
|
90
|
+
- spec/spec_helper.rb
|
91
|
+
- spec/ci/before_script
|
92
|
+
- spec/ci/script
|
93
|
+
- spec/controllers/admin/snippets_controller_spec.rb
|
94
|
+
- spec/datasets/snippets_dataset.rb
|
95
|
+
- spec/lib/radiant/admin_ui_spec.rb
|
96
|
+
- spec/models/page_spec.rb
|
97
|
+
- spec/models/snippet_spec.rb
|
98
|
+
- spec/models/user_action_observer_spec.rb
|
99
|
+
- features/admin/configuration.feature
|
100
|
+
- features/admin/pagination.feature
|
101
|
+
- features/admin/snippets_management.feature
|
102
|
+
- features/step_definitions/admin/snippet_steps.rb
|
103
|
+
- features/support/env.rb
|
104
|
+
- features/support/paths.rb
|