fixed-layout-mapper 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 49c68dd5dff9b06434fecdd63f60ba65deaa2fd4
4
+ data.tar.gz: bccad641631a17230d769ba03eefa6cc282fdba2
5
+ SHA512:
6
+ metadata.gz: 3eafe44a058e6e34730b4dfd59ea7f2c0f5a0fa2e67d3b9d79d534e6fecd0aa61759fa158a03566569074124bf88f1da04adf0089d6c739aff09ba96da1e753a
7
+ data.tar.gz: dc2275ebc8ef3dfb0940d89c029307c036468a9dc01a89d7f580908bc196c2ecfdb035cacf8946bb2c8effa5e9ef40824b4c1d0940a974debdb92eaada01fe74
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
- *.gem
2
- .bundle
3
- Gemfile.lock
4
- pkg/*
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ vendor
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in fixed-layout-mapper.gemspec
4
- gemspec
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in fixed-layout-mapper.gemspec
4
+ gemspec
data/README.md CHANGED
@@ -1,138 +1,143 @@
1
- Sample
2
- ====
3
- require 'pp'
4
- require 'fixed-layout-mapper'
5
-
6
- mapper = FixedLayoutMapper::Mapper.define_layout do
7
- layout :sub_layout do
8
- col :sub_f1, 1
9
- col :sub_f2, 2
10
- end
11
-
12
- col :f1, 5
13
- col :f2, :sub_layout
14
- col :f3, [2] * 3
15
- col :f4, [1, 2, :sub_layout]
16
- col :f5, 3 do |v|
17
- v * 2
18
- end
19
- end
20
-
21
- data = %w(12345 a bb 00 11 22 a bb c dd 123)
22
- pp mapper.map(data.join)
23
- #=> #<struct
24
- # f1="12345",
25
- # f2=#<struct sub_f1="a", sub_f2="bb">,
26
- # f3=["00", "11", "22"],
27
- # f4=["a", "bb", #<struct sub_f1="c", sub_f2="dd">],
28
- # f5="123123">
29
-
30
- Detail
31
- ====
32
-
33
- Define columns
34
- ----
35
- Layout definition is started in "define_layout" method.
36
-
37
- mapper = FixedLayoutMapper::Mapper.define_layout do
38
- definitions...
39
- end
40
-
41
- Columns are defined by "col" method.
42
-
43
- col symbol, record_def
44
-
45
- record_def is
46
- * numeric: define column which cut the string with its width.
47
-
48
- #ex)
49
- mapper = FixedLayoutMapper::Mapper.define_layout do
50
- col :f, 3
51
- end
52
-
53
- p mapper.map("0123")
54
- #=> #<struct f="012">
55
-
56
- * array: define array column with use its each contents.
57
-
58
- #ex)
59
- mapper = FixedLayoutMapper::Mapper.define_layout do
60
- col :f1, [1, 2]
61
- col :f2, [1] * 3
62
- end
63
-
64
- p mapper.map("001123")
65
- #=> #<struct f1=["0", "01"], f2=["1", "2", "3"]>
66
-
67
- * sub_layout_key: define column which use its layout.
68
- see Sub layout.
69
-
70
- Sub layout
71
- ----
72
-
73
- if you want to name to layout, you can use "layout" method.
74
- A field defined by "col" method called outside the block of "layout" method is implicitly defined in the default layout.
75
-
76
- layout layout_name(symbol) do
77
- column definitions...
78
- end
79
-
80
- When sub layout is defined, you can use its layout in other layout definition.
81
-
82
- mapper = FixedLayoutMapper::Mapper.define_layout do
83
- layout :sub1 do
84
- col :sub_f1, 1
85
- col :sub_f2, 2
86
- end
87
- col :f1, 1
88
- col :f2, :sub1
89
- end
90
-
91
- p mapper.map("0123")
92
- #=> #<struct f1="0", f2=#<struct sub_f1="1", sub_f2="23">>
93
-
94
- You can also use sub layout in array def.
95
-
96
- mapper = FixedLayoutMapper::Mapper.define_layout do
97
- layout :sub1 do
98
- col :sub_f1, 1
99
- col :sub_f2, 2
100
- end
101
- col :f1, [1, :sub1]
102
- end
103
-
104
- p mapper.map("0123")
105
- #=> #<struct f1=["0", #<struct sub_f1="1", sub_f2="23"]>
106
-
107
- you can use sub layout in map method.
108
- if symbol is passed to the second argument in map method, use its layout.
109
-
110
- mapper = FixedLayoutMapper::Mapper.define_layout do
111
- layout :sub1 do
112
- col :sub_f1, 1
113
- col :sub_f2, 2
114
- end
115
- col :f1, [1, :sub1]
116
- end
117
-
118
- p mapper.map("0123")
119
- #=> #<struct f1=["0", #<struct sub_f1="1", sub_f2="23"]>
120
- p mapper.map("0123", :sub1)
121
- #=> #<struct sub_f1="0", sub_f2="12">
122
-
123
-
124
- Conversion
125
- ----
126
-
127
- If "col" method is given block, the raw value is passed to the block and
128
- the field value becomes the return value of the block.
129
-
130
- mapper = FixedLayoutMapper::Mapper.define_layout do
131
- col :f1, 3 do |v|
132
- v + "_" + v
133
- end
134
- col :f2, 3, &:upcase
135
- end
136
-
137
- p mapper.map("012abc")
138
- #=> #<struct f1="012_012", f2="ABC">
1
+ Description
2
+ ====
3
+
4
+ fixed_layout_mapper is a library that maps to RubyObject from fixed-length data.
5
+
6
+ Sample
7
+ ====
8
+ require 'pp'
9
+ require 'fixed-layout-mapper'
10
+
11
+ mapper = FixedLayoutMapper::Mapper.define_layout do
12
+ layout :sub_layout do
13
+ col :sub_f1, 1
14
+ col :sub_f2, 2
15
+ end
16
+
17
+ col :f1, 5
18
+ col :f2, :sub_layout
19
+ col :f3, [2] * 3
20
+ col :f4, [1, 2, :sub_layout]
21
+ col :f5, 3 do |v|
22
+ v * 2
23
+ end
24
+ end
25
+
26
+ data = %w(12345 a bb 00 11 22 a bb c dd 123)
27
+ pp mapper.map(data.join)
28
+ #=> #<struct
29
+ # f1="12345",
30
+ # f2=#<struct sub_f1="a", sub_f2="bb">,
31
+ # f3=["00", "11", "22"],
32
+ # f4=["a", "bb", #<struct sub_f1="c", sub_f2="dd">],
33
+ # f5="123123">
34
+
35
+ Detail
36
+ ====
37
+
38
+ Define columns
39
+ ----
40
+ Layout definition is started in "define_layout" method.
41
+
42
+ mapper = FixedLayoutMapper::Mapper.define_layout do
43
+ definitions...
44
+ end
45
+
46
+ Columns are defined by "col" method.
47
+
48
+ col symbol, record_def
49
+
50
+ record_def is
51
+ * numeric: define column which cut the string with its width.
52
+
53
+ #ex)
54
+ mapper = FixedLayoutMapper::Mapper.define_layout do
55
+ col :f, 3
56
+ end
57
+
58
+ p mapper.map("0123")
59
+ #=> #<struct f="012">
60
+
61
+ * array: define array column with use its each contents.
62
+
63
+ #ex)
64
+ mapper = FixedLayoutMapper::Mapper.define_layout do
65
+ col :f1, [1, 2]
66
+ col :f2, [1] * 3
67
+ end
68
+
69
+ p mapper.map("001123")
70
+ #=> #<struct f1=["0", "01"], f2=["1", "2", "3"]>
71
+
72
+ * sub_layout_key: define column which use its layout.
73
+ see Sub layout.
74
+
75
+ Sub layout
76
+ ----
77
+
78
+ if you want to name to layout, you can use "layout" method.
79
+ A field defined by "col" method called outside the block of "layout" method is implicitly defined in the default layout.
80
+
81
+ layout layout_name(symbol) do
82
+ column definitions...
83
+ end
84
+
85
+ When sub layout is defined, you can use its layout in other layout definition.
86
+
87
+ mapper = FixedLayoutMapper::Mapper.define_layout do
88
+ layout :sub1 do
89
+ col :sub_f1, 1
90
+ col :sub_f2, 2
91
+ end
92
+ col :f1, 1
93
+ col :f2, :sub1
94
+ end
95
+
96
+ p mapper.map("0123")
97
+ #=> #<struct f1="0", f2=#<struct sub_f1="1", sub_f2="23">>
98
+
99
+ You can also use sub layout in array def.
100
+
101
+ mapper = FixedLayoutMapper::Mapper.define_layout do
102
+ layout :sub1 do
103
+ col :sub_f1, 1
104
+ col :sub_f2, 2
105
+ end
106
+ col :f1, [1, :sub1]
107
+ end
108
+
109
+ p mapper.map("0123")
110
+ #=> #<struct f1=["0", #<struct sub_f1="1", sub_f2="23"]>
111
+
112
+ you can use sub layout in map method.
113
+ if symbol is passed to the second argument in map method, use its layout.
114
+
115
+ mapper = FixedLayoutMapper::Mapper.define_layout do
116
+ layout :sub1 do
117
+ col :sub_f1, 1
118
+ col :sub_f2, 2
119
+ end
120
+ col :f1, [1, :sub1]
121
+ end
122
+
123
+ p mapper.map("0123")
124
+ #=> #<struct f1=["0", #<struct sub_f1="1", sub_f2="23"]>
125
+ p mapper.map("0123", :sub1)
126
+ #=> #<struct sub_f1="0", sub_f2="12">
127
+
128
+
129
+ Conversion
130
+ ----
131
+
132
+ If "col" method is given block, the raw value is passed to the block and
133
+ the field value becomes the return value of the block.
134
+
135
+ mapper = FixedLayoutMapper::Mapper.define_layout do
136
+ col :f1, 3 do |v|
137
+ v + "_" + v
138
+ end
139
+ col :f2, 3, &:upcase
140
+ end
141
+
142
+ p mapper.map("012abc")
143
+ #=> #<struct f1="012_012", f2="ABC">
data/Rakefile CHANGED
@@ -1,8 +1,8 @@
1
- require "bundler/gem_tasks"
2
-
3
- require 'rspec/core'
4
- require 'rspec/core/rake_task'
5
-
6
- RSpec::Core::RakeTask.new(:spec) do |spec|
7
- spec.pattern = FileList['test/**/*_spec.rb']
8
- end
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = FileList['test/**/*_spec.rb']
8
+ end
@@ -1,24 +1,25 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "fixed-layout-mapper/version"
4
-
5
- Gem::Specification.new do |s|
6
- s.name = "fixed-layout-mapper"
7
- s.version = FixedLayoutMapper::VERSION
8
- s.authors = ["pocari"]
9
- s.email = ["caffelattenonsugar@gmail.com"]
10
- s.homepage = "https://github.com/pocari"
11
- s.summary = %q{Mapping fixed layout record to ruby's Struct or Array object.}
12
- s.description = ""
13
-
14
- s.rubyforge_project = "fixed-layout-mapper"
15
-
16
- s.files = `git ls-files`.split("\n")
17
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
- s.require_paths = ["lib"]
20
-
21
- # specify any dependencies here; for example:
22
- # s.add_development_dependency "rspec"
23
- # s.add_runtime_dependency "rest-client"
24
- end
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "fixed-layout-mapper/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "fixed-layout-mapper"
7
+ s.version = FixedLayoutMapper::VERSION
8
+ s.authors = ["pocari"]
9
+ s.email = ["caffelattenonsugar@gmail.com"]
10
+ s.homepage = "https://github.com/pocari"
11
+ s.summary = %q{Mapping fixed layout record to ruby's Struct or Array object.}
12
+ s.description = ""
13
+
14
+ s.rubyforge_project = "fixed-layout-mapper"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec"
23
+ s.add_development_dependency "rake"
24
+ # s.add_runtime_dependency "rest-client"
25
+ end
@@ -1,250 +1,250 @@
1
- require "fixed-layout-mapper/version"
2
-
3
- module FixedLayoutMapper
4
- LENGTH_CONDITION_STRICT = :strict
5
- LENGTH_CONDITION_ALLOW_LONG = :allow_long
6
-
7
- class Mapper
8
- class ColumnMapper
9
- attr_accessor :layout_mapper, :converter
10
- def initialize(layout_mapper, converter = nil)
11
- @layout_mapper = layout_mapper
12
- @converter = converter
13
- end
14
-
15
- def convert(value)
16
- if @converter
17
- @converter.(value)
18
- else
19
- value
20
- end
21
- end
22
- end
23
-
24
- class SimpleMapper < ColumnMapper
25
- def initialize(layout_mapper, len, converter = nil)
26
- super layout_mapper, converter
27
- @len = len
28
- end
29
-
30
- def length
31
- @len
32
- end
33
-
34
- def map(bytes)
35
- value = bytes.take(@len).pack("C*").force_encoding(layout_mapper.encoding)
36
- #value = converter.(value) if converter
37
- [convert(value), bytes.drop(@len)]
38
- end
39
- end
40
-
41
- class SubLayoutMapper < ColumnMapper
42
- def initialize(layout_mapper, layout_id, converter = nil)
43
- super layout_mapper, converter
44
- @layout_id = layout_id
45
- end
46
-
47
- def map(bytes)
48
- value, rest = layout_mapper.get_layout(@layout_id).map(bytes, layout_mapper.get_result_class(@layout_id))
49
- [convert(value), rest]
50
- end
51
-
52
- def length
53
- layout_mapper.get_layout(@layout_id).length
54
- end
55
- end
56
-
57
- class ArrayMapper < ColumnMapper
58
- def initialize(layout_mapper, mappers, converter = nil)
59
- super layout_mapper, converter
60
- @mappers = mappers
61
- end
62
-
63
- def map(bytes)
64
- ret = []
65
- rest = @mappers.inject(bytes) do |acc, mapper|
66
- value, acc = mapper.map(acc)
67
- ret << value
68
- acc
69
- end
70
- [convert(ret), rest]
71
- end
72
-
73
- def length
74
- @mappers.inject(0) do |acc, m|
75
- acc += m.length
76
- end
77
- end
78
- end
79
-
80
- class Layout
81
- def initialize(length_condition)
82
- @length_condition = length_condition
83
- @layout = []
84
- @length = nil
85
- end
86
-
87
- def syms
88
- @layout.map{|e| e[0]}
89
- end
90
-
91
- def add(sym, mapper)
92
- @layout << [sym, mapper]
93
- end
94
-
95
- def length
96
- @length ||= calc_length
97
- end
98
-
99
- def calc_length
100
- @layout.inject(0) do |acc, (sym, mapper)|
101
- acc += mapper.length
102
- end
103
- end
104
-
105
- def map(bytes, result_class)
106
- case @length_condition
107
- when LENGTH_CONDITION_STRICT
108
- raise "byte length is invalid" unless bytes.length == length
109
- when LENGTH_CONDITION_ALLOW_LONG
110
- raise "byte length is too short" if bytes.length < length
111
- else
112
- raise "unknown LENGTH_CONDIGION #{@length_condition}"
113
- end
114
-
115
- obj = result_class.new
116
- rest = @layout.inject(bytes) do |acc, (sym, mapper)|
117
- value, acc = mapper.map(acc)
118
- obj[sym] = value
119
- acc
120
- end
121
- [obj, rest]
122
- end
123
- end
124
-
125
- class << self
126
- def define_layout(opts = {:encoding => Encoding.default_external}, &block)
127
- obj = Mapper.new(opts)
128
- obj.define_layout(&block)
129
- obj
130
- end
131
- end
132
-
133
- attr_reader :encoding, :length_condition
134
- def initialize(opts = {:encoding => Encoding.default_external})
135
- @current_layout = :default_layout
136
- @current_length_condition = LENGTH_CONDITION_ALLOW_LONG
137
- @layouts = {}
138
- @result_class = {}
139
- @length_conditions = {}
140
- @encoding = opts[:encoding]
141
- end
142
-
143
- def define_layout(&block)
144
- instance_eval(&block)
145
- build_layout
146
- end
147
-
148
- def map(data, layout_id = @current_layout)
149
- obj, = @layouts[layout_id].map(data.unpack("C*"), @result_class[layout_id])
150
- obj
151
- end
152
-
153
- def get_layout(layout_id = @current_layout)
154
- @layouts[layout_id]
155
- end
156
-
157
- def get_result_class(layout_id)
158
- @result_class[layout_id]
159
- end
160
-
161
- def length_condigion(value)
162
- @length_condition = value
163
- end
164
-
165
- private
166
- def build_layout
167
- @layouts.each do |layout_id, layout|
168
- @result_class[layout_id] = Struct.new(*layout.syms) do
169
- def to_hash
170
- each_pair.inject({}) do |acc, (key, value)|
171
- case
172
- when value.respond_to?(:to_hash)
173
- acc[key] = value.to_hash
174
- when Array === value
175
- acc[key] = value.map{|e|
176
- if e.respond_to?(:to_hash)
177
- e.to_hash
178
- else
179
- e
180
- end
181
- }
182
- else
183
- acc[key] = value
184
- end
185
- acc
186
- end
187
- end
188
- end
189
- end
190
- end
191
-
192
- def layout(layout_id, length_condition = LENGTH_CONDITION_ALLOW_LONG, &block)
193
- change_current_layout(layout_id, length_condition) do
194
- instance_eval(&block)
195
- end
196
- end
197
-
198
- def col(sym, map_info, layout_id = @current_layout, &block)
199
- case map_info
200
- when Numeric
201
- col_len(sym, map_info, layout_id, block)
202
- when Symbol
203
- col_from_sub_layout(sym, map_info, layout_id, block)
204
- when Array
205
- col_array(sym, map_info, layout_id, block)
206
- end
207
- end
208
-
209
- def create_layout
210
- Layout.new(@current_length_condition)
211
- end
212
-
213
- def col_len(sym, len, layout_id = @current_layout, block)
214
- @layouts[layout_id] ||= create_layout
215
- @layouts[layout_id].add(sym, SimpleMapper.new(self, len, block))
216
- end
217
-
218
- def col_from_sub_layout(sym, sub_layout, layout_id = @current_layout, block)
219
- @layouts[layout_id] ||= create_layout
220
- @layouts[layout_id].add(sym, SubLayoutMapper.new(self, sub_layout, block))
221
- end
222
-
223
- def col_array(sym, array_param, layout_id = @current_layout, block)
224
- @layouts[layout_id] ||= create_layout
225
- @layouts[layout_id].add(sym, ArrayMapper.new(self,
226
- array_param.map{|arg|
227
- case arg
228
- when Numeric
229
- mapper_class = SimpleMapper
230
- when Symbol
231
- mapper_class = SubLayoutMapper
232
- else
233
- raise "elements must be Numeric or Symbol"
234
- end
235
- mapper_class.new(self, arg)
236
- }, block)
237
- )
238
- end
239
-
240
- def change_current_layout(layout, length_condition)
241
- tmp = @current_layout
242
- tmp = @current_length_condition
243
- @current_layout = layout
244
- @current_length_condition = length_condition
245
- yield
246
- @current_layout = tmp
247
- @current_length_condition = tmp
248
- end
249
- end
250
- end
1
+ require "fixed-layout-mapper/version"
2
+
3
+ module FixedLayoutMapper
4
+ LENGTH_CONDITION_STRICT = :strict
5
+ LENGTH_CONDITION_ALLOW_LONG = :allow_long
6
+
7
+ class Mapper
8
+ class ColumnMapper
9
+ attr_accessor :layout_mapper, :converter
10
+ def initialize(layout_mapper, converter = nil)
11
+ @layout_mapper = layout_mapper
12
+ @converter = converter
13
+ end
14
+
15
+ def convert(value)
16
+ if @converter
17
+ @converter.(value)
18
+ else
19
+ value
20
+ end
21
+ end
22
+ end
23
+
24
+ class SimpleMapper < ColumnMapper
25
+ def initialize(layout_mapper, len, converter = nil)
26
+ super layout_mapper, converter
27
+ @len = len
28
+ end
29
+
30
+ def length
31
+ @len
32
+ end
33
+
34
+ def map(bytes)
35
+ value = bytes.take(@len).pack("C*").force_encoding(layout_mapper.encoding)
36
+ #value = converter.(value) if converter
37
+ [convert(value), bytes.drop(@len)]
38
+ end
39
+ end
40
+
41
+ class SubLayoutMapper < ColumnMapper
42
+ def initialize(layout_mapper, layout_id, converter = nil)
43
+ super layout_mapper, converter
44
+ @layout_id = layout_id
45
+ end
46
+
47
+ def map(bytes)
48
+ value, rest = layout_mapper.get_layout(@layout_id).map(bytes, layout_mapper.get_result_class(@layout_id))
49
+ [convert(value), rest]
50
+ end
51
+
52
+ def length
53
+ layout_mapper.get_layout(@layout_id).length
54
+ end
55
+ end
56
+
57
+ class ArrayMapper < ColumnMapper
58
+ def initialize(layout_mapper, mappers, converter = nil)
59
+ super layout_mapper, converter
60
+ @mappers = mappers
61
+ end
62
+
63
+ def map(bytes)
64
+ ret = []
65
+ rest = @mappers.inject(bytes) do |acc, mapper|
66
+ value, acc = mapper.map(acc)
67
+ ret << value
68
+ acc
69
+ end
70
+ [convert(ret), rest]
71
+ end
72
+
73
+ def length
74
+ @mappers.inject(0) do |acc, m|
75
+ acc += m.length
76
+ end
77
+ end
78
+ end
79
+
80
+ class Layout
81
+ def initialize(length_condition)
82
+ @length_condition = length_condition
83
+ @layout = []
84
+ @length = nil
85
+ end
86
+
87
+ def syms
88
+ @layout.map{|e| e[0]}
89
+ end
90
+
91
+ def add(sym, mapper)
92
+ @layout << [sym, mapper]
93
+ end
94
+
95
+ def length
96
+ @length ||= calc_length
97
+ end
98
+
99
+ def calc_length
100
+ @layout.inject(0) do |acc, (sym, mapper)|
101
+ acc += mapper.length
102
+ end
103
+ end
104
+
105
+ def map(bytes, result_class)
106
+ case @length_condition
107
+ when LENGTH_CONDITION_STRICT
108
+ raise "byte length is invalid" unless bytes.length == length
109
+ when LENGTH_CONDITION_ALLOW_LONG
110
+ raise "byte length is too short" if bytes.length < length
111
+ else
112
+ raise "unknown LENGTH_CONDIGION #{@length_condition}"
113
+ end
114
+
115
+ obj = result_class.new
116
+ rest = @layout.inject(bytes) do |acc, (sym, mapper)|
117
+ value, acc = mapper.map(acc)
118
+ obj[sym] = value
119
+ acc
120
+ end
121
+ [obj, rest]
122
+ end
123
+ end
124
+
125
+ class << self
126
+ def define_layout(opts = {:encoding => Encoding.default_external}, &block)
127
+ obj = Mapper.new(opts)
128
+ obj.define_layout(&block)
129
+ obj
130
+ end
131
+ end
132
+
133
+ attr_reader :encoding, :length_condition
134
+ def initialize(opts = {:encoding => Encoding.default_external})
135
+ @current_layout = :default_layout
136
+ @current_length_condition = LENGTH_CONDITION_ALLOW_LONG
137
+ @layouts = {}
138
+ @result_class = {}
139
+ @length_conditions = {}
140
+ @encoding = opts[:encoding]
141
+ end
142
+
143
+ def define_layout(&block)
144
+ instance_eval(&block)
145
+ build_layout
146
+ end
147
+
148
+ def map(data, layout_id = @current_layout)
149
+ obj, = @layouts[layout_id].map(data.unpack("C*"), @result_class[layout_id])
150
+ obj
151
+ end
152
+
153
+ def get_layout(layout_id = @current_layout)
154
+ @layouts[layout_id]
155
+ end
156
+
157
+ def get_result_class(layout_id)
158
+ @result_class[layout_id]
159
+ end
160
+
161
+ def length_condigion(value)
162
+ @length_condition = value
163
+ end
164
+
165
+ private
166
+ def build_layout
167
+ @layouts.each do |layout_id, layout|
168
+ @result_class[layout_id] = Struct.new(*layout.syms) do
169
+ def to_hash
170
+ each_pair.inject({}) do |acc, (key, value)|
171
+ case
172
+ when value.respond_to?(:to_hash)
173
+ acc[key] = value.to_hash
174
+ when Array === value
175
+ acc[key] = value.map{|e|
176
+ if e.respond_to?(:to_hash)
177
+ e.to_hash
178
+ else
179
+ e
180
+ end
181
+ }
182
+ else
183
+ acc[key] = value
184
+ end
185
+ acc
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ def layout(layout_id, length_condition = LENGTH_CONDITION_ALLOW_LONG, &block)
193
+ change_current_layout(layout_id, length_condition) do
194
+ instance_eval(&block)
195
+ end
196
+ end
197
+
198
+ def col(sym, map_info, layout_id = @current_layout, &block)
199
+ case map_info
200
+ when Numeric
201
+ col_len(sym, map_info, layout_id, block)
202
+ when Symbol
203
+ col_from_sub_layout(sym, map_info, layout_id, block)
204
+ when Array
205
+ col_array(sym, map_info, layout_id, block)
206
+ end
207
+ end
208
+
209
+ def create_layout
210
+ Layout.new(@current_length_condition)
211
+ end
212
+
213
+ def col_len(sym, len, layout_id = @current_layout, block)
214
+ @layouts[layout_id] ||= create_layout
215
+ @layouts[layout_id].add(sym, SimpleMapper.new(self, len, block))
216
+ end
217
+
218
+ def col_from_sub_layout(sym, sub_layout, layout_id = @current_layout, block)
219
+ @layouts[layout_id] ||= create_layout
220
+ @layouts[layout_id].add(sym, SubLayoutMapper.new(self, sub_layout, block))
221
+ end
222
+
223
+ def col_array(sym, array_param, layout_id = @current_layout, block)
224
+ @layouts[layout_id] ||= create_layout
225
+ @layouts[layout_id].add(sym, ArrayMapper.new(self,
226
+ array_param.map{|arg|
227
+ case arg
228
+ when Numeric
229
+ mapper_class = SimpleMapper
230
+ when Symbol
231
+ mapper_class = SubLayoutMapper
232
+ else
233
+ raise "elements must be Numeric or Symbol"
234
+ end
235
+ mapper_class.new(self, arg)
236
+ }, block)
237
+ )
238
+ end
239
+
240
+ def change_current_layout(layout, length_condition)
241
+ tmp = @current_layout
242
+ tmp = @current_length_condition
243
+ @current_layout = layout
244
+ @current_length_condition = length_condition
245
+ yield
246
+ @current_layout = tmp
247
+ @current_length_condition = tmp
248
+ end
249
+ end
250
+ end