meta-record 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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