fixed-layout-mapper 0.0.1

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.
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: []