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 +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
|