prawn 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/Rakefile +2 -31
- data/lib/prawn.rb +1 -0
- data/lib/prawn/document.rb +20 -15
- data/lib/prawn/document/bounding_box.rb +10 -2
- data/lib/prawn/document/span.rb +2 -1
- data/lib/prawn/font.rb +6 -4
- data/lib/prawn/font_metric_cache.rb +7 -6
- data/lib/prawn/fonts/afm.rb +18 -16
- data/lib/prawn/fonts/ttf.rb +55 -29
- data/lib/prawn/graphics.rb +11 -11
- data/lib/prawn/graphics/color.rb +18 -16
- data/lib/prawn/graphics/join_style.rb +1 -1
- data/lib/prawn/graphics/patterns.rb +57 -49
- data/lib/prawn/graphics/transformation.rb +3 -3
- data/lib/prawn/grid.rb +36 -1
- data/lib/prawn/images.rb +29 -27
- data/lib/prawn/images/jpg.rb +4 -1
- data/lib/prawn/images/png.rb +2 -1
- data/lib/prawn/outline.rb +6 -5
- data/lib/prawn/repeater.rb +1 -1
- data/lib/prawn/security.rb +39 -36
- data/lib/prawn/text.rb +18 -18
- data/lib/prawn/text/box.rb +10 -9
- data/lib/prawn/text/formatted/arranger.rb +38 -19
- data/lib/prawn/text/formatted/box.rb +20 -15
- data/lib/prawn/text/formatted/line_wrap.rb +18 -16
- data/lib/prawn/text/formatted/parser.rb +28 -26
- data/lib/prawn/text/formatted/wrap.rb +15 -11
- data/lib/prawn/version.rb +1 -1
- data/lib/prawn/view.rb +2 -2
- data/manual/basic_concepts/measurement.rb +1 -1
- data/manual/bounding_box/canvas.rb +3 -3
- data/manual/bounding_box/nesting.rb +1 -1
- data/manual/document_and_page_options/background.rb +6 -2
- data/manual/document_and_page_options/document_and_page_options.rb +3 -3
- data/manual/document_and_page_options/print_scaling.rb +2 -1
- data/manual/graphics/common_lines.rb +1 -1
- data/manual/graphics/fill_and_stroke.rb +1 -1
- data/manual/graphics/fill_rules.rb +4 -3
- data/manual/graphics/helper.rb +11 -4
- data/manual/graphics/lines_and_curves.rb +1 -1
- data/manual/graphics/stroke_dash.rb +5 -5
- data/manual/graphics/translate.rb +2 -1
- data/manual/images/horizontal.rb +2 -2
- data/manual/images/scale.rb +3 -3
- data/manual/images/vertical.rb +6 -3
- data/manual/images/width_and_height.rb +3 -3
- data/manual/layout/content.rb +2 -2
- data/manual/outline/outline.rb +1 -1
- data/manual/security/permissions.rb +4 -2
- data/manual/security/security.rb +1 -1
- data/manual/text/alignment.rb +2 -2
- data/manual/text/column_box.rb +2 -2
- data/manual/text/font_style.rb +5 -2
- data/manual/text/formatted_callbacks.rb +17 -12
- data/manual/text/formatted_text.rb +7 -4
- data/manual/text/free_flowing_text.rb +3 -3
- data/manual/text/kerning_and_character_spacing.rb +4 -4
- data/manual/text/paragraph_indentation.rb +4 -5
- data/manual/text/rendering_and_color.rb +1 -1
- data/manual/text/right_to_left_text.rb +6 -6
- data/manual/text/rotation.rb +8 -3
- data/manual/text/text_box_extensions.rb +1 -1
- data/manual/text/text_box_overflow.rb +11 -9
- data/manual/text/win_ansi_charset.rb +9 -9
- data/prawn.gemspec +4 -10
- data/spec/prawn/document/bounding_box_spec.rb +82 -78
- data/spec/prawn/document/security_spec.rb +1 -1
- data/spec/prawn/document_span_spec.rb +14 -6
- data/spec/prawn/document_spec.rb +29 -26
- data/spec/prawn/font_spec.rb +16 -14
- data/spec/prawn/graphics_spec.rb +79 -44
- data/spec/prawn/images_spec.rb +13 -8
- data/spec/prawn/outline_spec.rb +127 -27
- data/spec/prawn/repeater_spec.rb +9 -8
- data/spec/prawn/soft_mask_spec.rb +1 -1
- data/spec/prawn/stamp_spec.rb +3 -2
- data/spec/prawn/text/box_spec.rb +57 -59
- data/spec/prawn/text/formatted/arranger_spec.rb +10 -10
- data/spec/prawn/text/formatted/box_spec.rb +8 -5
- data/spec/prawn/text/formatted/line_wrap_spec.rb +2 -1
- data/spec/prawn/text_draw_text_spec.rb +9 -8
- data/spec/prawn/text_spacing_spec.rb +2 -2
- data/spec/prawn/text_spec.rb +9 -9
- data/spec/prawn/text_with_inline_formatting_spec.rb +1 -1
- data/spec/prawn_manual_spec.rb +4 -4
- metadata +14 -98
- metadata.gz.sig +0 -0
@@ -14,23 +14,24 @@ module Prawn
|
|
14
14
|
class Parser
|
15
15
|
# @group Extension API
|
16
16
|
|
17
|
-
PARSER_REGEX =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
17
|
+
PARSER_REGEX =
|
18
|
+
begin
|
19
|
+
regex_string = "\n|" \
|
20
|
+
'<b>|</b>|' \
|
21
|
+
'<i>|</i>|' \
|
22
|
+
'<u>|</u>|' \
|
23
|
+
'<strikethrough>|</strikethrough>|' \
|
24
|
+
'<sub>|</sub>|' \
|
25
|
+
'<sup>|</sup>|' \
|
26
|
+
'<link[^>]*>|</link>|' \
|
27
|
+
'<color[^>]*>|</color>|' \
|
28
|
+
'<font[^>]*>|</font>|' \
|
29
|
+
'<strong>|</strong>|' \
|
30
|
+
'<em>|</em>|' \
|
31
|
+
'<a[^>]*>|</a>|' \
|
32
|
+
"[^<\n]+"
|
33
|
+
Regexp.new(regex_string, Regexp::MULTILINE)
|
34
|
+
end
|
34
35
|
|
35
36
|
def self.format(string, *_args)
|
36
37
|
tokens = string.gsub(%r{<br\s*/?>}, "\n").scan(PARSER_REGEX)
|
@@ -54,7 +55,7 @@ module Prawn
|
|
54
55
|
subscript: '</sub>',
|
55
56
|
superscript: '</sup>'
|
56
57
|
}
|
57
|
-
array.
|
58
|
+
array.map do |hash|
|
58
59
|
prefix = ''
|
59
60
|
suffix = ''
|
60
61
|
hash[:styles]&.each do |style|
|
@@ -81,14 +82,15 @@ module Prawn
|
|
81
82
|
end
|
82
83
|
|
83
84
|
if hash[:color]
|
84
|
-
prefix +=
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
85
|
+
prefix +=
|
86
|
+
if hash[:color].is_a?(Array)
|
87
|
+
"<color c='#{hash[:color][0]}'" \
|
88
|
+
" m='#{hash[:color][1]}'" \
|
89
|
+
" y='#{hash[:color][2]}'" \
|
90
|
+
" k='#{hash[:color][3]}'>"
|
91
|
+
else
|
92
|
+
"<color rgb='#{hash[:color]}'>"
|
93
|
+
end
|
92
94
|
suffix = '</color>'
|
93
95
|
end
|
94
96
|
|
@@ -98,8 +98,10 @@ module Prawn
|
|
98
98
|
fragments_this_line.each do |fragment_this_line|
|
99
99
|
fragment_this_line.default_direction = @direction
|
100
100
|
format_and_draw_fragment(
|
101
|
-
fragment_this_line,
|
102
|
-
|
101
|
+
fragment_this_line,
|
102
|
+
accumulated_width,
|
103
|
+
@line_wrap.width,
|
104
|
+
word_spacing
|
103
105
|
)
|
104
106
|
accumulated_width += fragment_this_line.width
|
105
107
|
end
|
@@ -122,11 +124,12 @@ module Prawn
|
|
122
124
|
@line_height = @arranger.max_line_height
|
123
125
|
@descender = @arranger.max_descender
|
124
126
|
@ascender = @arranger.max_ascender
|
125
|
-
diff =
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
127
|
+
diff =
|
128
|
+
if @baseline_y.zero?
|
129
|
+
@ascender + @descender
|
130
|
+
else
|
131
|
+
@descender + @line_height + @leading
|
132
|
+
end
|
130
133
|
require_relatived_total_height = @baseline_y.abs + diff
|
131
134
|
if require_relatived_total_height > @height + 0.0001
|
132
135
|
# no room for the full height of this line
|
@@ -152,12 +155,13 @@ module Prawn
|
|
152
155
|
@everything_printed = false
|
153
156
|
end
|
154
157
|
|
155
|
-
def format_and_draw_fragment(fragment, accumulated_width,
|
156
|
-
line_width, word_spacing)
|
158
|
+
def format_and_draw_fragment(fragment, accumulated_width, line_width, word_spacing)
|
157
159
|
@arranger.apply_color_and_font_settings(fragment) do
|
158
160
|
draw_fragment(
|
159
|
-
fragment,
|
160
|
-
|
161
|
+
fragment,
|
162
|
+
accumulated_width,
|
163
|
+
line_width,
|
164
|
+
word_spacing
|
161
165
|
)
|
162
166
|
end
|
163
167
|
end
|
data/lib/prawn/version.rb
CHANGED
data/lib/prawn/view.rb
CHANGED
@@ -72,11 +72,11 @@ module Prawn
|
|
72
72
|
def method_missing(method_name, *arguments, &block)
|
73
73
|
return super unless document.respond_to?(method_name)
|
74
74
|
|
75
|
-
document.
|
75
|
+
document.public_send(method_name, *arguments, &block)
|
76
76
|
end
|
77
77
|
|
78
78
|
def respond_to_missing?(method_name, _include_all = false)
|
79
|
-
document.respond_to?(method_name)
|
79
|
+
document.respond_to?(method_name) || super
|
80
80
|
end
|
81
81
|
|
82
82
|
# Syntactic sugar that uses +instance_eval+ under the hood to provide
|
@@ -18,7 +18,7 @@ Prawn::ManualBuilder::Example.generate(filename) do
|
|
18
18
|
require 'prawn/measurement_extensions'
|
19
19
|
|
20
20
|
%i[mm cm dm m in yd ft].each do |measurement|
|
21
|
-
text "1 #{measurement} in PDF Points: #{1.
|
21
|
+
text "1 #{measurement} in PDF Points: #{1.public_send(measurement)} pt"
|
22
22
|
move_down 5.mm
|
23
23
|
end
|
24
24
|
end
|
@@ -15,9 +15,9 @@ require_relative '../example_helper'
|
|
15
15
|
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
16
16
|
Prawn::ManualBuilder::Example.generate(filename) do
|
17
17
|
canvas do
|
18
|
-
fill_circle [bounds.left, bounds.top],
|
19
|
-
fill_circle [bounds.right, bounds.top],
|
18
|
+
fill_circle [bounds.left, bounds.top], 30
|
19
|
+
fill_circle [bounds.right, bounds.top], 30
|
20
20
|
fill_circle [bounds.right, bounds.bottom], 30
|
21
|
-
fill_circle [0, 0],
|
21
|
+
fill_circle [0, 0], 30
|
22
22
|
end
|
23
23
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# Normally when we provide the top left corner of a bounding box we
|
4
4
|
# express the coordinates relative to the margin box. This is not the
|
5
5
|
# case when we have nested bounding boxes. Once nested the inner bounding box
|
6
|
-
# coordinates are relative to the
|
6
|
+
# coordinates are relative to the outer bounding box.
|
7
7
|
#
|
8
8
|
# This example shows some nested bounding boxes with fixed and stretchy heights.
|
9
9
|
# Note how the <code>cursor</code> method returns coordinates relative to
|
@@ -16,10 +16,14 @@ Prawn::Document.generate(filename, background: img, margin: 100) do
|
|
16
16
|
move_down font.height * 2
|
17
17
|
|
18
18
|
text 'Here is my text explaning this report. ' * 20,
|
19
|
-
size: 12,
|
19
|
+
size: 12,
|
20
|
+
align: :left,
|
21
|
+
leading: 2
|
20
22
|
|
21
23
|
move_down font.height
|
22
24
|
|
23
25
|
text "I'm using a soft background. " * 40,
|
24
|
-
size: 12,
|
26
|
+
size: 12,
|
27
|
+
align: :left,
|
28
|
+
leading: 2
|
25
29
|
end
|
@@ -7,10 +7,10 @@ require_relative '../example_helper'
|
|
7
7
|
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
8
8
|
Prawn::ManualBuilder::Example.generate(filename, page_size: 'FOLIO') do
|
9
9
|
package 'document_and_page_options' do |p|
|
10
|
-
p.example 'page_size',
|
10
|
+
p.example 'page_size', eval_source: false, full_source: true
|
11
11
|
p.example 'page_margins', eval_source: false, full_source: true
|
12
|
-
p.example 'background',
|
13
|
-
p.example 'metadata',
|
12
|
+
p.example 'background', eval_source: false, full_source: true
|
13
|
+
p.example 'metadata', eval_source: false, full_source: true
|
14
14
|
p.example 'print_scaling', eval_source: false, full_source: true
|
15
15
|
|
16
16
|
p.intro do
|
@@ -15,7 +15,8 @@ require_relative '../example_helper'
|
|
15
15
|
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
16
16
|
Prawn::Document.generate(
|
17
17
|
filename,
|
18
|
-
page_layout: :landscape,
|
18
|
+
page_layout: :landscape,
|
19
|
+
print_scaling: :none
|
19
20
|
) do
|
20
21
|
text 'When you print this document, the scale to fit in print preview '\
|
21
22
|
'should be disabled by default.'
|
@@ -25,9 +25,10 @@ Prawn::ManualBuilder::Example.generate(filename) do
|
|
25
25
|
stroke_color 'ff0000'
|
26
26
|
line_width 2
|
27
27
|
|
28
|
-
text_box 'Nonzero Winding Number',
|
29
|
-
|
30
|
-
|
28
|
+
text_box 'Nonzero Winding Number',
|
29
|
+
at: [50, 215],
|
30
|
+
width: 170,
|
31
|
+
align: :center
|
31
32
|
polygon(*pentagram.map { |x, y| [x + 50, y] })
|
32
33
|
fill_and_stroke
|
33
34
|
|
data/manual/graphics/helper.rb
CHANGED
@@ -17,11 +17,18 @@ filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
|
17
17
|
Prawn::ManualBuilder::Example.generate(filename) do
|
18
18
|
stroke_axis
|
19
19
|
stroke_axis(
|
20
|
-
at: [70, 70],
|
21
|
-
|
20
|
+
at: [70, 70],
|
21
|
+
height: 200,
|
22
|
+
step_length: 50,
|
23
|
+
negative_axes_length: 5,
|
24
|
+
color: '0000FF'
|
22
25
|
)
|
23
26
|
stroke_axis(
|
24
|
-
at: [140, 140],
|
25
|
-
|
27
|
+
at: [140, 140],
|
28
|
+
width: 200,
|
29
|
+
height: cursor.to_i - 140,
|
30
|
+
step_length: 20,
|
31
|
+
negative_axes_length: 40,
|
32
|
+
color: 'FF0000'
|
26
33
|
)
|
27
34
|
end
|
@@ -29,7 +29,7 @@ Prawn::ManualBuilder::Example.generate(filename) do
|
|
29
29
|
line_to 0, 100
|
30
30
|
|
31
31
|
curve_to [150, 250], bounds: [[20, 200], [120, 200]]
|
32
|
-
curve_to [200, 0],
|
32
|
+
curve_to [200, 0], bounds: [[150, 200], [450, 10]]
|
33
33
|
end
|
34
34
|
|
35
35
|
# line and curve
|
@@ -28,16 +28,16 @@ Prawn::ManualBuilder::Example.generate(filename) do
|
|
28
28
|
|
29
29
|
24.times do |i|
|
30
30
|
length = (i / 4) + 1
|
31
|
-
space = length
|
32
|
-
phase = 0
|
31
|
+
space = length # space between dashes same length as dash
|
32
|
+
phase = 0 # start with dash
|
33
33
|
|
34
34
|
case i % 4
|
35
35
|
when 0 then base_y -= 5
|
36
|
-
when 1 then phase = length
|
36
|
+
when 1 then phase = length # start with space between dashes
|
37
37
|
when 2 then space = length * 0.5 # space between dashes half as long as dash
|
38
38
|
when 3
|
39
|
-
space = length * 0.5
|
40
|
-
phase = length
|
39
|
+
space = length * 0.5 # space between dashes half as long as dash
|
40
|
+
phase = length # start with space between dashes
|
41
41
|
end
|
42
42
|
base_y -= 5
|
43
43
|
|
@@ -16,7 +16,8 @@ Prawn::ManualBuilder::Example.generate(filename) do
|
|
16
16
|
# Draw a point on the new origin
|
17
17
|
fill_circle [0, 0], 2
|
18
18
|
draw_text "New origin after translation to [#{x}, #{y}]",
|
19
|
-
at: [5, -2],
|
19
|
+
at: [5, -2],
|
20
|
+
size: 8
|
20
21
|
|
21
22
|
stroke_rectangle [100, 75], 100, 50
|
22
23
|
text_box 'Top left corner at [100,75]',
|
data/manual/images/horizontal.rb
CHANGED
@@ -14,11 +14,11 @@ Prawn::ManualBuilder::Example.generate(filename) do
|
|
14
14
|
stroke_bounds
|
15
15
|
|
16
16
|
%i[left center right].each do |position|
|
17
|
-
text
|
17
|
+
text "Image aligned to the #{position}."
|
18
18
|
image "#{Prawn::DATADIR}/images/stef.jpg", position: position
|
19
19
|
end
|
20
20
|
|
21
|
-
text
|
21
|
+
text 'The next image has a 50 point offset from the left boundary'
|
22
22
|
image "#{Prawn::DATADIR}/images/stef.jpg", position: 50
|
23
23
|
end
|
24
24
|
end
|
data/manual/images/scale.rb
CHANGED
@@ -8,14 +8,14 @@ require_relative '../example_helper'
|
|
8
8
|
|
9
9
|
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
10
10
|
Prawn::ManualBuilder::Example.generate(filename) do
|
11
|
-
text
|
11
|
+
text 'Normal size'
|
12
12
|
image "#{Prawn::DATADIR}/images/stef.jpg"
|
13
13
|
move_down 20
|
14
14
|
|
15
|
-
text
|
15
|
+
text 'Scaled to 50%'
|
16
16
|
image "#{Prawn::DATADIR}/images/stef.jpg", scale: 0.5
|
17
17
|
move_down 20
|
18
18
|
|
19
|
-
text
|
19
|
+
text 'Scaled to 200%'
|
20
20
|
image "#{Prawn::DATADIR}/images/stef.jpg", scale: 2
|
21
21
|
end
|
data/manual/images/vertical.rb
CHANGED
@@ -16,12 +16,15 @@ Prawn::ManualBuilder::Example.generate(filename) do
|
|
16
16
|
%i[top center bottom].each do |vposition|
|
17
17
|
text "Image vertically aligned to the #{vposition}.", valign: vposition
|
18
18
|
image "#{Prawn::DATADIR}/images/stef.jpg",
|
19
|
-
position: 250,
|
19
|
+
position: 250,
|
20
|
+
vposition: vposition
|
20
21
|
end
|
21
22
|
|
22
23
|
text_box 'The next image has a 100 point offset from the top boundary',
|
23
|
-
at: [bounds.width - 110, bounds.top - 10],
|
24
|
+
at: [bounds.width - 110, bounds.top - 10],
|
25
|
+
width: 100
|
24
26
|
image "#{Prawn::DATADIR}/images/stef.jpg",
|
25
|
-
position: :right,
|
27
|
+
position: :right,
|
28
|
+
vposition: 100
|
26
29
|
end
|
27
30
|
end
|
@@ -11,14 +11,14 @@ require_relative '../example_helper'
|
|
11
11
|
|
12
12
|
filename = File.basename(__FILE__).gsub('.rb', '.pdf')
|
13
13
|
Prawn::ManualBuilder::Example.generate(filename) do
|
14
|
-
text
|
14
|
+
text 'Scale by setting only the width'
|
15
15
|
image "#{Prawn::DATADIR}/images/pigs.jpg", width: 150
|
16
16
|
move_down 20
|
17
17
|
|
18
|
-
text
|
18
|
+
text 'Scale by setting only the height'
|
19
19
|
image "#{Prawn::DATADIR}/images/pigs.jpg", height: 100
|
20
20
|
move_down 20
|
21
21
|
|
22
|
-
text
|
22
|
+
text 'Stretch to fit the width and height provided'
|
23
23
|
image "#{Prawn::DATADIR}/images/pigs.jpg", width: 500, height: 100
|
24
24
|
end
|
data/manual/layout/content.rb
CHANGED
@@ -15,10 +15,10 @@ Prawn::ManualBuilder::Example.generate(filename) do
|
|
15
15
|
define_grid(columns: 5, rows: 8, gutter: 10)
|
16
16
|
|
17
17
|
grid([5, 0], [7, 1]).bounding_box do
|
18
|
-
text "Adding some content to this multi_box.\n
|
18
|
+
text "Adding some content to this multi_box.\n#{' _ ' * 200}"
|
19
19
|
end
|
20
20
|
|
21
21
|
grid(6, 3).bounding_box do
|
22
|
-
text "Just a little snippet here.\n
|
22
|
+
text "Just a little snippet here.\n#{' _ ' * 10}"
|
23
23
|
end
|
24
24
|
end
|
data/manual/outline/outline.rb
CHANGED
@@ -11,7 +11,7 @@ Prawn::ManualBuilder::Example.generate('outline.pdf', page_size: 'FOLIO') do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
p.section 'Adding nodes later' do |s|
|
14
|
-
s.example 'add_subsection_to',
|
14
|
+
s.example 'add_subsection_to', eval_source: false
|
15
15
|
s.example 'insert_section_after', eval_source: false
|
16
16
|
end
|
17
17
|
|
@@ -21,7 +21,8 @@ require_relative '../example_helper'
|
|
21
21
|
Prawn::ManualBuilder::Example.generate('cannot_print.pdf') do
|
22
22
|
text "If you used the user password you won't be able to print the doc."
|
23
23
|
encrypt_document(
|
24
|
-
user_password: 'foo',
|
24
|
+
user_password: 'foo',
|
25
|
+
owner_password: 'bar',
|
25
26
|
permissions: { print_document: false }
|
26
27
|
)
|
27
28
|
end
|
@@ -30,7 +31,8 @@ end
|
|
30
31
|
Prawn::ManualBuilder::Example.generate('no_permissions.pdf') do
|
31
32
|
text "You may only view this and won't be able to use the owner password."
|
32
33
|
encrypt_document(
|
33
|
-
user_password: 'foo',
|
34
|
+
user_password: 'foo',
|
35
|
+
owner_password: :random,
|
34
36
|
permissions: {
|
35
37
|
print_document: false,
|
36
38
|
modify_contents: false,
|
data/manual/security/security.rb
CHANGED
@@ -6,7 +6,7 @@ require_relative '../example_helper'
|
|
6
6
|
|
7
7
|
Prawn::ManualBuilder::Example.generate('security.pdf', page_size: 'FOLIO') do
|
8
8
|
package 'security' do |p|
|
9
|
-
p.example 'encryption',
|
9
|
+
p.example 'encryption', eval_source: false, full_source: true
|
10
10
|
p.example 'permissions', eval_source: false, full_source: true
|
11
11
|
|
12
12
|
p.intro do
|