snippr 0.15.19 → 0.15.21

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 566f9eaf54ec97f8798e00a5c6f39fd8025d70cf
4
- data.tar.gz: dc5b9310b774e6867a160e5cccbceea2677f6430
3
+ metadata.gz: cd5e914f8d8ff99c134b3c0e3ae285bd4d7579c9
4
+ data.tar.gz: a4f5bb34d2ddf5b38704bdee282b5e2d92a9e21b
5
5
  SHA512:
6
- metadata.gz: a77b2ea7277bfe2c8865730e08499886a805ac65c25bac36459778c14b817e27783a6d890aa951d45753f58efa6309d8980dbeb43c04b699975ef88d27632d89
7
- data.tar.gz: 5a31458383d4c464f03ec25ff141f4839c878bd98124444a0fe0655f9ee61f38b172a0d493a5b7d351ca6fe31982e4bd05ed9a7c0b5871e487bcb7de3d36c016
6
+ metadata.gz: 4989ca6050828708d6dc54134185ead7e052913913a7b3f90586f3430f41d841bc9aa7f191ca1c3b05ea224ebfa25383876696ab7ba96d6e9ed4017d9174a899
7
+ data.tar.gz: 203ed4d06059918c6eea51c21b47f52e37ed0e5991650bf14b4cd01bc6d026fb331b51ee4ca341baeba00d54fc199dc738943202698a6c4508dfc4261e23238d
data/README.md CHANGED
@@ -98,6 +98,9 @@ If that is not what you want and you want to force snippr to call the method on
98
98
  That would result in an ``NoMethodError``.
99
99
  This can be very useful if you call a method on a proxy object with dynamic method generation that isn't so polite as to implement a meaningful ``respond_to?`` or ``respond_to_missing?`` (eg. The Draper::HelperProxy)
100
100
 
101
+ ### Defaulting an empty {snippet_variable}
102
+ You can use `{variable|default_value}` to default this snippet to a value, here it would result in `default_value`. This also works on method invocations.
103
+
101
104
  ### Meta Infos
102
105
 
103
106
  A snippet can not only hold content but also meta infos for this snippet.
@@ -127,12 +130,21 @@ Or with Segmentfilter:
127
130
 
128
131
  ### Including snippr files inside other files
129
132
 
133
+ {snip:absolute/path/from/snippr/path}
134
+ {snip:./relative/path/from/including/snippet}
135
+ {snip:../relative/path/from/including/snippet}
136
+
130
137
  A snippr file can include another snippr file:
131
138
 
132
139
  This snippr file includes another {snip:filepath/of/snip} file
133
140
 
134
141
  This will cause `filepath/of/snip.snip` to be included in place.
135
142
 
143
+ You can also include relative to the including snippet. A snippet in `some/deep/path/deep.snip` containing `{snip:../../twoDown}` will include `some/twoDown.snip`.
144
+ Also `{snip:./further/we/go}` would include `some/deep/path/further/we/go.snip`.
145
+
146
+ Note that it is not allowed to go outside of the designated `Snippr.path`: `{snip:../../../etc/password}` would then output `<!-- missing snippr: ./etc/password -->` wven if the file exists.
147
+
136
148
  Dynamic values of the parent snip will be accessable inside the included snip file.
137
149
 
138
150
  You can pass additional dynamic values when using `{snip}`. These will override any parent parameter.
@@ -9,20 +9,48 @@ module Snippr
9
9
  class Dynamics
10
10
 
11
11
  def process(content, opts = {})
12
- opts.inject(content) do |c, pv|
13
- placeholder, value = pv
14
- c.gsub(/\{(!?)#{placeholder}(?:\.(.*?)\(["]?(.*?)["]?\))?\}/m) do |match|
15
- if $2 && (value.respond_to?($2) || $1 == "!")
16
- method = $2
17
- params = ($3 || "").gsub(/[\t\r\n]/,"").split("\",\"")
18
- value.send(method, *params).to_s
19
- elsif $2
20
- match
21
- else
22
- value.to_s
23
- end
12
+ matches = []
13
+ # convert array of arrays to array of matchdata
14
+ content.scan(regex) { matches << $~ }
15
+
16
+ matches.each do |match_data|
17
+ replacement = match_data[:all]
18
+ value = opts[match_data[:placeholder].to_sym]
19
+ if match_data[:method] && (value.respond_to?(match_data[:method]) || match_data[:respond_to_check] == "!")
20
+ params = (match_data[:parameters] || "").gsub(/[\t\r\n]/,"").split("\",\"")
21
+ replacement = value.send(match_data[:method], *params).to_s
22
+ elsif match_data[:method]
23
+ replacement = match_data[:all]
24
+ else
25
+ replacement = value.to_s
24
26
  end
27
+
28
+ # default set?
29
+ replacement = match_data[:default_when_empty].strip if replacement.empty? && match_data[:default_when_empty]
30
+
31
+ content.gsub!(match_data[:all], replacement)
25
32
  end
33
+ content
34
+ end
35
+
36
+ private
37
+
38
+ def regex
39
+ %r{
40
+ (?<all> # group caputing all
41
+ \{ # start of dynamic value
42
+ (?<respond_to_check>!?) # use ! to call the method on an object even if :respond_to fails
43
+ (?<placeholder>.*?) # variable holding value or object
44
+ (?:\.(?<method>.*?) # about to call an method on the 'placeholder'
45
+ \( # non-optional bracket to merk method call
46
+ ["]? # optional opening double quote
47
+ (?<parameters>.*?) # paramters for method call
48
+ ["]? # optional closing double quote
49
+ \))? # mandatory closing bracket and group end
50
+ (\|(?<default_when_empty>.*?))? # optional default value when snippet content empty
51
+ \} # and thats it
52
+ ) # end all group
53
+ }xm
26
54
  end
27
55
 
28
56
  end
@@ -18,12 +18,13 @@ module Snippr
18
18
  content
19
19
  end
20
20
 
21
- private
21
+ private
22
22
 
23
23
  # expand another snip
24
24
  # {snip:path/to/snippet}
25
25
  def cmd_snip(unprocessed_content, opts, original_options)
26
26
  path = opts[:default].split("/")
27
+ path = recursive_include_from_path(path, opts[:_parent]) if path.first.in? [".", ".."]
27
28
  snip_content = Snippr::Snip.new(*path + [opts]).content
28
29
  unprocessed_content.gsub("{snip:#{original_options}}", snip_content)
29
30
  end
@@ -52,6 +53,14 @@ module Snippr
52
53
  options
53
54
  end
54
55
 
56
+ def recursive_include_from_path(path, parent)
57
+ target_pathname = Pathname.new(parent.pathname + path.join(File::SEPARATOR))
58
+ if target_pathname.to_s =~ /^#{Snippr.path}/
59
+ target_pathname.to_s.gsub(/^#{Snippr.path}\//, "").split(File::SEPARATOR)
60
+ else
61
+ path
62
+ end
63
+ end
55
64
  end
56
65
 
57
66
  end
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  # = Snippr::Processor
2
3
  #
3
4
  # Provides methods to process snippr content.
@@ -10,7 +11,8 @@ module Snippr
10
11
  end
11
12
 
12
13
  # Sends the given content and opts to all the configured processors and returns the result.
13
- def self.process(content, opts)
14
+ def self.process(content, opts, including_snippet)
15
+ opts[:_parent] = including_snippet
14
16
  @processors.inject(content) {|c, processor| processor.process c, opts}
15
17
  end
16
18
 
data/lib/snippr/snip.rb CHANGED
@@ -2,26 +2,28 @@
2
2
  # = Snippr::Snip
3
3
  #
4
4
  # Represents a single snip and provides methods to read data.
5
+ require 'pathname'
5
6
 
6
7
  module Snippr
7
8
  class Snip
8
9
 
10
+ attr_reader :name, :path, :opts, :unprocessed_content, :meta, :pathname
11
+
9
12
  FILE_EXTENSION = 'snip'
10
13
 
11
14
  def initialize(*names)
12
15
  names = strip_empty_values(names)
13
16
  @opts = names.last.kind_of?(Hash) ? names.pop : {}
14
17
  @opts.symbolize_keys!
15
- @name = "#{Path.normalize_name(*names)}#{ I18n.locale(@opts[:i18n]) }"
18
+ @name = "#{Path.normalize_name(*names)}#{I18n.locale(@opts[:i18n])}"
16
19
  @path = Path.path_from_name @name, (@opts[:extension] || FILE_EXTENSION)
17
20
  @unprocessed_content = raw_content
18
21
  @meta = {}
22
+ @pathname = Pathname.new(@path).dirname
19
23
  content
20
24
  after_initialize
21
25
  end
22
26
 
23
- attr_reader :name, :path, :opts, :unprocessed_content, :meta
24
-
25
27
  # Returns the processed and decorated content.
26
28
  def content
27
29
  @content ||= begin
@@ -30,7 +32,7 @@ module Snippr
30
32
  else
31
33
  content = SegmentParser.new(raw_content).content
32
34
  @unprocessed_content, @meta = MetaData.extract(name, content)
33
- content = Processor.process @unprocessed_content, opts
35
+ content = Processor.process @unprocessed_content, opts, self
34
36
  "<!-- starting snippr: #{name} -->\n#{content}\n<!-- closing snippr: #{name} -->"
35
37
  end
36
38
  end
data/lib/snippr.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ require 'active_support'
2
3
  require 'active_support/core_ext'
3
4
 
4
5
  require 'snippr/snippr'
data/snippr.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |s|
3
3
  s.name = "snippr"
4
- s.version = '0.15.19'
4
+ s.version = '0.15.21'
5
5
  s.date = Time.now
6
6
  s.platform = Gem::Platform::RUBY
7
7
  s.authors = ["Daniel Harrington", "Thomas Jachmann", "Frank Schumacher"]
@@ -0,0 +1 @@
1
+ ALSO IN BASE DIR
@@ -0,0 +1 @@
1
+ Base includes {snip:./alsoInBase} and {snip:./subdir/snippet}
@@ -0,0 +1 @@
1
+ IN SUBDIR
@@ -0,0 +1 @@
1
+ LIKE {snip:../././../base}
@@ -0,0 +1 @@
1
+ ERROR {snip:../../../../base}
@@ -0,0 +1 @@
1
+ LIKE {snip:../../base}
@@ -7,7 +7,8 @@ describe Snippr::Processor::Dynamics do
7
7
  def method; "METHOD"; end
8
8
  def method2(param); "METHOD WITH #{param}"; end
9
9
  def method3(param1, param2); "METHOD WITH #{param1} AND #{param2}"; end
10
- end
10
+ def method4; ""; end
11
+ end
11
12
 
12
13
  it "replaces placeholders with dynamic values" do
13
14
  today = Date.today
@@ -43,8 +44,7 @@ describe Snippr::Processor::Dynamics do
43
44
  end
44
45
 
45
46
  it "keeps the {snip} if calling a method but the method is not defined" do
46
- tpl = "An instance {var.method_not_exist()}"
47
- subject.process(tpl, :var => Klass.new).should == tpl
47
+ subject.process("An instance {var.method_not_exist()}", :var => Klass.new).should == "An instance {var.method_not_exist()}"
48
48
  end
49
49
 
50
50
  it "calls a bang(!) method even if the receiver does not respond_to the method" do
@@ -52,4 +52,14 @@ describe Snippr::Processor::Dynamics do
52
52
  lambda { subject.process(tpl, :var => Klass.new) }.should raise_error(NoMethodError)
53
53
  end
54
54
 
55
+ it "defaults the value if the content is empty" do
56
+ tpl = "{empty|default}"
57
+ expect(subject.process(tpl, empty: "")).to eq "default"
58
+ end
59
+
60
+ it "defaults the value if the content is present" do
61
+ tpl = "{var.method4()|default2}"
62
+ expect(subject.process(tpl, var: Klass.new )).to eq "default2"
63
+ end
64
+
55
65
  end
@@ -36,8 +36,35 @@ describe Snippr::Processor::Functions do
36
36
  }).should == "Include a <!-- starting snippr: topup/success -->\n<p>You're topup of A B C at 123 was successful.</p>\n<!-- closing snippr: topup/success --> inside a snip"
37
37
  end
38
38
 
39
- context "for home/show/blauappOverviewBoxMobile (regression test)" do
39
+ context "relative inclusion via {snip:./name}" do
40
+ context "via {snip:../../name}" do
41
+ it "allows inclusion" do
42
+ snippet = Snippr.load(:snip, :subdir, :subdir2, :snippet)
43
+ expect(snippet.content).to eq "<!-- starting snippr: snip/subdir/subdir2/snippet -->\nLIKE <!-- starting snippr: snip/base -->\nBase includes <!-- starting snippr: snip/alsoInBase -->\nALSO IN BASE DIR\n<!-- closing snippr: snip/alsoInBase --> and <!-- starting snippr: snip/subdir/snippet -->\nIN SUBDIR\n<!-- closing snippr: snip/subdir/snippet -->\n<!-- closing snippr: snip/base -->\n<!-- closing snippr: snip/subdir/subdir2/snippet -->"
44
+ end
45
+
46
+ it "returns a missing snippet when the inclusion recurses past the Snippr.path" do
47
+ snippet = Snippr.load(:snip, :subdir, :subdir2, :past_snippr_path)
48
+ expect(snippet.content).to eq "<!-- starting snippr: snip/subdir/subdir2/pastSnipprPath -->\nERROR <!-- missing snippr: ../../../../base -->\n<!-- closing snippr: snip/subdir/subdir2/pastSnipprPath -->"
49
+ end
50
+ end
40
51
 
52
+ context "via {snip:./name}" do
53
+ it "allows relative inclusion via {snip:../name}" do
54
+ snippet = Snippr.load(:snip, :base)
55
+ expect(snippet.content).to eq "<!-- starting snippr: snip/base -->\nBase includes <!-- starting snippr: snip/alsoInBase -->\nALSO IN BASE DIR\n<!-- closing snippr: snip/alsoInBase --> and <!-- starting snippr: snip/subdir/snippet -->\nIN SUBDIR\n<!-- closing snippr: snip/subdir/snippet -->\n<!-- closing snippr: snip/base -->"
56
+ end
57
+ end
58
+
59
+ context "mixed {snip:../././../name}" do
60
+ it "allows inclusion" do
61
+ snippet = Snippr.load(:snip, :subdir, :subdir2, :mixed)
62
+ expect(snippet.content).to eq "<!-- starting snippr: snip/subdir/subdir2/mixed -->\nLIKE <!-- starting snippr: snip/base -->\nBase includes <!-- starting snippr: snip/alsoInBase -->\nALSO IN BASE DIR\n<!-- closing snippr: snip/alsoInBase --> and <!-- starting snippr: snip/subdir/snippet -->\nIN SUBDIR\n<!-- closing snippr: snip/subdir/snippet -->\n<!-- closing snippr: snip/base -->\n<!-- closing snippr: snip/subdir/subdir2/mixed -->"
63
+ end
64
+ end
65
+ end
66
+
67
+ context "for home/show/blauappOverviewBoxMobile (regression test)" do
41
68
  before do
42
69
  Snippr::Normalizer.normalizers << Snippr::Normalizer::DeRester.new # add a second normalizer to ensure chain behaviour
43
70
  Snippr::I18n.enabled = true
@@ -51,7 +78,6 @@ describe Snippr::Processor::Functions do
51
78
  it "works" do
52
79
  subject.process("{snip:home/show/blauappOverviewBoxMobile}").should == "<!-- missing snippr: home/show/blauappOverviewBoxMobile_de -->"
53
80
  end
54
-
55
81
  end
56
82
 
57
83
  end
@@ -24,11 +24,12 @@ describe Snippr::Processor do
24
24
  describe ".process" do
25
25
 
26
26
  it "calls process on all processors, passing the content between them and returning the last result" do
27
+ parent = Snippr::Snip.new
27
28
  subject.processors.each_with_index do |processor, i|
28
29
  processor.should respond_to(:process)
29
- expect(processor).to receive(:process).with(i.to_s, {'1' => '2'}).and_return((i + 1).to_s)
30
+ expect(processor).to receive(:process).with(i.to_s, {'1' => '2', :_parent => parent}).and_return((i + 1).to_s)
30
31
  end
31
- subject.process('0', {'1' => '2'}).should == subject.processors.size.to_s
32
+ subject.process('0', {'1' => '2'}, parent).should == subject.processors.size.to_s
32
33
  end
33
34
 
34
35
  end
@@ -127,12 +127,12 @@ describe Snippr::Snip do
127
127
  describe "content" do
128
128
 
129
129
  it "calls Snippr::Processor.process with opts and return decorated result" do
130
- Snippr::Processor.should_receive(:process).with('<p>Home</p>', {:a => :b}).and_return('processed')
130
+ Snippr::Processor.should_receive(:process).with('<p>Home</p>', {:a => :b}, anything()).and_return('processed')
131
131
  Snippr::Snip.new(:home, :a => :b).content.should == "<!-- starting snippr: home -->\nprocessed\n<!-- closing snippr: home -->"
132
132
  end
133
133
 
134
134
  it "stores the processed data instead of processing it again" do
135
- Snippr::Processor.should_receive(:process).with('<p>Home</p>', {:a => :b}).once.and_return('processed')
135
+ Snippr::Processor.should_receive(:process).with('<p>Home</p>', {:a => :b}, anything()).once.and_return('processed')
136
136
  snip = Snippr::Snip.new(:home, :a => :b)
137
137
  snip.content.should == "<!-- starting snippr: home -->\nprocessed\n<!-- closing snippr: home -->"
138
138
  snip.content.should == "<!-- starting snippr: home -->\nprocessed\n<!-- closing snippr: home -->"
@@ -203,7 +203,7 @@ describe Snippr::ViewHelper do
203
203
  end
204
204
 
205
205
  it "camelizes controller and action names" do
206
- snippr_with_path(:a_snippet).should == "<!-- starting snippr: withUnderscore/andUnderscore/aSnippet -->\nan underscored snippet with param {param}\n<!-- closing snippr: withUnderscore/andUnderscore/aSnippet -->"
206
+ snippr_with_path(:a_snippet).should == "<!-- starting snippr: withUnderscore/andUnderscore/aSnippet -->\nan underscored snippet with param \n<!-- closing snippr: withUnderscore/andUnderscore/aSnippet -->"
207
207
  end
208
208
 
209
209
  it "works with a given block" do
data/spec/spec_helper.rb CHANGED
@@ -5,6 +5,8 @@ require 'snippr'
5
5
 
6
6
  Bundler.require(:default, :development)
7
7
 
8
+ I18n.enforce_available_locales = false
9
+
8
10
  RSpec.configure do |config|
9
11
  config.before do
10
12
  Snippr::I18n.enabled = nil
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snippr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.19
4
+ version: 0.15.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Harrington
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-05-21 00:00:00.000000000 Z
13
+ date: 2014-06-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: i18n
@@ -161,6 +161,12 @@ files:
161
161
  - spec/fixtures/meta/withContentAndSegmentfilter.snip
162
162
  - spec/fixtures/meta/withContentNoNewline.snip
163
163
  - spec/fixtures/meta/withNoContent.snip
164
+ - spec/fixtures/snip/alsoInBase.snip
165
+ - spec/fixtures/snip/base.snip
166
+ - spec/fixtures/snip/subdir/snippet.snip
167
+ - spec/fixtures/snip/subdir/subdir2/mixed.snip
168
+ - spec/fixtures/snip/subdir/subdir2/pastSnipprPath.snip
169
+ - spec/fixtures/snip/subdir/subdir2/snippet.snip
164
170
  - spec/fixtures/topup/someError.snip
165
171
  - spec/fixtures/topup/success.snip
166
172
  - spec/fixtures/withBlock.snip
@@ -230,6 +236,12 @@ test_files:
230
236
  - spec/fixtures/meta/withContentAndSegmentfilter.snip
231
237
  - spec/fixtures/meta/withContentNoNewline.snip
232
238
  - spec/fixtures/meta/withNoContent.snip
239
+ - spec/fixtures/snip/alsoInBase.snip
240
+ - spec/fixtures/snip/base.snip
241
+ - spec/fixtures/snip/subdir/snippet.snip
242
+ - spec/fixtures/snip/subdir/subdir2/mixed.snip
243
+ - spec/fixtures/snip/subdir/subdir2/pastSnipprPath.snip
244
+ - spec/fixtures/snip/subdir/subdir2/snippet.snip
233
245
  - spec/fixtures/topup/someError.snip
234
246
  - spec/fixtures/topup/success.snip
235
247
  - spec/fixtures/withBlock.snip