respect 0.1.0
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.
- data/MIT-LICENSE +20 -0
- data/README.md +289 -0
- data/RELATED_WORK.md +40 -0
- data/RELEASE_NOTES.md +23 -0
- data/Rakefile +31 -0
- data/STATUS_MATRIX.html +137 -0
- data/lib/respect.rb +231 -0
- data/lib/respect/any_schema.rb +22 -0
- data/lib/respect/array_def.rb +28 -0
- data/lib/respect/array_schema.rb +203 -0
- data/lib/respect/boolean_schema.rb +32 -0
- data/lib/respect/composite_schema.rb +86 -0
- data/lib/respect/core_statements.rb +206 -0
- data/lib/respect/datetime_schema.rb +27 -0
- data/lib/respect/def_without_name.rb +6 -0
- data/lib/respect/divisible_by_validator.rb +20 -0
- data/lib/respect/doc_helper.rb +24 -0
- data/lib/respect/doc_parser.rb +37 -0
- data/lib/respect/dsl_dumper.rb +181 -0
- data/lib/respect/equal_to_validator.rb +20 -0
- data/lib/respect/fake_name_proxy.rb +116 -0
- data/lib/respect/float_schema.rb +27 -0
- data/lib/respect/format_validator.rb +136 -0
- data/lib/respect/global_def.rb +79 -0
- data/lib/respect/greater_than_or_equal_to_validator.rb +19 -0
- data/lib/respect/greater_than_validator.rb +19 -0
- data/lib/respect/has_constraints.rb +34 -0
- data/lib/respect/hash_def.rb +40 -0
- data/lib/respect/hash_schema.rb +218 -0
- data/lib/respect/in_validator.rb +19 -0
- data/lib/respect/integer_schema.rb +27 -0
- data/lib/respect/ip_addr_schema.rb +23 -0
- data/lib/respect/ipv4_addr_schema.rb +27 -0
- data/lib/respect/ipv6_addr_schema.rb +27 -0
- data/lib/respect/items_def.rb +21 -0
- data/lib/respect/json_schema_html_formatter.rb +143 -0
- data/lib/respect/less_than_or_equal_to_validator.rb +19 -0
- data/lib/respect/less_than_validator.rb +19 -0
- data/lib/respect/match_validator.rb +19 -0
- data/lib/respect/max_length_validator.rb +20 -0
- data/lib/respect/min_length_validator.rb +20 -0
- data/lib/respect/multiple_of_validator.rb +10 -0
- data/lib/respect/null_schema.rb +26 -0
- data/lib/respect/numeric_schema.rb +33 -0
- data/lib/respect/org3_dumper.rb +213 -0
- data/lib/respect/regexp_schema.rb +19 -0
- data/lib/respect/schema.rb +285 -0
- data/lib/respect/schema_def.rb +16 -0
- data/lib/respect/string_schema.rb +21 -0
- data/lib/respect/unit_test_helper.rb +37 -0
- data/lib/respect/uri_schema.rb +23 -0
- data/lib/respect/utc_time_schema.rb +17 -0
- data/lib/respect/validator.rb +51 -0
- data/lib/respect/version.rb +3 -0
- data/test/any_schema_test.rb +79 -0
- data/test/array_def_test.rb +113 -0
- data/test/array_schema_test.rb +487 -0
- data/test/boolean_schema_test.rb +89 -0
- data/test/composite_schema_test.rb +30 -0
- data/test/datetime_schema_test.rb +83 -0
- data/test/doc_helper_test.rb +34 -0
- data/test/doc_parser_test.rb +109 -0
- data/test/dsl_dumper_test.rb +395 -0
- data/test/fake_name_proxy_test.rb +138 -0
- data/test/float_schema_test.rb +146 -0
- data/test/format_validator_test.rb +224 -0
- data/test/hash_def_test.rb +126 -0
- data/test/hash_schema_test.rb +613 -0
- data/test/integer_schema_test.rb +142 -0
- data/test/ip_addr_schema_test.rb +78 -0
- data/test/ipv4_addr_schema_test.rb +71 -0
- data/test/ipv6_addr_schema_test.rb +71 -0
- data/test/json_schema_html_formatter_test.rb +214 -0
- data/test/null_schema_test.rb +46 -0
- data/test/numeric_schema_test.rb +294 -0
- data/test/org3_dumper_test.rb +784 -0
- data/test/regexp_schema_test.rb +54 -0
- data/test/respect_test.rb +108 -0
- data/test/schema_def_test.rb +405 -0
- data/test/schema_test.rb +290 -0
- data/test/string_schema_test.rb +209 -0
- data/test/support/circle.rb +11 -0
- data/test/support/color.rb +24 -0
- data/test/support/point.rb +11 -0
- data/test/support/respect/circle_schema.rb +16 -0
- data/test/support/respect/color_def.rb +19 -0
- data/test/support/respect/color_schema.rb +33 -0
- data/test/support/respect/point_schema.rb +19 -0
- data/test/support/respect/rgba_schema.rb +20 -0
- data/test/support/respect/universal_validator.rb +25 -0
- data/test/support/respect/user_macros.rb +12 -0
- data/test/support/rgba.rb +11 -0
- data/test/test_helper.rb +90 -0
- data/test/uri_schema_test.rb +54 -0
- data/test/utc_time_schema_test.rb +63 -0
- data/test/validator_test.rb +22 -0
- metadata +288 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module Respect
|
2
|
+
class BooleanSchema < Schema
|
3
|
+
include HasConstraints
|
4
|
+
|
5
|
+
public_class_method :new
|
6
|
+
|
7
|
+
def validate_type(object)
|
8
|
+
case object
|
9
|
+
when String
|
10
|
+
if object == "true"
|
11
|
+
true
|
12
|
+
elsif object == "false"
|
13
|
+
false
|
14
|
+
else
|
15
|
+
raise ValidationError,
|
16
|
+
"malformed boolean value: `#{object}'"
|
17
|
+
end
|
18
|
+
when TrueClass, FalseClass
|
19
|
+
object
|
20
|
+
when NilClass
|
21
|
+
if allow_nil?
|
22
|
+
nil
|
23
|
+
else
|
24
|
+
raise ValidationError, "object is nil but this #{self.class} does not allow nil"
|
25
|
+
end
|
26
|
+
else
|
27
|
+
raise ValidationError, "object is not a boolean but a '#{object.class}'"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end # class BooleanSchema
|
32
|
+
end # module Respect
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Respect
|
2
|
+
# A composite schema is a schema composed of another schema.
|
3
|
+
#
|
4
|
+
# Sub-classing {CompositeSchema} is the easiest way to add a user-defined
|
5
|
+
# schema. Indeed, you just have to overwrite {#schema_definition} and optionally
|
6
|
+
# {#sanitize}. Your schema will be handled properly by all other part
|
7
|
+
# of the library (i.e. mainly dumpers and the DSL).
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
# module Respect
|
11
|
+
# class PointSchema < CompositeSchema
|
12
|
+
# def schema_defintion
|
13
|
+
# HashSchema.define do |s|
|
14
|
+
# s.numeric "x"
|
15
|
+
# s.numeric "y"
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# def sanitize(object)
|
20
|
+
# # Assuming you have defined a Point class.
|
21
|
+
# Point.new(object[:x], object[:y])
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# A "point" method will be available in the DSL so you could use
|
27
|
+
# your schema like that:
|
28
|
+
#
|
29
|
+
# Example:
|
30
|
+
# HashSchema.define do |s|
|
31
|
+
# s.point "origin"
|
32
|
+
# end
|
33
|
+
class CompositeSchema < Schema
|
34
|
+
|
35
|
+
class << self
|
36
|
+
def inherited(subclass)
|
37
|
+
subclass.public_class_method :new
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(options = {})
|
42
|
+
super
|
43
|
+
@schema = self.schema_definition
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the schema composing this schema.
|
47
|
+
attr_reader :schema
|
48
|
+
|
49
|
+
# Overloaded methods (see {Schema#validate}).
|
50
|
+
def validate(object)
|
51
|
+
# Handle nil case.
|
52
|
+
if object.nil?
|
53
|
+
if allow_nil?
|
54
|
+
self.sanitized_object = nil
|
55
|
+
return true
|
56
|
+
else
|
57
|
+
raise ValidationError, "object is nil but this #{self.class.name} does not allow nil"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
@schema.validate(object)
|
61
|
+
self.sanitized_object = sanitize(@schema.sanitized_object)
|
62
|
+
true
|
63
|
+
rescue ValidationError => e
|
64
|
+
# Reset sanitized object.
|
65
|
+
self.sanitized_object = nil
|
66
|
+
raise e
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns the schema composing this composite schema.
|
70
|
+
# Overwrite this methods in sub-class.
|
71
|
+
def schema_definition
|
72
|
+
raise NoMethodError, "implement me in sub-class"
|
73
|
+
end
|
74
|
+
|
75
|
+
# Sanitize the given validated +object+. Overwrite this method
|
76
|
+
# in sub-class and returns the object that would be inserted
|
77
|
+
# in the sanitized object. The object passed as argument
|
78
|
+
# is an already sanitized sub-part of the overall object
|
79
|
+
# being validated. By default this method is a no-op. It
|
80
|
+
# returns the given +object+.
|
81
|
+
def sanitize(object)
|
82
|
+
object
|
83
|
+
end
|
84
|
+
|
85
|
+
end # class CompositeSchema
|
86
|
+
end # module Respect
|
@@ -0,0 +1,206 @@
|
|
1
|
+
module Respect
|
2
|
+
# Core DSL statements definition module.
|
3
|
+
#
|
4
|
+
# This module holds all the basic statements available in the DSL. It is included
|
5
|
+
# in all the DSL context using {Respect.extend_dsl_with}. Thus all basic DSL
|
6
|
+
# contexts provides its feature.
|
7
|
+
#
|
8
|
+
# Most of the statements are available as dynamic methods.
|
9
|
+
# For each "FooSchema" class defined in the {Respect} module (and following certain
|
10
|
+
# condition described at {Respect.schema_for}), there is a statement
|
11
|
+
# "foo" (see {Schema.statement_name}) expecting a name, some options and a block.
|
12
|
+
# This statement defines a new "FooSchema" with the given options and block. This
|
13
|
+
# schema is stored in the current context using the given name. The name may be used
|
14
|
+
# differently depending on the context. In a hash definition context it will be
|
15
|
+
# used as a property name whereas it will be simply ignored in the
|
16
|
+
# context of an array. Context classes including the {DefWithoutName} module ignore
|
17
|
+
# the name argument whereas others do not. The {FakeNameProxy} is in charge of
|
18
|
+
# transparently passing +nil+ for the name in contexts including the {DefWithoutName}
|
19
|
+
# module.
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
# HashSchema.define do |s|
|
23
|
+
# # method_missing calls:
|
24
|
+
# # update_context("i", IntegerSchema.define({greater_than: 42}))
|
25
|
+
# s.integer "i", greater_than: 42
|
26
|
+
# end
|
27
|
+
# ArraySchema.define do |s|
|
28
|
+
# # method_missing calls:
|
29
|
+
# # update_context(nil, IntegerSchema.define({greater_than: 42}))
|
30
|
+
# s.integer greater_than: 42
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# Classes including this module must implement the +update_context+ method
|
34
|
+
# which is supposed to update the schema under definition with the given
|
35
|
+
# name and schema created by the method.
|
36
|
+
#
|
37
|
+
# Do not include your helper module in this module since definition classes
|
38
|
+
# including it won't be affected due to the
|
39
|
+
# {dynamic module include problem}[http://eigenclass.org/hiki/The+double+inclusion+problem].
|
40
|
+
# To extend the DSL use {Respect.extend_dsl_with} instead.
|
41
|
+
#
|
42
|
+
# It is recommended that your macros implementation be based on core statements
|
43
|
+
# because +update_context+ API is *experimental*. If you do so anyway your
|
44
|
+
# macros may not work properly with the {#doc} and {#with_options} statements.
|
45
|
+
module CoreStatements
|
46
|
+
|
47
|
+
# @!method string(name, options = {})
|
48
|
+
# Define a {StringSchema} with the given +options+ and stores it in the
|
49
|
+
# current context using +name+ as index.
|
50
|
+
# @!method integer(name, options = {})
|
51
|
+
# Define a {IntegerSchema} with the given +options+ and stores it in the
|
52
|
+
# current context using +name+ as index.
|
53
|
+
# @!method float(name, options = {})
|
54
|
+
# Define a {FloatSchema} with the given +options+ and stores it in the
|
55
|
+
# current context using +name+ as index.
|
56
|
+
# @!method numeric(name, options = {})
|
57
|
+
# Define a {NumericSchema} with the given +options+ and stores it in the
|
58
|
+
# current context using +name+ as index.
|
59
|
+
# @!method any(name, options = {})
|
60
|
+
# Define a {AnySchema} with the given +options+ and stores it in the
|
61
|
+
# current context using +name+ as index.
|
62
|
+
# @!method null(name, options = {})
|
63
|
+
# Define a {NullSchema} with the given +options+ and stores it in the
|
64
|
+
# current context using +name+ as index.
|
65
|
+
# @!method boolean(name, options = {})
|
66
|
+
# Define a {BooleanSchema} with the given +options+ and stores it in the
|
67
|
+
# current context using +name+ as index.
|
68
|
+
# @!method uri(name, options = {})
|
69
|
+
# Define a {URISchema} with the given +options+ and stores it in the
|
70
|
+
# current context using +name+ as index.
|
71
|
+
# @!method hash(name, options = {}, &block)
|
72
|
+
# Define a {HashSchema} with the given +options+ and +block+ stores it
|
73
|
+
# in the current context using +name+ as index.
|
74
|
+
# @!method array(name, options = {})
|
75
|
+
# Define a {ArraySchema} with the given +options+ and +block+ stores it
|
76
|
+
# in the current context using +name+ as index.
|
77
|
+
# @!method datetime(name, options = {})
|
78
|
+
# Define a {DatetimeSchema} with the given +options+ and stores it in the
|
79
|
+
# current context using +name+ as index.
|
80
|
+
# @!method ip_addr(name, options = {})
|
81
|
+
# Define a {IPAddrSchema} with the given +options+ and stores it in the
|
82
|
+
# current context using +name+ as index.
|
83
|
+
# @!method ipv4_addr(name, options = {})
|
84
|
+
# Define a {Ipv4AddrSchema} with the given +options+ and stores it in the
|
85
|
+
# current context using +name+ as index.
|
86
|
+
# @!method ipv6_addr(name, options = {})
|
87
|
+
# Define a {Ipv6AddrSchema} with the given +options+ and stores it in the
|
88
|
+
# current context using +name+ as index.
|
89
|
+
# @!method regexp(name, options = {})
|
90
|
+
# Define a {RegexpSchema} with the given +options+ and stores it in the
|
91
|
+
# current context using +name+ as index.
|
92
|
+
# @!method utc_time(name, options = {})
|
93
|
+
# Define a {UTCTimeSchema} with the given +options+ and stores it in the
|
94
|
+
# current context using +name+ as index.
|
95
|
+
#
|
96
|
+
# Call +update_context+ using the first argument as index and passes the rest
|
97
|
+
# to the {Schema.define} class method of the schema class associated with the method name.
|
98
|
+
# As a consequence any call to missing method +foo+ will define a +FooSchema+
|
99
|
+
# schema using +FooSchema.define+.
|
100
|
+
#
|
101
|
+
# The options are merged with the default options which may include the +:doc+
|
102
|
+
# option if {#doc} has been called before. The current documentation is reset
|
103
|
+
# after this call.
|
104
|
+
#
|
105
|
+
# Note that if you define a new schema named after a method already defined in
|
106
|
+
# a context class such as {GlobalDef} or its sub-classes or in +Object+, the
|
107
|
+
# dynamic dispatch won't work. For instance even if you have defined the
|
108
|
+
# +ClassSchema+ class the following code won't work as expected:
|
109
|
+
#
|
110
|
+
# Schema.define do |s|
|
111
|
+
# s.class # Call Object#class !!!!!
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# To prevent this problem you must undefine the method in the DSL by doing
|
115
|
+
# something like that:
|
116
|
+
#
|
117
|
+
# module Respect
|
118
|
+
# class GlobalDef
|
119
|
+
# undef_method :class
|
120
|
+
# end
|
121
|
+
# end
|
122
|
+
#
|
123
|
+
# or you can overwrite the +class+ method in the context of your choice:
|
124
|
+
#
|
125
|
+
# module Respect
|
126
|
+
# class GlobalDef
|
127
|
+
# def class(name, options = {}, &block)
|
128
|
+
# update_context name, ClassSchema.define(options, &block)
|
129
|
+
# end
|
130
|
+
# end
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# Do not un-define or overwrite 'method' and 'methods' since {FakeNameProxy}
|
134
|
+
# use them.
|
135
|
+
def method_missing(method_name, *args, &block)
|
136
|
+
if respond_to_missing?(method_name, false)
|
137
|
+
size_range = 1..2
|
138
|
+
if size_range.include? args.size
|
139
|
+
name = args.shift
|
140
|
+
default_options = {}
|
141
|
+
default_options.merge!(@options) unless @options.nil?
|
142
|
+
default_options[:doc] = @doc unless @doc.nil?
|
143
|
+
if options = args.shift
|
144
|
+
options = default_options.merge(options)
|
145
|
+
else
|
146
|
+
options = default_options
|
147
|
+
end
|
148
|
+
@doc = nil
|
149
|
+
update_context name, Respect.schema_for(method_name).define(options, &block)
|
150
|
+
else
|
151
|
+
expected_size = args.size > size_range.end ? size_range.end : size_range.begin
|
152
|
+
raise ArgumentError, "wrong number of argument (#{args.size} for #{expected_size})"
|
153
|
+
end
|
154
|
+
else
|
155
|
+
super
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def respond_to_missing?(method_name, include_all)
|
160
|
+
Respect.schema_defined_for?(method_name)
|
161
|
+
end
|
162
|
+
|
163
|
+
# @!method email(name, options = {})
|
164
|
+
# Define a string formatted an email (see {FormatValidator#validate_email}).
|
165
|
+
# @!method phone_number(name, options = {})
|
166
|
+
# Define a string formatted as a phone number (see {FormatValidator#validate_phone_number}).
|
167
|
+
# @!method hostname(name, options = {})
|
168
|
+
# Define a string formatted as a machine host name (see {FormatValidator#validate_hostname}).
|
169
|
+
[
|
170
|
+
:email,
|
171
|
+
:phone_number,
|
172
|
+
:hostname,
|
173
|
+
].each do |meth_name|
|
174
|
+
define_method(meth_name) do |name, options = {}|
|
175
|
+
string name, options.dup.merge(format: meth_name)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Define the current documentation text. It will be used as documentation for the
|
180
|
+
# next defined schema. It can be used once, so it is reset once it has been affected
|
181
|
+
# to a schema.
|
182
|
+
#
|
183
|
+
# Example:
|
184
|
+
# s = HashSchema.define do |s|
|
185
|
+
# s.doc "A magic number"
|
186
|
+
# s.integer "magic"
|
187
|
+
# s.integer "nodoc"
|
188
|
+
# s.doc "A parameter..."
|
189
|
+
# s.string "param"
|
190
|
+
# end
|
191
|
+
# s["magic"].doc #=> "A magic number"
|
192
|
+
# s["nodoc"].doc #=> nil
|
193
|
+
# s["param"].doc #=> "A parameter..."
|
194
|
+
def doc(text)
|
195
|
+
@doc = text
|
196
|
+
end
|
197
|
+
|
198
|
+
# Use +options+ as the default for all schema created within +block+.
|
199
|
+
def with_options(options, &block)
|
200
|
+
@options = options
|
201
|
+
FakeNameProxy.new(self).eval(&block)
|
202
|
+
@options = nil
|
203
|
+
end
|
204
|
+
|
205
|
+
end # module CoreStatements
|
206
|
+
end # module Respect
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Respect
|
2
|
+
# Validate a date and time string following RFC 3399.
|
3
|
+
# +DateTime+, +Time+, and +Date+ object are accepted.
|
4
|
+
# If validation succeed the sanitized object is a +DateTime+ object.
|
5
|
+
class DatetimeSchema < StringSchema
|
6
|
+
|
7
|
+
def validate_type(object)
|
8
|
+
case object
|
9
|
+
when NilClass
|
10
|
+
if allow_nil?
|
11
|
+
nil
|
12
|
+
else
|
13
|
+
raise ValidationError, "object is nil but this #{self.class} does not allow nil"
|
14
|
+
end
|
15
|
+
when DateTime
|
16
|
+
object
|
17
|
+
when Time
|
18
|
+
object.to_datetime
|
19
|
+
when Date
|
20
|
+
object.to_date
|
21
|
+
else
|
22
|
+
FormatValidator.new(:datetime).validate(object)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end # class DatetimeSchema
|
27
|
+
end # module Respect
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Respect
|
2
|
+
class DivisibleByValidator < Validator
|
3
|
+
def initialize(divider)
|
4
|
+
@divider = divider
|
5
|
+
end
|
6
|
+
|
7
|
+
def validate(value)
|
8
|
+
unless (value % @divider).zero?
|
9
|
+
raise ValidationError, "#{value} is not divisible by #@divider"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def to_h_org3
|
16
|
+
{ 'divisibleBy' => @divider }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Respect
|
2
|
+
# Convenient module to ease usage of {DocParser}.
|
3
|
+
#
|
4
|
+
# Include it in classes returning their documentation via a +documentation+ method.
|
5
|
+
# This module provides a {#title} and {#description} methods for extracting
|
6
|
+
# them from the documentation.
|
7
|
+
module DocHelper
|
8
|
+
# Returns the title part of the documentation returned by +documentation+ method
|
9
|
+
# (+nil+ if it does not have any).
|
10
|
+
def title
|
11
|
+
if documentation
|
12
|
+
DocParser.new.parse(documentation.to_s).title
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the description part of the documentation returned by +documentation+ method
|
17
|
+
# (+nil+ if it does not have any).
|
18
|
+
def description
|
19
|
+
if documentation
|
20
|
+
DocParser.new.parse(documentation.to_s).description
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end # module Respect
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Respect
|
2
|
+
class DocParser
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@title = nil
|
6
|
+
@description = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse(string)
|
10
|
+
ss = StringScanner.new(string)
|
11
|
+
if ss.scan_until(/\n/)
|
12
|
+
if ss.eos?
|
13
|
+
@title = ss.pre_match
|
14
|
+
else
|
15
|
+
if ss.scan(/\n+/)
|
16
|
+
@title = ss.pre_match.chop
|
17
|
+
unless ss.rest.empty?
|
18
|
+
@description = ss.rest
|
19
|
+
end
|
20
|
+
else
|
21
|
+
if ss.eos?
|
22
|
+
@title = string.chop
|
23
|
+
else
|
24
|
+
@description = string
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
else
|
29
|
+
@title = string
|
30
|
+
end
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :title, :description
|
35
|
+
|
36
|
+
end # class DocParser
|
37
|
+
end # module Respect
|