meta-record 1.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.
@@ -0,0 +1,364 @@
1
+ require 'metarecord/model'
2
+ require 'metarecord/generator_base'
3
+ require 'metarecord/generators/crails/helpers/validations'
4
+
5
+ class CrailsEditGenerator < GeneratorBase
6
+ def reset
7
+ super
8
+ @required_params = []
9
+ end
10
+
11
+ def generate_for object
12
+ @modelname = object[:name].underscore
13
+ @klassname = get_classname(object)
14
+ reset
15
+ generate_edit_method object
16
+ generate_json_methods object
17
+ generate_parameter_validator object
18
+ generate_relations object
19
+ @src
20
+ end
21
+
22
+ def generate_relations object
23
+ @rendering_has_many = true
24
+ self.instance_eval &object[:block]
25
+ @rendering_has_many = false
26
+ end
27
+
28
+ def generate_edit_method object
29
+ _append "void #{@klassname}::edit(Data data)"
30
+ _append "{"
31
+ @indent += 1
32
+ self.instance_eval &object[:block]
33
+ @indent -= 1
34
+ _append "}\n"
35
+ end
36
+
37
+ def generate_json_methods object
38
+ @rendering_to_json = true
39
+ _append "std::string #{@klassname}::to_json() const"
40
+ _append "{"
41
+ @indent += 1
42
+ _append "DataTree data;\n"
43
+ self.instance_eval &object[:block]
44
+ _append "return data.to_json();"
45
+ @indent -= 1
46
+ _append "}\n"
47
+ @rendering_to_json = false
48
+ end
49
+
50
+ def generate_parameter_validator object
51
+ _append "std::vector<std::string> #{@klassname}::find_missing_parameters(Data data) const"
52
+ _append "{"
53
+ @indent += 1
54
+ if @required_params.size > 0
55
+ _append "return data.find_missing_keys({#{@required_params.join ','}});"
56
+ else
57
+ _append "return {};"
58
+ end
59
+ @indent -= 1
60
+ _append "}"
61
+
62
+ _append "bool #{@klassname}::is_valid()"
63
+ _append "{"
64
+ @indent += 1
65
+ _append "errors.clear();"
66
+ @rendering_validations = true
67
+ self.instance_eval &object[:block]
68
+ @rendering_validations = false
69
+ _append "return errors.as_data().get_keys().size() == 0;"
70
+ @indent -= 1
71
+ _append "}"
72
+ end
73
+
74
+ def resource_name name
75
+ if @rendering_has_many
76
+ _append "const std::string #{@klassname}::scope = #{name.to_s.inspect};"
77
+ _append "const std::string #{@klassname}::plural_scope = #{(get_pluralized_name name.to_s).inspect};"
78
+ _append "const std::string #{@klassname}::view = \"${view-placeholder}/data/#{@modelname}\";"
79
+ end
80
+ end
81
+
82
+ def rendering_edit?
83
+ not ([@rendering_has_many, @rendering_validations, @rendering_to_json].include? true)
84
+ end
85
+
86
+ def property type, name, options = {}
87
+ if @rendering_validations && !options[:validate].nil?
88
+ validation type, name, options[:validate]
89
+ elsif @rendering_to_json
90
+ case type
91
+ when "DataTree" then _append "data[\"#{name}\"].merge(#{name});"
92
+ when "LocaleString" then _append "data[\"#{name}\"] = #{name}.to_string();"
93
+ else _append "data[\"#{name}\"] = #{name};"
94
+ end
95
+ elsif options[:read_only] != true && rendering_edit?
96
+ setter = "set_#{name}(data[\"#{name}\"]);"
97
+ setter = "set_#{name}(data[\"#{name}\"].as<int>());" if not (type =~ /^(unsigned)?\s*char$/).nil?
98
+ if options[:required] == true
99
+ @required_params.push "\"#{name}\""
100
+ _append setter
101
+ else
102
+ _append "if (data[\"#{name}\"].exists())"
103
+ @indent += 1
104
+ _append setter
105
+ @indent -= 1
106
+ end
107
+ end
108
+ end
109
+
110
+ def validation type, name, data
111
+ if !data[:min].nil?
112
+ _append validate_number_min name, data[:min]
113
+ end
114
+ if !data[:max].nil?
115
+ _append validate_number_max name, data[:max]
116
+ end
117
+ if data[:required] == true
118
+ _append validate_required type, name
119
+ end
120
+ if data[:self_reference] == true
121
+ _append validate_self_reference type, name
122
+ end
123
+ if data[:uniqueness] == true
124
+ puts "/!\\ WARNING: uniqueness validations not available for crails generators"
125
+ #_append validate_uniqueness type, name
126
+ end
127
+ end
128
+
129
+ def has_one_getter type, name, options
130
+ type = get_type type
131
+ tptr = ptr_type type
132
+ _append "#{tptr} #{@klassname}::get_#{name}() const"
133
+ _append "{"
134
+ _append " auto& database = *#{GeneratorBase.odb_connection[:object]}::instance;"
135
+ _append " #{tptr} result;\n"
136
+ _append " database.find_one(result, #{name}_id);"
137
+ _append " return result;"
138
+ _append "}\n"
139
+ end
140
+
141
+ def has_one_setter type, name, options
142
+ type = get_type type
143
+ tptr = ptr_type type
144
+ _append "void #{@klassname}::set_#{name}(#{tptr} v)"
145
+ _append "{"
146
+ _append " #{name}_id = v != nullptr ? v->get_id() : #{null_id};"
147
+ _append "}\n"
148
+ end
149
+
150
+ def joined_has_one_edit type, name, options
151
+ tptr = ptr_type type
152
+ data_id = "data[\"#{name}_id\"]"
153
+ _append "{"
154
+ _append " if (#{data_id} == #{null_id})"
155
+ _append " set_#{name}(nullptr);"
156
+ _append " else if (!get_#{name}() || #{data_id} != get_#{name}()->get_id())"
157
+ _append " {"
158
+ _append " auto& database = *#{GeneratorBase.odb_connection[:object]}::instance;"
159
+ _append " #{tptr} linked_resource;"
160
+ _append " database.find_one(linked_resource, data[\"#{name}_id\"].as<#{id_type}>());"
161
+ _append " set_#{name}(linked_resource);"
162
+ _append " }"
163
+ _append "}"
164
+ end
165
+
166
+ def has_one type, name, options = {}
167
+ type = get_type type
168
+ tptr = ptr_type type
169
+ if @rendering_has_many
170
+ if options[:joined] == false
171
+ has_one_getter type, name, options
172
+ has_one_setter type, name, options
173
+ else
174
+ _append "#{id_type} #{@klassname}::get_#{name}_id() const"
175
+ _append "{"
176
+ _append " return #{name} ? #{name}->get_id() : #{null_id};"
177
+ _append "}\n"
178
+ end
179
+ elsif @rendering_validations
180
+ if not options[:validate].nil?
181
+ if options[:joined] == false
182
+ validation "#{id_type}", "#{name}_id", options[:validate]
183
+ else
184
+ validation tptr, name, options[:validate]
185
+ end
186
+ end
187
+ elsif @rendering_to_json
188
+ if options[:joined] == false
189
+ _append "data[\"#{name}_id\"] = get_#{name}_id();"
190
+ else
191
+ _append "if (#{name} != nullptr)"
192
+ _append " data[\"#{name}_id\"] = get_#{name}_id();"
193
+ end
194
+ elsif options[:read_only] != true
195
+ data_id = "data[\"#{name}_id\"]"
196
+ _append "if (#{data_id}.exists())"
197
+ if options[:joined] != false
198
+ joined_has_one_edit type, name, options
199
+ else
200
+ _append "set_#{name}_id(data[\"#{name}_id\"]);"
201
+ end
202
+ end
203
+ end
204
+
205
+ def has_many type, name, options = {}
206
+ type = get_type type
207
+ if @rendering_has_many
208
+ if options[:joined] != false
209
+ _join_based_has_many type, name, options
210
+ else
211
+ _id_based_has_many type, name, options
212
+ end
213
+ _append "void #{@klassname}::collect_#{name}(std::map<#{id_type}, #{ptr_type type}>& results)"
214
+ _append "{"
215
+ @indent += 1
216
+ _append "for (auto model : get_#{name}())"
217
+ _append "{"
218
+ @indent += 1
219
+ _append "if (results.find(model->get_id()) == results.end())"
220
+ _append " results[model->get_id()] = model;"
221
+ @indent -= 1
222
+ _append "}"
223
+ @indent -= 1
224
+ _append "}"
225
+ elsif @rendering_validations
226
+ elsif @rendering_to_json
227
+ _append "data[\"#{get_singular_name name}_ids\"].from_vector<#{id_type}>(get_#{get_singular_name name}_ids());"
228
+ elsif options[:read_only] != true
229
+ data = "data[\"#{get_singular_name name}_ids\"]"
230
+ _append "if (#{data}.exists())"
231
+ _append " update_#{name}(#{data});"
232
+ end
233
+ end
234
+
235
+ def _join_based_has_many type, name, options
236
+ tptr = ptr_type type
237
+ list_type = "std::list<#{tptr} >"
238
+ singular_name = get_singular_name name
239
+ _append "std::vector<#{id_type}> #{@klassname}::get_#{singular_name}_ids() const"
240
+ _append "{"
241
+ @indent += 1
242
+ _append "return collect_ids_from(get_#{name}());"
243
+ @indent -= 1
244
+ _append "}\n"
245
+
246
+ _append "bool #{@klassname}::update_#{name}(Data ids)"
247
+ _append "{"
248
+ @indent += 1
249
+ _append "return update_id_list<#{type}>(#{name}, ids);"
250
+ @indent -= 1
251
+ _append "}\n"
252
+
253
+ _append "void #{@klassname}::add_#{singular_name}(#{tptr} resource)"
254
+ _append "{"
255
+ @indent += 1
256
+ _append "remove_#{singular_name}(*resource);"
257
+ _append "#{name}.push_back(resource);"
258
+ @indent -= 1
259
+ _append "}\n"
260
+
261
+ _append "void #{@klassname}::remove_#{singular_name}(const #{type}& resource)"
262
+ _append "{"
263
+ @indent += 1
264
+ _append "#{name}.remove_if([&resource](#{tptr} comp)"
265
+ _append "{"
266
+ @indent += 1
267
+ _append "return comp->get_id() == resource.get_id();"
268
+ @indent -= 1
269
+ _append "});"
270
+ @indent -= 1
271
+ _append "}\n"
272
+ end
273
+
274
+ def _id_based_has_many type, name, options
275
+ tptr = ptr_type type
276
+ singular_name = get_singular_name name
277
+
278
+ _append "bool #{@klassname}::update_#{name}(Data ids)"
279
+ _append "{"
280
+ @indent += 1
281
+ _append "return update_id_list<#{type}>(this->#{singular_name}_ids, ids);"
282
+ @indent -= 1
283
+ _append "}\n"
284
+
285
+ _append "void #{@klassname}::add_#{singular_name}(#{tptr} v)"
286
+ _append "{"
287
+ @indent += 1
288
+ _append "remove_#{singular_name}(*v);"
289
+ _append "#{singular_name}_ids.push_back(v->get_id());"
290
+ @indent -= 1
291
+ _append "}\n"
292
+
293
+ _append "void #{@klassname}::remove_#{singular_name}(const #{type}& v)"
294
+ _append "{"
295
+ @indent += 1
296
+ _append "auto it = remove_if(#{singular_name}_ids.begin(), #{singular_name}_ids.end(), [v](#{id_type} id)"
297
+ _append "{"
298
+ @indent += 1
299
+ _append "return id == v.get_id();"
300
+ @indent -= 1
301
+ _append "});"
302
+ _append "if (it != #{singular_name}_ids.end())"
303
+ @indent += 1
304
+ _append "#{singular_name}_ids.erase(it);"
305
+ @indent -= 2
306
+ _append "}\n"
307
+
308
+ _append "const std::list<#{tptr} >& #{@klassname}::get_#{name}()"
309
+ _append "{"
310
+ @indent += 1
311
+ _append "if (!#{name}_fetched)"
312
+ @indent += 1
313
+ _append "fetch_#{name}();"
314
+ @indent -= 1
315
+ _append "return #{name};"
316
+ @indent -= 1
317
+ _append "}\n"
318
+
319
+ has_many_fetch type, name, options
320
+ end
321
+
322
+ def has_many_fetch type, name, options
323
+ singular_name = get_singular_name name
324
+
325
+ _append "void #{@klassname}::fetch_#{name}()"
326
+ _append "{"
327
+ @indent += 1
328
+ _append "typedef odb::pgsql::query<#{type}> query;"
329
+ _append "auto& database = *#{GeneratorBase.odb_connection[:object]}::instance;"
330
+ _append "odb::result<#{type}> results;"
331
+ _append "#{name}.clear();"
332
+ _append "database.find<#{type}>(results,"
333
+ _append " query::id + \"=\" + ODB::any(#{singular_name}_ids, \"int\")"
334
+ _append ");"
335
+ _append "for (auto model : results)"
336
+ _append " #{name}.push_back(std::make_shared<#{type}>(model));"
337
+ _append "#{name}_fetched = true;"
338
+ @indent -= 1
339
+ _append "}\n"
340
+ end
341
+
342
+ class << self
343
+ def extension ; ".cpp" ; end
344
+
345
+ def generate_includes
346
+ source = "#include <crails/odb/helpers.hpp>\n"
347
+ source += "#include <crails/odb/any.hpp>\n"
348
+ source += "#include <#{GeneratorBase.odb_connection[:include]}>\n"
349
+ source += "#include \"lib/odb/application-odb.hxx\"\n"
350
+ end
351
+
352
+ def make_file filename, data
353
+ base = "lib/" + filename[0...-3]
354
+ include = base + ".hpp"
355
+ data[:bodies].each do |body|
356
+ body.gsub! "${view-placeholder}", base.split("/")[0...-2].join("/")
357
+ end
358
+ source = "#include \"#{include}\"\n"
359
+ source += generate_includes
360
+ source += (collect_includes_for filename).join "\n"
361
+ source += "\n\n" + (data[:bodies].join "\n")
362
+ end
363
+ end
364
+ end
@@ -0,0 +1,77 @@
1
+ def validate_number_min name, min
2
+ code = <<CPP
3
+ if (#{name} < #{min})
4
+ {
5
+ errors["#{name}"]["t"] = "validate.min";
6
+ errors["#{name}"]["params"]["min"] = #{min};
7
+ errors["#{name}"]["params"]["val"] = #{name};
8
+ }
9
+ CPP
10
+ end
11
+
12
+ def validate_number_max name, max
13
+ code = <<CPP
14
+ if (#{name} < #{max})
15
+ {
16
+ errors["#{name}"]["t"] = "validate.max";
17
+ errors["#{name}"]["params"]["max"] = #{min};
18
+ errors["#{name}"]["params"]["val"] = #{name};
19
+ }
20
+ CPP
21
+ end
22
+
23
+ def validate_uniqueness source_type, name
24
+ if source_type.start_with?("std::shared_ptr")
25
+ type = source_type[16..-2]
26
+ <<CPP
27
+ {
28
+ auto& database = *#{GeneratorBase.odb_connection[:object]}::instance;
29
+ odb::result<#{source_type}> results;
30
+
31
+ database.find(results, odb::query<#{source_type}::#{name}->id == get_#{name}_id());
32
+ if (results.size() > 0)
33
+ errors["#{name}"]["t"] = "validate.uniqueness";
34
+ }
35
+ CPP
36
+ else
37
+ <<CPP
38
+ {
39
+ auto& database = *#{GeneratorBase.odb_connection[:object]}::instance;
40
+ odb::result<#{source_type}> results;
41
+
42
+ database.find(results, odb::query<#{source_type}::#{name} == #{name});
43
+ if (results.size() > 0)
44
+ errors["#{name}"]["t"] = "validate.uniqueness";
45
+ }
46
+ CPP
47
+ end
48
+ end
49
+
50
+ def validate_required type, name
51
+ if type == "std::string"
52
+ <<CPP
53
+ if (#{name} == "")
54
+ errors["#{name}"]["t"] = "validate.required";
55
+ CPP
56
+ elsif type.start_with?("std::shared_ptr")
57
+ <<CPP
58
+ if (#{name} == nullptr)
59
+ errors["#{name}_id"]["t"] = "validate.required";
60
+ CPP
61
+ elsif type == "ODB::id_type"
62
+ <<CPP
63
+ if (#{name} == 0)
64
+ errors["#{name}"]["t"] = "validate.required";
65
+ CPP
66
+ else
67
+ ""
68
+ end
69
+ end
70
+
71
+ def validate_self_reference type, name
72
+ raw_ptr_type = type.gsub /std::shared_ptr<(.*)>/, '\1*'
73
+ <<CPP
74
+ if (#{name} != nullptr && #{name}->get_id() == static_cast<#{raw_ptr_type}>(this)->get_id())
75
+ errors["#{name}"]["t"] = "validate.self-reference";
76
+ CPP
77
+ end
@@ -0,0 +1,88 @@
1
+ require 'metarecord/model'
2
+ require 'metarecord/generator_base'
3
+ require 'metarecord/generators/crails/helpers/validations'
4
+
5
+ class CrailsQueryGenerator < GeneratorBase
6
+ def reset
7
+ super
8
+ @required_params = []
9
+ end
10
+
11
+ def generate_for object
12
+ @headerfile = object[:header]
13
+ unless @headerfile.nil?
14
+ @modelname = object[:name].underscore
15
+ @klassname = get_classname(object)
16
+ @finalklass = object[:classname]
17
+ reset
18
+ self.instance_eval &object[:block]
19
+ "# include \"#{@headerfile}\"\n" + @src
20
+ else
21
+ @src
22
+ end
23
+ end
24
+
25
+ def resource_name name
26
+ end
27
+
28
+ def property type, name, options = {}
29
+ end
30
+
31
+ def validation type, name, data
32
+ end
33
+
34
+ def has_one type, name, options = {}
35
+ end
36
+
37
+ def has_many type, name, options = {}
38
+ type = get_type type
39
+ if options[:joined] != false
40
+ _join_based_has_many type, name, options
41
+ end
42
+ end
43
+
44
+ def _join_based_has_many type, name, options
45
+ tptr = ptr_type type
46
+ list_type = "std::list<#{tptr} >"
47
+ singular_name = get_singular_name name
48
+
49
+ outer_name = (name.split(/-|_|\s/).each do |i| i.capitalize! end).join
50
+ query_object = "#{@finalklass.split("::").join}By#{outer_name}"
51
+
52
+ _append <<CPP
53
+ #pragma db view pointer(std::shared_ptr) object(#{type}) object(#{@finalklass} inner)
54
+ struct #{query_object}
55
+ {
56
+ std::string get_database_name() const { return #{type}().get_database_name(); }
57
+ #{id_type} get_id() const { return self ? self->get_id() : #{null_id}; }
58
+ std::shared_ptr<#{@finalklass}> self;
59
+ operator #{@finalklass}() const { return *self; }
60
+
61
+ #pragma db view pointer(std::shared_ptr) object(#{type}) object(#{@finalklass} inner)
62
+ struct Count
63
+ {
64
+ #pragma db column("count(" + #{@finalklass}::id + ")")
65
+ size_t value;
66
+ };
67
+ };
68
+ CPP
69
+ end
70
+
71
+ class << self
72
+ def extension ; ".queries.hpp" ; end
73
+
74
+ def make_file filename, data
75
+ base = "lib/" + filename[0...-3]
76
+ include = base + ".hpp"
77
+ file_define = "_#{filename[0...-3].upcase.gsub "/", "_"}_QUERIES_HPP"
78
+ source = <<CPP
79
+ #ifndef #{file_define}
80
+ # define #{file_define}
81
+ #{(collect_includes_for filename).join "\n"}
82
+ #{data[:bodies].join "\n"}
83
+ #endif
84
+ CPP
85
+ end
86
+ end
87
+ end
88
+
@@ -0,0 +1,80 @@
1
+ require 'metarecord/model'
2
+ require 'metarecord/generator_base'
3
+
4
+ class CrailsViewGenerator < GeneratorBase
5
+ def self.is_file_based? ; false ; end
6
+
7
+ def reset
8
+ @src = ""
9
+ @declarations = []
10
+ super
11
+ end
12
+
13
+ def should_generate_for object
14
+ (not object[:header].nil?) && (not object[:classname].nil?)
15
+ end
16
+
17
+ def generate_for object
18
+ reset
19
+ @declarations << "#include \"#{object[:header]}\"\n"
20
+ @declarations << "#{object[:classname]}& @model;"
21
+ property id_type, "id"
22
+ self.instance_eval &object[:block]
23
+ @src
24
+ end
25
+
26
+ def get_headers
27
+ @declarations.join "\n"
28
+ end
29
+
30
+ def property type, name, options = {}
31
+ return if (not options[:client].nil?) && options[:client][:ignore] == true
32
+ if type == "DataTree" || type == "LocaleString"
33
+ _append "json(#{name.inspect}, model.get_#{name}().as_data());"
34
+ else
35
+ _append "json(#{name.inspect}, model.get_#{name}());"
36
+ end
37
+ end
38
+
39
+ def has_one type, name, options = {}
40
+ return if (not options[:client].nil?) && options[:client][:ignore] == true
41
+ if options[:joined] == false
42
+ _append "if (model.get_#{name}_id() != 0)"
43
+ _append " json(\"#{name}_id\", model.get_#{name}_id());"
44
+ else
45
+ _append "if (model.get_#{name}())"
46
+ _append " json(\"#{name}_id\", model.get_#{name}()->get_id());"
47
+ end
48
+ end
49
+
50
+ def has_many type, name, options = {}
51
+ return if (not options[:client].nil?) && options[:client][:ignore] == true
52
+ _append "{"
53
+ @indent += 1
54
+ id_attr = (get_singular_name name) + "_ids"
55
+ id_collector = if options[:joined] == false
56
+ "model.get_#{id_attr}()"
57
+ else
58
+ "collect_ids_from(model.get_#{name}())"
59
+ end
60
+ _append "const auto relation_ids = #{id_collector};"
61
+ _append "json_array(#{id_attr.inspect}, relation_ids.begin(), relation_ids.end());"
62
+ @indent -= 1
63
+ _append "}"
64
+ end
65
+
66
+ class << self
67
+ def extension ; ".cjson" ; end
68
+
69
+ def make_file filename, data
70
+ source = ""
71
+ source += "#include <crails/odb/helpers.hpp>\n"
72
+ #source += ((Includes.list[filename] || []).collect {|a| "#include \"#{a}\""}).join "\n"
73
+ source += (collect_includes_for filename).join("\n")
74
+ source += "\n" + (data[:headers].join "\n")
75
+ source += "\n// END LINKING\n"
76
+ source += "\n" + (data[:bodies].join "\n")
77
+ source
78
+ end
79
+ end
80
+ end