snippr 0.3.0 → 0.13.1

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 (57) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +4 -0
  5. data/README.md +143 -0
  6. data/Rakefile +5 -4
  7. data/lib/snippr.rb +14 -2
  8. data/lib/snippr/i18n.rb +9 -10
  9. data/lib/snippr/links.rb +59 -0
  10. data/lib/snippr/meta_data.rb +33 -0
  11. data/lib/snippr/normalizer.rb +18 -0
  12. data/lib/snippr/normalizer/camelizer.rb +18 -0
  13. data/lib/snippr/normalizer/de_rester.rb +25 -0
  14. data/lib/snippr/path.rb +27 -6
  15. data/lib/snippr/processor.rb +18 -0
  16. data/lib/snippr/processor/dynamics.rb +31 -0
  17. data/lib/snippr/processor/functions.rb +50 -0
  18. data/lib/snippr/processor/links.rb +20 -0
  19. data/lib/snippr/processor/wikilinks.rb +20 -0
  20. data/lib/snippr/snip.rb +57 -0
  21. data/lib/snippr/snippr.rb +41 -45
  22. data/lib/snippr/view_helper.rb +71 -0
  23. data/snippr.gemspec +30 -0
  24. data/spec/fixtures/a/path/aSnippet.snip +1 -0
  25. data/spec/fixtures/a/path/aSnippetWithParam.snip +1 -0
  26. data/spec/fixtures/controller/action/aSnippet.snip +1 -0
  27. data/spec/fixtures/empty.snip +3 -0
  28. data/spec/fixtures/i18n/list_de.snip +0 -0
  29. data/spec/fixtures/meta/broken.snip +5 -0
  30. data/spec/fixtures/meta/withContent.snip +5 -0
  31. data/spec/fixtures/meta/withContentNoNewline.snip +4 -0
  32. data/spec/fixtures/meta/withNoContent.snip +4 -0
  33. data/spec/fixtures/withUnderscore/andUnderscore/aSnippet.snip +1 -0
  34. data/spec/fixtures/withViewHelperMethod.snip +1 -0
  35. data/spec/snippr/i18n_spec.rb +30 -0
  36. data/spec/snippr/links_spec.rb +137 -0
  37. data/spec/snippr/normalizer/camelizer_spec.rb +13 -0
  38. data/spec/snippr/normalizer/de_rester_spec.rb +24 -0
  39. data/spec/snippr/normalizer_spec.rb +40 -0
  40. data/spec/snippr/path_spec.rb +87 -0
  41. data/spec/snippr/processor/dynamics_spec.rb +49 -0
  42. data/spec/snippr/processor/functions_spec.rb +72 -0
  43. data/spec/snippr/processor/links_spec.rb +12 -0
  44. data/spec/snippr/processor/wikilinks_spec.rb +12 -0
  45. data/spec/snippr/processor_spec.rb +35 -0
  46. data/spec/snippr/snip_spec.rb +179 -0
  47. data/spec/snippr/snippr_spec.rb +60 -55
  48. data/spec/snippr/view_helper_spec.rb +221 -0
  49. data/spec/spec_helper.rb +17 -3
  50. metadata +178 -87
  51. data/README.rdoc +0 -77
  52. data/lib/snippr/core_ext.rb +0 -12
  53. data/lib/snippr/helper.rb +0 -23
  54. data/lib/snippr/link.rb +0 -26
  55. data/spec/fixtures/tariff/einheit.snip +0 -1
  56. data/spec/fixtures/wiki.snip +0 -1
  57. data/spec/snippr/core_ext_spec.rb +0 -11
@@ -0,0 +1,18 @@
1
+ # = Snippr::Processor
2
+ #
3
+ # Provides methods to process snippr content.
4
+ module Snippr
5
+ module Processor
6
+
7
+ # Returns a (modifiable) list of processors that'll be used to process the content.
8
+ def self.processors
9
+ @processors ||= []
10
+ end
11
+
12
+ # Sends the given content and opts to all the configured processors and returns the result.
13
+ def self.process(content, opts)
14
+ @processors.inject(content) {|c, processor| processor.process c, opts}
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ # = Snippr::Processor::Dynamics
2
+ #
3
+ # Replaces {placeholder} placeholders in the content with values taken from the given opts.
4
+ module Snippr
5
+
6
+ module Processor
7
+
8
+ class Dynamics
9
+
10
+ def process(content, opts = {})
11
+ opts.inject(content) do |c, pv|
12
+ placeholder, value = pv
13
+ c.gsub(/\{#{placeholder}(?:\.(.*?)\(["]?(.*?)["]?\))?\}/m) do |match|
14
+ if $1 && value.respond_to?($1)
15
+ method = $1
16
+ params = ($2 || "").gsub(/[\t\r\n]/,"").split("\",\"")
17
+ value.send(method, *params).to_s
18
+ elsif $1
19
+ match
20
+ else
21
+ value.to_s
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,50 @@
1
+ # = Snippr::Processor::Functions
2
+ #
3
+ # Processes several functions in {command:options} syntax.
4
+ module Snippr
5
+
6
+ module Processor
7
+
8
+ class Functions
9
+
10
+ def process(content, opts = {})
11
+ content.scan(/\{(.*?):(.*?)\}/) do |match|
12
+ command, func_options = match
13
+ options = opts.merge(hashify(func_options))
14
+ command = "cmd_#{command}"
15
+ content = send(command, content, options, func_options) if respond_to?(command, true)
16
+ end
17
+ content
18
+ end
19
+
20
+ private
21
+
22
+ # expand another snip
23
+ # {snip:path/to/snippet}
24
+ def cmd_snip(unprocessed_content, opts, original_options)
25
+ path = opts[:default].split("/")
26
+ snip_content = Snippr::Snip.new(*path + [opts]).content
27
+ unprocessed_content.gsub("{snip:#{original_options}}", snip_content)
28
+ end
29
+
30
+ # home
31
+ # snip=home
32
+ # snip=home,var=1
33
+ def hashify(func_options="")
34
+ options = {}
35
+ func_options.split(",").each do |option|
36
+ opt_key, opt_value = option.split("=")
37
+ unless opt_value
38
+ opt_value = opt_key
39
+ opt_key = :default
40
+ end
41
+ options[opt_key.to_sym] = opt_value
42
+ end
43
+ options
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,20 @@
1
+ # = Snippr::Processor::Links
2
+ #
3
+ # Adjusts URLs in links.
4
+ module Snippr
5
+
6
+ module Processor
7
+
8
+ class Links
9
+
10
+ def process(content, opts = {})
11
+ content.gsub /<a [^>]+>[^<]*<\/a>/i do |match|
12
+ Snippr::Links.adjust_link match
13
+ end
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,20 @@
1
+ # = Snippr::Processor::Wikilinks
2
+ #
3
+ # Replaces links in [url|title] syntax with real links.
4
+ module Snippr
5
+
6
+ module Processor
7
+
8
+ class Wikilinks
9
+
10
+ def process(content, opts = {})
11
+ content.gsub /\[\[([^|]+)\|([^\]]+)\]\]/ do |match|
12
+ Snippr::Links.adjust_link "<a href=\"#{$1}\">#{$2}</a>"
13
+ end
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,57 @@
1
+ # # -*- encoding : utf-8 -*-
2
+ # = Snippr::Snip
3
+ #
4
+ # Represents a single snip and provides methods to read data.
5
+ module Snippr
6
+ class Snip
7
+
8
+ extend ActiveSupport::Memoizable
9
+
10
+ FILE_EXTENSION = 'snip'
11
+
12
+ def initialize(*names)
13
+ names = strip_empty_values(names)
14
+ @opts = names.last.kind_of?(Hash) ? names.pop : {}
15
+ @opts.symbolize_keys!
16
+ @name = "#{Path.normalize_name(*names)}#{ I18n.locale(@opts[:i18n]) }"
17
+ @path = Path.path_from_name @name, (@opts[:extension] || FILE_EXTENSION)
18
+ @unprocessed_content, @meta = MetaData.extract(names, raw_content)
19
+ end
20
+
21
+ attr_reader :name, :path, :opts, :unprocessed_content, :meta
22
+
23
+ # Returns the processed and decorated content.
24
+ def content
25
+ if missing?
26
+ "<!-- missing snippr: #{name} -->"
27
+ else
28
+ content = Processor.process unprocessed_content, opts
29
+ "<!-- starting snippr: #{name} -->\n#{content}\n<!-- closing snippr: #{name} -->"
30
+ end
31
+ end
32
+ memoize :content
33
+ alias :to_s :content
34
+
35
+ def raw_content
36
+ missing? ? '' : File.read(@path).strip
37
+ end
38
+ memoize :raw_content
39
+
40
+ # Returns whether the snip is missing or not.
41
+ def missing?
42
+ !File.exist? @path
43
+ end
44
+
45
+ # Returns whether the snip is empty or not.
46
+ def empty?
47
+ unprocessed_content.blank?
48
+ end
49
+
50
+ private
51
+
52
+ def strip_empty_values(names)
53
+ names - [nil, ""]
54
+ end
55
+
56
+ end
57
+ end
data/lib/snippr/snippr.rb CHANGED
@@ -1,7 +1,4 @@
1
- require "snippr/core_ext"
2
- require "snippr/path"
3
- require "snippr/i18n"
4
- require "snippr/link"
1
+ require "logger"
5
2
 
6
3
  # = Snippr
7
4
  # ==== File based content management
@@ -9,56 +6,55 @@ require "snippr/link"
9
6
  # A snippr file is a piece of HTML or raw text to be included in a website. They are plain text
10
7
  # files stored on the file system. Snippr files end with ".snip" and are read from the Snippr path.
11
8
  module Snippr
12
- extend Snippr::Path
13
- extend Snippr::I18n
14
- extend Snippr::Link
9
+ extend self
15
10
 
16
- class << self
17
-
18
- # The snippr file extension.
19
- FileExtension = ".snip"
20
-
21
- # The comments wrapping a snippr.
22
- SnipprComments = "<!-- starting snippr: %s -->\n%s\n<!-- closing snippr: %s -->"
11
+ # Returns the path to the snippr files (from JVM properties if available).
12
+ def path
13
+ Path.path
14
+ end
23
15
 
24
- # The fallback tag for a missing snippr.
25
- MissingSnipprTag = '<samp class="missing snippr" />'
16
+ # Sets the path to the snippr files.
17
+ def path=(path)
18
+ Path.path = path
19
+ end
26
20
 
21
+ # Returns whether to use I18n snippr files.
22
+ def i18n?
23
+ I18n.enabled?
24
+ end
27
25
 
28
- # Expects the name of a snippr file. Also accepts a Hash of placeholders
29
- # to be replaced with dynamic values.
30
- def load(*args)
31
- @dynamics = args.last.kind_of?(Hash) ? args.pop : {}
32
- @name = name_from args
33
- SnipprComments % [@name, content, @name]
34
- end
26
+ # Sets whether to use I18n snippr files.
27
+ def i18n=(enabled)
28
+ I18n.enabled = enabled
29
+ end
35
30
 
36
- private
31
+ # Returns the regular expressions used to determine which urls to exclude from adjustment.
32
+ def adjust_urls_except
33
+ Links.adjust_urls_except
34
+ end
37
35
 
38
- # Returns the name of a snippr from a given Array of +args+.
39
- def name_from(args)
40
- args.map { |arg| arg.kind_of?(Symbol) ? arg.to_s.lower_camelcase : arg }.join("/") + locale
41
- end
36
+ # Sets the regular expressions used to determine which urls to exclude from adjustment.
37
+ def adjust_urls_except=(adjust_urls_except)
38
+ Links.adjust_urls_except = adjust_urls_except
39
+ end
42
40
 
43
- # Returns the raw snippr content or a +MissingSnipprTag+ in case the snippr
44
- # file does not seem to exist.
45
- def content
46
- return MissingSnipprTag unless File.exist? file
47
-
48
- content = File.read(file).strip
49
- insert_dynamics content
50
- linkify content
51
- end
41
+ # Sets the logger to use.
42
+ attr_writer :logger
52
43
 
53
- # Returns the complete path to a snippr file.
54
- def file
55
- File.join path, [@name, FileExtension].join
56
- end
44
+ # Returns the logger. Uses the Rails logger when available or falls back to a custom logger.
45
+ def logger
46
+ @logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
47
+ end
57
48
 
58
- # Replaces placeholders found in a given snippr +content+ with dynamic values.
59
- def insert_dynamics(content)
60
- @dynamics.each { |placeholder, value| content.gsub! "{#{placeholder}}", value.to_s }
61
- end
49
+ # Expects the name of a snippr file. Also accepts a Hash of placeholders
50
+ # to be replaced with dynamic values.
51
+ def load(*args)
52
+ Snip.new(*args)
53
+ end
62
54
 
55
+ # Lists all snips inside a snippr directory specified by path parts.
56
+ def list(*args)
57
+ Path.list *args
63
58
  end
59
+
64
60
  end
@@ -0,0 +1,71 @@
1
+ # = Snippr::ViewHelper
2
+ #
3
+ # This module is automatically included into +ActionView::Base+ when using the Snippr
4
+ # component with Rails. It provides a +snippr+ helper method for loading snippr files.
5
+ #
6
+ # %h1 Topup successful
7
+ # .topup.info
8
+ # = snippr :topup, :success
9
+ # %h1 Conditional output
10
+ # - snippr :topup, :conditional_snippr do |snip|
11
+ # #cond= snip
12
+ module Snippr
13
+ module ViewHelper
14
+
15
+ # Returns a snippr specified via +args+.
16
+ def snippr(*args)
17
+ snip = Snip.new *add_view_to_snippr_args(args)
18
+ content = snip.content.html_safe
19
+
20
+ if block_given?
21
+ if snip.missing? || snip.blank?
22
+ concat content
23
+ elsif content.strip.present?
24
+ yield content
25
+ end
26
+ 0
27
+ else
28
+ extend_snip_content(content, snip)
29
+ end
30
+ end
31
+
32
+ def snippr_with_path(*args, &block)
33
+ path = if controller_name == "pages"
34
+ params[:id].split('/')
35
+ else
36
+ [controller_name, params[:action]]
37
+ end.compact.map { |e| e.to_s.camelize(:lower).to_sym }
38
+ snippr(*(path + args), &block)
39
+ end
40
+
41
+ private
42
+
43
+ def add_view_to_snippr_args(args)
44
+ variables = args.last.kind_of?(Hash) ? args.pop : {}
45
+ variables[:view] = self
46
+ args << variables
47
+ args
48
+ end
49
+
50
+ def extend_snip_content(content, snip)
51
+ content.class_eval %(
52
+ def missing?
53
+ #{snip.missing?}
54
+ end
55
+ def exists?
56
+ #{!snip.missing?}
57
+ end
58
+ def meta(key = nil)
59
+ meta = #{snip.meta.inspect}
60
+ key ? meta[key] : meta
61
+ end
62
+ )
63
+ content
64
+ end
65
+
66
+ end
67
+ end
68
+
69
+ if defined? ActionView::Base
70
+ ActionView::Base.send :include, Snippr::ViewHelper
71
+ end
data/snippr.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |s|
3
+ s.name = "snippr"
4
+ s.version = "0.13.1"
5
+ s.date = Time.now
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ["Daniel Harrington", "Thomas Jachmann"]
8
+ s.email = ["me@rubiii.com", "self@thomasjachmann.com"]
9
+ s.homepage = "http://github.com/blaulabs/snippr"
10
+ s.summary = %q{File based content management}
11
+ s.description = %q{This gem provides ways to access file based cms resources from a rails app.}
12
+
13
+ s.rubyforge_project = "snippr"
14
+
15
+ s.add_runtime_dependency "i18n"
16
+ s.add_runtime_dependency "activesupport"
17
+
18
+ s.add_development_dependency "ci_reporter", "~> 1.6.5"
19
+ s.add_development_dependency "rspec", "~> 2.6.0"
20
+ s.add_development_dependency "mocha", "0.9.12"
21
+ s.add_development_dependency "rake", "~> 0.9.0"
22
+ # pin down ZenTest so that autotest works without upgrading rubygems
23
+ # see http://stackoverflow.com/questions/6802610/autotest-problem [mw, 2011-08-10]
24
+ s.add_development_dependency "ZenTest", "4.5.0"
25
+
26
+ s.files = `git ls-files`.split("\n")
27
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
28
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
29
+ s.require_paths = ["lib"]
30
+ end
@@ -0,0 +1 @@
1
+ a snippet
@@ -0,0 +1 @@
1
+ a snippet with param {param}
@@ -0,0 +1 @@
1
+ SNIPPET
@@ -0,0 +1,3 @@
1
+
2
+
3
+
File without changes
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: Die mit dem Fluegli
3
+ keywords: blau Mobilfunk GmbH, blau.de, blauworld, handy, sim
4
+ ---
5
+ <p>Broken!</p>
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: Die mit dem Fluegli
3
+ keywords: blau Mobilfunk GmbH, blau.de, blauworld, handy, sim
4
+ ---
5
+ <p>So meta!</p>