snippr 0.15.19 → 0.15.21

Sign up to get free protection for your applications and to get access to all the features.
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