respect 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|