comet-cpp 0.9.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.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/bin/comet-html +69 -0
  3. data/bin/comet-make +53 -0
  4. data/bin/comet-new +62 -0
  5. data/bin/comet-web +14 -0
  6. data/lib/comet-html/generator.rb +220 -0
  7. data/lib/comet-html/header-generator.rb +145 -0
  8. data/lib/comet-html/parser-binding.rb +42 -0
  9. data/lib/comet-html/parser-class.rb +168 -0
  10. data/lib/comet-html/parser-context.rb +61 -0
  11. data/lib/comet-html/parser-reference.rb +98 -0
  12. data/lib/comet-html/parser-repeater.rb +58 -0
  13. data/lib/comet-html/parser-slot.rb +108 -0
  14. data/lib/comet-html/source-generator.rb +285 -0
  15. data/lib/comet-html/utils.rb +88 -0
  16. data/lib/guard/comet-html.rb +32 -0
  17. data/lib/guard/comet.rb +36 -0
  18. data/vendor/project/Gemfile +4 -0
  19. data/vendor/project/Guardfile +5 -0
  20. data/vendor/project/app/application.hpp +29 -0
  21. data/vendor/project/app/collections/.gitkeep +0 -0
  22. data/vendor/project/app/controllers/.gitkeep +0 -0
  23. data/vendor/project/app/main.cpp +6 -0
  24. data/vendor/project/app/models/.gitkeep +0 -0
  25. data/vendor/project/app/routes.cpp +7 -0
  26. data/vendor/project/app/views/layouts/.gitkeep +0 -0
  27. data/vendor/project/public/index.html +7 -0
  28. data/vendor/src/anchorable_element.hpp +79 -0
  29. data/vendor/src/append_semantics.hpp +73 -0
  30. data/vendor/src/bindable.cpp +99 -0
  31. data/vendor/src/bindable.hpp +106 -0
  32. data/vendor/src/cheerp_parse_cookie_values.cpp +58 -0
  33. data/vendor/src/comment_element.cpp +11 -0
  34. data/vendor/src/comment_element.hpp +17 -0
  35. data/vendor/src/cookies.cpp +94 -0
  36. data/vendor/src/cookies.hpp +60 -0
  37. data/vendor/src/custom_element.hpp +61 -0
  38. data/vendor/src/datatree.cpp +198 -0
  39. data/vendor/src/datatree.hpp +233 -0
  40. data/vendor/src/document.cpp +62 -0
  41. data/vendor/src/document.hpp +31 -0
  42. data/vendor/src/element.cpp +358 -0
  43. data/vendor/src/element.hpp +138 -0
  44. data/vendor/src/events.hpp +76 -0
  45. data/vendor/src/exception.cpp +13 -0
  46. data/vendor/src/exception.hpp +11 -0
  47. data/vendor/src/from_string.cpp +99 -0
  48. data/vendor/src/from_string.hpp +37 -0
  49. data/vendor/src/globals.cpp +6 -0
  50. data/vendor/src/globals.hpp +15 -0
  51. data/vendor/src/http.cpp +93 -0
  52. data/vendor/src/http.hpp +72 -0
  53. data/vendor/src/lexical_cast.hpp +51 -0
  54. data/vendor/src/local_storage.cpp +75 -0
  55. data/vendor/src/local_storage.hpp +53 -0
  56. data/vendor/src/mvc/collection.hpp +154 -0
  57. data/vendor/src/mvc/controller.hpp +59 -0
  58. data/vendor/src/mvc/id_type.hpp +9 -0
  59. data/vendor/src/mvc/layout.hpp +44 -0
  60. data/vendor/src/mvc/model.cpp +89 -0
  61. data/vendor/src/mvc/model.hpp +50 -0
  62. data/vendor/src/object.cpp +71 -0
  63. data/vendor/src/object.hpp +298 -0
  64. data/vendor/src/parse_cookie_values.hpp +12 -0
  65. data/vendor/src/promise.cpp +50 -0
  66. data/vendor/src/promise.hpp +43 -0
  67. data/vendor/src/repeater.hpp +116 -0
  68. data/vendor/src/router.cpp +62 -0
  69. data/vendor/src/router.hpp +34 -0
  70. data/vendor/src/router_base.hpp +107 -0
  71. data/vendor/src/signal.hpp +150 -0
  72. data/vendor/src/slot_element.hpp +61 -0
  73. data/vendor/src/url.cpp +19 -0
  74. data/vendor/src/url.hpp +15 -0
  75. data/vendor/src/window.cpp +22 -0
  76. data/vendor/src/window.hpp +24 -0
  77. metadata +134 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 76cf704d8d362f1b97c3f6fb318d9176899355ed9e59d868d777cc05f81ee72a
4
+ data.tar.gz: d5fb5a73fe2a213689448bb1579cf9eec90339fc52df6273435ebb359baabbcc
5
+ SHA512:
6
+ metadata.gz: c4c4858560da8cc6d439028fdc4c8fa19906018681c425e1eee1e537243d26d3caf0769050aa434923479c061f89a10b437276a0127735a3718b5d544d457aa9
7
+ data.tar.gz: cd4b469ae6f9e3162f8520311fbcf1c67df47346a55bb9752b610d7f7dd6cbb0faa32d9e450ce5580bd5caa746c4f86d3471b593e43f1f5f00deb7290cd79458
data/bin/comet-html ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'fileutils'
4
+ require 'json'
5
+ require 'comet-html/generator'
6
+
7
+ @source_path = "app"
8
+ @output_path = "comet-elements/html"
9
+ @config_path = "config/comet.json"
10
+
11
+ OptionParser.new do |opts|
12
+ opts.on "-i PATH", "--source-path=PATH" do |v| @source_path = v end
13
+ opts.on "-o PATH", "--output=PATH" do |v| @output_path = v end
14
+ opts.on "-c PATH", "--config=PATH" do |v| @config_path = v end
15
+ end.parse!
16
+
17
+ @source_path = File.expand_path(@source_path)
18
+ @output_path = File.expand_path(@output_path)
19
+ @config_path = File.expand_path(@config_path)
20
+
21
+ def purge_old_files whitelist
22
+ Dir["#{@output_path}/**/*"].each do |file|
23
+ next if File.directory? file
24
+ File.delete file unless whitelist.include? file
25
+ end
26
+ end
27
+
28
+ def write_if_content_changed file, content
29
+ if !File.exists?(file) || File.read(file) != content
30
+ File.open file, 'w' do |f| f.write content end
31
+ puts "[comet] Generated #{file}"
32
+ end
33
+ end
34
+
35
+ files = Dir["#{@source_path}/**/*.html"]
36
+ config = if File.exists?(@config_path) then JSON.parse(File.read @config_path) else nil end
37
+ success = true
38
+ current_filename = nil
39
+
40
+ begin
41
+ generated_files = []
42
+ files.each do |file|
43
+ current_filename = file
44
+ generator = ::Comet::Generator.new @output_path, @source_path, file
45
+ generator.set_config config unless config.nil?
46
+ header_file = generator.compiled_header_path
47
+ source_file = generator.compiled_source_path
48
+
49
+ FileUtils.mkdir_p File.dirname(header_file)
50
+ code = generator.generate
51
+ write_if_content_changed header_file, code[:header]
52
+ write_if_content_changed source_file, code[:source]
53
+ generated_files << header_file << source_file
54
+ rescue ::Comet::ParseError => e
55
+ STDERR.puts "[comet] parse error at #{current_filename}:#{e.line}: #{e.message}"
56
+ success = false
57
+ rescue ::Comet::HtmlParseError => e
58
+ STDERR.puts "[comet] parse error at #{current_filename}:#{e.message}"
59
+ success = false
60
+ rescue ::Comet::LoadError => e
61
+ STDERR.puts "[comet] #{e.message}"
62
+ success = false
63
+ rescue StandardError => e
64
+ STDERR.puts e.message
65
+ e.backtrace.each do |line| STDERR.puts line end
66
+ success = false
67
+ end
68
+ purge_old_files generated_files
69
+ end
data/bin/comet-make ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'pathname'
4
+ require 'fileutils'
5
+ require 'pty'
6
+
7
+ @cheerp_path = "/opt/cheerp"
8
+ @build_type = "RELEASE"
9
+ @build_path = ".comet-build"
10
+ @cmakelists_path = "./"
11
+ @output_path = "public"
12
+
13
+ OptionParser.new do |opts|
14
+ opts.on "-c PATH", "--cheerp-path=PATH" do |v| @cheerp_path = v end
15
+ opts.on "-g", "--debug" do |v| @build_type = "DEBUG" end
16
+ opts.on "-i PATH", "--cmakelists=PATH" do |v| @cmakelists_path = v end
17
+ opts.on "-o PATH", "--output=PATH" do |v| @output_path = v end
18
+ end.parse!
19
+
20
+ @cmakelists_path = File.expand_path @cmakelists_path
21
+ @output_path = File.expand_path @output_path
22
+ @build_path = File.expand_path @build_path
23
+ @cheerp_path = File.expand_path @cheerp_path
24
+
25
+ cmake_options = []
26
+ cmake_options << "-DCMAKE_TOOLCHAIN_FILE=#{@cheerp_path}/share/cmake/Modules/CheerpToolchain.cmake"
27
+ cmake_options << "-DCMAKE_BUILD_TYPE=#{@build_type}"
28
+
29
+ def run_command cmd
30
+ puts "+ #{cmd}"
31
+ PTY.spawn(cmd) do |stdout,stdin,pid|
32
+ begin
33
+ stdout.each {|line| print line}
34
+ rescue Errno::EIO
35
+ end
36
+ Process.wait(pid)
37
+ end
38
+ exit -1 unless $?.success?
39
+ end
40
+
41
+ FileUtils.mkdir_p @build_path
42
+
43
+ Dir.chdir @build_path do
44
+ run_command "cmake #{cmake_options.join ' '} #{@cmakelists_path}"
45
+ run_command "make"
46
+ end
47
+
48
+ FileUtils.mkdir_p @output_path
49
+ Dir["#{@build_path}/*.{js,js.map}"].each do |target|
50
+ path = Pathname.new target
51
+ basename = path.basename
52
+ FileUtils.mv target, "#{@output_path}/#{@basename}"
53
+ end
data/bin/comet-new ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'fileutils'
4
+
5
+ @build_path = ".comet-build"
6
+ @vendor_path = "#{@build_path}/vendor"
7
+ @project_dir = "./"
8
+ @project_name = "project-name"
9
+ @skip_project = false
10
+
11
+ OptionParser.new do |opts|
12
+ opts.on "-p PATH", "--project-path=PATH" do |v| @project_dir = v end
13
+ opts.on "-n NAME", "--name=NAME" do |v| @project_name = v end
14
+ opts.on "-v PATH", "--vendor-path=PATH" do |v| @vendor_path = v end
15
+ opts.on "-z", "--skip-project-files" do |v| @skip_project = v end
16
+ end.parse!
17
+
18
+ @build_path = File.expand_path @build_path
19
+ @project_dir = File.expand_path @project_dir
20
+ @vendor_path = File.expand_path @vendor_path
21
+ @comet_src_path = "#{@vendor_path}/comet"
22
+
23
+ FileUtils.mkdir_p @comet_src_path
24
+
25
+ gemspec = Gem::Specification.find_all_by_name("comet-cpp").first
26
+ vendor_dir = File.expand_path "#{gemspec.bin_dir}/../vendor"
27
+ src_dir = vendor_dir + "/src"
28
+ app_dir = vendor_dir + "/project"
29
+
30
+ def initialize_directory src_dir, output_dir, override
31
+ Dir["#{src_dir}/**/*"].each do |filepath|
32
+ next if File.directory? filepath
33
+ relative_path = filepath[src_dir.length + 1..-1]
34
+ dest_path = output_dir + '/' + relative_path
35
+ FileUtils.mkdir_p(File.dirname dest_path)
36
+ if !File.exists?(dest_path) || override
37
+ FileUtils.cp filepath, dest_path
38
+ end
39
+ end
40
+ end
41
+
42
+ initialize_directory src_dir, @comet_src_path, true
43
+ initialize_directory app_dir, @project_dir, false unless @skip_project
44
+
45
+ File.open "#{@project_dir}/CMakeLists.txt", "w" do |file|
46
+ relative_vendor_path = Pathname.new(@vendor_path).relative_path_from(Pathname.new(@project_dir)).to_s
47
+ file.write <<EOF
48
+ cmake_minimum_required(VERSION 3.0)
49
+
50
+ project(#{@project_name})
51
+ include_directories(#{relative_vendor_path} comet-elements)
52
+
53
+ if (CMAKE_BUILD_TYPE EQUAL "DEBUG")
54
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -cheerp-sourcemap=application.js.map -cheerp-sourcemap-standalone")
55
+ endif()
56
+
57
+ file(GLOB_RECURSE vendor_src #{relative_vendor_path}/*.cpp)
58
+ file(GLOB_RECURSE app_src app/*.cpp comet-elements/*.cpp)
59
+
60
+ add_executable(application ${vendor_src} ${app_src})
61
+ EOF
62
+ end unless @skip_project || (File.exists? "#{@project_dir}/CMakeLists.txt")
data/bin/comet-web ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+
4
+ @output_path = "public"
5
+
6
+ OptionParser.new do |opts|
7
+ opts.on "-o PATH", "--output=PATH" do |v| @output_path = v end
8
+ end.parse!
9
+
10
+ @output_path = File.expand_path @output_path
11
+
12
+ server = WEBrick::HTTPServer.new Port: 8000, DocumentRoot: @output_path
13
+ trap 'INT' do server.shutdown end
14
+ server.start
@@ -0,0 +1,220 @@
1
+ require 'comet-html/parser-class'
2
+ require 'comet-html/parser-repeater'
3
+ require 'comet-html/parser-slot'
4
+ require 'comet-html/utils'
5
+ require 'comet-html/header-generator'
6
+ require 'comet-html/source-generator'
7
+ require 'nokogiri'
8
+ require 'pathname'
9
+
10
+ module Comet
11
+ class LoadError < StandardError
12
+ attr_reader :message
13
+ def initialize
14
+ @message = "Load error: #{message}"
15
+ end
16
+ end
17
+
18
+ class ParseError < StandardError
19
+ attr_reader :el, :message
20
+
21
+ def initialize el, message
22
+ @el = el
23
+ @message = message
24
+ end
25
+
26
+ def line
27
+ el.line
28
+ end
29
+ end
30
+
31
+ class HtmlParseError < StandardError
32
+ attr_reader :message, :line
33
+
34
+ def initialize error
35
+ @message = error.message
36
+ @line = error.line
37
+ end
38
+ end
39
+
40
+ class Generator
41
+ def initialize output, input, file
42
+ @output = output
43
+ @input = input
44
+ @filepath = file
45
+ end
46
+
47
+ def set_config config
48
+ @config = config
49
+ end
50
+
51
+ def relative_filepath
52
+ @filepath[@input.length..-1]
53
+ end
54
+
55
+ def compiled_header_path
56
+ @output + relative_filepath.delete_suffix(File.extname relative_filepath) + ".hpp"
57
+ end
58
+
59
+ def compiled_source_path
60
+ @output + relative_filepath.delete_suffix(File.extname relative_filepath) + ".cpp"
61
+ end
62
+
63
+ def append_classes_predeclaration
64
+ result = ""
65
+ Context.classes.each do |object|
66
+ next if object.should_skip?
67
+ result += " class #{object.typename};\n"
68
+ end
69
+ result
70
+ end
71
+
72
+ def load_includes head
73
+ head.css("include").each do |include_attribute|
74
+ unless include_attribute["require"].nil?
75
+ type = include_attribute["require"].to_s
76
+ tag = if include_attribute["tag-name"].nil?
77
+ parts = type.split("<")
78
+ parts[0].gsub! "::", "_"
79
+ parts.join("<").dasherize
80
+ else
81
+ include_attribute["tag-name"].to_s
82
+ end
83
+ Context.element_types[tag] = type
84
+ end
85
+ end
86
+ end
87
+
88
+ def prepare_includes head
89
+ result = ""
90
+ included_paths = []
91
+ head.css("include").each do |include_attribute|
92
+ included_paths << include_attribute["src"].to_s
93
+ end
94
+ included_paths.uniq.each do |include_path|
95
+ result += "# include \"#{include_path}\"\n"
96
+ end
97
+ @config["elements"].each do |element_data|
98
+ if Context.referenced_types.include? element_data["require"]
99
+ result += "# include \"#{element_data["include"]}\"\n"
100
+ end
101
+ end if !@config.nil? && !@config["elements"].nil?
102
+ result
103
+ end
104
+
105
+ def html5_tags
106
+ ["article", "aside", "details", "figcaption", "figure", "footer", "header", "main", "mark", "nav", "section", "summary", "time"]
107
+ end
108
+
109
+ def whitelisted_tags
110
+ ["svg"] + (if @config.nil? then [] else @config["whitelisted-tags"] || [] end)
111
+ end
112
+
113
+ def internal_tags
114
+ ["include", "attribute", "slot"]
115
+ end
116
+
117
+ def is_error_fatal? error
118
+ matches = error.message.match(/^[0-9]+:[0-9]+: ERROR: Tag (.+) invalid$/)
119
+ unless matches.nil?
120
+ tagName = matches[1]
121
+ return false if (internal_tags + html5_tags + whitelisted_tags).include? tagName
122
+ return (if Context.element_types[tagName].nil? then true else false end)
123
+ end
124
+ return false if error.message.end_with?("ERROR: htmlParseEntityRef: no name") ||
125
+ error.message.end_with?("ERROR: Expected a doctype token$")
126
+ true
127
+ end
128
+
129
+ def generate
130
+ src = File.read @filepath
131
+ src.gsub! /<template/i, "<html"
132
+ src.gsub! /<\/template/i, "</html>"
133
+
134
+ document = Nokogiri::HTML.parse(src) do |config| config.strict end
135
+
136
+ html = document.xpath("html").first
137
+ head = document.xpath("//head")
138
+ body = document.xpath("//body")
139
+ raise LoadError.new "could not load html template `#{@filepath}`" if html.nil?
140
+ raise LoadError.new "missing body element in `#{@filepath}`" if body.first.nil?
141
+
142
+ Context.reset
143
+ Context.filename = @filepath
144
+ if !@config.nil? && !@config["elements"].nil?
145
+ Context.load_global_element_types @config["elements"]
146
+ end
147
+
148
+ load_includes head
149
+
150
+ fatal_error = nil
151
+ document.errors.each do |error|
152
+ if is_error_fatal? error
153
+ puts "[comet-html] #{@filepath}:#{error.line}:#{error.column}: #{error.message}"
154
+ fatal_error ||= error
155
+ error_count += 1
156
+ end
157
+ end
158
+ raise HtmlParseError.new fatal_error unless fatal_error.nil?
159
+
160
+ main_element = Comet::Class.new body
161
+ main_element.typename = if html["classname"].nil?
162
+ File.basename(@filepath, ".html").to_s.camelize
163
+ else
164
+ html["classname"]
165
+ end
166
+ main_element.superclass = html["extends"] unless html["extends"].nil?
167
+ main_element.probe
168
+
169
+ main_element.inline_code = ""
170
+ head.css("script").each do |script|
171
+ main_element.inline_code += script.text.strip + "\n"
172
+ end
173
+
174
+ head.css("attribute").each do |attribute|
175
+ main_element.refs << (CppReference.new attribute, attribute["type"], attribute["name"], attribute["value"])
176
+ end
177
+
178
+ define_name = "__COMET_CUSTOM_ELEMENT_#{main_element.typename.upcase}__"
179
+ header_code = ""
180
+ source_code = ""
181
+ Context.classes.reverse.each do |object|
182
+ next if object.should_skip?
183
+ header_code += HeaderGenerator.new(object).generate + "\n"
184
+ source_code += SourceGenerator.new(object).generate + "\n"
185
+ end
186
+
187
+ includes_str = prepare_includes head
188
+
189
+ header = <<HEADER
190
+ #ifndef #{define_name}
191
+ # define #{define_name}
192
+
193
+ # include <comet/bindable.hpp>
194
+ # include <comet/repeater.hpp>
195
+ # include <comet/slot_element.hpp>
196
+ # include <comet/comment_element.hpp>
197
+ # include <comet/custom_element.hpp>
198
+ # include <comet/signal.hpp>
199
+ #{includes_str}
200
+
201
+ namespace HtmlTemplate
202
+ {
203
+ #{append_classes_predeclaration}
204
+ #{header_code}
205
+ }
206
+
207
+ #endif
208
+ HEADER
209
+
210
+ source = <<SOURCE
211
+ #include <comet/lexical_cast.hpp>
212
+ #include "#{compiled_header_path}"
213
+
214
+ #{source_code}
215
+ SOURCE
216
+
217
+ { header: header, source: source }
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,145 @@
1
+ module Comet
2
+ class HeaderGenerator
3
+ attr_reader :object, :source
4
+
5
+ def initialize object
6
+ @object = object
7
+ end
8
+
9
+ def indent
10
+ " "
11
+ end
12
+
13
+ def signaler_definition
14
+ return "" if object.implements_ibindable_view?
15
+ # Signaler def. Must be a reference if the object isn't the root object
16
+ signaler_type = "Comet::Signal<std::string>"
17
+ signaler_type += "&" unless object.parent.nil?
18
+ "#{signaler_type} signaler;"
19
+ end
20
+
21
+ def bound_attributes_definition
22
+ return "" if object.implements_ibindable_view?
23
+ "Comet::BoundAttributes bound_attributes;"
24
+ end
25
+
26
+ def generate_getters_setters
27
+ result = ""
28
+ object.refs.each do |ref|
29
+ is_ptr = ref.type.end_with? '*'
30
+ if ref.has_getter?
31
+ if is_ptr
32
+ result += indent + "#{ref.type} get_#{ref.name}() const { return #{ref.name}; }\n"
33
+ else
34
+ result += indent + "const #{ref.type}& get_#{ref.name}() const { return #{ref.name}; }\n"
35
+ end
36
+ end
37
+ if ref.has_setter?
38
+ if is_ptr
39
+ result += indent + "virtual void set_#{ref.name}(#{ref.type} __v) { #{ref.name} = __v; }\n"
40
+ else
41
+ result += indent + "virtual void set_#{ref.name}(const #{ref.type}& __v) { #{ref.name} = __v; }\n"
42
+ end
43
+ end
44
+ end
45
+ result
46
+ end
47
+
48
+ def generate_anchors
49
+ result = ""
50
+ anchor_count = 0
51
+ object.collect_children.each do |child|
52
+ if child.is_anchorable?
53
+ anchor_count += 1
54
+ child.anchor_name = "anchor_#{anchor_count}"
55
+ result += indent + "Comet::CommentElement #{child.anchor_name};\n"
56
+ end
57
+ end
58
+ result
59
+ end
60
+
61
+ def friends_def
62
+ result = ""
63
+ object.context.classes.each do |klass|
64
+ result += indent + "friend class #{klass.typename};\n" unless klass == object
65
+ end
66
+ result
67
+ end
68
+
69
+ def generate_properties
70
+ protected_properties_def = ""
71
+ public_properties_def = ""
72
+ private_properties_def = ""
73
+ if object.is_root?
74
+ protected_properties_def += indent + "#{object.typename}* root;\n"
75
+ else
76
+ public_properties_def += indent + "#{object.parent.typename}* parent;\n"
77
+ protected_properties_def += indent + "#{object.root.typename}* root;\n"
78
+ end
79
+ object.refs.each do |ref|
80
+ suffix = " = #{ref.default_value}" if ref.has_default_value?
81
+ if ref.is_explicit?
82
+ protected_properties_def += indent + "#{ref.type} #{ref.name}#{suffix};\n"
83
+ elsif ref.is_implicit?
84
+ private_properties_def += indent + "#{ref.type} #{ref.name}#{suffix};\n"
85
+ end
86
+ end
87
+ ## BEGIN Repeaters
88
+ object.repeaters.each do |repeater|
89
+ protected_properties_def += indent + "Comet::Repeater<#{repeater.list_type}, #{repeater.typename}> #{repeater.repeater_name};\n"
90
+ end
91
+ if object.class == Repeater
92
+ public_properties_def += indent + "#{object.value_type} #{object.value_name};\n"
93
+ end
94
+ ## END Repeaters
95
+ ## BEGIN SLOTS
96
+ # Slots emanate from the root object.
97
+ if object.is_root?
98
+ all_slots = object.slots
99
+ object.recursively_collect_children.each do |child|
100
+ all_slots += child.slots
101
+ end
102
+ all_slots.each do |slot|
103
+ public_properties_def += indent + "Comet::SlotElement #{slot.slot_ref};\n"
104
+ end
105
+ else
106
+ object.slots.each do |slot|
107
+ public_properties_def += indent + "Comet::SlotElement& #{slot.slot_ref};\n"
108
+ end
109
+ end
110
+ ## END SLOTS
111
+ [public_properties_def, protected_properties_def, private_properties_def]
112
+ end
113
+
114
+ def generate
115
+ anchors_def = generate_anchors
116
+ public_properties_def, protected_properties_def, private_properties_def = generate_properties
117
+
118
+ <<HEADER
119
+ class #{object.typename} : public #{object.superclass}
120
+ {
121
+ #{friends_def}
122
+ protected:
123
+ #{bound_attributes_definition}
124
+ #{protected_properties_def}
125
+ private:
126
+ #{anchors_def}
127
+ #{private_properties_def}
128
+ public:
129
+ #{signaler_definition}
130
+ #{public_properties_def}
131
+
132
+ #{object.constructor_decl}
133
+ virtual ~#{object.typename}() {}
134
+
135
+ void bind_attributes();
136
+ void trigger_binding_updates();
137
+
138
+ #{generate_getters_setters}
139
+
140
+ #{object.inline_code}
141
+ };
142
+ HEADER
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,42 @@
1
+ require 'comet-html/utils'
2
+
3
+ module Comet
4
+ class EventListener
5
+ attr_reader :el, :attribute_name, :code, :is_cpp
6
+
7
+ def initialize el, parent, key, value
8
+ @el = el
9
+ @parent = parent
10
+ @is_cpp = key.start_with?("cpp::")
11
+ @attribute_name = key.delete_suffix(".trigger")
12
+ @attribute_name = @attribute_name[5..-1] if is_cpp
13
+ @code = value
14
+
15
+ if @parent.find_reference_for(el).nil?
16
+ @parent.refs << (Reference.new(el, @parent))
17
+ end
18
+ end
19
+ end
20
+
21
+ class Binding
22
+ attr_reader :el, :attribute_name, :is_cpp, :bind_mode, :code
23
+
24
+ def initialize el, parent, key, value
25
+ @el = el
26
+ @parent = parent
27
+
28
+ @attribute_name = key.delete_suffix(".bind")
29
+ @is_cpp = attribute_name.start_with?("cpp::")
30
+ @attribute_name = attribute_name[5..-1] if is_cpp
31
+ @code, @bind_mode = extract_bind_mode_from value
32
+
33
+ if @parent.find_reference_for(el).nil?
34
+ @parent.refs << (Reference.new(el, @parent))
35
+ end
36
+ end
37
+
38
+ def binds_to_cpp_property?
39
+ @is_cpp
40
+ end
41
+ end
42
+ end