@@ -19,19 +19,23 @@ module FatFreeCRM
19
19
module Callback
20
20
@@classes = [ ] # Classes that inherit from FatFreeCRM::Callback::Base.
21
21
@@responder = { } # Class instances that respond to (i.e. implement) hook methods.
22
+ # Also includes class instances that implement a
23
+ # set of view hook operations (insert_after, replace, etc).
22
24
23
25
# Adds a class inherited from from FatFreeCRM::Callback::Base.
24
26
#--------------------------------------------------------------------------
25
27
def self . add ( klass )
26
28
@@classes << klass
27
29
end
28
30
31
+ # [Controller] and [Legacy View] Hooks
32
+ # -----------------------------------------------------------------------------
33
+
29
34
# Finds class instance that responds to given method.
30
35
#------------------------------------------------------------------------------
31
36
def self . responder ( method )
32
37
@@responder [ method ] ||= @@classes . map { |klass | klass . instance } . select { |instance | instance . respond_to? ( method ) }
33
38
end
34
-
35
39
# Invokes the hook named :method and captures its output. The hook returns:
36
40
# - empty array if no hook with this name was detected.
37
41
# - array with single item returned by the hook.
@@ -43,14 +47,58 @@ def self.hook(method, caller, context = {})
43
47
end
44
48
end
45
49
50
+
51
+ # [View] Hooks
52
+ # -----------------------------------------------------------------------------
53
+
54
+ # Find class instances that contain operations for the given view hook.
55
+ #------------------------------------------------------------------------------
56
+ def self . view_responder ( method )
57
+ @@responder [ method ] ||= @@classes . map { |klass | klass . instance } . select { |instance | instance . class . view_hooks [ method ] }
58
+ end
59
+ # Invokes the view hook Proc stored under :hook and captures its output.
60
+ # => Instead of defining methods on the class, view hooks are
61
+ # stored as Procs in a hash. This allows the same hook to be manipulated in
62
+ # multiple ways from within a single Callback subclass.
63
+ # The hook returns:
64
+ # - empty hash if no hook with this name was detected.
65
+ # - a hash of arrays containing Procs and positions to insert content.
66
+ #--------------------------------------------------------------------------
67
+ def self . view_hook ( hook , caller , context = { } )
68
+ view_responder ( hook ) . inject ( Hash . new ( [ ] ) ) do |response , instance |
69
+ # Process each operation within each view hook, storing the data in a hash.
70
+ instance . class . view_hooks [ hook ] . each do |op |
71
+ response [ op [ :position ] ] += [ op [ :proc ] . call ( caller , context ) ]
72
+ end
73
+ response
74
+ end
75
+ end
76
+
46
77
#--------------------------------------------------------------------------
47
78
class Base
48
79
include Singleton
49
-
50
80
def self . inherited ( child )
51
81
FatFreeCRM ::Callback . add ( child )
82
+ # Positioning hash to determine where content is placed.
83
+ child . class_eval do
84
+ @view_hooks = Hash . new ( [ ] )
85
+ end
52
86
super
53
87
end
88
+
89
+ class << self
90
+ attr_accessor :view_hooks
91
+
92
+ def add_view_hook ( hook , proc , position )
93
+ @view_hooks [ hook ] += [ { :proc => proc ,
94
+ :position => position } ]
95
+ end
96
+
97
+ def insert_before ( hook , &block ) ; add_view_hook ( hook , block , :before ) ; end
98
+ def insert_after ( hook , &block ) ; add_view_hook ( hook , block , :after ) ; end
99
+ def replace ( hook , &block ) ; add_view_hook ( hook , block , :replace ) ; end
100
+ def remove ( hook ) ; add_view_hook ( hook , Proc . new { "" } , :replace ) ; end
101
+ end
54
102
55
103
end # class Base
56
104
@@ -59,9 +107,32 @@ def self.inherited(child)
59
107
# data otherwise.
60
108
#--------------------------------------------------------------------------
61
109
module Helper
62
- def hook ( method , caller , context = { } )
63
- data = FatFreeCRM ::Callback . hook ( method , caller , context )
64
- caller . class . to_s . start_with? ( "ActionView" ) ? data . join : data
110
+ def hook ( method , caller , context = { } , &block )
111
+ is_view_hook = caller . class . to_s . start_with? ( "ActionView" )
112
+
113
+ # If a block was given, hooks are able to replace, append or prepend view content.
114
+ if block_given? and is_view_hook
115
+ hooks = FatFreeCRM ::Callback . view_hook ( method , caller , context )
116
+ # Add content to the view in the following order:
117
+ # -- before
118
+ # -- replace || original block
119
+ # -- after
120
+ view_data = [ ]
121
+ hooks [ :before ] . each { |data | view_data << data }
122
+ # Only render the original view block if there are no pending :replace operations
123
+ if hooks [ :replace ] . empty?
124
+ view_data << capture ( &block )
125
+ else
126
+ hooks [ :replace ] . each { |data | view_data << data }
127
+ end
128
+ hooks [ :after ] . each { |data | view_data << data }
129
+ view_data . join
130
+
131
+ else
132
+ # Hooks called without blocks are either controller or legacy view hooks
133
+ data = FatFreeCRM ::Callback . hook ( method , caller , context )
134
+ is_view_hook ? data . join : data
135
+ end
65
136
end
66
137
end # module Helper
67
138
0 commit comments