jabara 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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +9 -0
- data/bin/jabara +3 -0
- data/jabara.gemspec +25 -0
- data/lib/jabara/data.rb +56 -0
- data/lib/jabara/mysql/output.rb +64 -0
- data/lib/jabara/mysql/schema.rb +201 -0
- data/lib/jabara/mysql.rb +2 -0
- data/lib/jabara/mysql_bulk/output.rb +70 -0
- data/lib/jabara/parse_com/input.rb +21 -0
- data/lib/jabara/parse_com/schema.rb +257 -0
- data/lib/jabara/parse_com.rb +2 -0
- data/lib/jabara/transformer/flatten.rb +61 -0
- data/lib/jabara/transformer/key_value.rb +25 -0
- data/lib/jabara/transformer/nest.rb +13 -0
- data/lib/jabara/version.rb +3 -0
- data/lib/jabara.rb +6 -0
- data/test/minitest_helper.rb +4 -0
- data/test/test_jabara.rb +11 -0
- metadata +126 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b920b55bea41121729957fd1afeda678ff1b1c8e
|
4
|
+
data.tar.gz: b9e152304622564a2fa3b2526de4f952056fa7a1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4cd0bdddb3d6651183eb30f2ab620a2c85eb07811ebe602a7960b956dd3e122b21468c9b794700c1b28ffbfdb9b7d901c6f58bb5e891c93cfbd490d91c4e605b
|
7
|
+
data.tar.gz: 8cc7d405f8849bbdc3388cfddfe071c7413164f436189362ad8626e33e4638cdeb1ac1387743d8410063315a8fd8ff8d97576f1d67522692e5a5b4430410c7f9
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Yuki Takeichi
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Jabara
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'jabara'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install jabara
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/jabara/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/jabara
ADDED
data/jabara.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'jabara/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "jabara"
|
8
|
+
spec.version = Jabara::VERSION
|
9
|
+
spec.authors = ["Yuki Takeichi"]
|
10
|
+
spec.email = ["yuki.takeichi@gmail.com"]
|
11
|
+
spec.summary = "A gem for pluggable data transformation."
|
12
|
+
spec.description = "Nested data to flat, and flat data to nested."
|
13
|
+
spec.homepage = "https://github.com/yuki-takeichi/jabara"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "scheman", "0.0.5"
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "minitest"
|
25
|
+
end
|
data/lib/jabara/data.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module Jabara
|
2
|
+
# Jabara中間表現のためのコンストラクタ/アクセサ
|
3
|
+
# 抽象データ型なので、Plugin側からIndexを渡して直接Arrayにアクセスするのは禁止
|
4
|
+
|
5
|
+
# コンストラクタ
|
6
|
+
|
7
|
+
def self.primitive(tag, data)
|
8
|
+
[tag, data]
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.null
|
12
|
+
[:null]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.object(object_type, data, id=nil)
|
16
|
+
[:object, data, object_type, id]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.array(reprs)
|
20
|
+
[:array, reprs]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.set(reprs)
|
24
|
+
[:set, reprs]
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.tag(repr)
|
28
|
+
repr[0]
|
29
|
+
end
|
30
|
+
|
31
|
+
# アクセサ
|
32
|
+
|
33
|
+
def self.data(repr)
|
34
|
+
repr[1]
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.set_data(repr, data)
|
38
|
+
repr[1] = data
|
39
|
+
end
|
40
|
+
|
41
|
+
# use only for :object
|
42
|
+
def self.object_type(repr)
|
43
|
+
repr[2]
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.primitive?(repr)
|
47
|
+
tag = self.tag(repr)
|
48
|
+
not [:array, :object, :set].include?(tag)
|
49
|
+
end
|
50
|
+
|
51
|
+
# use only for :object
|
52
|
+
def self.id(repr)
|
53
|
+
return nil if repr.length < 4
|
54
|
+
return repr[3]
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'jabara/data'
|
2
|
+
|
3
|
+
module Jabara
|
4
|
+
module MySQL
|
5
|
+
class Output
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
end
|
9
|
+
|
10
|
+
def encode(object_repr)
|
11
|
+
table_name = ::Jabara.object_type(object_repr)
|
12
|
+
mysql_value_hash = {}
|
13
|
+
::Jabara.data(object_repr).each do |key, repr|
|
14
|
+
mysql_value_hash[key] = mysql_value(repr)
|
15
|
+
end
|
16
|
+
build_insert_query(table_name, mysql_value_hash)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def quote(str)
|
21
|
+
# MySQL の nobackquote_escape option が false であることが前提
|
22
|
+
'"%s"' % str.gsub('"', '""').gsub(/\\/, "\\\\\\") # \ を \\ に置き換える
|
23
|
+
end
|
24
|
+
|
25
|
+
# Jabara中間表現をMySQL表現に変換する
|
26
|
+
def mysql_value(repr)
|
27
|
+
raise ArgumentError, 'repr must not be nil' if repr.nil?
|
28
|
+
|
29
|
+
data = ::Jabara.data(repr)
|
30
|
+
tag = ::Jabara.tag(repr)
|
31
|
+
case tag
|
32
|
+
when :null
|
33
|
+
'null'
|
34
|
+
when :integer
|
35
|
+
data
|
36
|
+
when :float
|
37
|
+
data
|
38
|
+
when :datetime
|
39
|
+
quote(data.strftime("%Y-%m-%d %H:%M:%S"))
|
40
|
+
when :string
|
41
|
+
quote(data)
|
42
|
+
when :boolean
|
43
|
+
data
|
44
|
+
when :object, :array
|
45
|
+
raise ArgumentError, "Can't accept nested object or array!"
|
46
|
+
else
|
47
|
+
raise ArgumentError, "Invalid tag was found: %s" % tag
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Insertクエリを生成する
|
52
|
+
def build_insert_query(table_name, mysql_repr)
|
53
|
+
cols = []
|
54
|
+
vals = []
|
55
|
+
mysql_repr.each do |column, value|
|
56
|
+
cols << column
|
57
|
+
vals << value
|
58
|
+
end
|
59
|
+
"insert into %s (%s) values(%s);\n" % [table_name, cols.join(', '), vals.join(', ')]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'jabara/data'
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'scheman'
|
5
|
+
|
6
|
+
module Jabara
|
7
|
+
module MySQL
|
8
|
+
module Schema
|
9
|
+
|
10
|
+
class Char
|
11
|
+
def self.tag
|
12
|
+
:string
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(max:nil)
|
16
|
+
@max = max
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse
|
20
|
+
# TODO
|
21
|
+
# unquote and de-escape
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate(repr)
|
25
|
+
return false, 'not string type.' unless ::Jabara.tag(repr) == :string
|
26
|
+
return false, 'max length exceeded.' unless ::Jabara.data(repr).length > @max
|
27
|
+
return true, nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Text
|
32
|
+
def self.tag
|
33
|
+
:string
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse
|
40
|
+
# TODO
|
41
|
+
# unquote and de-escape
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate(repr)
|
45
|
+
return false, 'not string type.' unless ::Jabara.tag(repr) == :string
|
46
|
+
return true, nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Boolean
|
51
|
+
def self.tag
|
52
|
+
:boolean
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse(data)
|
56
|
+
raise ArgumentError, 'must be string' unless data.is_a? ::String
|
57
|
+
return true if data == 'true'
|
58
|
+
return false if data == 'false'
|
59
|
+
raise ArgumentError, 'must be "true" or "false"'
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate(repr)
|
63
|
+
data = ::Jabara.data(repr)
|
64
|
+
return true if [true, false].include? data
|
65
|
+
return false, 'must be "true" or "false"'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Integer
|
70
|
+
def self.tag
|
71
|
+
:integer
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse(parse)
|
75
|
+
Integer(data) # may raise ArgumentError
|
76
|
+
end
|
77
|
+
|
78
|
+
def validate(repr)
|
79
|
+
return ::Jabara.data(repr).is_a? ::Integer
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class DateTime
|
84
|
+
def self.tag
|
85
|
+
:datetime
|
86
|
+
end
|
87
|
+
|
88
|
+
def parse(data)
|
89
|
+
raise ArgumentError, 'must be string' unless data.is_a? ::String
|
90
|
+
begin
|
91
|
+
DateTime.strptime(date, '%Y-%m-%d %H:%M:%S.%N')
|
92
|
+
rescue ArgumentError
|
93
|
+
DateTime.strptime(date, '%Y-%m-%d %H:%M:%S') # may raise ArgumentError
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def validate(repr)
|
98
|
+
return ::Jabara.data(repr).is_a? ::DateTime
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Schema
|
103
|
+
|
104
|
+
attr_accessor :columns, :table_name
|
105
|
+
|
106
|
+
def initialize
|
107
|
+
@columns = []
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class Builder
|
112
|
+
|
113
|
+
attr_reader :schema
|
114
|
+
|
115
|
+
def initialize
|
116
|
+
@schema = Schema.new
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.build(&block)
|
120
|
+
this = Builder.new
|
121
|
+
this.instance_eval(&block)
|
122
|
+
return this.schema
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.build_from_create_stmt(crete_table_str)
|
126
|
+
parser = Scheman::Parsers::Mysql.new
|
127
|
+
create_stmt_hash = parser.parse(crete_table_str).to_hash[0][:create_table]
|
128
|
+
|
129
|
+
this = Builder.new
|
130
|
+
this.table_name create_stmt_hash[:name]
|
131
|
+
create_stmt_hash[:fields].each do |field_hash|
|
132
|
+
field_hash = field_hash[:field]
|
133
|
+
type = case field_hash[:type]
|
134
|
+
when "char", "varchar"
|
135
|
+
max = field_hash[:values][0]
|
136
|
+
if max.nil? then Char.new else Char.new max: max end
|
137
|
+
when "text"
|
138
|
+
Text.new
|
139
|
+
when "integer"
|
140
|
+
max = field_hash[:values][0]
|
141
|
+
if max.nil? then Integer.new else Integer.new max: max end
|
142
|
+
when "boolean"
|
143
|
+
Boolean.new
|
144
|
+
when "double"
|
145
|
+
Double.new
|
146
|
+
when "datetime"
|
147
|
+
DateTime.new
|
148
|
+
else
|
149
|
+
raise ArgumentError, 'invalid type'
|
150
|
+
end
|
151
|
+
|
152
|
+
if field_hash[:qualifiers].include?({qualifier: {type: "not_null"}}) then
|
153
|
+
this.column field_hash[:name], type, :not_null
|
154
|
+
else
|
155
|
+
this.column field_hash[:name], type
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
return this.schema
|
160
|
+
end
|
161
|
+
|
162
|
+
# 以下DSLメソッド。buildに渡すblock内で使う。
|
163
|
+
|
164
|
+
def table_name(table_name)
|
165
|
+
@schema.table_name = table_name
|
166
|
+
end
|
167
|
+
|
168
|
+
def column(key, type, *args)
|
169
|
+
@schema.columns.push({key: key, type: type, constraints: args})
|
170
|
+
end
|
171
|
+
|
172
|
+
def not_null
|
173
|
+
:not_null
|
174
|
+
end
|
175
|
+
|
176
|
+
def boolean
|
177
|
+
Boolean.new
|
178
|
+
end
|
179
|
+
|
180
|
+
def integer
|
181
|
+
Integer.new
|
182
|
+
end
|
183
|
+
|
184
|
+
def varchar(max)
|
185
|
+
Char.new max: max
|
186
|
+
end
|
187
|
+
|
188
|
+
def char(max, char_set:nil)
|
189
|
+
# TODO char_set
|
190
|
+
Char.new max: max
|
191
|
+
end
|
192
|
+
|
193
|
+
def datetime
|
194
|
+
DateTime.new
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
data/lib/jabara/mysql.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'jabara/data'
|
2
|
+
require 'jabara/mysql/output'
|
3
|
+
|
4
|
+
module Jabara
|
5
|
+
module MySQLBulk
|
6
|
+
class Output < Jabara::MySQL::Output
|
7
|
+
def initialize(schema:, tuple_size:1000)
|
8
|
+
@table_name = schema.table_name
|
9
|
+
@schema = schema
|
10
|
+
@tuple_size = tuple_size
|
11
|
+
reset_buffer
|
12
|
+
end
|
13
|
+
|
14
|
+
def encode(repr)
|
15
|
+
data = ::Jabara.data(repr)
|
16
|
+
tuple_str = "(%s)" % @schema.columns.map { |column|
|
17
|
+
key = column[:key]
|
18
|
+
mysql_value(data[key])
|
19
|
+
}.join(", ")
|
20
|
+
|
21
|
+
@buf << tuple_str
|
22
|
+
@buf << ","
|
23
|
+
@tuple_count = @tuple_count + 1
|
24
|
+
|
25
|
+
if @tuple_count >= @tuple_size then
|
26
|
+
return flush
|
27
|
+
else
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def flush
|
33
|
+
return nil if @tuple_count == 0
|
34
|
+
|
35
|
+
terminate
|
36
|
+
ret = @buf
|
37
|
+
reset_buffer
|
38
|
+
return ret
|
39
|
+
end
|
40
|
+
|
41
|
+
def reset_buffer
|
42
|
+
@buf = insert_head
|
43
|
+
@tuple_count = 0
|
44
|
+
end
|
45
|
+
|
46
|
+
def terminate
|
47
|
+
@buf.chop!
|
48
|
+
@buf << ";\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
def insert_head
|
52
|
+
"insert into `%s` %s values" % [@table_name, column_def]
|
53
|
+
end
|
54
|
+
|
55
|
+
def column_def
|
56
|
+
"(%s)" % @schema.columns.map {|column| "`%s`" % column[:key] }.join(", ")
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate
|
60
|
+
@schema.columns.each do |column|
|
61
|
+
return false, 'violate "not null" constraint.' if column[:constraints].include? :not_null and data.nil?
|
62
|
+
valid, err = column[:type].validate(data)
|
63
|
+
return false, err unless valid
|
64
|
+
end
|
65
|
+
|
66
|
+
return true, nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'jabara/data'
|
2
|
+
|
3
|
+
module Jabara
|
4
|
+
module ParseCom
|
5
|
+
class Input
|
6
|
+
def initialize(parse_com_schema)
|
7
|
+
@schema = parse_com_schema
|
8
|
+
end
|
9
|
+
|
10
|
+
# ParseObjectをJabara中間表現のobjectに変換する
|
11
|
+
def decode(hash)
|
12
|
+
data = {}
|
13
|
+
@schema.key_defs.each do |key, parser|
|
14
|
+
data[key] = parser.parse(hash[key]) # raises ArgumentError
|
15
|
+
end
|
16
|
+
id = ::Jabara.primitive(:string, hash[@schema.id_key_name])
|
17
|
+
return ::Jabara.object(@schema.object_type, data, id)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,257 @@
|
|
1
|
+
require 'jabara/data'
|
2
|
+
require 'jabara/transformer/key_value'
|
3
|
+
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
module Jabara
|
7
|
+
module ParseCom
|
8
|
+
module Schema
|
9
|
+
|
10
|
+
# TODO
|
11
|
+
#class Object
|
12
|
+
# def parse(data)
|
13
|
+
# ::Jabara.object(...)
|
14
|
+
# end
|
15
|
+
#end
|
16
|
+
|
17
|
+
# TODO
|
18
|
+
#class Array
|
19
|
+
# def parse(data)
|
20
|
+
# ::Jabara.array(...array...)
|
21
|
+
# end
|
22
|
+
#end
|
23
|
+
|
24
|
+
class TimeStamp # Parseが自動生成するカラム (createdAt, updatedAt)
|
25
|
+
def parse(data)
|
26
|
+
datetime = ::DateTime.iso8601(data)
|
27
|
+
::Jabara.primitive(:datetime, datetime)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Integer
|
32
|
+
def initialize(default: nil) # default = nil の場合はnullを許容する
|
33
|
+
raise TypeError, 'default must be integer' unless default.is_a? ::Integer or default.nil?
|
34
|
+
@default = default
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse(data)
|
38
|
+
return ::Jabara.null if data.nil? and @default.nil?
|
39
|
+
return ::Jabara.primitive(:integer, @default) if data.nil?
|
40
|
+
|
41
|
+
raise TypeError, 'default must be integer' unless data.is_a? ::Integer
|
42
|
+
::Jabara.primitive(:integer, data)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Float
|
47
|
+
def initialize(default: nil) # default = nil の場合はnullを許容する
|
48
|
+
raise TypeError, 'default must be integer' unless default.is_a? ::Float or default.nil?
|
49
|
+
@default = default
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse(data)
|
53
|
+
return ::Jabara.null if data.nil? and @default.nil?
|
54
|
+
return ::Jabara.primitive(:float, @default) if data.nil?
|
55
|
+
|
56
|
+
raise TypeError, 'default must be float' unless data.is_a? ::Float
|
57
|
+
::Jabara.primitive(:float, data)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class ObjectId
|
62
|
+
def parse(data)
|
63
|
+
::Jabara.primitive(:string, data)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class File
|
68
|
+
def parse(data)
|
69
|
+
return ::Jabara.null if data.nil?
|
70
|
+
raise ArgumentError, 'File object is collapsed' if data['url'].nil?
|
71
|
+
::Jabara.primitive(:string, data['url'])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class DateTime
|
76
|
+
def initialize(default: nil) # default = nil の場合はnullを許容する
|
77
|
+
raise TypeError, 'default must be datetime' unless default.is_a? ::DateTime or default.nil?
|
78
|
+
@default = default
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse(data)
|
82
|
+
return ::Jabara.null if data.nil? and @default.nil?
|
83
|
+
raise ArgumentError, 'datetime object is collapsed' if data['iso'].nil?
|
84
|
+
::Jabara.primitive(:datetime, ::DateTime.iso8601(data['iso']))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class String
|
89
|
+
def initialize(max: 1000, default: nil) # default = nil の場合はnullを許容する
|
90
|
+
raise TypeError, 'default must be string' unless default.is_a? ::String or default.nil?
|
91
|
+
raise TypeError, 'max must be integer' unless max.is_a? ::Integer
|
92
|
+
@max = max
|
93
|
+
@default = default
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse(data)
|
97
|
+
return ::Jabara.null if data.nil? and @default.nil?
|
98
|
+
return @default if data.nil?
|
99
|
+
raise TypeError, 'data must be string' unless data.is_a? ::String or default.nil?
|
100
|
+
::Jabara.primitive(:string, data.slice(0, @max))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class Pointer
|
105
|
+
def parse(data)
|
106
|
+
return ::Jabara.null if data.nil?
|
107
|
+
raise ArgumentError, 'pointer object is collapsed' if data['objectId'].nil?
|
108
|
+
::Jabara.primitive(:string, data['objectId'])
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class Boolean
|
113
|
+
def initialize(default: nil) # default = nil の場合はnullを許容する
|
114
|
+
raise ArgumentError, 'default must be true of false' unless [true, false].include? default
|
115
|
+
@default = default
|
116
|
+
end
|
117
|
+
|
118
|
+
def parse(data)
|
119
|
+
return ::Jabara.null if data.nil? and @default.nil?
|
120
|
+
return ::Jabara.primitive(:boolean, @default) if data.nil?
|
121
|
+
raise TypeError, 'data must be true or false' unless [true, false].include? data
|
122
|
+
::Jabara.primitive(:boolean, data)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class ACL
|
127
|
+
def initialize user_acl_object_type: , role_acl_object_type:
|
128
|
+
@user_acl_object_type = user_acl_object_type
|
129
|
+
@role_acl_object_type = role_acl_object_type
|
130
|
+
end
|
131
|
+
|
132
|
+
def parse(data)
|
133
|
+
return ::Jabara.set([]) if hash.nil?
|
134
|
+
elems = ::Jabara::Transformer::KeyValue.new.to_entries(hash).map {|entry|
|
135
|
+
if entry[:key].start_with? 'role:' then
|
136
|
+
decode_acl_role_entry(entry[:key].gsub(/role:/, ''), entry[:value])
|
137
|
+
else
|
138
|
+
decode_acl_user_entry(entry[:key], entry[:value])
|
139
|
+
end
|
140
|
+
}
|
141
|
+
|
142
|
+
return ::Jabara.set(elems)
|
143
|
+
end
|
144
|
+
|
145
|
+
def decode_acl_user_entry(user_object_id, acl_permission)
|
146
|
+
data = {}
|
147
|
+
data['userObjectId'] = ::Jabara.primitive(:string, user_object_id)
|
148
|
+
data['read'] = ::Jabara.primitive(:boolean, acl_permission['read'] || false)
|
149
|
+
data['write'] = ::Jabara.primitive(:boolean, acl_permission['write'] || false)
|
150
|
+
::Jabara.object(@user_acl_object_type, data)
|
151
|
+
end
|
152
|
+
|
153
|
+
def decode_acl_role_entry(role_name, acl_permission)
|
154
|
+
data = {}
|
155
|
+
data['roleName'] = ::Jabara.primitive(:string, role_name)
|
156
|
+
data['read'] = ::Jabara.primitive(:boolean, acl_permission['read'] || false)
|
157
|
+
data['write'] = ::Jabara.primitive(:boolean, acl_permission['write'] || false)
|
158
|
+
::Jabara.object(@role_acl_object_type, data)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class Schema
|
163
|
+
|
164
|
+
attr_accessor :key_defs, :object_type, :id_key_name
|
165
|
+
|
166
|
+
def initialize
|
167
|
+
@key_defs = {}
|
168
|
+
@object_type = nil
|
169
|
+
@id_key_name = nil
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
class Builder
|
175
|
+
|
176
|
+
attr_reader :schema
|
177
|
+
|
178
|
+
def self.build(&block)
|
179
|
+
this = Builder.new
|
180
|
+
this.instance_eval(&block)
|
181
|
+
return this.schema
|
182
|
+
end
|
183
|
+
|
184
|
+
def initialize
|
185
|
+
@schema = Schema.new
|
186
|
+
end
|
187
|
+
|
188
|
+
# 以下DSLメソッド。buildに渡すblock内で使う。
|
189
|
+
|
190
|
+
# オブジェクトのtype
|
191
|
+
def type(object_type)
|
192
|
+
@schema.object_type = object_type
|
193
|
+
end
|
194
|
+
|
195
|
+
def id(id_key_name)
|
196
|
+
@schema.id_key_name = id_key_name
|
197
|
+
end
|
198
|
+
|
199
|
+
# スキーマにキーを登録する
|
200
|
+
def key(key_string, type)
|
201
|
+
raise ArgumentError, 'key_name must be string' unless key_string.is_a? ::String
|
202
|
+
(@schema.key_defs)[key_string] = type
|
203
|
+
end
|
204
|
+
|
205
|
+
# TODO
|
206
|
+
#def object
|
207
|
+
# Object.new
|
208
|
+
#end
|
209
|
+
|
210
|
+
# TODO
|
211
|
+
#def array
|
212
|
+
# Array.new
|
213
|
+
#end
|
214
|
+
|
215
|
+
def timestamp
|
216
|
+
TimeStamp.new
|
217
|
+
end
|
218
|
+
|
219
|
+
def integer default: nil
|
220
|
+
Integer.new default: default
|
221
|
+
end
|
222
|
+
|
223
|
+
def float default: nil
|
224
|
+
Float.new default: default
|
225
|
+
end
|
226
|
+
|
227
|
+
def parse_object_id
|
228
|
+
ObjectId.new
|
229
|
+
end
|
230
|
+
|
231
|
+
def file
|
232
|
+
File.new
|
233
|
+
end
|
234
|
+
|
235
|
+
def datetime default: nil
|
236
|
+
DateTime.new default: default
|
237
|
+
end
|
238
|
+
|
239
|
+
def string max: 1000, default: nil
|
240
|
+
String.new max: max, default: default
|
241
|
+
end
|
242
|
+
|
243
|
+
def pointer
|
244
|
+
Pointer.new
|
245
|
+
end
|
246
|
+
|
247
|
+
def boolean default: nil
|
248
|
+
Boolean.new default: default
|
249
|
+
end
|
250
|
+
|
251
|
+
def acl user_acl_object_type: ,role_acl_object_type:
|
252
|
+
ACL.new(user_acl_object_type: user_acl_object_type, role_acl_object_type: role_acl_object_type)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'jabara/data'
|
2
|
+
|
3
|
+
module Jabara
|
4
|
+
module Transformer
|
5
|
+
|
6
|
+
# TODO array と set のflat化は別クラスに責務を切り出した方がいいかも
|
7
|
+
class Flatten
|
8
|
+
def initialize(id_key_name: 'id', index_key_name: 'index')
|
9
|
+
@id_key_name = id_key_name
|
10
|
+
@index_key_name = index_key_name
|
11
|
+
end
|
12
|
+
|
13
|
+
# 与えられたオブジェクトを第1正規化する
|
14
|
+
def convert(object_repr)
|
15
|
+
new_repr_hash = {}
|
16
|
+
divided_reprs = []
|
17
|
+
|
18
|
+
id = ::Jabara.id(object_repr)
|
19
|
+
::Jabara.data(object_repr).each do |key, repr|
|
20
|
+
data = ::Jabara.data(repr)
|
21
|
+
if ::Jabara.primitive?(repr) then
|
22
|
+
new_repr_hash[key] = repr
|
23
|
+
else
|
24
|
+
tag = ::Jabara.tag(repr)
|
25
|
+
case tag
|
26
|
+
when :object
|
27
|
+
_object_repr = data
|
28
|
+
::Jabara.data(_object_repr)[@id_key_name] = id
|
29
|
+
divided_reprs.push(_object_repr)
|
30
|
+
when :array
|
31
|
+
# TODO ヘテロな要素が来るケースへの対応
|
32
|
+
# TODO primitiveな要素が来るケースへの対応
|
33
|
+
|
34
|
+
ar = data
|
35
|
+
ar.each_with_index do |_object_repr, index|
|
36
|
+
::Jabara.data(_object_repr)[@id_key_name] = id
|
37
|
+
::Jabara.data(_object_repr)[@index_key_name] = index
|
38
|
+
divided_reprs.push(_object_repr)
|
39
|
+
end
|
40
|
+
when :set
|
41
|
+
# TODO ヘテロな要素が来るケースへの対応
|
42
|
+
# TODO primitiveな要素が来るケースへの対応
|
43
|
+
|
44
|
+
st = data
|
45
|
+
st.each do |_object_repr|
|
46
|
+
::Jabara.data(_object_repr)[@id_key_name] = id
|
47
|
+
divided_reprs.push(_object_repr)
|
48
|
+
end
|
49
|
+
else
|
50
|
+
raise ArgumentError, "Invalid tag was found :%s" % tag
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
::Jabara.set_data(object_repr, new_repr_hash)
|
56
|
+
return object_repr, divided_reprs
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Jabara
|
2
|
+
module Transformer
|
3
|
+
|
4
|
+
# inspired by jq 'to_entries', 'from_entries' and 'with_entries' functions
|
5
|
+
# http://stedolan.github.io/jq/manual/
|
6
|
+
class KeyValue
|
7
|
+
|
8
|
+
#def from_entries()
|
9
|
+
#end
|
10
|
+
|
11
|
+
def to_entries(hash)
|
12
|
+
entries = []
|
13
|
+
hash.each do |key, value|
|
14
|
+
entry = {:key => key, :value => value}
|
15
|
+
entries.push(entry)
|
16
|
+
end
|
17
|
+
return entries
|
18
|
+
end
|
19
|
+
|
20
|
+
#def with_entries()
|
21
|
+
#end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'jabara/data'
|
2
|
+
|
3
|
+
module Jabara
|
4
|
+
module Transformer
|
5
|
+
class Nest
|
6
|
+
def convert(repr_hash, inner_repr_hash, key_string)
|
7
|
+
raise ArgumentError, 'key_string must be string' unless key_string.is_a? ::String
|
8
|
+
repr_hash[key_string] = inner_repr_hash
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
data/lib/jabara.rb
ADDED
data/test/test_jabara.rb
ADDED
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jabara
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yuki Takeichi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: scheman
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.5
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Nested data to flat, and flat data to nested.
|
70
|
+
email:
|
71
|
+
- yuki.takeichi@gmail.com
|
72
|
+
executables:
|
73
|
+
- jabara
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".travis.yml"
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- bin/jabara
|
84
|
+
- jabara.gemspec
|
85
|
+
- lib/jabara.rb
|
86
|
+
- lib/jabara/data.rb
|
87
|
+
- lib/jabara/mysql.rb
|
88
|
+
- lib/jabara/mysql/output.rb
|
89
|
+
- lib/jabara/mysql/schema.rb
|
90
|
+
- lib/jabara/mysql_bulk/output.rb
|
91
|
+
- lib/jabara/parse_com.rb
|
92
|
+
- lib/jabara/parse_com/input.rb
|
93
|
+
- lib/jabara/parse_com/schema.rb
|
94
|
+
- lib/jabara/transformer/flatten.rb
|
95
|
+
- lib/jabara/transformer/key_value.rb
|
96
|
+
- lib/jabara/transformer/nest.rb
|
97
|
+
- lib/jabara/version.rb
|
98
|
+
- test/minitest_helper.rb
|
99
|
+
- test/test_jabara.rb
|
100
|
+
homepage: https://github.com/yuki-takeichi/jabara
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata: {}
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 2.2.2
|
121
|
+
signing_key:
|
122
|
+
specification_version: 4
|
123
|
+
summary: A gem for pluggable data transformation.
|
124
|
+
test_files:
|
125
|
+
- test/minitest_helper.rb
|
126
|
+
- test/test_jabara.rb
|