gluer 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # Gluer
2
2
 
3
- A configuration reloader lib.
3
+ A configuration reload tool. Useful when you want to keep registration code
4
+ next to the objects being registered, in an enviroment where another library is
5
+ already doing code reload for you. Gluer provides you a way to unregister and
6
+ re-register configuration code as the code's file is reloaded.
4
7
 
5
8
  ## Installation
6
9
 
@@ -16,9 +19,140 @@ Or install it yourself as:
16
19
 
17
20
  $ gem install gluer
18
21
 
19
- ## Usage
22
+ ## The problem this gem tries to solve
20
23
 
21
- TODO: Write usage instructions here
24
+ Let's suppose you are using a library which holds registrations in a global
25
+ registry and its usage is something along these lines:
26
+
27
+ ```ruby
28
+ require 'foo_registry'
29
+
30
+ class MyFoo
31
+ # MyFoo code...
32
+ end
33
+
34
+ FooRegistry.add_foo(MyFoo, as: 'bar') { MyBaz.init! }
35
+ ```
36
+
37
+ This is simple and harmless, until you start to use some tool or lib to reload
38
+ code for you, like ActiveSupport. If that code is put in a file which is
39
+ reloaded in every request in your app (as in development mode) you'll end up
40
+ with many registrations of `MyFoo` in `FooRegistry`. After two requests:
41
+
42
+ ```ruby
43
+ FooRegistry.get_all_foos
44
+ #=> [[MyFoo, 'bar', #<Proc:0x...>], [MyFoo, 'bar', #<Proc:0x...>]]
45
+ ```
46
+
47
+ And this sometimes is not good even when developing.
48
+
49
+ ## A solution
50
+
51
+ The way Gluer allows you to escape from this is by enclosing the registration
52
+ code in a block:
53
+
54
+ ```ruby
55
+ class MyFoo
56
+ # MyFoo code...
57
+ end
58
+
59
+ Gluer.setup(MyFoo) do
60
+ foo 'bar' { MyBaz.init! }
61
+ end
62
+ ```
63
+
64
+ That way that registration happens once even after many requests. Well, it
65
+ actually happens in every request, but gets properly removed before being
66
+ added again. So, after many requests you'll get this:
67
+
68
+ ```ruby
69
+ FooRegistry.get_all_foos
70
+ #=> [[MyFoo, 'bar', #<Proc:0x...>]]
71
+ ```
72
+
73
+ But firstly, you must configure Gluer in order to make it recognize that
74
+ `foo` call. If you are using Rails, this goes well in an initializer file, or
75
+ in a place where you are sure that `MyFoo`'s file was not loaded yet. Assuming
76
+ that the class `MyFoo`, its configuration and every other code we want Gluer to
77
+ look at are defined in `app/` folder:
78
+
79
+ ```ruby
80
+ require 'foo_registry'
81
+ require 'gluer'
82
+
83
+ # Basic configuration
84
+ Gluer.configure do |config|
85
+ # The base path is the root to start searchin files which contain
86
+ # registration code to be reloaded. In this example we'll lookup files only
87
+ # in 'app/' folder in a Rails app.
88
+ config.base_path = File.join(Rails.root, 'app')
89
+
90
+ # The file loader is a Proc that will be called to load each file found.
91
+ # In a Rails app you probably want to use ActiveSupport.
92
+ # Defaults to ->(f) { load(f) }.
93
+ config.file_loader = ->(f) { ActiveSupport::Dependencies.depend_on(f) }
94
+ end
95
+
96
+ # Registration definitions. Here we define the `foo` method.
97
+ Gluer.define_registration :foo do |registration|
98
+ registration.on_commit do |registry, context, arg, &block|
99
+ registry.add_foo(context, as: arg, &block)
100
+ end
101
+
102
+ registration.on_rollback do |registry, context, arg, &block|
103
+ registry.remove_foo(context, as: arg, &block)
104
+ end
105
+
106
+ registration.registry { FooRegistry }
107
+ end
108
+
109
+ # Put other registration definitions here.
110
+ ```
111
+
112
+ The commit hook is called when the registration is to be performed. `registry`
113
+ is, as you may guess, is the `FooRegistry` object. `context` is the argument
114
+ given to `Gluer.setup`, in this case the `MyFoo` class object. All remaining
115
+ arguments and block are forwarded from the call to `foo` in `Gluer.setup`'s
116
+ block.
117
+
118
+ The rollback hook receives the same arguments as the commit hook.
119
+
120
+ Next, add some code to do a first load. This could go in your environment.rb
121
+ file if you are using Rails:
122
+
123
+ ```ruby
124
+ config.after_initialize do
125
+ Gluer.reload # initial loading
126
+ end
127
+ ```
128
+
129
+ Lastly, in a place that runs early in every request (like a ``before_filter``
130
+ in `ApplicationController`, if you're using Rails):
131
+
132
+ ```ruby
133
+ before_filter do
134
+ unless Rails.configuration.cache_classes
135
+ # If classes were cached, our initializer already has loaded everything
136
+ # Gluer is interested in, and we don't need the overhead of loading stuff
137
+ # in every request.
138
+ Gluer.reload
139
+ end
140
+ end
141
+ ```
142
+
143
+ When the file containing `MyFoo` is reloaded, the previous registration is
144
+ rolled back, and a new registration is done. This keeps the registry
145
+ `FooRegistry` free of repetitions. In fact, you must know that `Gluer.reload`
146
+ will load that file eagerly, instead of letting your reloader lib do that for
147
+ you lazily (even if you customize the file loader, as in the example given).
148
+
149
+ ## Caveats
150
+
151
+ 1. `FooRegistry` must provide a way to unregister.
152
+ 2. It uses `grep` to get the files with `Gluer.setup`. Probably a problem in
153
+ Windows. Tested only in Linux.
154
+ 3. Loads the found files eagerly. So, you should account for the side effects
155
+ of this.
22
156
 
23
157
  ## Contributing
24
158
 
data/lib/gluer/api.rb CHANGED
@@ -5,7 +5,7 @@ module Gluer
5
5
  class << self
6
6
  def setup(context=nil, &block)
7
7
  path = block.binding.eval('__FILE__')
8
- file = file_pool.get(path)
8
+ return unless file = file_pool.get(path)
9
9
  collect_registrations(context, block) do |registration|
10
10
  file.add_registration(registration)
11
11
  end
@@ -28,7 +28,7 @@ module Gluer
28
28
 
29
29
  def default_file_filter
30
30
  Proc.new do |base_path, magic_signature|
31
- output = %x{cd '#{base_path}' && grep -IlFr '#{magic_signature}' --exclude-dir 'spec' .}
31
+ output = %x{cd '#{base_path}' && grep -IlFr '#{magic_signature}' --include=*.rb --exclude-dir 'spec' .}
32
32
  output.lines.map do |line|
33
33
  ::File.expand_path(line.chomp, base_path)
34
34
  end
data/lib/gluer/dsl.rb CHANGED
@@ -2,9 +2,13 @@ require "gluer/registration"
2
2
  require "gluer/registration_definition"
3
3
 
4
4
  module Gluer
5
- def self.define_registration(name)
5
+ def self.define_registration(name, &block)
6
6
  definition = RegistrationDefinition.new(name)
7
- yield definition
7
+ if block.arity == 1
8
+ block.call(definition)
9
+ else
10
+ definition.instance_exec(&block)
11
+ end
8
12
  DSL.add_registration_definition(name, definition)
9
13
  end
10
14
 
@@ -24,6 +28,10 @@ module Gluer
24
28
  defined_registrations.fetch(name)
25
29
  end
26
30
 
31
+ def clear
32
+ @defined_registrations = nil
33
+ end
34
+
27
35
  private
28
36
 
29
37
  def defined_registrations
@@ -9,7 +9,7 @@ module Gluer
9
9
  end
10
10
 
11
11
  def get(path)
12
- files.fetch(path)
12
+ files[path]
13
13
  end
14
14
 
15
15
  def clear
@@ -1,5 +1,3 @@
1
- require 'gluer/ordered_set'
2
-
3
1
  module Gluer
4
2
  class RegistrationPool
5
3
  def initialize
@@ -15,7 +13,7 @@ module Gluer
15
13
  end
16
14
 
17
15
  def add(registration)
18
- registrations.add(registration)
16
+ registrations.push(registration)
19
17
  end
20
18
 
21
19
  def replace(registration_pool)
@@ -23,7 +21,7 @@ module Gluer
23
21
  end
24
22
 
25
23
  def clear
26
- @registrations = OrderedSet.new
24
+ @registrations = []
27
25
  end
28
26
 
29
27
  protected
data/lib/gluer/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Gluer
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'gluer/dsl'
3
+
4
+ describe Gluer::DSL do
5
+ describe "Gluer.define_registration" do
6
+ let(:commit_block) { Proc.new { |_, _| commit_called } }
7
+ let(:rollback_block) { Proc.new { |_, _| rollback_called } }
8
+ let(:registry_block) { Proc.new { registry_called } }
9
+
10
+ before do
11
+ Gluer.define_registration(:foo, &block)
12
+ end
13
+
14
+ after do
15
+ Gluer::DSL.clear
16
+ end
17
+
18
+ context "when block given doesn't accept an argument" do
19
+ let(:block) do
20
+ # Just adding these to the current scope. The block will be
21
+ # instance-eval'ed.
22
+ commit_block = commit_block()
23
+ rollback_block = rollback_block()
24
+ registry_block = registry_block()
25
+
26
+ lambda do
27
+ on_commit(&commit_block)
28
+ on_rollback(&rollback_block)
29
+ registry(&registry_block)
30
+ end
31
+ end
32
+
33
+ it "adds the registration" do
34
+ should_receive(:commit_called)
35
+ should_receive(:rollback_called)
36
+ should_receive(:registry_called)
37
+
38
+ reg_def = Gluer::DSL.get_registration_definition(:foo)
39
+
40
+ reg_def.commit_hook.call(stub('registry'), stub('context'))
41
+ reg_def.rollback_hook.call(stub('registry'), stub('context'))
42
+ reg_def.registry_factory.call
43
+ end
44
+ end
45
+
46
+ context "when block given accepts a single argument" do
47
+ let(:block) do
48
+ # Just adding these to the current scope. The block will be
49
+ # instance-eval'ed.
50
+ commit_block = commit_block()
51
+ rollback_block = rollback_block()
52
+ registry_block = registry_block()
53
+
54
+ lambda do |registration|
55
+ registration.on_commit(&commit_block)
56
+ registration.on_rollback(&rollback_block)
57
+ registration.registry(&registry_block)
58
+ end
59
+ end
60
+
61
+ it "adds the registration" do
62
+ should_receive(:commit_called)
63
+ should_receive(:rollback_called)
64
+ should_receive(:registry_called)
65
+
66
+ reg_def = Gluer::DSL.get_registration_definition(:foo)
67
+
68
+ reg_def.commit_hook.call(stub('registry'), stub('context'))
69
+ reg_def.rollback_hook.call(stub('registry'), stub('context'))
70
+ reg_def.registry_factory.call
71
+ end
72
+ end
73
+ end
74
+ end
@@ -1,3 +1,4 @@
1
+ require 'spec_helper'
1
2
  require 'gluer/file_pool'
2
3
  require 'set'
3
4
 
@@ -35,9 +36,9 @@ describe Gluer::FilePool do
35
36
  end
36
37
 
37
38
  it "makes them available from #get" do
38
- expect { subject.get('path1') }.to_not raise_error
39
- expect { subject.get('path2') }.to_not raise_error
40
- expect { subject.get('path3') }.to_not raise_error
39
+ expect(subject.get('path1')).to_not be_nil
40
+ expect(subject.get('path2')).to_not be_nil
41
+ expect(subject.get('path3')).to_not be_nil
41
42
  end
42
43
 
43
44
  context "when filtering doesn't change in next call" do
@@ -59,7 +60,7 @@ describe Gluer::FilePool do
59
60
 
60
61
  it "keeps existing files available from #get" do
61
62
  subject.update
62
- expect { subject.get('path1') }.to_not raise_error
63
+ expect(subject.get('path1')).to_not be_nil
63
64
  end
64
65
 
65
66
  it "reloads existing files" do
@@ -95,9 +96,9 @@ describe Gluer::FilePool do
95
96
  subject.update
96
97
  end
97
98
 
98
- specify "getting the unloaded file results in key error" do
99
+ specify "getting the unloaded file results in nil" do
99
100
  subject.update
100
- expect { subject.get('path1') }.to raise_error(KeyError)
101
+ expect(subject.get('path1')).to be_nil
101
102
  end
102
103
 
103
104
  it "reloads existing files" do
@@ -114,7 +115,7 @@ describe Gluer::FilePool do
114
115
 
115
116
  it "makes the new file available from #get" do
116
117
  subject.update
117
- expect { subject.get('path4') }.to_not raise_error
118
+ expect(subject.get('path4')).to_not be_nil
118
119
  end
119
120
  end
120
121
  end
@@ -1,3 +1,4 @@
1
+ require 'spec_helper'
1
2
  require 'gluer/registration'
2
3
 
3
4
  describe Gluer::Registration do
@@ -52,8 +52,10 @@ describe "Default usage" do
52
52
  end
53
53
  registration.registry { Registry }
54
54
  end
55
+ end
55
56
 
56
- f = File.open("temp_code.rb", "w")
57
+ before do
58
+ f = File.new("temp_code.rb", "w")
57
59
  f.write(<<-CODE)
58
60
  require 'gluer'
59
61
  Context = Object.new
@@ -86,7 +88,7 @@ describe "Default usage" do
86
88
  before do
87
89
  Gluer.reload
88
90
 
89
- f = File.open("temp_code.rb", "w")
91
+ f = File.new("temp_code.rb", "w")
90
92
  f.write(<<-CODE)
91
93
  require 'gluer'
92
94
  Context = Object.new
@@ -115,7 +117,7 @@ describe "Default usage" do
115
117
  before do
116
118
  Gluer.reload
117
119
 
118
- f = File.open("temp_code.rb", "w")
120
+ f = File.new("temp_code.rb", "w")
119
121
  f.write(<<-CODE)
120
122
  require 'gluer'
121
123
  CODE
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gluer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-08 00:00:00.000000000 Z
12
+ date: 2013-05-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -112,16 +112,16 @@ files:
112
112
  - lib/gluer/dsl.rb
113
113
  - lib/gluer/file.rb
114
114
  - lib/gluer/file_pool.rb
115
- - lib/gluer/ordered_set.rb
116
115
  - lib/gluer/registration.rb
117
116
  - lib/gluer/registration_collection.rb
118
117
  - lib/gluer/registration_definition.rb
119
118
  - lib/gluer/registration_hook.rb
120
119
  - lib/gluer/registration_pool.rb
121
120
  - lib/gluer/version.rb
122
- - spec/file_pool_spec.rb
121
+ - spec/gluer/dsl_spec.rb
122
+ - spec/gluer/file_pool_spec.rb
123
+ - spec/gluer/registration_spec.rb
123
124
  - spec/integration/default_usage_spec.rb
124
- - spec/registration_spec.rb
125
125
  - spec/spec_helper.rb
126
126
  homepage: ''
127
127
  licenses:
@@ -138,7 +138,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
138
138
  version: '0'
139
139
  segments:
140
140
  - 0
141
- hash: -2018170584366941344
141
+ hash: 2279684241105677342
142
142
  required_rubygems_version: !ruby/object:Gem::Requirement
143
143
  none: false
144
144
  requirements:
@@ -147,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
147
  version: '0'
148
148
  segments:
149
149
  - 0
150
- hash: -2018170584366941344
150
+ hash: 2279684241105677342
151
151
  requirements: []
152
152
  rubyforge_project:
153
153
  rubygems_version: 1.8.25
@@ -155,7 +155,8 @@ signing_key:
155
155
  specification_version: 3
156
156
  summary: Configuration reloader
157
157
  test_files:
158
- - spec/file_pool_spec.rb
158
+ - spec/gluer/dsl_spec.rb
159
+ - spec/gluer/file_pool_spec.rb
160
+ - spec/gluer/registration_spec.rb
159
161
  - spec/integration/default_usage_spec.rb
160
- - spec/registration_spec.rb
161
162
  - spec/spec_helper.rb
@@ -1,23 +0,0 @@
1
- module Gluer
2
- class OrderedSet
3
- include Enumerable
4
-
5
- def initialize(initial=[])
6
- @collection = initial
7
- end
8
-
9
- def each
10
- @collection.each { |item| yield(item) }
11
- end
12
-
13
- def add(new)
14
- @collection.delete_if { |existing| existing == new }
15
- @collection << new
16
- end
17
-
18
- def -(other)
19
- raise ArgumentError unless other.is_a?(OrderedSet)
20
- self.class.new(@collection - other.instance_eval { @collection })
21
- end
22
- end
23
- end