1 require 'ostruct'
2 module Radiant
3 module ResourceResponses
4 def self.extended(base)
5 base.send :class_inheritable_writer, :responses
6 base.send :include, InstanceMethods
7 end
8
9 def responses
10 r = (self.responses = read_inheritable_attribute(:responses) || Collector.new)
11 yield r if block_given?
12 r
13 end
14
15 module InstanceMethods
16 def response_for(action)
17 responses = self.class.responses.send(action)
18 respond_to do |wants|
19 responses.each_format do |f, format_block|
20 if format_block
21 wants.send(f, &wrap(format_block))
22 else
23 wants.send(f)
24 end
25 end
26 responses.each_published do |pub, pub_block|
27 wants.send(pub, &wrap(pub_block))
28 end
29 if responses.default
30 wants.any(&wrap(responses.default))
31 else
32 wants.any
33 end
34 end
35 end
36
37 def wrap(proc)
38 # Makes sure our response blocks get evaluated in the right context
39 lambda do
40 # Ruby 1.9.2 yields self in instance_eval... see https://gist.github.com/479572
41 # lambdas are as strict as methods in 1.9.x, making sure that the args match, Procs are not.
42 if RUBY_VERSION =~ /^1\.9/ and proc.lambda? and proc.arity != 1
43 raise "You can only pass a proc ('Proc.new') or a lambda that takes exactly one arg (for self) to the wrap method."
44 end
45
46 instance_eval(&proc)
47 end
48 end
49 end
50
51 class Collector < OpenStruct
52 def initialize
53 super
54 @table = Hash.new {|h,k| h[k] = Response.new }
55 end
56
57 def initialize_copy(orig)
58 super
59 @table.keys.each do |key|
60 @table[key] = orig.send(key).dup
61 end
62 end
63 end
64
65 class Response
66 attr_reader :publish_formats, :publish_block, :blocks, :block_order
67 def initialize
68 @publish_formats = []
69 @blocks = {}
70 @block_order = []
71 end
72
73 def initialize_copy(orig)
74 @publish_formats = orig.publish_formats.dup
75 @blocks = orig.blocks.dup
76 @block_order = orig.block_order.dup
77 @publish_block = orig.publish_block.dup if orig.publish_block
78 @default = orig.default.dup if orig.default
79 end
80
81 def default(&block)
82 if block_given?
83 @default = block
84 end
85 @default
86 end
87
88 def publish(*formats, &block)
89 @publish_formats.concat(formats)
90 if block_given?
91 @publish_block = block
92 else
93 raise ArgumentError, "Block required to publish" unless @publish_block
94 end
95 end
96
97 def each_published
98 publish_formats.each do |format|
99 yield format, publish_block if block_given?
100 end
101 end
102
103 def each_format
104 @block_order.each do |format|
105 yield format, @blocks[format] if block_given?
106 end
107 end
108
109 def method_missing(method, *args, &block)
110 if block_given?
111 @blocks[method] = block
112 @block_order << method unless @block_order.include?(method)
113 elsif args.empty?
114 @block_order << method
115 else
116 super
117 end
118 end
119 end
120 end
121 en