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,153 @@
1
+ require 'metarecord/generators/crails/edit_generator'
2
+
3
+ class CometEditGenerator < CrailsEditGenerator
4
+ def _append_macro str
5
+ @src ||= ""
6
+ @src += str + "\n"
7
+ end
8
+
9
+ def generate_json_methods object
10
+ super
11
+ @rendering_from_json = true
12
+ _append_macro "#ifdef #{CometDataGenerator.client_define}"
13
+ _append "void #{@klassname}::from_json(Data data)"
14
+ _append "{"
15
+ @indent += 1
16
+ _append "id = data[\"id\"].defaults_to<#{id_type}>(#{null_id});"
17
+ _append "edit(data);"
18
+ @indent -= 1
19
+ _append "}"
20
+ _append_macro "#endif"
21
+ @rendering_from_json = false
22
+ end
23
+
24
+ def validation type, name, data
25
+ if data[:uniqueness] == true
26
+ _append_macro "#ifndef #{CometDataGenerator.client_define}"
27
+ _append validate_uniqueness type, name
28
+ _append_macro "#endif"
29
+ data[:uniqueness] = false
30
+ end
31
+ super type, name, data
32
+ end
33
+
34
+ def has_one_getter type, name, options
35
+ type = get_type type
36
+ tptr = ptr_type type
37
+ _append_macro "#ifndef #{CometDataGenerator.client_define}"
38
+ super
39
+ _append_macro "#else"
40
+ _append "#{tptr} #{@klassname}::get_#{name}() const"
41
+ _append "{"
42
+ _append " #{tptr} model = std::make_shared<#{type}>();"
43
+ _append " model->set_id(get_#{name}_id());"
44
+ _append " model->fetch();"
45
+ _append " return model;"
46
+ _append "}"
47
+ _append_macro "#endif"
48
+ end
49
+
50
+ def joined_has_one_edit type, name, options
51
+ data_id = "data[\"#{name}_id\"]"
52
+ inline_data = "data[\"#{name}\"]"
53
+ _append_macro "#ifndef #{CometDataGenerator.client_define}"
54
+ super
55
+ _append_macro "#else"
56
+ _append "{"
57
+ _append " if (#{data_id} == 0)"
58
+ _append " set_#{name}(nullptr);"
59
+ _append " else if (!get_#{name}() || #{data_id} != get_#{name}()->get_id())"
60
+ _append " {"
61
+ _append " auto linked_resource = std::make_shared<#{type}>();"
62
+ _append " linked_resource->set_id(#{data_id}.as<#{id_type}>());"
63
+ _append " set_#{name}(linked_resource);"
64
+ _append " }"
65
+ _append "}"
66
+ _append "else if (#{inline_data}.exists())"
67
+ _append "{"
68
+ _append " auto linked_resource = std::make_shared<#{type}>();"
69
+ _append " linked_resource->from_json(#{inline_data});"
70
+ _append " set_#{name}(linked_resource);"
71
+ _append "}"
72
+ _append_macro "#endif"
73
+ end
74
+
75
+ def has_many_fetch type, name, options
76
+ tptr = ptr_type type
77
+ singular_name = get_singular_name name
78
+
79
+ _append_macro "#ifndef #{CometDataGenerator.client_define}"
80
+ super type, name, options
81
+ _append_macro "#else"
82
+ _append "Comet::Promise #{@klassname}::fetch_#{name}()"
83
+ _append "{"
84
+ @indent += 1
85
+ _append "std::vector<Comet::Promise> promises;\n"
86
+ _append "for (auto id : #{singular_name}_ids)"
87
+ _append "{"
88
+ @indent += 1
89
+ _append "#{tptr} model;\n"
90
+ _append "model->set_id(id);"
91
+ _append "promises.push_back(model->fetch());"
92
+ _append "#{name}.push_back(model);"
93
+ _append "#{name}_fetched = true;"
94
+ @indent -= 1
95
+ _append "}"
96
+ _append "return Comet::Promise::all(promises);"
97
+ @indent -= 1
98
+ _append "}"
99
+ _append_macro "#endif"
100
+ end
101
+
102
+ def property type, name, options = {}
103
+ if options[:ready_only] == true && rendering_edit?
104
+ options[:read_only] = false
105
+ _append_macro "#ifdef #{CometDataGenerator.client_define}"
106
+ super
107
+ _append_macro "#endif"
108
+ else
109
+ super
110
+ end
111
+ end
112
+
113
+ def has_many type, name, options = {}
114
+ if options[:read_only] == true && rendering_edit?
115
+ options[:read_only] = false
116
+ _append_macro "#ifdef #{CometDataGenerator.client_define}"
117
+ super
118
+ _append_macro "#endif"
119
+ else
120
+ super
121
+ end
122
+ end
123
+
124
+ def has_one type, name, options = {}
125
+ if options[:read_only] == true && rendering_edit?
126
+ options[:read_only] = false
127
+ _append_macro "#ifdef #{CometDataGenerator.client_define}"
128
+ super
129
+ _append_macro "#endif"
130
+ else
131
+ super
132
+ end
133
+ end
134
+
135
+ class << self
136
+ def generate_includes
137
+ <<CPP
138
+ #ifndef #{CometDataGenerator.client_define}
139
+ #{super}
140
+ #else
141
+ # include <crails/comet/mvc/helpers.hpp>
142
+ #endif
143
+ CPP
144
+ end
145
+
146
+ def sourcefile_to_destfile sourcefile
147
+ base = super sourcefile
148
+ basepath = Pathname.new base
149
+ parentdir = basepath.dirname.to_s + "/shared"
150
+ "#{parentdir}/#{basepath.basename}"
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,289 @@
1
+ require 'metarecord/model'
2
+ require 'metarecord/generator_base'
3
+
4
+ class CrailsDataGenerator < GeneratorBase
5
+ attr_accessor :forward_declarations
6
+
7
+ def reset
8
+ super
9
+ @forward_declarations = []
10
+ @src = {}
11
+ end
12
+
13
+ def _append str, opts = {}
14
+ @src[@current_visibility] ||= ""
15
+ @src[@current_visibility] += " " * (@indent * @tab_size)
16
+ @src[@current_visibility] += str + "\n" unless opts[:no_line_return]
17
+ end
18
+
19
+ def _append_macro str
20
+ @src[@current_visibility] ||= ""
21
+ @src[@current_visibility] += str + "\n"
22
+ end
23
+
24
+ def get_headers
25
+ forward_source = ""
26
+ @forward_declarations.each do |type|
27
+ parts = type.split('::').reject {|part| part.empty?}
28
+ last_part = parts.last
29
+ if parts.length > 1
30
+ parts[0...parts.size-1].each do |part|
31
+ forward_source += "namespace #{part} { "
32
+ end
33
+ forward_source += "class #{last_part};"
34
+ (parts.size - 1).times do forward_source += " } " end
35
+ forward_source += "\n"
36
+ else
37
+ forward_source += "class #{last_part};\n"
38
+ end
39
+ end
40
+ forward_source
41
+ end
42
+
43
+ def generate_class_head object
44
+ _append "#pragma db object abstract"
45
+ _append "struct #{object[:name]}"
46
+ end
47
+
48
+ def generate_for object
49
+ reset
50
+ @forward_declarations << object[:classname] unless object[:classname].nil?
51
+ @current_visibility = :_preblock
52
+ @indent += 1
53
+ generate_class_head object
54
+ _append "{"
55
+ @indent += 1
56
+ _append "friend class odb::access;"
57
+ _append "#pragma db transient"
58
+ _append "DataTree errors;"
59
+ @current_visibility = :public
60
+ edit_resource_declaration
61
+ prepare_order_by
62
+ self.instance_eval &object[:block]
63
+ @current_visibility = :_postblock
64
+ @indent -= 1
65
+ _append "};"
66
+ @indent -= 1
67
+ render
68
+ end
69
+
70
+ def edit_resource_declaration
71
+ _append "virtual std::vector<std::string> find_missing_parameters(Data) const;"
72
+ _append "virtual void edit(Data);"
73
+ _append "virtual std::string to_json() const;"
74
+ _append "virtual bool is_valid();"
75
+ _append "virtual void on_dependent_destroy(#{id_type});"
76
+ _append ""
77
+ end
78
+
79
+ def prepare_order_by
80
+ _append "template<typename QUERY>"
81
+ _append "static QUERY default_order_by(QUERY query)"
82
+ _append "{"
83
+ _append " return query; // order_by"
84
+ _append "}\n"
85
+ end
86
+
87
+ def render
88
+ @current_visibility = :_result
89
+ result = @src[:_preblock]
90
+ [:public, :protected, :private].each do |key|
91
+ unless @src[key].nil?
92
+ result += " #{key}:\n"
93
+ result += @src[key]
94
+ end
95
+ end
96
+ result += @src[:_postblock]
97
+ result
98
+ end
99
+
100
+ def visibility value
101
+ @current_visibility = value
102
+ end
103
+
104
+ def with_visibility value, &block
105
+ old_visibility = @current_visibility
106
+ visibility value
107
+ self.instance_eval &block
108
+ visibility old_visibility
109
+ end
110
+
111
+ def resource_name name
112
+ visibility :public
113
+ _append "static const std::string scope;"
114
+ _append "static const std::string plural_scope;"
115
+ _append "static const std::string view;"
116
+ end
117
+
118
+ def order_by name, flow = nil
119
+ src = "return query + \"ORDER BY\" + QUERY::#{name}"
120
+ src += " + \"#{flow.to_s.upcase}\"" unless flow.nil?
121
+ src += ";"
122
+ @src[:public] = @src[:public].gsub /^(\s*).*\/\/ order_by$/, '\1' + src
123
+ end
124
+
125
+ def datatree_property name, options
126
+ with_visibility :public do
127
+ virt = if options[:allow_override] == true then "virtual " else "" end
128
+ _append "#{virt}DataTree& get_#{name}() { return #{name}; }"
129
+ _append "#{virt}const DataTree& get_#{name}() const { return #{name}; }"
130
+ _append "#{virt}void set_#{name}(Data value) { this->#{name}.clear(); this->#{name}.as_data().merge(value); }"
131
+ end
132
+ end
133
+
134
+ def property type, name, options = {}
135
+ if type == 'DataTree'
136
+ datatree_property name, options
137
+ else
138
+ with_visibility :public do
139
+ virt = if options[:allow_override] == true then "virtual " else "" end
140
+ _append "#{virt}#{type} get_#{name}() const { return #{name}; }"
141
+ _append "#{virt}void set_#{name}(#{type} value) { this->#{name} = value; }"
142
+ end
143
+ end
144
+ make_pragma_db options[:db] unless options[:db].nil?
145
+ if options[:default].nil?
146
+ _append "#{type} #{name};"
147
+ else
148
+ _append "#{type} #{name} = #{get_value options[:default]};"
149
+ end
150
+ end
151
+
152
+ def make_pragma_db options, authorized_options = []
153
+ is_option_available = lambda {|a|
154
+ (not options[a].nil?) && (authorized_options.size == 0 || authorized_options.include?(a))
155
+ }
156
+ src = ""
157
+ src += "transient " if is_option_available.call(:transient) && options[:transient] == true
158
+ src += "type(\"#{options[:type]}\") " if is_option_available.call :type
159
+ src += "column(\"#{options[:column]}\") " if is_option_available.call :column
160
+ src += "default(#{get_value options[:default]}) " if is_option_available.call :default
161
+ src += "null " if is_option_available.call(:null) && options[:null] == true
162
+ _append "#pragma db #{src}" if src.size > 0
163
+ end
164
+
165
+ def has_one type, name, options = {}
166
+ @forward_declarations << type
167
+ type = get_type type
168
+ tptr = ptr_type type
169
+ virt = if options[:allow_override] == true then "virtual " else "" end
170
+ if options[:joined] != false
171
+ with_visibility :public do
172
+ _append "#{virt}#{tptr} get_#{name}() const { return #{name}; }"
173
+ _append "#{virt}void set_#{name}(#{tptr} v) { this->#{name} = v; }"
174
+ _append "#{virt}#{id_type} get_#{name}_id() const;"
175
+ end
176
+ _append "#{tptr} #{name};"
177
+ else
178
+ with_visibility :public do
179
+ _append "#{virt}#{tptr} get_#{name}() const;"
180
+ _append "#{virt}void set_#{name}(#{tptr} v);"
181
+ _append "#{id_type} get_#{name}_id() const { return #{name}_id; }"
182
+ _append "void set_#{name}_id(#{id_type} v) { #{name}_id = v; }"
183
+ end
184
+ make_pragma_db options[:db] unless options[:db].nil?
185
+ _append "#{id_type} #{name}_id = #{null_id};"
186
+ end
187
+ with_visibility :public do
188
+ _append <<CPP
189
+
190
+ template<typename ARRAY>
191
+ static void collect_#{name}(ARRAY& array, std::map<#{id_type}, #{tptr} >& results)
192
+ {
193
+ for (auto model : array)
194
+ {
195
+ if (results.find(model.get_#{name}_id()) == results.end())
196
+ results[model.get_#{name}_id()] = model.get_#{name}();
197
+ }
198
+ }
199
+
200
+ CPP
201
+ end
202
+ end
203
+
204
+ def has_many type, name, options = {}
205
+ @forward_declarations << type
206
+ type = get_type type
207
+ tptr = ptr_type type
208
+ list_type = "std::list<#{tptr} >"
209
+ singular_name = get_singular_name name
210
+ with_visibility :public do
211
+ virt = if options[:allow_override] == true then "virtual " else "" end
212
+ _append "#{virt}bool update_#{name}(Data);"
213
+ _append "#{virt}void add_#{singular_name}(#{tptr});"
214
+ _append "#{virt}void remove_#{singular_name}(const #{type}&);"
215
+ _append "#{virt}void collect_#{name}(std::map<#{id_type}, #{tptr} >&);"
216
+ end
217
+ if options[:joined] != false
218
+ _join_based_has_many list_type, name, options
219
+ else
220
+ _id_based_has_many list_type, name, options
221
+ end
222
+ end
223
+
224
+ def _join_based_has_many list_type, name, options
225
+ singular_name = get_singular_name name
226
+ with_visibility :public do
227
+ _append "const #{list_type}& get_#{name}() const { return #{name}; }"
228
+ _append "std::vector<#{id_type}> get_#{singular_name}_ids() const;"
229
+ end
230
+ _append "#{list_type} #{name};"
231
+ end
232
+
233
+ def has_many_fetch list_type, name, options
234
+ with_visibility :public do
235
+ _append "void fetch_#{name}();"
236
+ end
237
+ end
238
+
239
+ def _id_based_has_many list_type, name, options
240
+ singular_name = get_singular_name name
241
+ store_type = "std::vector<#{id_type}>"
242
+ with_visibility :public do
243
+ _append "const #{store_type}& get_#{singular_name}_ids() const { return #{singular_name}_ids; }"
244
+ _append "const #{list_type}& get_#{name}();"
245
+ _append "void set_#{singular_name}_ids(const #{store_type}& val) { #{singular_name}_ids = val; #{name}_fetched = false; }"
246
+ end
247
+ has_many_fetch list_type, name, options
248
+ options[:db] ||= {}
249
+ options[:db][:type] ||= "INTEGER[]"
250
+ make_pragma_db options[:db], [:type, :column, :null]
251
+ _append "#{store_type} #{singular_name}_ids;"
252
+ make_pragma_db transient: true
253
+ _append "#{list_type} #{name};"
254
+ make_pragma_db transient: true
255
+ _append "bool #{name}_fetched = false;"
256
+ end
257
+
258
+ class << self
259
+ def extension ; ".hpp" ; end
260
+
261
+ def generate_includes
262
+ <<CPP
263
+ # include <vector>
264
+ # include <list>
265
+ # include <map>
266
+ # include <string>
267
+ # include <crails/datatree.hpp>
268
+ # include <crails/odb/id_type.hpp>
269
+ CPP
270
+ end
271
+
272
+ def make_file filename, data
273
+ file_define = "_#{filename[0...-3].upcase.gsub "/", "_"}_HPP"
274
+ source = <<CPP
275
+ #ifndef #{file_define}
276
+ # define #{file_define}
277
+ #{generate_includes}
278
+ #{collect_includes_for(filename, true).join "\n"}
279
+ namespace odb { class access; }
280
+ #{data[:headers].join ""}
281
+ namespace #{METARECORD_NAMESPACE}
282
+ {
283
+ #{data[:bodies].join "\n"}}
284
+
285
+ #endif
286
+ CPP
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,178 @@
1
+
2
+ require 'metarecord/model'
3
+ require 'metarecord/generator_base'
4
+
5
+ class CrailsDestroyPreparator < GeneratorBase
6
+ attr_accessor :data
7
+
8
+ def prepare
9
+ @data = {}
10
+ Model.list.each do |model|
11
+ @model = model
12
+ self.instance_eval &model[:block]
13
+ end
14
+ end
15
+
16
+ def has_many type, name, options = {}
17
+ unless options[:dependent].nil?
18
+ @data[type] ||= []
19
+ @data[type] << ({
20
+ type: "has_many", class: @model[:classname],
21
+ field: name, options: options
22
+ })
23
+ end
24
+ end
25
+
26
+ def has_one type, name, options = {}
27
+ unless options[:dependent].nil?
28
+ @data[type] ||= []
29
+ @data[type] << ({
30
+ type: "has_one", class: @model[:classname],
31
+ field: name, options: options
32
+ })
33
+ end
34
+ end
35
+ end
36
+
37
+ class CrailsDestroyGenerator < GeneratorBase
38
+ class << self
39
+ attr_accessor :destroy_data
40
+
41
+ def my_prepare
42
+ destroy_preparator = CrailsDestroyPreparator.new
43
+ destroy_preparator.prepare
44
+ @destroy_data = destroy_preparator.data
45
+ end
46
+
47
+ def extension ; ".destroy.cpp" ; end
48
+
49
+ def make_file filename, data
50
+ include = "lib/" + filename[0...-2] + "hpp"
51
+ source = "#include \"#{include}\"\n"
52
+ source += "#include \"lib/#{filename[0...-2]}queries.hpp\"\n"
53
+ source += "#include <crails/odb/helpers.hpp>\n"
54
+ source += "#include <crails/odb/any.hpp>\n"
55
+ source += "#include <#{GeneratorBase.odb_connection[:include]}>\n"
56
+ source += "#include \"lib/odb/application-odb.hxx\"\n"
57
+ source += (collect_includes_for filename).join "\n"
58
+ source += "\n" + (data[:bodies].join "\n")
59
+ end
60
+ end
61
+
62
+ def generate_for object
63
+ reset
64
+ CrailsDestroyGenerator.my_prepare if CrailsDestroyGenerator.destroy_data.nil?
65
+ @finalclass = object[:classname]
66
+ @klassname = get_classname(object)
67
+ _append "void #{@klassname}::on_dependent_destroy(#{id_type} self_id)"
68
+ _append "{"
69
+ unless object[:classname].nil?
70
+ @indent += 1
71
+ depended_by = CrailsDestroyGenerator.destroy_data[object[:classname]]
72
+ if depended_by.class == Array
73
+ _append "auto& database = *#{GeneratorBase.odb_connection[:object]}::instance;"
74
+ depended_by.each do |relation|
75
+ _append "// #{relation[:class]}"
76
+ _append "{"
77
+ @indent += 1
78
+ if relation[:type] == "has_one"
79
+ one_to_many relation
80
+ elsif relation[:type] == "has_many"
81
+ many_to_many relation
82
+ end
83
+ @indent -= 1
84
+ _append "}\n"
85
+ end
86
+ end
87
+ @indent -= 1
88
+ end
89
+ _append "}"
90
+ @src
91
+ end
92
+
93
+ def one_to_many relation
94
+ _append "typedef odb::query<::#{relation[:class]}> Query;"
95
+ _append "odb::result<::#{relation[:class]}> models;"
96
+ _append "Query query;\n"
97
+
98
+ if relation[:options][:joined] != false
99
+ _append "query = Query::#{relation[:field]}->id == self_id;"
100
+ else
101
+ _append "query = Query::#{relation[:field]}_id == self_id;"
102
+ end
103
+ _append "database.find<::#{relation[:class]}>(models, query);"
104
+ _append "for (auto model : odb::to_vector<::#{relation[:class]}>(models))"
105
+ if relation[:options][:dependent] == :destroy
106
+ _append " database.destroy(model);"
107
+ elsif relation[:options][:dependent] == :unlink
108
+ _append "{"
109
+ if relation[:options][:joined] != false
110
+ _append " model.set_#{relation[:field]}(nullptr);"
111
+ else
112
+ _append " model.set_#{relation[:field]}_id(0);"
113
+ end
114
+ _append " database.save(model);"
115
+ _append "}"
116
+ end
117
+ end
118
+
119
+ def many_to_many relation
120
+ if relation[:options][:joined] != false
121
+ outer_name = (relation[:field].split(/-|_|\s/).each do |i| i.capitalize! end).join
122
+ view_klass = "#{relation[:class].split("::").join}By#{outer_name}"
123
+ klassname = @klassname.split("::").last
124
+ singular_name = get_singular_name relation[:field]
125
+ _append "typedef odb::query<#{view_klass}> Query;"
126
+ _append "odb::result<#{view_klass}> models;"
127
+ _append "Query query;\n"
128
+ _append "query = Query::#{klassname}::id == self_id;"
129
+ _append "database.find<#{view_klass}>(models, query);"
130
+ _append "for (auto model : odb::to_vector<#{relation[:class]}, #{view_klass}>(models))"
131
+ _append "{"
132
+ @indent += 1
133
+ if relation[:options][:dependent] == :destroy
134
+ _append "if (model.get_#{relation[:field]}().size() == 1)"
135
+ _append " database.destroy(model);"
136
+ _append "else"
137
+ _append "{"
138
+ _append " model.remove_#{singular_name}(*static_cast<#{@finalclass}*>(this));"
139
+ _append " database.save(model);"
140
+ _append "}"
141
+ else relation[:options][:dependent] == :unlink
142
+ _append "model.remove_#{singular_name}(*static_cast<#{@finalclass}*>(this));"
143
+ _append "database.save(model);"
144
+ end
145
+ @indent -= 1
146
+ _append "}"
147
+ else
148
+ _append "typedef odb::query<#{relation[:class]}> Query;"
149
+ _append "std::vector<#{id_type}> ids = { self_id };"
150
+ _append "odb::result<#{relation[:class]}> models;"
151
+ _append "Query query;\n"
152
+ id_field = "#{get_singular_name relation[:field]}_ids"
153
+ _append "query = Query::#{id_field} + \"@>\" + ODB::array_to_string(ids, \"int\");"
154
+ _append "database.find<#{relation[:class]}>(models, query);"
155
+ _append "for (auto model : odb::to_vector<#{relation[:class]}>(models))"
156
+ _append "{"
157
+ @indent += 1
158
+ if relation[:options][:dependent] == :destroy
159
+ _append "auto id_list = model.get_#{id_field}();\n"
160
+ _append "if (id_list.size() == 1)"
161
+ _append " database.destroy(model);"
162
+ _append "else"
163
+ _append "{"
164
+ _append " id_list.erase(std::find(id_list.begin(), id_list.end(), self_id));"
165
+ _append " model.set_#{id_field}(id_list);"
166
+ _append " database.save(model);"
167
+ _append "}"
168
+ elsif relation[:options][:dependent] == :unlink
169
+ _append "auto id_list = model.get_#{id_field}();\n"
170
+ _append "id_list.erase(std::find(id_list.begin(), id_list.end(), self_id));"
171
+ _append "model.set_#{id_field}(id_list);"
172
+ _append "database.save(model);"
173
+ end
174
+ @indent -= 1
175
+ _append "}"
176
+ end
177
+ end
178
+ end