big_spoon 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES.md CHANGED
@@ -1,4 +1,9 @@
1
1
  # CHANGELOG
2
+ ## 0.1.1
3
+ ### :if, :unless, and tested in production!
4
+ - :if => :method and :unless => lambda { false } are now supported.
5
+ - That, or any variation of those two you can imagine.
6
+
2
7
  ## 0.0.1
3
8
  ### Basic functionality
4
9
  - Hooks / callbacks are working BEFORE OR AFTER a method gets defined
data/README.md CHANGED
@@ -1,58 +1,116 @@
1
1
  # Big Spoon
2
2
  Like the big spoon, **Big Spoon** wraps around your own methods.
3
3
  It adds before and after callbacks to ANY method in any Ruby class.
4
+ Basically, now you can add hooks and/or callback to **any** Ruby method without fear of reprisals.
4
5
 
5
6
  There were those who wanted to call it "sandwich," but they were killed.
6
7
 
7
- **Big Spoon** adds _hooks_ around ANY method. It can do this before
8
- or after the methods are defined, making it awesome for fun stuff like adding extra hooks around events at the top of
9
- a class definition without having to worry about when the method gets defined.
8
+ ## Basic Usage
10
9
 
11
- I think I'm going to import it into all of my libraries to avoid alias\_method_chaining anything ever again.
10
+ Use **Big Spoon** like you would any other callbacks! Of course there's, like, at least three ways to do that. So **Big Spoon** supports all of em. All
11
+ three let you define before- and after-methods or blocks to any method your class could, like, ever call. The safest is block form:
12
12
 
13
- Anyway, here's how it works for now, DON'T use it cause I haven't tested it with shit:
14
-
15
- ## Usage
13
+ ```ruby
14
+ class User
15
+ hooks do
16
+ before :get_your_hands_off_of_my_woman, :listen_to_the_darkness
17
+ after :get_your_hands_off_of_my_woman { listen_to_moar_darkness! }
18
+ end
16
19
 
17
- Let's say, for example, you want to reset an instance variable in a model when it gets reloaded.
20
+ def get_your_hands_off_of_my_woman
21
+ puts "Get your hands off of my woman, motherf*cker!"
22
+ end
18
23
 
19
- For shits and giggles and real-world-clarity, I'll use an ActiveRecord model but this could be
20
- anything that knows about `reload`.
24
+ protected
25
+ def listen_to_the_darkness!
26
+ `osascript "tell iTunes to play some awesome"`
27
+ end
21
28
 
22
- ```
23
- class User < ActiveRecord::Base
24
- def name
25
- @name ||= "#{first_name} #{last_name}"
29
+ def listen_to_moar_darkness
30
+ `osascript "tell iTuens to continue not to suck after that last rad song"`
26
31
  end
27
32
  end
28
33
  ```
29
34
 
30
- So now, when you do something like this:
35
+ This is designed not to conflict with Rails callbacks and their siblings. **But if'n
36
+ you're a real scofflaw (_and you f*cking should be!_), you can just do it normal-like:**
31
37
 
32
- ```
33
- user = User.new(:first_name => "Flip", :last_name => "Sasser")
34
- user.name #=> "Flip Sasser"
35
- user.first_name = "Elizabeth"
36
- user.name #=> "Flip Sasser"
37
- # OH NOE MY INSTANCE VARIABLE GOT CACHED LIKE IT SHOULD
38
- user.reload.name #=> "Flip Sasser"
39
- # OH NOE RELOAD DOESN'T RESET INSTANCE VARIABLES
40
- ```
38
+ ```ruby
39
+ class User
40
+ before_believe_in_a_thing_called_love :listen_to_the_rhythm_of_my_heart
41
+
42
+ def believe_in_a_thing_called_love
43
+ puts "We'll be rockin til the sun goes down!"
44
+ end
45
+
46
+ def listen_to_the_rhythm_of_my_heart
47
+ listen("127.0.0.1") do
48
+ match /(lub|dub)/ do
49
+ puts "Edgar Allan F*cking Poe?!"
50
+ end
51
+ end
52
+ end
53
+ ````
54
+
55
+ So. Add hooks. To any Ruby method. That's pretty damn awesome, where I come form. I SAID "FORM," son!
56
+
57
+ But, as they say, "love is only a feeling." So spoon like there's no tomorrow.
58
+
59
+ ## But there's (conditionally) more!
60
+
61
+ Because ActiveModel callbacks are just so damn delightful, I've added some fun conditional sugar to match their wonderful. So g'head and add some `:if` conditions to your callbacks:
62
+
63
+ ```ruby
64
+ class User
65
+ before :love_on_the_rocks, :add_ice, :if => :no_ice?
41
66
 
42
- But what if you could hook into `ActiveRecord::Base#reload`?
67
+ def love_on_the_rocks
68
+ puts "Loo-OOOVE ON THE ROCKS! YOU'D DO ANYTHING FOR A QUIET LIFE!"
69
+ end
70
+
71
+ def no_ice?
72
+ Ice.empty?
73
+ end
43
74
 
75
+ protected
76
+ def add_ice
77
+ Ice.create!
78
+ end
79
+ end
44
80
  ```
45
- class User < ActiveRecord::Base
46
- hooks do
47
- before_reload { @name = nil }
81
+
82
+ Conditional callbacks also support `:unless`, just like their ActiveModel ancestors. Or should I say "inspiritors?" Is that word? Shut up, of course it is. Anyway:
83
+
84
+ ```ruby
85
+ class User
86
+ before :love_on_the_rocks, :add_ice, :unless => :not_in_tys_mazada?
87
+
88
+ def not_in_tys_mazada?
89
+ !((mazda = User.find_by_slug("ty").car) && mazda.has_rad_bass?)
48
90
  end
49
91
 
50
- def name
51
- @name ||= "#{first_name} #{last_name}"
92
+ protected
93
+ def add_ice
94
+ Ice.create!
52
95
  end
53
96
  end
54
97
  ```
55
98
 
56
- Now you can reset the instance variable. Neat, right? Obviously this is way more awesome way more places but that's the long and short of it.
99
+ And to recap! Just as with the believing-in-things-called-love example, both could be re-written as
100
+
101
+ ```ruby
102
+ before_love_on_the_rocks :add_ice, :if => :no_ice?
103
+ ```
104
+
105
+ and
106
+
107
+ ```ruby
108
+ before_love_on_the_rocks :add_ice, :unless => :not_in_tys_mazda?
109
+ ```
110
+
111
+ respectively.
112
+
113
+ HAPPY **CALLING-OF-THE-BACK**, FRIENDS!
114
+
57
115
 
58
116
  Copyright © 2012 Delightful Widgets Inc. No warranty so don't sue me or my company THANKS!
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ begin
9
9
  BigSpoon will add a hooks method to every class. Call that method with a block and add all kinds of fun hooks before and after your methods.
10
10
  }
11
11
  gemspec.email = "flip@x451.com"
12
- gemspec.homepage = "http://github.com/Plinq/big_spoon"
12
+ gemspec.homepage = "https://github.com/flipsasser/big_spoon"
13
13
  gemspec.authors = ["Flip Sasser"]
14
14
  end
15
15
  rescue LoadError
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.1.0
data/big_spoon.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "big_spoon"
8
- s.version = "0.0.1"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Flip Sasser"]
@@ -32,7 +32,7 @@ Gem::Specification.new do |s|
32
32
  "spec/lib/big_spoon_spec.rb",
33
33
  "spec/spec_helper.rb"
34
34
  ]
35
- s.homepage = "http://github.com/Plinq/big_spoon"
35
+ s.homepage = "https://github.com/flipsasser/big_spoon"
36
36
  s.require_paths = ["lib"]
37
37
  s.rubygems_version = "1.8.24"
38
38
  s.summary = "Adds before and after hooks to any method, because that's just how things should be"
@@ -14,13 +14,13 @@ module BigSpoon
14
14
  attr_accessor :klass
15
15
 
16
16
  # Define a method to execute after another
17
- def after(method_to_hook, method_to_call = nil, &block)
18
- hook(:after, method_to_hook, method_to_call, &block)
17
+ def after(method_to_hook, method_to_call = nil, options = {}, &block)
18
+ hook(:after, method_to_hook, method_to_call, options, &block)
19
19
  end
20
20
 
21
21
  # Define a method to execute before another
22
- def before(method_to_hook, method_to_call = nil, &block)
23
- hook(:before, method_to_hook, method_to_call, &block)
22
+ def before(method_to_hook, method_to_call = nil, options = {}, &block)
23
+ hook(:before, method_to_hook, method_to_call, options, &block)
24
24
  end
25
25
 
26
26
  # def clear!
@@ -45,14 +45,14 @@ module BigSpoon
45
45
  hooked_method = hooked_method(method_to_hook)
46
46
  original_method = original_method(method_to_hook)
47
47
  line = __LINE__; alias_these_hooks = <<-hooks
48
- alias :#{original_method} :#{method_to_hook}
49
- def #{hooked_method}(*args)
50
- Hook.for(self.class).execute_before(:#{method_to_hook}, self)
51
- result = #{original_method}
52
- Hook.for(self.class).execute_after(:#{method_to_hook}, self)
53
- result
54
- end
55
- alias :#{method_to_hook} :#{hooked_method}
48
+ alias :#{original_method} :#{method_to_hook}
49
+ def #{hooked_method}(*args)
50
+ Hook.for(self.class).execute_before(:#{method_to_hook}, self)
51
+ result = #{original_method}
52
+ Hook.for(self.class).execute_after(:#{method_to_hook}, self)
53
+ result
54
+ end
55
+ alias :#{method_to_hook} :#{hooked_method}
56
56
  hooks
57
57
  klass.class_eval alias_these_hooks, __FILE__, line.succ
58
58
  end
@@ -69,8 +69,8 @@ module BigSpoon
69
69
  hooked_method = hooked_method(method_to_hook)
70
70
  original_method = original_method(method_to_hook)
71
71
  line = __LINE__; alias_these_hooks = <<-hooks
72
- alias :#{method_to_hook} #{original_method}
73
- remove_method #{hooked_method}
72
+ alias :#{method_to_hook} #{original_method}
73
+ remove_method #{hooked_method}
74
74
  hooks
75
75
  klass.class_eval alias_these_hooks, __FILE__, line.succ
76
76
  end
@@ -79,18 +79,35 @@ module BigSpoon
79
79
  def execute(before_or_after, method_to_hook, instance)
80
80
  methods[method_to_hook] ||= {}
81
81
  methods[method_to_hook][before_or_after] ||= []
82
- methods[method_to_hook][before_or_after].each do |hook_to_call|
83
- case hook_to_call
84
- when Proc
85
- instance.instance_eval(&hook_to_call)
82
+ methods[method_to_hook][before_or_after].each do |method_definition|
83
+ execute_for_reals = if method_definition[:if].is_a?(Proc)
84
+ instance.instance_eval(&method_definition[:if])
85
+ elsif method_definition[:if]
86
+ instance.send(method_definition[:if])
87
+ elsif method_definition[:unless].is_a?(Proc)
88
+ !instance.instance_eval(&method_definition[:unless])
89
+ elsif method_definition[:unless]
90
+ !instance.send(method_definition[:unless])
86
91
  else
87
- instance.send(hook_to_call)
92
+ true
93
+ end
94
+ if execute_for_reals
95
+ case method_definition[:method]
96
+ when Proc
97
+ instance.instance_eval(&method_definition[:method])
98
+ else
99
+ instance.send(method_definition[:method])
100
+ end
88
101
  end
89
102
  end
90
103
  end
91
104
 
92
- def hook(before_or_after, method_to_hook, method_to_call = nil, &block)
105
+ def hook(before_or_after, method_to_hook, method_to_call = nil, options = {}, &block)
93
106
  method_to_hook = method_to_hook.to_sym
107
+ if method_to_call.is_a? Hash
108
+ options = method_to_call
109
+ method_to_call = nil
110
+ end
94
111
  if block_given?
95
112
  method_to_call = block
96
113
  else
@@ -99,7 +116,10 @@ module BigSpoon
99
116
 
100
117
  methods[method_to_hook] ||= {}
101
118
  methods[method_to_hook][before_or_after] ||= []
102
- methods[method_to_hook][before_or_after].push(method_to_call) unless methods[method_to_hook][before_or_after].include?(method_to_call)
119
+ method_definition = options.merge({:method => method_to_call})
120
+ methods[method_to_hook][before_or_after].push(method_definition) unless methods[method_to_hook][before_or_after].any? do |other_method_definition|
121
+ other_method_definition[:method] == method_to_call
122
+ end
103
123
 
104
124
  hook!(method_to_hook) if should_hook?(method_to_hook)
105
125
  end
@@ -112,8 +132,8 @@ module BigSpoon
112
132
  klass.method_defined?(hooked_method(method_to_hook))
113
133
  end
114
134
 
115
- def hooked_method(method_to_hook)
116
- "_big_spoon_alias_#{method_to_hook}"
135
+ def hooked_method(method_to_hook)
136
+ "_big_spoon_alias_#{method_to_hook}"
117
137
  end
118
138
 
119
139
  def method_missing(method_name, *args)
@@ -131,8 +151,8 @@ module BigSpoon
131
151
  @methods ||= {}
132
152
  end
133
153
 
134
- def original_method(method_to_hook)
135
- "_big_spoon_original_#{method_to_hook}"
154
+ def original_method(method_to_hook)
155
+ "_big_spoon_original_#{method_to_hook}"
136
156
  end
137
157
  end
138
158
  end
data/lib/big_spoon.rb CHANGED
@@ -19,6 +19,18 @@ module BigSpoon
19
19
  end
20
20
  @hooks
21
21
  end # `hooks` method
22
+
23
+ private
24
+ def method_missing(method_name, *args)
25
+ case method_name.to_s
26
+ when /^after_(.+)$/
27
+ hooks.after $1, *args
28
+ when /^before_(.+)$/
29
+ hooks.before $1, *args
30
+ else
31
+ super
32
+ end
33
+ end
22
34
  end # `ClassMethods` module
23
35
  end # `BigSpoon` module
24
36
 
@@ -18,20 +18,12 @@ class BigSpoonTest
18
18
  true
19
19
  end
20
20
 
21
- def dont_hook
21
+ def please_dont_hook
22
22
  false
23
23
  end
24
24
 
25
- def hook_1
26
- :hook_1
27
- end
28
-
29
- def hook_2
30
- :hook_2
31
- end
32
-
33
- def hook_3
34
- :hook_3
25
+ (1..8).each do |index|
26
+ define_method("hook_#{index}") { "hook_#{index}"}
35
27
  end
36
28
  end
37
29
 
@@ -88,4 +80,40 @@ describe BigSpoon do
88
80
  end
89
81
  @big_spoon_test.foo!
90
82
  end
83
+
84
+ it "should let me hook outside of the hooks block" do
85
+ @big_spoon_test.should_receive(:by_all_means_hook)
86
+ BigSpoonTest.before_foo! :by_all_means_hook
87
+ @big_spoon_test.foo!
88
+ end
89
+
90
+ it "should not hook if an :if method returns false" do
91
+ @big_spoon_test.should_not_receive(:hook_4)
92
+ BigSpoonTest.before_foo! :hook_4, :if => :please_dont_hook
93
+ @big_spoon_test.foo!
94
+ end
95
+
96
+ it "SHOULD hook if an :if method returns true" do
97
+ @big_spoon_test.should_receive(:hook_5)
98
+ BigSpoonTest.before_foo! :hook_5, :if => :by_all_means_hook
99
+ @big_spoon_test.foo!
100
+ end
101
+
102
+ it "should not hook if an :unless method returns true" do
103
+ @big_spoon_test.should_not_receive(:hook_6)
104
+ BigSpoonTest.before_foo! :hook_6, :unless => :by_all_means_hook
105
+ @big_spoon_test.foo!
106
+ end
107
+
108
+ it "SHOULD hook if an :unless method returns false" do
109
+ @big_spoon_test.should_receive(:hook_7)
110
+ BigSpoonTest.before_foo! :hook_7, :unless => :please_dont_hook
111
+ @big_spoon_test.foo!
112
+ end
113
+
114
+ it "should meta-program before_* hooks" do
115
+ @big_spoon_test.should_receive(:hook_8)
116
+ BigSpoonTest.before_foo! :hook_8
117
+ @big_spoon_test.foo!
118
+ end
91
119
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: big_spoon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -35,7 +35,7 @@ files:
35
35
  - lib/big_spoon/hook.rb
36
36
  - spec/lib/big_spoon_spec.rb
37
37
  - spec/spec_helper.rb
38
- homepage: http://github.com/Plinq/big_spoon
38
+ homepage: https://github.com/flipsasser/big_spoon
39
39
  licenses: []
40
40
  post_install_message:
41
41
  rdoc_options: []
@@ -49,7 +49,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
49
  version: '0'
50
50
  segments:
51
51
  - 0
52
- hash: 4152067215125062110
52
+ hash: -3518133336514115333
53
53
  required_rubygems_version: !ruby/object:Gem::Requirement
54
54
  none: false
55
55
  requirements: