-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
QOL make our gem requireable and autoload/eager load some constants #171
Changes from all commits
ff77682
ecdc7b5
7dab057
e39361d
260c5d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
lib/protoboeuf/google/**/*.rb linguist-generated=true | ||
test/fixtures/autoloadergen/google/**/*.rb linguist-generated=true | ||
test/fixtures/autoloadergen/google/test_protos.correct.rb linguist-generated=false |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# frozen_string_literal: true | ||
|
||
module ProtoBoeuf | ||
autoload :CodeGen, "protoboeuf/codegen" | ||
autoload :Google, "protoboeuf/google" | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
# frozen_string_literal: true | ||
|
||
require "erb" | ||
require "syntax_tree" | ||
require "pathname" | ||
|
||
module ProtoBoeuf | ||
class AutoloaderGen | ||
# This class generates top-level autoloader modules for our well known types. Given autogenerated .rb files like: | ||
# - lib/protoboeuf/google/protobuf/foo.rb | ||
# - lib/protoboeuf/google/protobuf/bar.rb | ||
# | ||
# generate lib/protoboeuf/google/protobuf.rb that looks like: | ||
# | ||
# module ProtoBoeuf | ||
# module Google | ||
# module Protobuf | ||
# autoload :FooMessage1, "protoboeuf/google/protobuf/foo" | ||
# autoload :FooMessage2, "protoboeuf/google/protobuf/foo" | ||
# autoload :BarMessage1, "protoboeuf/google/protobuf/bar" | ||
# end | ||
# end | ||
# end | ||
|
||
BASE_LIB_DIR = File.expand_path("..", __dir__) | ||
|
||
attr_reader :module_filename, | ||
:child_ruby_filenames, | ||
:generated_autoloader_module_parts, | ||
:parent_module_parts, | ||
:require_paths_for_child_constants | ||
|
||
def initialize(module_filename, parent_module = "ProtoBoeuf::Google") | ||
@module_filename = module_filename | ||
@parent_module_parts = parent_module.split("::") | ||
|
||
# Given lib/protoboeuf/google.rb, glob lib/protoboeuf/google/**/*.rb | ||
@child_ruby_filenames = Dir[module_filename.pathmap("%X/**/*.rb")].sort | ||
autoloader_full_module_name = nil | ||
|
||
# Build a map of what we want to autoload :ConstantName => protoboeuf/require/path | ||
@require_paths_for_child_constants = child_ruby_filenames.each_with_object({}) do |filename, require_paths| | ||
child_constants = constants_for_child_ruby_filename(filename) | ||
# For the autoloader_full_module_name we can just pick the first child constant we come across and take up until | ||
# the first child constant of the parent_module. | ||
# For example, ProtoBoeuf::Google::Api::FieldBehavior would be ProtoBoeuf::Google::Api. | ||
if autoloader_full_module_name.nil? | ||
autoloader_full_module_name = child_constants.first.sub(/(#{parent_module}::.+?)::.*/, "\\1") | ||
end | ||
|
||
# Make our absolute filename relative to the base lib directory for our autoload calls. | ||
require_path = Pathname.new(filename).relative_path_from(BASE_LIB_DIR).sub_ext("") | ||
child_constants.each do |child_constant| | ||
# child_constant is fully qualified, but we just want the last part | ||
require_paths[child_constant.split("::").last] = require_path | ||
end | ||
end | ||
|
||
@generated_autoloader_module_parts = autoloader_full_module_name.split("::") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we use this value for something else, or are we joining it just to split it again? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use it in <%- generated_autoloader_module_parts.each do |module_name| -%>
module <%= module_name %>
<%- end -%> There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, what I was referring to is that when we set the |
||
end | ||
|
||
def to_ruby | ||
SyntaxTree.format(ERB.new(<<~RUBY, trim_mode: "-").result(binding)) | ||
# frozen_string_literal: true | ||
# rubocop:disable all | ||
|
||
# Autogenerated by `rake well_known_types`. Do not edit! | ||
<%- generated_autoloader_module_parts.each do |module_name| -%> | ||
module <%= module_name %> | ||
<%- end -%> | ||
|
||
<%- | ||
# Iterating over the sorted keys gives us lexographically sorted autoload statements | ||
-%> | ||
<%- require_paths_for_child_constants.keys.sort.each do |constant_name| -%> | ||
<%- require_path = require_paths_for_child_constants[constant_name] -%> | ||
autoload :<%= constant_name %>, "<%= require_path %>" | ||
<%- end -%> | ||
|
||
<%- generated_autoloader_module_parts.each do |module_name| -%> | ||
end | ||
<%- end -%> | ||
RUBY | ||
end | ||
|
||
private | ||
|
||
def constants_for_child_ruby_filename(filename) | ||
@constants_for_child_ruby_filename ||= {} | ||
|
||
return @constants_for_child_ruby_filename[filename] if @constants_for_child_ruby_filename.key?(filename) | ||
|
||
loaded = Module.new do | ||
module_eval File.binread(filename) | ||
end | ||
|
||
@constants_for_child_ruby_filename[filename] = loaded::ProtoBoeuf::Google.constants.flat_map do |const_name| | ||
mod = loaded | ||
parent_module_parts.each do |part| | ||
mod = mod.const_get(part) | ||
end | ||
mod = mod.const_get(const_name) | ||
|
||
next unless mod.is_a?(Module) | ||
|
||
# The top-level module will be our anonymous Module we created above | ||
parent_module_name = mod.name.split("::")[1..].join("::") | ||
|
||
mod.constants.map { |const_name| "#{parent_module_name}::#{const_name}" } | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# frozen_string_literal: true | ||
|
||
module ProtoBoeuf | ||
module Google | ||
autoload :Api, "protoboeuf/google/api" | ||
autoload :Protobuf, "protoboeuf/google/protobuf" | ||
end | ||
end |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# frozen_string_literal: true | ||
|
||
require "helper" | ||
require "protoboeuf/autoloadergen" | ||
|
||
class AutoloaderGenTest < ProtoBoeuf::Test | ||
FIXTURE_PATH = File.expand_path("fixtures/autoloadergen/google", __dir__) | ||
|
||
def test_generates_autoloader_module | ||
# test/fixtures/autoloadergen/google/test_protos/*.proto needs an autoloader at | ||
# test/fixtures/autoloadergen/google/test_protos.rb | ||
autoloader_rb_path = File.expand_path("test_protos.rb", FIXTURE_PATH) | ||
|
||
autoloader_ruby = ProtoBoeuf::AutoloaderGen.new(autoloader_rb_path).to_ruby | ||
|
||
# If you ever want to regenerate the expected_autoloader_ruby, run: | ||
# File.binwrite(File.expand_path("test_protos.correct.rb", FIXTURE_PATH), autoloader_ruby) | ||
expected_autoloader_ruby = File.binread(File.expand_path("test_protos.correct.rb", FIXTURE_PATH)) | ||
|
||
assert_equal(expected_autoloader_ruby, autoloader_ruby) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# frozen_string_literal: true | ||
# rubocop:disable all | ||
|
||
# Autogenerated by `rake well_known_types`. Do not edit! | ||
module ProtoBoeuf | ||
module Google | ||
module TestProtos | ||
autoload :Bicycle, | ||
"../test/fixtures/autoloadergen/google/test_protos/transportation" | ||
autoload :Boat, | ||
"../test/fixtures/autoloadergen/google/test_protos/transportation" | ||
autoload :Color, "../test/fixtures/autoloadergen/google/test_protos/color" | ||
autoload :Vehicle, | ||
"../test/fixtures/autoloadergen/google/test_protos/transportation" | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
syntax = "proto3"; | ||
|
||
package google.test_protos; | ||
|
||
enum Color { | ||
UNKNOWN = 0; | ||
RED = 1; | ||
BLUE = 2; | ||
GREEN = 3; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
syntax = "proto3"; | ||
|
||
package google.test_protos; | ||
|
||
import "color.proto"; | ||
|
||
message Boat { | ||
string make = 1; | ||
int32 year = 2; | ||
Color color = 3; | ||
} | ||
|
||
message Bicycle { | ||
string make = 1; | ||
int32 year = 2; | ||
Color color = 3; | ||
} | ||
|
||
message Vehicle { | ||
string make = 1; | ||
string model = 2; | ||
int32 year = 3; | ||
Color color = 4; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This
String#pathmap
comes from rake.Can we simplify that to get rid of that dependency?
Maybe just
Dir["#{module_filename.delete_suffix(".rb")}/**/*.rb"]
would be enough?