ClsRuby 1.0.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.
Files changed (71) hide show
  1. data/LICENSE +26 -0
  2. data/README +55 -0
  3. data/THANKS +0 -0
  4. data/docs/base_formatting_methods +89 -0
  5. data/docs/base_parsing_methods +79 -0
  6. data/docs/constructor_params +131 -0
  7. data/docs/examples/log_single_line_format +3 -0
  8. data/docs/examples/service_description +3 -0
  9. data/docs/examples/sms-hist +3 -0
  10. data/docs/examples/tag_any +3 -0
  11. data/docs/fragments/custom_tag_field.rb +20 -0
  12. data/docs/fragments/custom_tag_include.rb +21 -0
  13. data/docs/fragments/field.cls +2 -0
  14. data/docs/fragments/include.cls +4 -0
  15. data/docs/fragments/inherit_tag_params.rb +21 -0
  16. data/docs/fragments/message.cls +6 -0
  17. data/docs/fragments/tag_field.rb +24 -0
  18. data/docs/fragments/tag_message.rb +74 -0
  19. data/docs/fragments/tags_order.rb +41 -0
  20. data/docs/principles +402 -0
  21. data/docs/std_tags_short_description +278 -0
  22. data/docs/syntax +227 -0
  23. data/docs/why_cls +178 -0
  24. data/examples/hex_stream.txt +1 -0
  25. data/examples/log_single_line_formatter.rb +79 -0
  26. data/examples/service_description.day_time.cfg +11 -0
  27. data/examples/service_description.rb +119 -0
  28. data/examples/sms-hist.rb +164 -0
  29. data/examples/space_concat.txt +3 -0
  30. data/examples/tag_any.rb +28 -0
  31. data/lib/cls-ruby/basic_scalars.rb +270 -0
  32. data/lib/cls-ruby/constraints/one_of.rb +36 -0
  33. data/lib/cls-ruby/default_formatter.rb +50 -0
  34. data/lib/cls-ruby/ex.rb +121 -0
  35. data/lib/cls-ruby/formatter.rb +31 -0
  36. data/lib/cls-ruby/lexers/char_classifier.rb +157 -0
  37. data/lib/cls-ruby/lexers/first_stage.rb +112 -0
  38. data/lib/cls-ruby/lexers/lexer.rb +74 -0
  39. data/lib/cls-ruby/oneline_formatter.rb +35 -0
  40. data/lib/cls-ruby/parser.rb +249 -0
  41. data/lib/cls-ruby/tag.rb +428 -0
  42. data/lib/cls-ruby/tag_any.rb +111 -0
  43. data/lib/cls-ruby/tag_no_value.rb +55 -0
  44. data/lib/cls-ruby/tag_scalar.rb +197 -0
  45. data/lib/cls-ruby/tag_scalar_helpers.rb +70 -0
  46. data/lib/cls-ruby/tag_scalar_vector.rb +148 -0
  47. data/lib/cls-ruby/tag_vector_of_different_tags.rb +172 -0
  48. data/lib/cls-ruby/tag_vector_of_tags.rb +116 -0
  49. data/lib/cls-ruby/vector_of_tags_impl.rb +129 -0
  50. data/lib/cls-ruby.rb +5 -0
  51. data/tests/tc_child_tag.rb +51 -0
  52. data/tests/tc_constraint_one_of.rb +47 -0
  53. data/tests/tc_format_helper.rb +27 -0
  54. data/tests/tc_formatters.rb +125 -0
  55. data/tests/tc_lexer.rb +124 -0
  56. data/tests/tc_lexer_char_classifier.rb +121 -0
  57. data/tests/tc_lexer_first_stage.rb +72 -0
  58. data/tests/tc_parser_simple.rb +93 -0
  59. data/tests/tc_scalar_parsers.rb +189 -0
  60. data/tests/tc_tag.rb +68 -0
  61. data/tests/tc_tag_no_value.rb +46 -0
  62. data/tests/tc_tag_scalar_int.rb +83 -0
  63. data/tests/tc_tag_scalar_nonspace_string.rb +46 -0
  64. data/tests/tc_tag_scalar_string.rb +47 -0
  65. data/tests/tc_tag_scalar_vector_int.rb +88 -0
  66. data/tests/tc_tag_scalar_vector_string.rb +48 -0
  67. data/tests/tc_tag_scalar_vector_string_empty.rb +40 -0
  68. data/tests/tc_tag_vector_of_different_tags.rb +109 -0
  69. data/tests/tc_tag_vector_of_tags.rb +103 -0
  70. data/tests/ts_cls_ruby.rb +7 -0
  71. metadata +140 -0
@@ -0,0 +1,197 @@
1
+ #
2
+ # ����� ���� ��� �������� ��������� �������� (����� �����, ����� � �.�.).
3
+ #
4
+
5
+ require 'cls-ruby/tag'
6
+ require 'cls-ruby/basic_scalars'
7
+ require 'cls-ruby/tag_scalar_helpers'
8
+
9
+ module ClsRuby
10
+
11
+ #
12
+ # ����� ���� ��� �������� ��������� �������� � ���������� �������������
13
+ # �� ���.
14
+ #
15
+ # ����������� ������������ ��������� �������������� ����� � ���� params:
16
+ # [:format] ������, ����������� ��������� ScalarFormat. ����
17
+ # ������ ����� ����������� ��� ��� �������������� �������
18
+ # �������� ������ � �������� ������� ������������ ����.
19
+ # [:constraint] ������, ����������� ��������� ScalarChecker. ���� ������,
20
+ # ���� �� �����, ����� ����������� ��� �������� �����������
21
+ # �� �������� ������ ��������.
22
+ # [:value] �������� ����. ���� ���� �������� ������, �� ��� ����� �����������
23
+ # ������������. ������������ �������� �� ����������.
24
+ #
25
+ # ������ �������������:
26
+ # ����� ���������� ������� ��������� ���������:
27
+ # {params
28
+ # {min <int:1..100>}
29
+ # {max <int:100..1000>}
30
+ # {password <str>}
31
+ # }
32
+ #
33
+ # ��� ����� ����� ��������� ���:
34
+ # class TagParams < ClsRuby::Tag
35
+ # def initialize( params )
36
+ # super( params )
37
+ #
38
+ # @min = ClsRuby::TagScalar.new(
39
+ # :name => 'min', :owner => self, :mandatory => true,
40
+ # :format => ClsRuby::SCALAR_INT,
41
+ # :constraint => 1..100 )
42
+ # @max = ClsRuby::TagScalar.new(
43
+ # :name => 'max', :owner => self, :mandatory => true,
44
+ # :format => ClsRuby::SCALAR_INT,
45
+ # :constraint => 100..1000 )
46
+ # @password = ClsRuby::TagScalar.new(
47
+ # :name => 'password', :owner => self, :mandatory => true,
48
+ # :format => ClsRuby::SCALAR_STRING )
49
+ # end
50
+ # end
51
+ #
52
+ # ��� ���������� �������� ������� ��������������� ������� TagScalar#value.
53
+ # ��, ���� ��� �� ��� ���������, �� TagScalar#value ��������� �������� nil.
54
+ # ��� ��������� ������ � ��������������� ������ ����� ���������������
55
+ # ������� fetch:
56
+ # || ������ ��� �������:
57
+ # || {age [{hours <uint>}] [{minutes <uint>]} [{seconds <uint>}]}
58
+ # || � ���������� � ������ value �������� � ��������.
59
+ # class TagAge < ClsRuby::TagNoValue
60
+ # child_tag :hours, ClsRuby::TagUintScalar, :constraint => 1..24
61
+ # child_tag :minutes, ClsRuby::TagUintScalar, :constraint => 1...60
62
+ # child_tag :seconds, ClsRuby::TagUintScalar, :constraint => 1...60
63
+ #
64
+ # def value
65
+ # @hours.fetch( 0 ) * 3600 +
66
+ # @minutes.fetch( 0 ) * 60 +
67
+ # @seconds.fetch( 0 )
68
+ # end
69
+ # end
70
+ #
71
+ class TagScalar < Tag
72
+ include TagScalarHelpers::ConstraintValidator
73
+ include TagScalarHelpers::ParamsExtractor
74
+
75
+ # ������ ������������ ����� ��������.
76
+ attr_reader :value
77
+
78
+ # ����������� ��������� ���������� FormatUndefinedEx, ���� � params
79
+ # ��� ����� :format, ��� ���� ����� ����� ������������� nil.
80
+ #
81
+ def initialize( params = {} )
82
+ super( params )
83
+
84
+ tag_scalar_extract_params( tag_params )
85
+ @value = nil
86
+
87
+ v = tag_params.fetch( :value, nil )
88
+ send( :value=, v ) if v
89
+ end
90
+
91
+ # ������ ��� ������������ � �������� ���������.
92
+ #
93
+ # <b>����������</b>. �������� �� ������������ �� �����������.
94
+ def value=( value )
95
+ @value = value
96
+ tag_make_defined
97
+ self
98
+ end
99
+
100
+ # ��������������� ����� ��� ���������� �������� �� ��������������� ����.
101
+ #
102
+ # ���� ��� �� ���������, �� ���������� �������� default.
103
+ def fetch( default )
104
+ tag_defined? ? value : default
105
+ end
106
+
107
+ # ���������� ��� � ��������� ���������.
108
+ def tag_reset
109
+ @value = nil
110
+ super
111
+ end
112
+
113
+ # ��������� ���������� TagAlreadyDefinedEx, ���� ��� ��� ���������.
114
+ def tag_on_start( name )
115
+ super( name )
116
+
117
+ raise TagAlreadyDefinedEx.new( "tag '#{tag_name}' already defined" ) if
118
+ tag_defined?
119
+ end
120
+
121
+ # ��������� ���������� ValueMissedEx, ���� � ������� �������� ����
122
+ # �������� �� ���� ����������.
123
+ def tag_on_finish
124
+ raise ValueMissedEx.new(
125
+ "value missed for tag '#{tag_name}'" ) unless @value
126
+
127
+ super
128
+ end
129
+
130
+ def tag_on_tok_nonspace( token )
131
+ tag_scalar_value_must_be_nil
132
+ @value = tag_scalar_check( token, @format.on_tok_nonspace( token ) )
133
+ end
134
+
135
+ def tag_on_tok_string( token )
136
+ tag_scalar_value_must_be_nil
137
+ @value = tag_scalar_check( token, @format.on_tok_string( token ) )
138
+ end
139
+
140
+ def tag_on_format( formatter )
141
+ formatter.value( @format.format( @value ) )
142
+ end
143
+
144
+ private
145
+ # ��������� ���������� ValueAlreadyDefinedEx, ���� @value ������� �� nil.
146
+ def tag_scalar_value_must_be_nil
147
+ raise ValueAlreadyDefinedEx.new(
148
+ "tag '#{tag_name}' already has value '#{@value}'" ) if @value
149
+ end
150
+ end
151
+
152
+ # ������� ����� ���� ��� �������� ����� �������� �� ������.
153
+ #
154
+ # ���:
155
+ # child_tag :min, ClsRuby::TagIntScalar
156
+ # �������� ������������:
157
+ # child_tag :min, ClsRuby::TagScalar, :format => ClsRuby::SCALAR_INT
158
+ #
159
+ class TagIntScalar < TagScalar
160
+ default_tag_params :format => SCALAR_INT
161
+ end
162
+
163
+ # ������� ����� ���� ��� �������� ����������� ����� ��������.
164
+ #
165
+ # ���:
166
+ # child_tag :min, ClsRuby::TagUintScalar
167
+ # �������� ������������:
168
+ # child_tag :min, ClsRuby::TagScalar, :format => ClsRuby::SCALAR_UINT
169
+ #
170
+ class TagUintScalar < TagScalar
171
+ default_tag_params :format => SCALAR_UINT
172
+ end
173
+
174
+ # ������� ����� ���� ��� �������� ��������� ��������.
175
+ #
176
+ # ���:
177
+ # child_tag :min, ClsRuby::TagStringScalar
178
+ # �������� ������������:
179
+ # child_tag :min, ClsRuby::TagScalar, :format => ClsRuby::SCALAR_STRING
180
+ #
181
+ class TagStringScalar < TagScalar
182
+ default_tag_params :format => SCALAR_STRING
183
+ end
184
+
185
+ # ������� ����� ���� ��� �������� ������������ ��������.
186
+ #
187
+ # ���:
188
+ # child_tag :min, ClsRuby::TagFloatScalar
189
+ # �������� ������������:
190
+ # child_tag :min, ClsRuby::TagScalar, :format => ClsRuby::SCALAR_FLOAT
191
+ #
192
+ class TagFloatScalar < TagScalar
193
+ default_tag_params :format => SCALAR_FLOAT
194
+ end
195
+
196
+ end # module ClsRuby
197
+
@@ -0,0 +1,70 @@
1
+ #
2
+ # ��������������� ������ � ������ ��� ���������� TagScalar � TagScalarVector.
3
+ #
4
+
5
+ module ClsRuby
6
+
7
+ module TagScalarHelpers
8
+
9
+ # ������, ������� ������������� ����� ��� ���������� �� Hash ����������
10
+ # ���� ������������� ��� TagScalar/TagScalarVector ���������� (�.�. :format,
11
+ # :constraint).
12
+ #
13
+ # ������ ���� �������� � ������, � ������� ���� ��������� instance variables:
14
+ # [@constraint] �������������� ������ �� ������, ����������� ScalarChecker.
15
+ # ���� � params ���� :constraint �� �����, �� ������
16
+ # ���������� �������� �������� nil.
17
+ # [@format] ������������ ������ �� ������, ����������� ��������� ScalarFormat.
18
+ #
19
+ module ParamsExtractor
20
+ # ��������� ���������� �� params �������� � ������� :format, :constraint.
21
+ #
22
+ # ��������� ���������� FormatUndefinedEx, ���� ������ ����������� ��
23
+ # ��������.
24
+ def tag_scalar_extract_params( params )
25
+ @format = params.fetch( :format, nil )
26
+ raise FormatUndefinedEx.new(
27
+ "format undefined for scalar '#{tag_name}'" ) unless @format
28
+
29
+ @constraint = params.fetch( :constraint, nil )
30
+ end
31
+ end
32
+
33
+ # ������, ������� ������������� ����� ��� �������� ������������
34
+ # ���������� ������������ ���������� ��������.
35
+ #
36
+ # ������ ���� �������� � ������, � ������� ���� ��������� instance variables:
37
+ # [@constraint] �������������� ������ �� ������, ����������� ScalarChecker.
38
+ # �� ����� �������������� ��� �������� ������������ ������������
39
+ # �������� (���� ������� �� nil).
40
+ #
41
+ module ConstraintValidator
42
+ # ��������� �������� ���������� �������� ���������� ������.
43
+ #
44
+ # ��������� ���������� ValueParsingErrorEx, ���� _parsed_value_ ����� nil.
45
+ #
46
+ # ���� ��������� @constraint, �� ���������� �������� � ��� �������.
47
+ # ��������� ���������� InvalidValueEx, ���� ��������� �����������
48
+ # ��������.
49
+ #
50
+ # � ������ ������ ���������� parsed_value.
51
+ #
52
+ def tag_scalar_check( token, parsed_value )
53
+ raise ValueParsingErrorEx.new(
54
+ "unable to parse token '#{token}' " +
55
+ "for tag '#{tag_name}'" ) unless parsed_value
56
+
57
+ if @constraint
58
+ raise InvalidValueEx.new(
59
+ "invalid value '#{parsed_value}' for tag '#{tag_name}'" ) \
60
+ unless @constraint === parsed_value
61
+ end
62
+
63
+ parsed_value
64
+ end
65
+ end
66
+
67
+ end # module TagScalarHelpers
68
+
69
+ end # module ClsRuby
70
+
@@ -0,0 +1,148 @@
1
+ #
2
+ # ����� ���� ��� �������� ������������������� ��������� ��������
3
+ # (����� �����, ����� � �.�.).
4
+ #
5
+
6
+ require 'cls-ruby/tag'
7
+ require 'cls-ruby/basic_scalars'
8
+ require 'cls-ruby/tag_scalar_helpers'
9
+
10
+ module ClsRuby
11
+
12
+ #
13
+ # ����� ���� ��� �������� ������������������� ��������� �������� � ����������
14
+ # ������������� �� ������ �� ���.
15
+ #
16
+ # ����������� ������������ ��������� �������������� ����� � ���� params:
17
+ # [:format] ������, ����������� ��������� ScalarFormat. ����
18
+ # ������ ����� ����������� ��� ��� �������������� �������
19
+ # �������� ������ � �������� ������� ������������ ����.
20
+ # [:constraint] ������, ����������� ��������� ScalarChecker. ���� ������,
21
+ # ���� �� �����, ����� ����������� ��� �������� �����������
22
+ # �� �������� ������ ��������.
23
+ # [:can_be_empty] true, ���� ����������� ������ ������������������ (��
24
+ # ��������� � ������������������ ������ ���� ���� �� ����
25
+ # �������).
26
+ # [:value] �������� ����. ���� ���� �������� ������, �� ��� ����� �����������
27
+ # ������������. ���������, ��� ��������� ����� Array.
28
+ # ������������ �������� �� ����������.
29
+ #
30
+ # ������ �������������:
31
+ # ����� ���������� ������� ��������� ���������:
32
+ # {params
33
+ # {init_bytes <uint:0..255>*}
34
+ # {shutdown_bytes <uint:0..255>*}
35
+ # {devices <str>+}
36
+ # }
37
+ # ��������:
38
+ # {params
39
+ # {init_bytes 0 0 254 254 0 0}
40
+ # {shutdown_bytes 0xff 0xff 0xff}
41
+ # {devices "com1" "com2" "com3" }
42
+ # }
43
+ #
44
+ # ��� ����� ����� ��������� ���:
45
+ # class TagParams < ClsRuby::Tag
46
+ # child_tag :init_bytes, ClsRuby::TagScalarVector,
47
+ # :format => ClsRuby::SCALAR_UINT,
48
+ # :constraint => 0..255,
49
+ # :can_be_empty => true
50
+ # child_tag :shutdown_bytes, ClsRuby::TagScalarVector,
51
+ # :format => ClsRuby::SCALAR_UINT,
52
+ # :constraint => 0..255,
53
+ # :can_be_empty => true
54
+ # child_tag :devices, ClsRuby::TagScalarVector,
55
+ # :format => ClsRuby::SCALAR_STRING,
56
+ # :mandatory => true
57
+ # end
58
+ #
59
+ # ��� ���������� �������� ������� ��������������� �������
60
+ # TagScalarVector#value. ��, ���� ��� �� ��� ���������, ��
61
+ # TagScalarVector#value ��������� �������� []. ��� ��������� ������ �
62
+ # ��������������� ������ ����� ��������������� ������� fetch:
63
+ # tag = ClsRuby::TagScalarVector.new( :name => 'init_bytes',
64
+ # :format => ClsRuby::SCALAR_UINT,
65
+ # :constraint => 0..255 )
66
+ # ...
67
+ # bytes = tag.fetch( [ 0, 0, 0 ] )
68
+ #
69
+ class TagScalarVector < Tag
70
+ include TagScalarHelpers::ConstraintValidator
71
+ include TagScalarHelpers::ParamsExtractor
72
+
73
+ # ������ ������������ ����� ��������.
74
+ # ������������ ������ ������, ���� �� ���� �������� �� ���� ���������.
75
+ attr_reader :value
76
+
77
+ # ����������� ��������� ���������� FormatUndefinedEx, ���� � params
78
+ # ��� ����� :format, ��� ���� ����� ����� ������������� nil.
79
+ #
80
+ def initialize( params = {} )
81
+ super( params )
82
+
83
+ tag_scalar_extract_params( tag_params )
84
+ @can_be_empty = tag_params.fetch( :can_be_empty, false )
85
+ @value = []
86
+
87
+ v = tag_params.fetch( :value, nil )
88
+ send( :value=, v ) if v
89
+ end
90
+
91
+ # ������ ��� ������������ � ��������� ����������.
92
+ #
93
+ # ������ �������� �������������.
94
+ #
95
+ # +����������+. �������� �� ������������ �� �����������.
96
+ #
97
+ # [values] ������ �������� ��� ����.
98
+ def value=( values )
99
+ @value = values
100
+ tag_make_defined
101
+ self
102
+ end
103
+
104
+ # ��������������� ����� ��� ���������� �������� �� ��������������� ����.
105
+ #
106
+ # ���� ��� �� ���������, �� ���������� �������� default.
107
+ def fetch( default )
108
+ tag_defined? ? value : default
109
+ end
110
+
111
+ def tag_reset
112
+ @value = []
113
+ super
114
+ end
115
+
116
+ # ��������� ���������� TagAlreadyDefinedEx, ���� ��� ��� ���������.
117
+ def tag_on_start( name )
118
+ super( name )
119
+
120
+ raise TagAlreadyDefinedEx.new( "tag '#{tag_name}' already defined" ) if
121
+ tag_defined?
122
+ end
123
+
124
+ # ��������� ���������� ValueMissedEx, ���� � ������� �������� ����
125
+ # �������� �� ���� ����������.
126
+ def tag_on_finish
127
+ raise ValueMissedEx.new(
128
+ "value missed for tag '#{tag_name}'" ) \
129
+ if !@can_be_empty && 0 == @value.size
130
+
131
+ super
132
+ end
133
+
134
+ def tag_on_tok_nonspace( token )
135
+ @value << tag_scalar_check( token, @format.on_tok_nonspace( token ) )
136
+ end
137
+
138
+ def tag_on_tok_string( token )
139
+ @value << tag_scalar_check( token, @format.on_tok_string( token ) )
140
+ end
141
+
142
+ def tag_on_format( formatter )
143
+ @value.each do |v| formatter.value( @format.format( v ) ) end
144
+ end
145
+ end
146
+
147
+ end # module ClsRuby
148
+
@@ -0,0 +1,172 @@
1
+ #
2
+ # �����, ������� ��������� ��������� �� ������� ������ ������������������
3
+ # ����������� �����.
4
+ #
5
+
6
+ require 'cls-ruby/ex'
7
+ require 'cls-ruby/vector_of_tags_impl'
8
+
9
+ module ClsRuby
10
+
11
+ # �����, ������� ��������� ��������� �� ������� ������ ������������������
12
+ # ����� � ������� ������� (�������� ������ �����).
13
+ #
14
+ # ��������, ���������� ����:
15
+ # {include <str>}
16
+ # {exclude <str>}
17
+ # ��� ������ � �������� ��� ������ �����:
18
+ # class Filter
19
+ # INCLUDE = 1
20
+ # EXCLUDE = 2
21
+ #
22
+ # attr_reader :type, :regex
23
+ #
24
+ # def initialize( type, regex )
25
+ # @type, @regex = type, regex
26
+ # end
27
+ # end
28
+ #
29
+ # class TagIncludeExclude < ClsRuby::TagStringScalar
30
+ # def initialize( params = {} )
31
+ # super( params )
32
+ #
33
+ # @type = Filter::INCLUDE
34
+ # end
35
+ #
36
+ # def tag_on_start( name )
37
+ # @type = name == 'include' ? INCLUDE : EXCLUDE
38
+ # end
39
+ #
40
+ # def value
41
+ # Filter.new( @type, super )
42
+ # end
43
+ # end
44
+ #
45
+ # ����� ��������� ��������� ������� �����, � ������� ���� {include}
46
+ # � {exclude} ����� ����������� ����� ������ ����. ��������:
47
+ # {filter
48
+ # {(include|exclude) <str>}*
49
+ # }
50
+ # ������ ����� ��������� ������� �� ���������� �� ������� ������.
51
+ #
52
+ # ��� ����, ����� �������� ������������ ��� ����� ��� ��������� �����
53
+ # TagExcludeInclude ����� ��������������� ����� TagVectorOfDifferentTags:
54
+ # class TagFilter < ClsRuby::TagNoValue
55
+ # child_tag :filters, ClsRuby::TagVectorOfDifferentTags,
56
+ # :nested_tags => [
57
+ # { :name => 'include', :type => TagIncludeExclude },
58
+ # { :name => 'exclude', :type => TagIncludeExclude } ]
59
+ # ...
60
+ # end
61
+ #
62
+ # ������� ������ TagVectorOfDifferentTags ������� � ���, ���
63
+ # TagVectorOfDifferentTags ������ ���� Proxy. �� ����������� �����������
64
+ # ������� ������� ������ Tag �:
65
+ # - � ������ tag_compare_name ��������� ���������� ������ �� ����, ��������
66
+ # � ��������� :nested_tags;
67
+ # - � ������ tag_on_start ������� ��������� ��������� ������� ���� ����,
68
+ # ������� ��� ����� ���������� :type (� ����������� ���� ������� ��� �����
69
+ # TagIncludeExclude), ����� ���� ������ ������������ ��� ��������� ���;
70
+ # - ������������ ������ tag_on_tag, tag_on_tok_*, tag_tags, tag_on_finish
71
+ # ���������� ����;
72
+ # - � ������ tag_on_finish, ���� ��������� ��� ��������� � tag_on_finish
73
+ # ��� ������, ��������� ��������� ��� � ������ ����������� ���������
74
+ # �����.
75
+ #
76
+ # ����� ������� �������� ������ ������ ����������� ��������� ����� �����
77
+ # �������� � ������� ������ nested_tags:
78
+ # class TagFilter < ClsRuby::TagNoValue
79
+ # ...
80
+ # Description = Struct.new( :name, :fields )
81
+ # ...
82
+ # def filters
83
+ # @filters.nested_tags.inject( [] ) do |r, f| r << f.value; r end
84
+ # end
85
+ # end
86
+ # ��� �� ����� ��������������� ������� TagVectorOfTags#collect_values:
87
+ # def filters
88
+ # @filters.collect_values do |t| t.value end
89
+ # end
90
+ # ��� ������� TagVectorOfTags#collect_values_by:
91
+ # def filters
92
+ # filters = @filters.collect_values_by( :value )
93
+ # end
94
+ #
95
+ class TagVectorOfDifferentTags < Tag
96
+ include VectorOfTagsImplementation
97
+
98
+ # ������ � ������ ����������� ��������� �����.
99
+ attr_reader :nested_tags
100
+
101
+ # �����������.
102
+ #
103
+ # ������������� � ����������� ������ ���������� �����:
104
+ # [:nested_tags] ���� ��������� �����. � ���� Array, ���������� ��������
105
+ # �������� Hash � ������� :name � :type.
106
+ # ������ ���� ����������� ������ �������������� � args.
107
+ #
108
+ def initialize( args = {} )
109
+ super( args )
110
+
111
+ handle_nested_tags_descriptions
112
+
113
+ @nested_tags = []
114
+ @current = nil
115
+ end
116
+
117
+ # ��������� ���������� � ����� �� ���� ��������� �����.
118
+ def tag_compare_name( name )
119
+ @names_to_types[ name ] ? true : false
120
+ end
121
+
122
+ # ������� �������� ��� ���� ����, �������� ������������� ��������� ���.
123
+ def tag_on_start( name )
124
+ type = @names_to_types[ name ]
125
+ next_tag = type.new( :name => name )
126
+ next_tag.tag_on_start( name )
127
+ @current = next_tag
128
+ end
129
+
130
+ private
131
+ # ����������, ������� �����������, ���� �������������� �����-����
132
+ # �������� � ��������� ��������� �����.
133
+ #
134
+ class InvalidNestedTagsEx < Ex
135
+ end
136
+
137
+ # ��������� �������� ��������� �����.
138
+ #
139
+ # ��������� ����������, ���� ���� :nested_tags � ������ ����������
140
+ # �� ������ ��� �������� ������������ ��������.
141
+ #
142
+ def handle_nested_tags_descriptions
143
+ nested_tags = tag_params.fetch( :nested_tags, nil )
144
+ raise InvalidNestedTagsEx.new(
145
+ "key :nested_tags not found!" ) unless nested_tags
146
+
147
+ # ������ ����� ���������, ��� ������ ��������� nested_tags ��������
148
+ # Hash, � ������� ���� ����� :name � :type.
149
+ #
150
+ # ������� �������� ��� ����� ��������� ����� � ��������� ���������.
151
+ names_to_types = {}
152
+ nested_tags.each do |hash|
153
+ raise InvalidNestedTagsEx.new(
154
+ "key :name missed or has no value in #{hash}" ) \
155
+ unless hash.fetch( :name, nil )
156
+
157
+ raise InvalidNestedTagsEx.new(
158
+ "key :type missed of has no value in #{hash}" ) \
159
+ unless hash.fetch( :type, nil )
160
+
161
+ names_to_types[ hash[ :name ] ] = hash[ :type ]
162
+ end
163
+
164
+ # ���� ������ �� ��������, ������ ����������� �������� �����
165
+ # ��������� � �������� ���������.
166
+ @names_to_types = names_to_types
167
+ end
168
+ end
169
+
170
+ end # module ClsRuby
171
+
172
+