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 +4 -4
- data/README.md +12 -0
- data/lib/snippr/processor/dynamics.rb +40 -12
- data/lib/snippr/processor/functions.rb +10 -1
- data/lib/snippr/processor.rb +3 -1
- data/lib/snippr/snip.rb +6 -4
- data/lib/snippr.rb +1 -0
- data/snippr.gemspec +1 -1
- data/spec/fixtures/snip/alsoInBase.snip +1 -0
- data/spec/fixtures/snip/base.snip +1 -0
- data/spec/fixtures/snip/subdir/snippet.snip +1 -0
- data/spec/fixtures/snip/subdir/subdir2/mixed.snip +1 -0
- data/spec/fixtures/snip/subdir/subdir2/pastSnipprPath.snip +1 -0
- data/spec/fixtures/snip/subdir/subdir2/snippet.snip +1 -0
- data/spec/snippr/processor/dynamics_spec.rb +13 -3
- data/spec/snippr/processor/functions_spec.rb +28 -2
- data/spec/snippr/processor_spec.rb +3 -2
- data/spec/snippr/snip_spec.rb +2 -2
- data/spec/snippr/view_helper_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd5e914f8d8ff99c134b3c0e3ae285bd4d7579c9
|
4
|
+
data.tar.gz: a4f5bb34d2ddf5b38704bdee282b5e2d92a9e21b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
data/lib/snippr/processor.rb
CHANGED
@@ -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)}#{
|
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
data/snippr.gemspec
CHANGED
@@ -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
|
-
|
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 "
|
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
|
data/spec/snippr/snip_spec.rb
CHANGED
@@ -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
|
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
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.
|
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-
|
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
|