Skip to content
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

Add support for lazy-loading by requiring octokit/lazy, rather than just octokit #1431

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/octokit.yml
Expand Up @@ -56,3 +56,9 @@ jobs:
GITHUB_CI: 1
RUBYOPT: --enable-frozen-string-literal
run: bundle exec rspec -w
- name: Test with RSpec in lazy-loaded mode
env:
GITHUB_CI: 1
RUBYOPT: --enable-frozen-string-literal
OCTOKIT_REQUIRE: octokit/lazy
run: bundle exec rspec -w
2 changes: 1 addition & 1 deletion Gemfile
Expand Up @@ -42,4 +42,4 @@ group :test, :development do
gem 'rubocop'
end

gemspec
gemspec require: false
6 changes: 5 additions & 1 deletion README.md
Expand Up @@ -70,7 +70,7 @@ client.readme 'al3x/sovereign', :accept => 'application/vnd.github.html'

## Installation

Install via Rubygems
Install via RubyGems

gem install octokit

Expand All @@ -82,6 +82,10 @@ Access the library in Ruby:

require 'octokit'

To speed up the boot of your application, you can `require 'octokit/lazy'` which will lazy-load required code as you use it, rather than requiring everything at once. You may also need to configure this in your `Gemfile` like this:

gem "octokit", "~> 4.0", require: "octokit/lazy"

## Making requests

[API methods][] are available as client instance methods.
Expand Down
59 changes: 6 additions & 53 deletions lib/octokit.rb
Expand Up @@ -3,56 +3,9 @@
require 'octokit/enterprise_admin_client'
require 'octokit/enterprise_management_console_client'

# Ruby toolkit for the GitHub API
module Octokit
class << self
include Octokit::Configurable

# API client based on configured options {Configurable}
#
# @return [Octokit::Client] API wrapper
def client
return @client if defined?(@client) && @client.same_options?(options)
@client = Octokit::Client.new(options)
end

# EnterpriseAdminClient client based on configured options {Configurable}
#
# @return [Octokit::EnterpriseAdminClient] API wrapper
def enterprise_admin_client
return @enterprise_admin_client if defined?(@enterprise_admin_client) && @enterprise_admin_client.same_options?(options)
@enterprise_admin_client = Octokit::EnterpriseAdminClient.new(options)
end

# EnterpriseManagementConsoleClient client based on configured options {Configurable}
#
# @return [Octokit::EnterpriseManagementConsoleClient] API wrapper
def enterprise_management_console_client
return @enterprise_management_console_client if defined?(@enterprise_management_console_client) && @enterprise_management_console_client.same_options?(options)
@enterprise_management_console_client = Octokit::EnterpriseManagementConsoleClient.new(options)
end

private

def respond_to_missing?(method_name, include_private=false)
client.respond_to?(method_name, include_private) ||
enterprise_admin_client.respond_to?(method_name, include_private) ||
enterprise_management_console_client.respond_to?(method_name, include_private)
end

def method_missing(method_name, *args, &block)
if client.respond_to?(method_name)
return client.send(method_name, *args, &block)
elsif enterprise_admin_client.respond_to?(method_name)
return enterprise_admin_client.send(method_name, *args, &block)
elsif enterprise_management_console_client.respond_to?(method_name)
return enterprise_management_console_client.send(method_name, *args, &block)
end

super
end

end
end

Octokit.setup
# We have already required `Client`, `EnterpriseAdminClient` and
# `EnterpriseManagementConsoleClient` above, so the `autoload` declaration in `lib/octokit/base.rb`
# won't do anything. This is the default for Octokit, and leads to a simpler and more reliable
# user experience, even if it makes booting your app a little slower compared to the lazy-loaded
# experience you get if you call `require 'octokit/lazy'`.
require 'octokit/base'
64 changes: 64 additions & 0 deletions lib/octokit/base.rb
@@ -0,0 +1,64 @@
# Ruby toolkit for the GitHub API
#
# NOTE: The `Octokit` module is defined here, rather than in `lib/octokit.rb`, to allow
# the gem to include a lazy-loaded version where dependencies are required as needed,
# as well as a default where everything is required together.
module Octokit
# These autoload statements won't do anything if the module has already been loaded, as
# will happen if the gem is required in the normal, automatic way. They will only come
# into affect if the user requires `octokit/lazy`.
autoload(:Client, File.join(__dir__, 'client'))
autoload(:EnterpriseAdminClient, File.join(__dir__, 'enterprise_admin_client'))
autoload(:EnterpriseManagementConsoleClient, File.join(__dir__, 'enterprise_management_console_client'))

class << self
include Octokit::Configurable

# API client based on configured options {Configurable}
#
# @return [Octokit::Client] API wrapper
def client
return @client if defined?(@client) && @client.same_options?(options)
@client = Octokit::Client.new(options)
end

# EnterpriseAdminClient client based on configured options {Configurable}
#
# @return [Octokit::EnterpriseAdminClient] API wrapper
def enterprise_admin_client
return @enterprise_admin_client if defined?(@enterprise_admin_client) && @enterprise_admin_client.same_options?(options)
@enterprise_admin_client = Octokit::EnterpriseAdminClient.new(options)
end

# EnterpriseManagementConsoleClient client based on configured options {Configurable}
#
# @return [Octokit::EnterpriseManagementConsoleClient] API wrapper
def enterprise_management_console_client
return @enterprise_management_console_client if defined?(@enterprise_management_console_client) && @enterprise_management_console_client.same_options?(options)
@enterprise_management_console_client = Octokit::EnterpriseManagementConsoleClient.new(options)
end

private

def respond_to_missing?(method_name, include_private=false)
client.respond_to?(method_name, include_private) ||
enterprise_admin_client.respond_to?(method_name, include_private) ||
enterprise_management_console_client.respond_to?(method_name, include_private)
end

def method_missing(method_name, *args, &block)
if client.respond_to?(method_name)
return client.send(method_name, *args, &block)
elsif enterprise_admin_client.respond_to?(method_name)
return enterprise_admin_client.send(method_name, *args, &block)
elsif enterprise_management_console_client.respond_to?(method_name)
return enterprise_management_console_client.send(method_name, *args, &block)
end

super
end

end
end

Octokit.setup
3 changes: 3 additions & 0 deletions lib/octokit/lazy.rb
@@ -0,0 +1,3 @@
require 'octokit/default'
require 'octokit/configurable'
require 'octokit/base'
9 changes: 8 additions & 1 deletion spec/helper.rb
Expand Up @@ -4,7 +4,14 @@
end

require 'json'
require 'octokit'

# This allows us to use the `OCTOKIT_REQUIRE` environment variable to switch in our
# tests between requiring all of Octokit (`require 'octokit'`) and using the lazy-
# loaded version (`require 'octokit/lazy'`).
octokit_require = ENV.fetch('OCTOKIT_REQUIRE', 'octokit')
require octokit_require

require 'faraday'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to manually require Faraday because, if we end up requiring the "lazy" version of Octokit above, then Faraday won't have been loaded when we try to look at its version later in this file.

require 'rspec'
require 'webmock/rspec'
require 'base64'
Expand Down