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.
Files changed (36) hide show
  1. data/README.md +7 -0
  2. data/Rakefile +109 -0
  3. data/app/controllers/admin/snippets_controller.rb +8 -0
  4. data/app/helpers/admin/snippets_helper.rb +41 -0
  5. data/app/models/snippet.rb +23 -0
  6. data/app/views/admin/snippets/_form.html.haml +33 -0
  7. data/app/views/admin/snippets/edit.html.haml +9 -0
  8. data/app/views/admin/snippets/index.html.haml +33 -0
  9. data/app/views/admin/snippets/new.html.haml +7 -0
  10. data/app/views/admin/snippets/remove.html.haml +17 -0
  11. data/config/initializers/radiant_config.rb +3 -0
  12. data/config/locales/en.yml +16 -0
  13. data/config/routes.rb +5 -0
  14. data/cucumber.yml +1 -0
  15. data/features/admin/configuration.feature +9 -0
  16. data/features/admin/pagination.feature +27 -0
  17. data/features/admin/snippets_management.feature +82 -0
  18. data/features/step_definitions/admin/snippet_steps.rb +25 -0
  19. data/features/support/env.rb +11 -0
  20. data/features/support/paths.rb +22 -0
  21. data/lib/radiant-snippets-extension.rb +8 -0
  22. data/lib/snippet_tags.rb +80 -0
  23. data/lib/tasks/snippets_extension_tasks.rake +47 -0
  24. data/radiant-snippets-extension.gemspec +24 -0
  25. data/snippets_extension.rb +52 -0
  26. data/spec/ci/before_script +23 -0
  27. data/spec/ci/script +2 -0
  28. data/spec/controllers/admin/snippets_controller_spec.rb +110 -0
  29. data/spec/datasets/snippets_dataset.rb +38 -0
  30. data/spec/lib/radiant/admin_ui_spec.rb +33 -0
  31. data/spec/models/page_spec.rb +80 -0
  32. data/spec/models/snippet_spec.rb +59 -0
  33. data/spec/models/user_action_observer_spec.rb +28 -0
  34. data/spec/spec.opts +6 -0
  35. data/spec/spec_helper.rb +36 -0
  36. metadata +104 -0
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # Snippets
2
+
3
+ Use Snippets as reusable bits of content.
4
+
5
+ This extension was pulled from the original core.
6
+
7
+ Created by Radiant CMS Development Team.
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,7 @@
1
+ - render_region :main do |main|
2
+ - main.edit_header do
3
+ %h1= t('new_snippet')
4
+ - main.edit_form do
5
+ = render :partial => 'form'
6
+
7
+ - content_for 'page_scripts', snippet_edit_javascripts
@@ -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&#8230;"} 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,3 @@
1
+ Radiant.config do |config|
2
+ # config.define "setting.name", :default => 'value', :select_from => ['foo', 'bar']
3
+ end
@@ -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
@@ -0,0 +1,5 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.namespace :admin, :member => { :remove => :get } do |admin|
3
+ admin.resources :snippets
4
+ end
5
+ end
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)
@@ -0,0 +1,8 @@
1
+ module RadiantSnippetsExtension
2
+ VERSION = "1.0.0"
3
+ SUMMARY = "Snippets for Radiant CMS"
4
+ DESCRIPTION = "Makes Radiant better by adding snippets!"
5
+ URL = "http://github.com/radiant"
6
+ AUTHORS = ["Jim Gay"]
7
+ EMAIL = ["radiant@radiantcms.org"]
8
+ end
@@ -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,2 @@
1
+ cd ~/radiant
2
+ bundle exec rake spec:extensions EXT=snippets
@@ -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
@@ -0,0 +1,6 @@
1
+ --colour
2
+ --format
3
+ progress
4
+ --loadby
5
+ mtime
6
+ --reverse
@@ -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