oinky 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/README.md +141 -0
- data/ext/extconf.rb +79 -0
- data/ext/include/oinky.h +424 -0
- data/ext/include/oinky.hpp +63 -0
- data/ext/include/oinky/nky_base.hpp +1116 -0
- data/ext/include/oinky/nky_core.hpp +1603 -0
- data/ext/include/oinky/nky_cursor.hpp +665 -0
- data/ext/include/oinky/nky_dialect.hpp +107 -0
- data/ext/include/oinky/nky_error.hpp +164 -0
- data/ext/include/oinky/nky_fixed_table.hpp +710 -0
- data/ext/include/oinky/nky_handle.hpp +334 -0
- data/ext/include/oinky/nky_index.hpp +1038 -0
- data/ext/include/oinky/nky_log.hpp +15 -0
- data/ext/include/oinky/nky_merge_itr.hpp +403 -0
- data/ext/include/oinky/nky_model.hpp +110 -0
- data/ext/include/oinky/nky_pool.hpp +760 -0
- data/ext/include/oinky/nky_public.hpp +808 -0
- data/ext/include/oinky/nky_serializer.hpp +1625 -0
- data/ext/include/oinky/nky_strtable.hpp +504 -0
- data/ext/include/oinky/nky_table.hpp +1996 -0
- data/ext/nky_lib.cpp +390 -0
- data/ext/nky_lib_core.hpp +212 -0
- data/ext/nky_lib_index.cpp +158 -0
- data/ext/nky_lib_table.cpp +224 -0
- data/lib/oinky.rb +1284 -0
- data/lib/oinky/compiler.rb +106 -0
- data/lib/oinky/cpp_emitter.rb +311 -0
- data/lib/oinky/dsl.rb +167 -0
- data/lib/oinky/error.rb +19 -0
- data/lib/oinky/modelbase.rb +12 -0
- data/lib/oinky/nbuffer.rb +152 -0
- data/lib/oinky/normalize.rb +132 -0
- data/lib/oinky/oc_builder.rb +44 -0
- data/lib/oinky/query.rb +193 -0
- data/lib/oinky/rb_emitter.rb +147 -0
- data/lib/oinky/shard.rb +40 -0
- data/lib/oinky/testsup.rb +104 -0
- data/lib/oinky/version.rb +9 -0
- data/oinky.gemspec +36 -0
- metadata +120 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
# This source is distributed under the terms of the MIT License. Refer
|
2
|
+
# to the 'LICENSE' file for details.
|
3
|
+
#
|
4
|
+
# Copyright (c) Jacob Lacouture, 2012
|
5
|
+
|
6
|
+
module Oinky
|
7
|
+
# This is a duplicate definition. It's here to decouple the compiler
|
8
|
+
# from the oinky gem. The gem requires building a native extension,
|
9
|
+
# whereas the compiler doesn't. This way the compiler can be run
|
10
|
+
# right out of the source directory without build/install.
|
11
|
+
# Consistency of these definitions is assured in the Oinky gem.
|
12
|
+
column_types = [:variant, :bit,
|
13
|
+
:int8, :int16, :int32, :int64,
|
14
|
+
:uint8, :uint16, :uint32, :uint64,
|
15
|
+
:float32, :float64,
|
16
|
+
:datetime, :string].sort
|
17
|
+
ColumnTypes ||= column_types
|
18
|
+
|
19
|
+
# ColumnTypes may have been previously defined (by the gem)
|
20
|
+
# but if so, the definition should have been identical.
|
21
|
+
unless ColumnTypes == column_types
|
22
|
+
raise OinkyException.new("Mismatched column types definition: " +
|
23
|
+
"#{ColumnTypes.inspect} != #{column_types.inspect}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'oinky/oc_builder'
|
28
|
+
# Just preload all the languages we know about. They are well namespaced
|
29
|
+
# so there's no harm.
|
30
|
+
require 'oinky/cpp_emitter'
|
31
|
+
require 'oinky/rb_emitter'
|
32
|
+
require 'oinky/modelbase'
|
33
|
+
require 'oinky/dsl'
|
34
|
+
|
35
|
+
# This is the oinky model compiler. Given a model specification, it
|
36
|
+
# can generate a set of accessor classes and basic migration routines
|
37
|
+
# in a target language.
|
38
|
+
#
|
39
|
+
# The purpose of this compiler is to permit the model to be defined
|
40
|
+
# exactly once, in one language. It can generate code in any language.
|
41
|
+
# There should be NO REASON WHATSOEVER to EVER MODIFY THE GENERATED CODE!!!
|
42
|
+
# That is always the wrong thing to do. Alter the generator if need be.
|
43
|
+
|
44
|
+
module Oinky
|
45
|
+
module Model
|
46
|
+
class DerivedType
|
47
|
+
def initialize(basetype, instancetype)
|
48
|
+
@basetype = basetype
|
49
|
+
@instancetype = instancetype
|
50
|
+
end
|
51
|
+
def instance_type(lang)
|
52
|
+
return @instancetype if @instancetype.is_a? String
|
53
|
+
return @instancetype[lang]
|
54
|
+
end
|
55
|
+
def oinky_type
|
56
|
+
return @basetype if @basetype.is_a? Symbol
|
57
|
+
return @basetype.oinky_type
|
58
|
+
end
|
59
|
+
# This can be overriden by any derived type. By default
|
60
|
+
# we just build from the base type.
|
61
|
+
def value_from_variant(src_exp, emitter)
|
62
|
+
emitter.value_from_variant(src_exp, oinky_type)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.generate(schema, ccls)
|
67
|
+
# Allow specification by class or symbol
|
68
|
+
ccls = ({:cpp=>Cpp, :CPP=>Cpp, :ruby=>Ruby, :rb=>Ruby}[ccls] || ccls)
|
69
|
+
ccls.new(schema).emit
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
=begin
|
75
|
+
|
76
|
+
class TableDefinition
|
77
|
+
def initialize(h)
|
78
|
+
@h = h
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_reader :h
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.define_table(h)
|
85
|
+
h[:indices] = (h[:indices] || h[:indexes] || [])
|
86
|
+
TableDefinition.new(h)
|
87
|
+
end
|
88
|
+
|
89
|
+
class Table
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
class Model
|
94
|
+
class << self
|
95
|
+
|
96
|
+
def create_table(tablename)
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
=end
|
104
|
+
|
105
|
+
require 'oinky/cpp_emitter'
|
106
|
+
|
@@ -0,0 +1,311 @@
|
|
1
|
+
# This source is distributed under the terms of the MIT License. Refer
|
2
|
+
# to the 'LICENSE' file for details.
|
3
|
+
#
|
4
|
+
# Copyright (c) Jacob Lacouture, 2012
|
5
|
+
|
6
|
+
require 'oinky/normalize.rb'
|
7
|
+
|
8
|
+
module Oinky
|
9
|
+
module Model
|
10
|
+
class Cpp
|
11
|
+
def initialize(schema)
|
12
|
+
@schema = Oinky::Model.normalize_schema(schema)
|
13
|
+
end
|
14
|
+
|
15
|
+
def cppify(str)
|
16
|
+
str.gsub(/($[0-9])|[^a-z0-9A-Z_]/,'_')
|
17
|
+
end
|
18
|
+
|
19
|
+
def schemaname_to_ns(str) ; cppify(str) ; end
|
20
|
+
def tablename_to_groupname(str) ; cppify(str) ; end
|
21
|
+
def handle_from_tablename(str) ; "#{cppify(str)}_handle" ; end
|
22
|
+
def accessor_from_tablename(str) ; cppify(str) ; end
|
23
|
+
|
24
|
+
def make_datetime_expression(d)
|
25
|
+
"Oinky::datetime_t::compose(#{d.year-1900},#{d.month - 1},#{d.day - 1}," +
|
26
|
+
"#{d.hour},#{d.minute},#{d.second},#{(d.second_fraction * 1000000).to_i})"
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_value_expression(coldef)
|
30
|
+
d = coldef[:default]
|
31
|
+
return "#{instance_type_expression(coldef[:type])}()" unless d
|
32
|
+
return d.inspect if d.is_a? String
|
33
|
+
return d.inspect if d.is_a? Fixnum
|
34
|
+
return make_datetime_expression(d) if d.is_a? DateTime
|
35
|
+
return d[:cpp]
|
36
|
+
end
|
37
|
+
|
38
|
+
def accessor_name(h)
|
39
|
+
return cppify(h[:accessor])
|
40
|
+
end
|
41
|
+
|
42
|
+
def index_column_name(col)
|
43
|
+
return col if col.is_a? String
|
44
|
+
return col.to_s if col.is_a? Symbol
|
45
|
+
return index_column_name(col[:name])
|
46
|
+
end
|
47
|
+
def index_column_ascending(col)
|
48
|
+
return col[:ascending] if col.is_a? Hash
|
49
|
+
# ascending is the default
|
50
|
+
return true
|
51
|
+
end
|
52
|
+
|
53
|
+
def instance_type_expression(type)
|
54
|
+
self.class.instance_type_expression(type)
|
55
|
+
end
|
56
|
+
def self.instance_type_expression(type)
|
57
|
+
case type
|
58
|
+
when :variant
|
59
|
+
return 'Oinky::variant_cv_t'
|
60
|
+
when :bit
|
61
|
+
return 'bool'
|
62
|
+
when :datetime
|
63
|
+
return 'Oinky::datetime_t'
|
64
|
+
when :string
|
65
|
+
return 'Oinky::db_string'
|
66
|
+
#int
|
67
|
+
when :int8
|
68
|
+
return 'int8_t'
|
69
|
+
when :int16
|
70
|
+
return 'int16_t'
|
71
|
+
when :int32
|
72
|
+
return 'int32_t'
|
73
|
+
when :int64
|
74
|
+
return 'int64_t'
|
75
|
+
#uint
|
76
|
+
when :uint8
|
77
|
+
return 'uint8_t'
|
78
|
+
when :uint16
|
79
|
+
return 'uint16_t'
|
80
|
+
when :uint32
|
81
|
+
return 'uint32_t'
|
82
|
+
when :uint64
|
83
|
+
return 'uint64_t'
|
84
|
+
#float
|
85
|
+
when :float32
|
86
|
+
return 'float32_t'
|
87
|
+
when :float64
|
88
|
+
return 'float64_t'
|
89
|
+
else
|
90
|
+
# This must be a derived type. It should support this method, to
|
91
|
+
# give us an expression we can use in the target language.
|
92
|
+
return type.instance_type(self)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def value_from_variant(src,type)
|
97
|
+
self.class.value_from_variant(src,type)
|
98
|
+
end
|
99
|
+
def self.value_from_variant(src,type)
|
100
|
+
case type
|
101
|
+
when :variant
|
102
|
+
return src
|
103
|
+
when :bit
|
104
|
+
return "#{src}.bit_value()"
|
105
|
+
when :datetime
|
106
|
+
return "#{src}.dt_value()"
|
107
|
+
when :string
|
108
|
+
return "#{src}.string_value()"
|
109
|
+
#int
|
110
|
+
when :int8
|
111
|
+
return "(int8_t) #{src}.int_value()"
|
112
|
+
when :int16
|
113
|
+
return "(int16_t) #{src}.int_value()"
|
114
|
+
when :int32
|
115
|
+
return "(int32_t) #{src}.int_value()"
|
116
|
+
when :int64
|
117
|
+
return "#{src}.int_value()"
|
118
|
+
#uint
|
119
|
+
when :uint8
|
120
|
+
return "(uint8_t) #{src}.uint_value()"
|
121
|
+
when :uint16
|
122
|
+
return "(uint16_t) #{src}.uint_value()"
|
123
|
+
when :uint32
|
124
|
+
return "(uint32_t) #{src}.uint_value()"
|
125
|
+
when :uint64
|
126
|
+
return "#{src}.uint_value()"
|
127
|
+
#float
|
128
|
+
when :float32
|
129
|
+
return "#{src}.f32_value()"
|
130
|
+
when :float64
|
131
|
+
return "#{src}.f64_value()"
|
132
|
+
|
133
|
+
else
|
134
|
+
# This must be a derived type. It should support this method, to
|
135
|
+
# give us an expression we can use in the target language.
|
136
|
+
return type.value_from_variant(src, self)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def type_code(h)
|
141
|
+
self.class.type_code(h)
|
142
|
+
end
|
143
|
+
def self.type_code(h)
|
144
|
+
h = h[:type].to_s
|
145
|
+
h[0] = h[0].upcase
|
146
|
+
return 'column_types::' + h
|
147
|
+
end
|
148
|
+
|
149
|
+
def make_index_name(idef)
|
150
|
+
cppify(idef[:name])
|
151
|
+
end
|
152
|
+
def make_index_handle_name(idef)
|
153
|
+
"_idx_handle__#{make_index_name(idef)}"
|
154
|
+
end
|
155
|
+
def make_table_classname(tn)
|
156
|
+
"CTable_#{cppify(tn)}"
|
157
|
+
end
|
158
|
+
|
159
|
+
def indices(t)
|
160
|
+
t[:indices] || t[:indexes] || []
|
161
|
+
end
|
162
|
+
|
163
|
+
#Generate a string, containing the C++ code for the model classes.
|
164
|
+
def emit
|
165
|
+
p = Oinky::Detail::Builder.new
|
166
|
+
ns = "namespace #{schemaname_to_ns(@schema[:name])}"
|
167
|
+
p.next("#{ns} {", "} //#{ns}") {
|
168
|
+
p << "using namespace Oinky;"
|
169
|
+
p << "using Oinky::Model::TableBase;"
|
170
|
+
p << "using Oinky::Model::SchemaBase;"
|
171
|
+
p << "using Oinky::db_t;"
|
172
|
+
p << ''
|
173
|
+
p << "class Schema;"
|
174
|
+
tables = @schema[:tables]
|
175
|
+
tables.each{|tn, t|
|
176
|
+
# struct_hdr = "struct #{tablename_to_groupname(t[:name])}"
|
177
|
+
# p << struct_hdr
|
178
|
+
# p.next("{", "}; //#{struct_hdr}") {
|
179
|
+
tbl_classname = make_table_classname(tn)
|
180
|
+
p << ''
|
181
|
+
p << "class #{tbl_classname} : public TableBase"
|
182
|
+
p.next("{", "}; //class #{tbl_classname}") {
|
183
|
+
p << "friend class Schema;"
|
184
|
+
p.write("public:", -1)
|
185
|
+
p.next("struct Row {", "}; //struct Row") {
|
186
|
+
maxw = t[:columns].max_by{|k,col| instance_type_expression(col[:type]).length}
|
187
|
+
maxwlen = maxw ? instance_type_expression(maxw[1][:type]).length : 0
|
188
|
+
t[:columns].each{|cn,col|
|
189
|
+
if col[:description]
|
190
|
+
col[:description].split("\n").each{|l|
|
191
|
+
p << ("// " + l)
|
192
|
+
}
|
193
|
+
end
|
194
|
+
# The column name is the default value of the accessor name,
|
195
|
+
# but it can be overridden
|
196
|
+
p << Kernel.sprintf("%-#{maxwlen}s %s;",
|
197
|
+
instance_type_expression(col[:type]),
|
198
|
+
accessor_name(col))
|
199
|
+
}
|
200
|
+
}
|
201
|
+
p << ''
|
202
|
+
p.write("private:", -1)
|
203
|
+
p << "typedef db_t::column_selector_t column_selector_t;"
|
204
|
+
p << 'column_selector_t row_selector;'
|
205
|
+
# index handles
|
206
|
+
indices(t).each {|iname, idef|
|
207
|
+
p << "const db_t::index_handle #{accessor_name(idef)};"
|
208
|
+
}
|
209
|
+
p << ''
|
210
|
+
# update_schema
|
211
|
+
p << 'static void up_schema(db_t::table_handle th)'
|
212
|
+
p.next('{','}') {
|
213
|
+
# Add columns
|
214
|
+
t[:columns].each{|cn, col|
|
215
|
+
p << "TableBase::create_column_if(th, #{cn.inspect}, #{type_code(col)}, #{default_value_expression(col)});"
|
216
|
+
}
|
217
|
+
# Add indices
|
218
|
+
indices(t).each_with_index {|idef,idx|
|
219
|
+
iname, idef = idef
|
220
|
+
# Column definitions
|
221
|
+
names = "idx_column_defs_#{idx}"
|
222
|
+
columns = idef[:columns]
|
223
|
+
p.next("static index_column_def #{names}[] = {", "};") {
|
224
|
+
columns.each_with_index{|col, i|
|
225
|
+
colname = index_column_name(col)
|
226
|
+
ascending = index_column_ascending(col) ? 'true' : 'false'
|
227
|
+
sep = (i == 0 ? '' : ',')
|
228
|
+
p << "#{sep} { db_string(\"#{colname}\"), #{ascending} }"
|
229
|
+
}
|
230
|
+
}
|
231
|
+
unique = idef[:unique] ? 'true' : 'false'
|
232
|
+
p << "TableBase::create_index_if(th, #{iname.inspect}, #{unique}, #{names}, #{names}+#{columns.size} );"
|
233
|
+
}
|
234
|
+
}
|
235
|
+
p << ''
|
236
|
+
p << "template<typename CURSOR>"
|
237
|
+
p.next("void priv_row_from_cursor(Row &row, const CURSOR &crs) {", "}") {
|
238
|
+
# Don't presume that enumeration with index is a consistent
|
239
|
+
# ordering
|
240
|
+
indexed_cols = []
|
241
|
+
p.next("static const char *column_names[] = {","};") {
|
242
|
+
t[:columns].each{|cn, col|
|
243
|
+
p << "#{cn.inspect},"
|
244
|
+
indexed_cols << col
|
245
|
+
}
|
246
|
+
p << 'NULL'
|
247
|
+
}
|
248
|
+
p.next("TableBase::check_init_row_accessor(", "") {
|
249
|
+
p << "row_selector, "
|
250
|
+
p << "column_names,"
|
251
|
+
p << "column_names + #{t[:columns].count}"
|
252
|
+
p << ");"
|
253
|
+
}
|
254
|
+
p.next("variant_cv_t *vals = (variant_cv_t *) ","") {
|
255
|
+
p << "alloca(sizeof(variant_cv_t) * #{t[:columns].count});"
|
256
|
+
}
|
257
|
+
p << "crs.select(row_selector).copy_to(vals, #{t[:columns].count});"
|
258
|
+
p << "// Select values into the target struct"
|
259
|
+
indexed_cols.each_with_index{|col,i|
|
260
|
+
val = value_from_variant("vals[#{i}]", col[:type])
|
261
|
+
p << "row.#{accessor_name(col)} = #{val};"
|
262
|
+
}
|
263
|
+
} # priv_row_from_cursor
|
264
|
+
p.write("public:", -1)
|
265
|
+
# Constructor - initialize index handles.
|
266
|
+
p.next("#{tbl_classname}(db_t::table_handle _th, db_t &db) : TableBase(_th, db)","{}") {
|
267
|
+
indices(t).each {|iname, idef|
|
268
|
+
p << ",#{accessor_name(idef)}(TableBase::get_index_handle(_th, \"#{iname}\"))"
|
269
|
+
}
|
270
|
+
}
|
271
|
+
# Row accessors
|
272
|
+
p << ''
|
273
|
+
p.next("void row_from_cursor(Row &row, const db_t::index_cursor_handle &crs) {", "}") {
|
274
|
+
p << "priv_row_from_cursor(row, crs);"
|
275
|
+
}
|
276
|
+
p.next("void row_from_cursor(Row &row, const db_t::table_cursor_handle &crs) {", "}") {
|
277
|
+
p << "priv_row_from_cursor(row, crs);"
|
278
|
+
}
|
279
|
+
} # class "#{tbl_classname}"
|
280
|
+
} # tables.each {}
|
281
|
+
|
282
|
+
p << ''
|
283
|
+
p << "class Schema : public SchemaBase"
|
284
|
+
p.next("{","}; //class Schema") {
|
285
|
+
p.next("static void up_schema(db_t &db) {","}") {
|
286
|
+
tables.each{|tn, t|
|
287
|
+
tbl_classname = make_table_classname(tn)
|
288
|
+
p << "#{tbl_classname}::up_schema(SchemaBase::get_table_handle(db, #{tn.inspect}));";
|
289
|
+
}
|
290
|
+
}
|
291
|
+
p << ''
|
292
|
+
p.write("public:", -1)
|
293
|
+
# Table accessors
|
294
|
+
tables.each {|tn, tdef|
|
295
|
+
tbl_classname = make_table_classname(tn)
|
296
|
+
p << "const #{tbl_classname} #{accessor_name(tdef)};"
|
297
|
+
}
|
298
|
+
p << ''
|
299
|
+
p.next("Schema(db_t &db) : SchemaBase(db, &up_schema)","{}") {
|
300
|
+
tables.each {|tn, tdef|
|
301
|
+
p << ",#{accessor_name(tdef)}(SchemaBase::get_table_handle(db, #{tn.inspect}), db)"
|
302
|
+
}
|
303
|
+
}
|
304
|
+
}
|
305
|
+
} # namespace <schemaname>
|
306
|
+
return p.format
|
307
|
+
end
|
308
|
+
end #class Cpp
|
309
|
+
end #module Model
|
310
|
+
end
|
311
|
+
|
data/lib/oinky/dsl.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
# We don't strictly need to normalize here. We'll do it
|
2
|
+
# again in the emit phase. However, normalize is also a
|
3
|
+
# syntax checker, so doing it early, as we're building the
|
4
|
+
# schema, makes error discovery more local and easier to diagnose.
|
5
|
+
|
6
|
+
require 'oinky/normalize'
|
7
|
+
|
8
|
+
module Oinky
|
9
|
+
module Model
|
10
|
+
module Internal
|
11
|
+
class IndexBuilder
|
12
|
+
def initialize(name, base = {})
|
13
|
+
@h = {
|
14
|
+
:name=>name.to_s,
|
15
|
+
:columns=>(base[:columns] or []).clone,
|
16
|
+
:unique=>(base[:unique] or false)
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_column(colname, ascending = true)
|
21
|
+
if colname.is_a? Hash
|
22
|
+
h = colname
|
23
|
+
else
|
24
|
+
h = {:name=>colname.to_s, :ascending=>ascending}
|
25
|
+
end
|
26
|
+
@h[:columns] << Oinky::Model.normalize_index_column_def(h)
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_unique(val)
|
30
|
+
@h[:unique] = (val ? true : false)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_h
|
34
|
+
@h
|
35
|
+
end
|
36
|
+
|
37
|
+
# override name
|
38
|
+
def name(newname)
|
39
|
+
@h[:name] = newname.to_s
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class TableBuilder
|
44
|
+
def initialize(base = {})
|
45
|
+
base = {:columns=>{}, :indices=>{}}.merge(base)
|
46
|
+
@h = {:columns=>base[:columns].clone, :indices=>base[:indices].clone}
|
47
|
+
end
|
48
|
+
Oinky::ColumnTypes.each {|ct|
|
49
|
+
define_method(ct) { |colname, h = {}|
|
50
|
+
add_column(colname, ct, h)
|
51
|
+
}
|
52
|
+
}
|
53
|
+
def add_column(colname, type, h = {})
|
54
|
+
v = {:type=>type}.merge(h)
|
55
|
+
k = colname.to_s
|
56
|
+
v = Oinky::Model.normalize_column_def(k,v)
|
57
|
+
@h[:columns][k] = v
|
58
|
+
end
|
59
|
+
def add_index(name, id = {:name=>name, :unique=>true, :columns=>[]})
|
60
|
+
k = name
|
61
|
+
if block_given?
|
62
|
+
s = IndexBuilder.new(name, id)
|
63
|
+
s.instance_eval &Proc.new
|
64
|
+
id = s.to_h
|
65
|
+
k = id[:name]
|
66
|
+
end
|
67
|
+
id = Oinky::Model.normalize_index_def(k,id)
|
68
|
+
id.delete(:name)
|
69
|
+
@h[:indices][k] = id
|
70
|
+
end
|
71
|
+
def to_h
|
72
|
+
@h
|
73
|
+
end
|
74
|
+
|
75
|
+
# override name
|
76
|
+
def name(newname)
|
77
|
+
@h[:name] = newname.to_s
|
78
|
+
end
|
79
|
+
def accessor(a)
|
80
|
+
@h[:accessor] = a.to_s
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class SchemaBuilder
|
85
|
+
def initialize(name, base = {})
|
86
|
+
@h = {}
|
87
|
+
name = name.to_s
|
88
|
+
# clone 2 levels deep. tabledef, not just tableset
|
89
|
+
base.each{|k,v|
|
90
|
+
if v.is_a? Hash
|
91
|
+
|
92
|
+
nv = {}
|
93
|
+
v.each{|k2,v2|
|
94
|
+
nv[k2] = v2.clone
|
95
|
+
}
|
96
|
+
@h[k] = nv
|
97
|
+
else
|
98
|
+
@h[k] = v
|
99
|
+
end
|
100
|
+
}
|
101
|
+
@h[:name] = name
|
102
|
+
@h[:version] ||= 1
|
103
|
+
@h[:tables] ||= {}
|
104
|
+
end
|
105
|
+
|
106
|
+
def create_table(tn, base = {})
|
107
|
+
if tn.is_a? Hash
|
108
|
+
base = tn
|
109
|
+
tn = tn[:name]
|
110
|
+
end
|
111
|
+
tn = tn.to_s
|
112
|
+
t = TableBuilder.new(base)
|
113
|
+
t.name tn
|
114
|
+
if block_given?
|
115
|
+
t.instance_eval &Proc.new
|
116
|
+
end
|
117
|
+
th = t.to_h
|
118
|
+
name = th[:name]
|
119
|
+
th = Oinky::Model.normalize_table_schema(name, th)
|
120
|
+
@h[:tables][name] = th
|
121
|
+
th
|
122
|
+
end
|
123
|
+
|
124
|
+
# override name
|
125
|
+
def name(newname)
|
126
|
+
@h[:name] = newname.to_s
|
127
|
+
end
|
128
|
+
|
129
|
+
def version(v)
|
130
|
+
@h[:version] = v
|
131
|
+
end
|
132
|
+
|
133
|
+
def classname(cn)
|
134
|
+
@h[:classname] = cn
|
135
|
+
end
|
136
|
+
|
137
|
+
def to_h
|
138
|
+
@h
|
139
|
+
end
|
140
|
+
end #module SchemaBuilder
|
141
|
+
end #module Internal
|
142
|
+
|
143
|
+
# Build an entire schema
|
144
|
+
def self.build_schema(name, base = {}, &b)
|
145
|
+
s = Internal::SchemaBuilder.new(name, base)
|
146
|
+
s.instance_eval &b
|
147
|
+
return s.to_h
|
148
|
+
end
|
149
|
+
|
150
|
+
# Build an entire schema, generate a ruby adapter class, and evaluate it
|
151
|
+
def self.build_schema_adapter(name, base = {}, &b)
|
152
|
+
s = build_schema(name, base, &b)
|
153
|
+
cls = eval Oinky::Model.generate(s, :ruby)
|
154
|
+
return cls
|
155
|
+
end
|
156
|
+
|
157
|
+
# Someone may want to create an unbound table-def and then reuse
|
158
|
+
# it later in multiple schemas, or as a base definition for derived
|
159
|
+
# tables.
|
160
|
+
def self.build_table(name, base = {})
|
161
|
+
s = Internal::TableBuilder.new(base)
|
162
|
+
s.name(name)
|
163
|
+
s.instance_eval &Proc.new
|
164
|
+
s.to_h
|
165
|
+
end
|
166
|
+
end # Model
|
167
|
+
end # Oinky
|