-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Impement basic CDP client generation code for Ruby bindings
This commits allows to generate Ruby classes and modules from CDP specification files (browser_protocol.json and js_protocol.json). The generated code supports both commands and events: # Commands driver.devtools.page.navigate(url: 'http://google.com') driver.devtools.console.clear_messages # Events driver.devtools.page.enable driver.devtools.page.on(:load_event_fired) { |params| puts("Page loaded in #{params['timestamp']}") } driver.navigate.to(url: 'http://google.com') The generated code attempts to be Rubyish meaning the following conversions are applied: * camelCase is converted into snake_case (clearMessages is clear_messages) * command parameters are implemented with keywords arguments * event names are implemented with snake_case symbols ('loadEventFired' is :load_event_fired) Support for a custom return objects is not implemented yet, so the commands and event callbacks now return simple hashes. See https://chromedevtools.github.io/devtools-protocol/ for details on what domains, commands and events exist in CDP. The generated code will be placed into devtools/ directory during the build to avoid storing all the generated files with the source code and exclude them from `//rb:lint` task. Likewise, generator files won't be added to the gem releases. The generation is implemented using a new task `//rb:cdp` which simply delegates to `CDPClientGenerator#call`. The task uses specification files provided by Java bindings code. It should be replaced by generation from Java Bazel task but it's yet to be implemented. There is also a new task `//rb:devtools` which depends on `//rb:cdp` and then copies generated files for tests/docs/gem release.
- Loading branch information
Showing
7 changed files
with
328 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
rake_tasks/crazy_fun/mappings/ruby_mappings/ruby_class_call.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
module CrazyFun | ||
module Mappings | ||
class RubyMappings | ||
class RubyClassCall < RubyLibrary | ||
def handle(_fun, dir, args) | ||
desc "Call class #{args[:name]} in build/#{dir}" | ||
task_name = task_name(dir, args[:name]) | ||
|
||
t = task task_name do | ||
puts "Preparing: #{task_name} in #{build_dir}/#{dir}" | ||
copy_sources dir, args[:srcs] | ||
copy_resources dir, args[:resources], build_dir if args[:resources] | ||
require_source build_dir, args[:require] | ||
create_output_dir build_dir, args[:output_dir] | ||
call_class args[:klass] | ||
remove_sources args[:srcs] | ||
end | ||
|
||
add_dependencies(t, dir, args[:deps]) | ||
add_dependencies(t, dir, args[:resources]) | ||
end | ||
|
||
def require_source(dir, src) | ||
require File.join(dir, src) | ||
end | ||
|
||
def create_output_dir(root_dir, output_dir) | ||
mkdir_p File.join(root_dir, output_dir) | ||
end | ||
|
||
def call_class(klass) | ||
Object.const_get(klass).new.call | ||
end | ||
|
||
def remove_sources(globs) | ||
globs.each do |glob| | ||
Dir[File.join(build_dir, 'rb', glob)].each do |file| | ||
rm_rf file | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# frozen_string_literal: true | ||
|
||
# Licensed to the Software Freedom Conservancy (SFC) under one | ||
# or more contributor license agreements. See the NOTICE file | ||
# distributed with this work for additional information | ||
# regarding copyright ownership. The SFC licenses this file | ||
# to you under the Apache License, Version 2.0 (the | ||
# "License"); you may not use this file except in compliance | ||
# with the License. You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, | ||
# software distributed under the License is distributed on an | ||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
# KIND, either express or implied. See the License for the | ||
# specific language governing permissions and limitations | ||
# under the License. | ||
|
||
# This file is automatically generated. Any changes will be lost! | ||
module Selenium | ||
module WebDriver | ||
class DevTools | ||
def <%= h.snake_case(domain[:domain]) %> | ||
@<%= h.snake_case(domain[:domain]) %> ||= <%= domain[:domain] %>.new(self) | ||
end | ||
|
||
class <%= domain[:domain] %> | ||
<% if domain[:events] %> | ||
EVENTS = { | ||
<% domain[:events].each do |event| %> | ||
<%= h.snake_case(event[:name]) %>: '<%= event[:name] %>', | ||
<% end %> | ||
} | ||
<% end %> | ||
|
||
def initialize(devtools) | ||
@devtools = devtools | ||
end | ||
|
||
def on(event, &block) | ||
event = EVENTS[event] if event.is_a?(Symbol) | ||
@devtools.callbacks["<%= domain[:domain] %>.#{event}"] << block | ||
end | ||
|
||
<% domain[:commands].each do |command| %> | ||
<% if command[:parameters] %> | ||
def <%= h.snake_case(command[:name]) %>(<%= h.kwargs(command[:parameters]) %>) | ||
<% else %> | ||
def <%= h.snake_case(command[:name]) %> | ||
<% end %> | ||
<% if command[:parameters] %> | ||
@devtools.send_cmd('<%= domain[:domain] %>.<%= command[:name] %>', | ||
<% until command[:parameters].empty? %> | ||
<% parameter = command[:parameters].shift %> | ||
<%= parameter[:name] %>: <%= h.snake_case(parameter[:name]) %><%= command[:parameters].empty? ? ')' : ',' %> | ||
<% end %> | ||
<% else %> | ||
@devtools.send_cmd('<%= domain[:domain] %>.<%= command[:name] %>') | ||
<% end %> | ||
end | ||
|
||
<% end %> | ||
end # <%= domain[:domain] %> | ||
end # DevTools | ||
end # WebDriver | ||
end # Selenium |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# frozen_string_literal: true | ||
|
||
# Licensed to the Software Freedom Conservancy (SFC) under one | ||
# or more contributor license agreements. See the NOTICE file | ||
# distributed with this work for additional information | ||
# regarding copyright ownership. The SFC licenses this file | ||
# to you under the Apache License, Version 2.0 (the | ||
# "License"); you may not use this file except in compliance | ||
# with the License. You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, | ||
# software distributed under the License is distributed on an | ||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
# KIND, either express or implied. See the License for the | ||
# specific language governing permissions and limitations | ||
# under the License. | ||
|
||
require 'erb' | ||
require 'json' | ||
|
||
module Selenium | ||
module WebDriver | ||
module Support | ||
class CDPClientGenerator | ||
DEVTOOLS_DIR = File.expand_path('../devtools', __dir__) | ||
BROWSER_PROTOCOL_PATH = File.expand_path('cdp/browser_protocol.json', __dir__) | ||
JS_PROTOCOL_PATH = File.expand_path('cdp/js_protocol.json', __dir__) | ||
TEMPLATE_PATH = File.expand_path('cdp/domain.rb.erb', __dir__) | ||
|
||
RESERVED_KEYWORDS = %w[end].freeze | ||
|
||
def initialize | ||
@browser_protocol = JSON.parse(File.read(BROWSER_PROTOCOL_PATH), symbolize_names: true) | ||
@js_protocol = JSON.parse(File.read(JS_PROTOCOL_PATH), symbolize_names: true) | ||
@template = ERB.new(File.read(TEMPLATE_PATH)) | ||
end | ||
|
||
def call | ||
@browser_protocol[:domains].each(&method(:process_domain)) | ||
@js_protocol[:domains].each(&method(:process_domain)) | ||
end | ||
|
||
def process_domain(domain) | ||
result = @template.result_with_hash(domain: domain, h: self) | ||
filename = File.join(DEVTOOLS_DIR, "#{snake_case(domain[:domain])}.rb") | ||
File.write(filename, remove_empty_lines(result)) | ||
end | ||
|
||
def snake_case(string) | ||
name = string.gsub(/([a-z])([A-Z])/, '\1_\2').downcase | ||
# Certain CDP parameters conflict with Ruby keywords | ||
# so we prefix the name with underscore. | ||
name = "_#{name}" if RESERVED_KEYWORDS.include?(name) | ||
|
||
name | ||
end | ||
|
||
def kwargs(parameters) | ||
parameters = parameters.map do |parameter| | ||
if parameter[:optional] | ||
"#{snake_case(parameter[:name])}: nil" | ||
else | ||
"#{snake_case(parameter[:name])}:" | ||
end | ||
end | ||
parameters.join(', ') | ||
end | ||
|
||
def remove_empty_lines(string) | ||
string.split("\n").reject { |l| l =~ /^\s+$/ }.join("\n") | ||
end | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.