oinky 0.1.0
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/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
|