prism_qa 0.2.0 → 0.2.1
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/gem/lib/prism_qa.rb +10 -2
- data/gem/lib/prism_qa/filesystem.rb +3 -3
- data/gem/lib/prism_qa/image.rb +0 -1
- data/gem/lib/prism_qa/imageset.rb +17 -19
- data/gem/lib/prism_qa/report.rb +42 -20
- data/gem/lib/prism_qa/reportset.rb +18 -16
- data/gem/lib/prism_qa/spectrum.rb +17 -12
- data/gem/lib/prism_qa/target.rb +1 -2
- data/gem/lib/prism_qa/version.rb +3 -1
- metadata +76 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f7ffb6cb149a2d8fc072403214e82b01c780aff
|
4
|
+
data.tar.gz: 9d602daab9d1d4325609ba94da5d59de95468a92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9913c2894bd81a6c2dab7010a94acdffb962d56176fe4a5e4633ab6e76c88fe9f0f13cfb0d6b033ce1238b72534e1912feafbfd1f7649e65b696be182e66094e
|
7
|
+
data.tar.gz: 882a08b879975b83544fd4f72ed76a0bb6caf651cf619da90fccfa009748a62911c74643b12b595db62845b1d58bfc7e6c8c7646b75e09a158dc2139b90dcbc0
|
data/gem/lib/prism_qa.rb
CHANGED
@@ -8,13 +8,21 @@ require 'prism_qa/spectrum'
|
|
8
8
|
require 'prism_qa/report'
|
9
9
|
require 'prism_qa/reportset'
|
10
10
|
|
11
|
-
|
11
|
+
# The top-level prism module
|
12
12
|
module PrismQA
|
13
13
|
|
14
|
+
# Create a prism report
|
15
|
+
#
|
16
|
+
# design_spectrum: a DesignSpectrum object
|
17
|
+
# app_spectra: an array of AppSpectrum objects
|
18
|
+
# title_for_attribute_fn: a function taking a string attribute (or nil) and returning a string title
|
19
|
+
# path_for_attribute_fn: a function taking a string attribute (or nil) and returning the path for the saved report
|
20
|
+
# web_document_root: a path relative to which images within the report should be referenced
|
21
|
+
# img_width_px: an integer width for images in the report
|
14
22
|
def self.report(design_spectrum, app_spectra, title_for_attribute_fn, path_for_attribute_fn, web_document_root, img_width_px)
|
15
23
|
# load source images
|
16
24
|
design_spectrum.load
|
17
|
-
app_spectra.each
|
25
|
+
app_spectra.each(&:load)
|
18
26
|
|
19
27
|
rs = PrismQA::ReportSet.new
|
20
28
|
rs.design_spectrum = design_spectrum
|
@@ -6,7 +6,7 @@ require_relative 'exceptions'
|
|
6
6
|
def ancestor?(base, other_path)
|
7
7
|
base_parts = File.expand_path(base).split('/')
|
8
8
|
path_parts = File.expand_path(other_path).split('/')
|
9
|
-
path_parts[0..base_parts.size-1] == base_parts
|
9
|
+
path_parts[0..(base_parts.size - 1)] == base_parts
|
10
10
|
end
|
11
11
|
|
12
12
|
# return the relative path from a document in a web root to a media element, given full paths to each
|
@@ -14,9 +14,9 @@ def web_relative_path(web_root, base_document, child_element)
|
|
14
14
|
c = File.expand_path(child_element)
|
15
15
|
r = File.expand_path(web_root)
|
16
16
|
unless ancestor?(r, c)
|
17
|
-
|
17
|
+
fail PrismQA::OperationalError, "Child element '#{c}' is not an ancestor of the web root '#{r}'"
|
18
18
|
end
|
19
|
-
base = Pathname.new
|
19
|
+
base = Pathname.new(File.dirname(File.expand_path(base_document)))
|
20
20
|
elem = Pathname.new c
|
21
21
|
(elem.relative_path_from base).to_s
|
22
22
|
end
|
data/gem/lib/prism_qa/image.rb
CHANGED
@@ -12,8 +12,8 @@ module PrismQA
|
|
12
12
|
end
|
13
13
|
|
14
14
|
# Safe way to add images to the container
|
15
|
-
def add
|
16
|
-
|
15
|
+
def add(image)
|
16
|
+
allow image
|
17
17
|
# fix relative paths
|
18
18
|
image.path = File.expand_path(image.path)
|
19
19
|
@images << image
|
@@ -22,28 +22,28 @@ module PrismQA
|
|
22
22
|
end
|
23
23
|
|
24
24
|
# Raise an error if the image is not appropriate for this type of set
|
25
|
-
def allow
|
25
|
+
def allow(_image)
|
26
26
|
puts " +++ If you're seeing this, #{self.class.name}.#{__method__} was not overridden"
|
27
27
|
end
|
28
|
-
end
|
29
28
|
|
29
|
+
end
|
30
30
|
|
31
31
|
# Design image sets need to be able to report on the images they contain
|
32
32
|
class DesignImageSet < ImageSet
|
33
33
|
|
34
|
-
def allow
|
34
|
+
def allow(image)
|
35
35
|
# Ensure that image objects have an "attribute" field, among other things
|
36
|
-
|
36
|
+
fail IncompatibilityError, 'Tried to add a non- DesignImage object to a DesignImageSet' unless image.is_a? DesignImage
|
37
37
|
|
38
38
|
# no duplicates allowed
|
39
|
-
if
|
40
|
-
|
39
|
+
if @images.map { |i| [i.id, i.attribute] }.include? [image.id, image.attribute]
|
40
|
+
fail OperationalError, "Tried to add an image with duplicate ID '#{image.id}' and attribute '#{image.attribute}'"
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
# Get the list of unique attributes contained by the images within
|
45
45
|
def contained_attributes
|
46
|
-
|
46
|
+
@images.map(&:attribute).uniq
|
47
47
|
end
|
48
48
|
|
49
49
|
# cache the image attributes
|
@@ -55,7 +55,7 @@ module PrismQA
|
|
55
55
|
@attributes_by_id = {}
|
56
56
|
@images.each do |img|
|
57
57
|
proper_key = img.id.to_s
|
58
|
-
@attributes_by_id[proper_key] = [] unless @attributes_by_id.
|
58
|
+
@attributes_by_id[proper_key] = [] unless @attributes_by_id.key? proper_key
|
59
59
|
@attributes_by_id[proper_key] << img.attribute
|
60
60
|
end
|
61
61
|
|
@@ -63,8 +63,8 @@ module PrismQA
|
|
63
63
|
end
|
64
64
|
|
65
65
|
# get the list of images that are valid for a particular attribute
|
66
|
-
def images_for_attribute
|
67
|
-
|
66
|
+
def images_for_attribute(attribute)
|
67
|
+
cache_image_attributes
|
68
68
|
|
69
69
|
# return the pared-down list
|
70
70
|
@images.select do |img|
|
@@ -81,20 +81,19 @@ module PrismQA
|
|
81
81
|
|
82
82
|
end
|
83
83
|
|
84
|
-
|
85
84
|
# App image sets are tied to a target
|
86
85
|
class AppImageSet < ImageSet
|
87
86
|
|
88
87
|
attr_accessor :target
|
89
88
|
|
90
|
-
def allow
|
89
|
+
def allow(image)
|
91
90
|
# no duplicates
|
92
|
-
if
|
93
|
-
|
91
|
+
if @images.map(&:id).include? image.id
|
92
|
+
fail OperationalError, "Tried to add an image with duplicate ID '#{image.id}'"
|
94
93
|
end
|
95
94
|
|
96
95
|
# App image sets don't need to worry about specific fields, but we keep it clean and symmetric.
|
97
|
-
|
96
|
+
fail IncompatibilityError, 'Tried to add a DesignImage object to a non- DesignImageSet' if image.is_a? DesignImage
|
98
97
|
end
|
99
98
|
|
100
99
|
def cache_image_lookups
|
@@ -109,11 +108,10 @@ module PrismQA
|
|
109
108
|
end
|
110
109
|
|
111
110
|
def best_image_for(id)
|
112
|
-
|
111
|
+
cache_image_lookups
|
113
112
|
@image_lookup.fetch(id.to_s, nil)
|
114
113
|
end
|
115
114
|
|
116
115
|
end
|
117
116
|
|
118
|
-
|
119
117
|
end
|
data/gem/lib/prism_qa/report.rb
CHANGED
@@ -3,7 +3,9 @@ require_relative 'filesystem'
|
|
3
3
|
|
4
4
|
module PrismQA
|
5
5
|
|
6
|
+
# A Prism report is an object whose to_s method returns HTML for the full report.
|
6
7
|
class Report
|
8
|
+
|
7
9
|
attr_accessor :title
|
8
10
|
attr_accessor :attribute
|
9
11
|
attr_accessor :design_spectrum
|
@@ -13,7 +15,7 @@ module PrismQA
|
|
13
15
|
attr_accessor :img_width_px
|
14
16
|
|
15
17
|
def css
|
16
|
-
width_string =
|
18
|
+
width_string = ''
|
17
19
|
width_string = "width: #{@img_width_px}px;" unless img_width_px.nil?
|
18
20
|
%(
|
19
21
|
body {color:white; background-color:#333;}
|
@@ -26,21 +28,41 @@ module PrismQA
|
|
26
28
|
end
|
27
29
|
|
28
30
|
# if necessary, modify the path to be relative (for web-based reports)
|
29
|
-
def path_transform
|
31
|
+
def path_transform(element_path)
|
30
32
|
unless @web_document_root.nil?
|
31
33
|
element_path = web_relative_path(@web_document_root, @destination_path, element_path)
|
32
34
|
end
|
33
35
|
element_path
|
34
36
|
end
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
# initial calculations - get the app spectra that support the attribute we are reporting on
|
39
|
-
candidates = @app_spectra.select do |app_spectrum|
|
38
|
+
def candidates_for_attribute
|
39
|
+
@app_spectra.select do |app_spectrum|
|
40
40
|
next true if @attribute.nil? # unless there is nothing in this candidate???? might be expensive to check.
|
41
|
-
|
42
41
|
app_spectrum.image_set.target.attribute == @attribute
|
43
42
|
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# make a list of problems found
|
46
|
+
def test_input
|
47
|
+
problems = []
|
48
|
+
@app_spectra.each do |app_spectrum|
|
49
|
+
problems << 'App spectrum has a nil target defined in its image set' if app_spectrum.image_set.target.nil?
|
50
|
+
end
|
51
|
+
problems
|
52
|
+
end
|
53
|
+
|
54
|
+
# raise an error if any problems are found
|
55
|
+
def validate_input
|
56
|
+
problems = test_input
|
57
|
+
fail OperationalError, "Found the following problems: #{problems}" unless problems.empty?
|
58
|
+
end
|
59
|
+
|
60
|
+
# render the report
|
61
|
+
def to_s
|
62
|
+
validate_input
|
63
|
+
|
64
|
+
# initial calculations - get the app spectra that support the attribute we are reporting on
|
65
|
+
candidates = candidates_for_attribute
|
44
66
|
design_images = @design_spectrum.image_set.images_for_attribute(@attribute)
|
45
67
|
columns = candidates.length + 1
|
46
68
|
|
@@ -52,7 +74,7 @@ module PrismQA
|
|
52
74
|
|
53
75
|
head do
|
54
76
|
title "#{me.title} | Prism QA"
|
55
|
-
style :
|
77
|
+
style type: 'text/css' do
|
56
78
|
me.css
|
57
79
|
end
|
58
80
|
end
|
@@ -60,12 +82,12 @@ module PrismQA
|
|
60
82
|
body do
|
61
83
|
h1 me.title
|
62
84
|
if design_images.empty?
|
63
|
-
p
|
85
|
+
p 'No input images were found.'
|
64
86
|
else
|
65
87
|
table.comparison do
|
66
88
|
# print out the first row of the table -- the target names
|
67
89
|
tr do
|
68
|
-
td
|
90
|
+
td 'Design'
|
69
91
|
candidates.each do |c|
|
70
92
|
td c.image_set.target.name
|
71
93
|
end
|
@@ -75,8 +97,8 @@ module PrismQA
|
|
75
97
|
design_images.each do |design_image|
|
76
98
|
# title
|
77
99
|
tr do
|
78
|
-
th :
|
79
|
-
a :
|
100
|
+
th colspan: columns do
|
101
|
+
a name: design_image.description do
|
80
102
|
design_image.description
|
81
103
|
end
|
82
104
|
end
|
@@ -84,10 +106,10 @@ module PrismQA
|
|
84
106
|
|
85
107
|
# images
|
86
108
|
tr do
|
87
|
-
td :
|
109
|
+
td align: 'right', valign: 'top' do
|
88
110
|
src = me.path_transform(design_image.path)
|
89
|
-
a :
|
90
|
-
img.masterimg :
|
111
|
+
a href: src do
|
112
|
+
img.masterimg src: src, alt: design_image.description
|
91
113
|
end
|
92
114
|
end
|
93
115
|
|
@@ -96,11 +118,11 @@ module PrismQA
|
|
96
118
|
if app_image.nil?
|
97
119
|
td { div.missing "#{design_image.description} on #{candidate.image_set.target.name}" }
|
98
120
|
else
|
99
|
-
td :
|
121
|
+
td align: 'left', valign: 'top' do
|
100
122
|
div.holder do
|
101
123
|
src = me.path_transform(app_image.path)
|
102
|
-
a :
|
103
|
-
img.appimg :
|
124
|
+
a href: src do
|
125
|
+
img.appimg src: src, alt: app_image.description
|
104
126
|
end
|
105
127
|
end
|
106
128
|
end
|
@@ -112,9 +134,9 @@ module PrismQA
|
|
112
134
|
end
|
113
135
|
end
|
114
136
|
end
|
115
|
-
|
137
|
+
mab.to_s
|
116
138
|
end
|
117
139
|
|
118
|
-
end
|
140
|
+
end
|
119
141
|
|
120
142
|
end
|
@@ -2,6 +2,7 @@ require_relative 'filesystem'
|
|
2
2
|
|
3
3
|
module PrismQA
|
4
4
|
|
5
|
+
# A prism ReportSet produces a set of reports, one for each attribute (plus one for the nil attribute)
|
5
6
|
class ReportSet
|
6
7
|
attr_accessor :design_spectrum
|
7
8
|
attr_accessor :app_spectra
|
@@ -11,35 +12,36 @@ module PrismQA
|
|
11
12
|
attr_accessor :img_width_px
|
12
13
|
|
13
14
|
# Check whether the path is correct, particularly if we are making a web-based report
|
14
|
-
def allow_path
|
15
|
+
def allow_path(path)
|
15
16
|
unless @web_document_root.nil?
|
16
17
|
unless ancestor?(@web_document_root, path)
|
17
|
-
|
18
|
+
fail OperationalError, "Report #{path} is not an ancestor of the web root #{@web_document_root}"
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
23
|
+
def _configured_report(attribute, output_path)
|
24
|
+
r = Report.new
|
25
|
+
r.title = @title_for_attribute_fn.call(attribute)
|
26
|
+
r.attribute = attribute
|
27
|
+
r.design_spectrum = @design_spectrum
|
28
|
+
r.app_spectra = @app_spectra
|
29
|
+
r.web_document_root = @web_document_root
|
30
|
+
r.destination_path = output_path
|
31
|
+
r.img_width_px = @img_width_px
|
32
|
+
r
|
33
|
+
end
|
34
|
+
|
22
35
|
def write
|
23
36
|
@design_spectrum.image_set.contained_attributes.map do |attr|
|
24
37
|
|
25
38
|
# first check whether the destination is ok
|
26
39
|
path = @path_for_attribute_fn.call(attr)
|
27
|
-
|
28
|
-
|
29
|
-
r = Report.new
|
30
|
-
r.title = @title_for_attribute_fn.call(attr)
|
31
|
-
r.attribute = attr
|
32
|
-
r.design_spectrum = @design_spectrum
|
33
|
-
r.app_spectra = @app_spectra
|
34
|
-
r.web_document_root = @web_document_root
|
35
|
-
r.destination_path = path
|
36
|
-
r.img_width_px = @img_width_px
|
40
|
+
allow_path path
|
37
41
|
|
38
|
-
|
42
|
+
rpt = _configured_report(attr, path)
|
43
|
+
File.open(path, 'w') { |f| f.write(rpt.to_s) }
|
39
44
|
end
|
40
45
|
end
|
41
|
-
|
42
46
|
end
|
43
|
-
|
44
|
-
|
45
47
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
|
2
|
+
# A Spectrum in Prism defines a set of images that can be loaded on demand
|
3
|
+
# It is not exposed in the PrismQA module
|
2
4
|
class Spectrum
|
3
5
|
|
4
6
|
attr_reader :image_set
|
@@ -8,8 +10,8 @@ class Spectrum
|
|
8
10
|
end
|
9
11
|
|
10
12
|
def load
|
11
|
-
image_set =
|
12
|
-
|
13
|
+
image_set = fetch_image_set
|
14
|
+
allow_image_set image_set
|
13
15
|
@image_set = image_set
|
14
16
|
end
|
15
17
|
|
@@ -19,39 +21,42 @@ class Spectrum
|
|
19
21
|
end
|
20
22
|
|
21
23
|
# implementation-specific: verify that an ImageSet is appropriate
|
22
|
-
def allow_image_set
|
24
|
+
def allow_image_set(_image_set)
|
23
25
|
puts " +++ If you're seeing this, #{self.class.name}.#{__method__} was not overridden"
|
24
26
|
end
|
25
27
|
|
26
28
|
end
|
27
29
|
|
30
|
+
# Extensions of Spectrum are exposed in the module
|
28
31
|
|
29
32
|
module PrismQA
|
30
33
|
|
34
|
+
# A DesignSpectrum defines an order on a set of images used to represent the design
|
31
35
|
class DesignSpectrum < Spectrum
|
32
36
|
|
33
37
|
def initialize
|
34
38
|
super
|
35
|
-
@order = []
|
39
|
+
@order = [] # will hold the sorted indexes into the image set array
|
36
40
|
end
|
37
41
|
|
38
|
-
def allow_image_set
|
39
|
-
|
42
|
+
def allow_image_set(image_set)
|
43
|
+
fail ImplementationError, "Got a nil DesignImageSet object; was #{self.class.name} properly extended?" if image_set.nil?
|
40
44
|
|
41
45
|
# Ensure that we are only looking at design images
|
42
|
-
|
46
|
+
unless image_set.is_a? DesignImageSet
|
47
|
+
fail IncompatibilityError, 'Tried to add a non- DesignImageSet object to DesignSpectrum'
|
48
|
+
end
|
43
49
|
end
|
44
|
-
|
45
50
|
end
|
46
51
|
|
47
|
-
|
52
|
+
# An AppSpectrum defines a set of images used to represent the actual app
|
48
53
|
class AppSpectrum < Spectrum
|
49
54
|
|
50
|
-
def allow_image_set
|
51
|
-
|
55
|
+
def allow_image_set(image_set)
|
56
|
+
fail ImplementationError, "Got a nil DesignImageSet object; was #{self.class.name} properly extended?" if image_set.nil?
|
52
57
|
|
53
58
|
# Ensure that we are only looking at implementation images
|
54
|
-
|
59
|
+
fail IncompatibilityError, 'Tried to add a DesignImageSet object to AppSpectrum' if image_set.is_a? DesignImageSet
|
55
60
|
end
|
56
61
|
|
57
62
|
end
|
data/gem/lib/prism_qa/target.rb
CHANGED
data/gem/lib/prism_qa/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prism_qa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ian Katz
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,6 +44,80 @@ dependencies:
|
|
44
44
|
- - ~>
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '10.0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rubocop
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.31'
|
54
|
+
- - '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 0.31.0
|
57
|
+
type: :development
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ~>
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0.31'
|
64
|
+
- - '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 0.31.0
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: rspec
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ~>
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '3.0'
|
74
|
+
type: :development
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ~>
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '3.0'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: simplecov
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ~>
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0.10'
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 0.10.0
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ~>
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0.10'
|
98
|
+
- - '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: 0.10.0
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: simplecov-json
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ~>
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0.2'
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.2.0
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.2'
|
118
|
+
- - '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 0.2.0
|
47
121
|
- !ruby/object:Gem::Dependency
|
48
122
|
name: markaby
|
49
123
|
requirement: !ruby/object:Gem::Requirement
|