fixed-layout-mapper 0.0.2 → 0.0.3

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 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