Gluer
A configuration reload tool. Useful when you want to keep registration code next to the objects being registered, in an enviroment where another library is already doing code reload for you. Gluer provides you a way to unregister and re-register configuration code as the code's file is reloaded.
Installation
Add this line to your application's Gemfile:
gem 'gluer'
And then execute:
$ bundle
Or install it yourself as:
$ gem install gluer
The problem this gem tries to solve
Let's suppose you are using a library which holds registrations in a global registry and its usage is something along these lines:
require 'foo_registry'
class MyFoo
# MyFoo code...
end
FooRegistry.add_foo(MyFoo, as: 'bar') { MyBaz.init! }
This is simple and harmless, until you start to use some tool or lib to reload
code for you, like ActiveSupport. If that code is put in a file which is
reloaded in every request in your app (as in development mode) you'll end up
with many registrations of MyFoo
in FooRegistry
. After two requests:
FooRegistry.get_all_foos
#=> [[MyFoo, 'bar', #<Proc:0x...>], [MyFoo, 'bar', #<Proc:0x...>]]
And this sometimes is not good even when developing.
A solution
The way Gluer allows you to escape from this is by enclosing the registration code in a block:
class MyFoo
# MyFoo code...
end
Gluer.setup(MyFoo) do
foo 'bar' { MyBaz.init! }
end
That way that registration happens once even after many requests. Well, it actually happens in every request, but gets properly removed before being added again. So, after many requests you'll get this:
FooRegistry.get_all_foos
#=> [[MyFoo, 'bar', #<Proc:0x...>]]
But firstly, you must configure Gluer in order to make it recognize that
foo
call. If you are using Rails, this goes well in an initializer file, or
in a place where you are sure that MyFoo
's file was not loaded yet. Assuming
that the class MyFoo
, its configuration and every other code we want Gluer to
look at are defined in app/
folder:
require 'foo_registry'
require 'gluer'
# Basic configuration
Gluer.configure do |config|
# The base path is the root to start searchin files which contain
# registration code to be reloaded. In this example we'll lookup files only
# in 'app/' folder in a Rails app.
config.base_path = File.join(Rails.root, 'app')
# The file loader is a Proc that will be called to load each file found.
# In a Rails app you probably want to use ActiveSupport.
# Defaults to ->(f) { load(f) }.
config.file_loader = ->(f) { ActiveSupport::Dependencies.depend_on(f) }
end
# Registration definitions. Here we define the `foo` method.
Gluer.define_registration :foo do |registration|
registration.on_commit do |registry, context, arg, &block|
registry.add_foo(context, as: arg, &block)
end
registration.on_rollback do |registry, context, arg, &block|
registry.remove_foo(context, as: arg, &block)
end
registration.registry { FooRegistry }
end
# Put other registration definitions here.
The commit hook is called when the registration is to be performed. registry
is, as you may guess, is the FooRegistry
object. context
is the argument
given to Gluer.setup
, in this case the MyFoo
class object. All remaining
arguments and block are forwarded from the call to foo
in Gluer.setup
's
block.
The rollback hook receives the same arguments as the commit hook.
Next, add some code to do a first load. This could go in your environment.rb file if you are using Rails:
config.after_initialize do
Gluer.reload # initial loading
end
Lastly, in a place that runs early in every request (like a before_filter
in ApplicationController
, if you're using Rails):
before_filter do
unless Rails.configuration.cache_classes
# If classes were cached, our initializer already has loaded everything
# Gluer is interested in, and we don't need the overhead of loading stuff
# in every request.
Gluer.reload
end
end
When the file containing MyFoo
is reloaded, the previous registration is
rolled back, and a new registration is done. This keeps the registry
FooRegistry
free of repetitions. In fact, you must know that Gluer.reload
will load that file eagerly, instead of letting your reloader lib do that for
you lazily (even if you customize the file loader, as in the example given).
Caveats
FooRegistry
must provide a way to unregister.- It uses
grep
to get the files withGluer.setup
. Probably a problem in Windows. Tested only in Linux. - Loads the found files eagerly. So, you should account for the side effects of this.
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request