fixed-layout-mapper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in fixed-layout-mapper.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ require 'pp'
2
+ require 'fixed-layout-mapper'
3
+
4
+ mapper = FixedLayoutMapper::Mapper.define_layout do
5
+ layout :sub_layout do
6
+ col :sub_f1, 1
7
+ col :sub_f2, 2
8
+ end
9
+
10
+ col :f1, 5
11
+ col :f2, :sub_layout
12
+ col :f3, [2] * 3
13
+ col :f4, [1, 2, :sub_layout]
14
+ col :f5, 3 do |v|
15
+ v * 2
16
+ end
17
+ end
18
+
19
+ data = %w(12345 a bb 00 11 22 a bb c dd 123)
20
+ pp mapper.map(data.join)
21
+ #=> #<struct
22
+ # f1="12345",
23
+ # f2=#<struct sub_f1="a", sub_f2="bb">,
24
+ # f3=["00", "11", "22"],
25
+ # f4=["a", "bb", #<struct sub_f1="c", sub_f2="dd">],
26
+ # f5="123123">
data/Rakefile ADDED
@@ -0,0 +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
@@ -0,0 +1,24 @@
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
@@ -0,0 +1,3 @@
1
+ module FixedLayoutMapper
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +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
@@ -0,0 +1,241 @@
1
+ #coding: Windows-31J
2
+
3
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/fixed-layout-mapper')
5
+ require 'rspec'
6
+
7
+ describe FixedLayoutMapper::Mapper do
8
+ def define_layout(&block)
9
+ FixedLayoutMapper::Mapper.define_layout(&block)
10
+ end
11
+
12
+ def genstr(ary)
13
+ ary.join
14
+ end
15
+
16
+ it "�T�C�Y�w��ŃJ�������`�ł��邱��" do
17
+ mapper = define_layout do
18
+ col :field1, 2
19
+ col :field2, 1
20
+ end
21
+ rt = mapper.map(%w(12 3).join)
22
+ rt.field1.should == "12"
23
+ rt.field2.should == "3"
24
+ end
25
+
26
+ it "�T�C�Y�̔z��ŃJ�������`����Ƃ��̌��ŕ������ꂽ�z��ɂȂ邱��" do
27
+ mapper = define_layout do
28
+ col :f1, 2
29
+ col :f2, [1, 2, 3]
30
+ col :f3, [4] * 2
31
+ end
32
+
33
+ rt = mapper.map(%w(12 a bb ccc 1234 5678).join)
34
+ rt.f1.should == "12"
35
+ rt.f2.should == %w(a bb ccc)
36
+ rt.f3.should == %w(1234 5678)
37
+ end
38
+
39
+ it "���C�A�E�g�ɖ��O���‚����邱��" do
40
+ mapper = define_layout do
41
+ layout :layout1 do
42
+ col :f1, 2
43
+ col :f2, 1
44
+ end
45
+ end
46
+ rt = mapper.map(%w(12 3).join, :layout1)
47
+ rt.f1.should == "12"
48
+ rt.f2.should == "3"
49
+ end
50
+
51
+ it "���C�A�E�g�����w�肵�Ă��Ȃ��ꍇ:default_layout�Œ�`����Ă��邱��" do
52
+ mapper = define_layout do
53
+ col :f1, 2
54
+ end
55
+
56
+ rt = mapper.map(%w(12).join, :default_layout)
57
+ rt.f1.should == "12"
58
+ end
59
+
60
+ it "���郌�C�A�E�g�ŕʂ̃��C�A�E�g(�ȉ��T�u���C�A�E�g)���g���邱��" do
61
+ mapper = define_layout do
62
+ layout :layout1 do
63
+ col :f1, 2
64
+ col :f2, 1
65
+ end
66
+
67
+ col :f, :layout1
68
+ end
69
+ rt = mapper.map(%w(12 3).join)
70
+ rt.f.f1.should == "12"
71
+ rt.f.f2.should == "3"
72
+ end
73
+
74
+ it "�T�u���C�A�E�g�̔z�����`�ł��邱��" do
75
+ mapper = define_layout do
76
+ layout :layout1 do
77
+ col :f1, 2
78
+ col :f2, 1
79
+ end
80
+
81
+ col :f, [:layout1, :layout1]
82
+ end
83
+ rt = mapper.map(%w(12 3 ab c).join)
84
+ rt.f[0].f1.should == "12"
85
+ rt.f[0].f2.should == "3"
86
+ rt.f[1].f1.should == "ab"
87
+ rt.f[1].f2.should == "c"
88
+ end
89
+
90
+ it "�z��ɂ̓T�C�Y�ł��T�u���C�A�E�g�ł��w��ł��邱��" do
91
+ mapper = define_layout do
92
+ layout :layout1 do
93
+ col :f1, 2
94
+ col :f2, 1
95
+ end
96
+
97
+ col :f, [:layout1, 1, 2]
98
+ end
99
+ rt = mapper.map(%w(12 3 a bc).join)
100
+ rt.f[0].f1.should == "12"
101
+ rt.f[0].f2.should == "3"
102
+ rt.f[1].should == "a"
103
+ rt.f[2].should == "bc"
104
+ end
105
+
106
+ it "�����̃��C�A�E�g���`�ł��邱��" do
107
+ mapper = define_layout do
108
+ layout :layout1 do
109
+ col :f1, 1
110
+ end
111
+
112
+ layout :layout2 do
113
+ col :f2, 2
114
+ end
115
+ end
116
+ rt = mapper.map(%w(1).join, :layout1)
117
+ rt.f1.should == "1"
118
+
119
+ rt = mapper.map(%w(ab).join, :layout2)
120
+ rt.f2.should == "ab"
121
+ end
122
+
123
+ it "�ϊ����ɃA�N�V�������`�����ꍇ���̖߂�l�Ń}�b�s���O����邱��" do
124
+ mapper = define_layout do
125
+ col :f1, 2 do |v|
126
+ v + v
127
+ end
128
+ end
129
+
130
+ rt = mapper.map(%w(12).join, :default_layout)
131
+ rt.f1.should == "1212"
132
+ end
133
+
134
+ it "�����O�X�w��̃��C�A�E�g�̃����O�X���擾�ł��邱��" do
135
+ mapper = define_layout do
136
+ col :f1, 2
137
+ end
138
+
139
+ mapper.get_layout.length.should == 2
140
+ end
141
+
142
+ it "�z��w��̃��C�A�E�g�̃����O�X���擾�ł��邱��" do
143
+ mapper = define_layout do
144
+ col :f1, [1, 2, 3]
145
+ end
146
+
147
+ mapper.get_layout.length.should == 6
148
+ end
149
+
150
+ it "�T�u���C�A�E�g�w��̃��C�A�E�g�̃����O�X���擾�ł��邱��" do
151
+ mapper = define_layout do
152
+ layout :layout1 do
153
+ col :f, 1
154
+ end
155
+
156
+ layout :layout2 do
157
+ col :f ,3
158
+ end
159
+
160
+ col :f1, :layout1
161
+ col :f2, :layout2
162
+ end
163
+
164
+ mapper.get_layout.length.should == 4
165
+ end
166
+
167
+ it "�z��+�T�u���C�A�E�g�w��̃��C�A�E�g�̃����O�X���擾�ł��邱��" do
168
+ mapper = define_layout do
169
+ layout :layout1 do
170
+ col :f, 1
171
+ end
172
+ col :f1, [10, :layout1, 1]
173
+ end
174
+ mapper.get_layout.length.should == 12
175
+ end
176
+
177
+ it "���C�A�E�g�Ƀ����O�X����(���Ȃ��Ƃ��f�[�^�̂ق�������)���w�肳�ꂽ�ꍇ�f�[�^�̃����O�X�̕��������Ă��G���[�ɂȂ�Ȃ�����" do
178
+ mapper = define_layout do
179
+ layout :layout1, FixedLayoutMapper::LENGTH_CONDITION_ALLOW_LONG do
180
+ col :f, 2
181
+ end
182
+ end
183
+ rt = mapper.map("12345", :layout1)
184
+ rt.f.should == "12"
185
+ end
186
+
187
+ it "���C�A�E�g�Ƀ����O�X����(���Ȃ��Ƃ��f�[�^�̂ق�������)���w�肳�ꂽ�ꍇ�f�[�^�̃����O�X�̕����Z���ꍇ��O���������邱��" do
188
+ mapper = define_layout do
189
+ layout :layout1, FixedLayoutMapper::LENGTH_CONDITION_ALLOW_LONG do
190
+ col :f, 2
191
+ end
192
+ end
193
+ proc {
194
+ mapper.map("1", :layout1)
195
+ }.should raise_error
196
+ end
197
+
198
+ it "���C�A�E�g�Ƀ����O�X����(�f�[�^�����Ɠ���)���w�肳�ꂽ�ꍇ�f�[�^�̃����O�X�̕��������ƃG���[�ɂȂ邱��" do
199
+ mapper = define_layout do
200
+ layout :layout1, FixedLayoutMapper::LENGTH_CONDITION_STRICT do
201
+ col :f, 2
202
+ end
203
+ end
204
+ proc {
205
+ mapper.map("12345", :layout1)
206
+ }.should raise_error
207
+ end
208
+
209
+ it "���C�A�E�g�Ƀ����O�X����(�f�[�^�����Ɠ���)���w�肳�ꂽ�ꍇ�f�[�^�̃����O�X�̕����Z���ƃG���[�ɂȂ邱��" do
210
+ mapper = define_layout do
211
+ layout :layout1, FixedLayoutMapper::LENGTH_CONDITION_STRICT do
212
+ col :f, 2
213
+ end
214
+ end
215
+ proc {
216
+ mapper.map("1", :layout1)
217
+ }.should raise_error
218
+ end
219
+
220
+ it "���C�A�E�g�Ƀ����O�X����(�f�[�^�����Ɠ���)���w�肳�ꂽ�ꍇ�f�[�^�̃����O�X�Ɠ����ꍇ�̓G���[���������Ȃ�����" do
221
+ mapper = define_layout do
222
+ layout :layout1, FixedLayoutMapper::LENGTH_CONDITION_STRICT do
223
+ col :f, 2
224
+ end
225
+ end
226
+
227
+ rt = mapper.map("12", :layout1)
228
+ rt.f.should == "12"
229
+ end
230
+
231
+ it "���C�A�E�g�Ƀ����O�X����(�f�[�^�������͒���)���w�肳�ꂽ�ꍇ�f�[�^�̃����O�X�̕����Z���ƃG���[�ɂȂ邱��" do
232
+ mapper = define_layout do
233
+ layout :layout1, FixedLayoutMapper::LENGTH_CONDITION_ALLOW_LONG do
234
+ col :f, 2
235
+ end
236
+ end
237
+ proc {
238
+ mapper.map("1", :layout1)
239
+ }.should raise_error
240
+ end
241
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fixed-layout-mapper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - pocari
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-15 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ''
15
+ email:
16
+ - caffelattenonsugar@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - README.md
24
+ - Rakefile
25
+ - fixed-layout-mapper.gemspec
26
+ - lib/fixed-layout-mapper.rb
27
+ - lib/fixed-layout-mapper/version.rb
28
+ - test/fixed_layout_mapper_spec.rb
29
+ homepage: https://github.com/pocari
30
+ licenses: []
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project: fixed-layout-mapper
49
+ rubygems_version: 1.8.23
50
+ signing_key:
51
+ specification_version: 3
52
+ summary: Mapping fixed layout record to ruby's Struct or Array object.
53
+ test_files: []