representations 0.0.2 → 0.0.3
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/README.markdown +23 -9
- data/VERSION +1 -1
- data/init.rb +1 -0
- data/lib/representations.rb +158 -91
- data/lib/view_helpers.rb +7 -1
- data/representations.gemspec +2 -2
- metadata +2 -2
data/README.markdown
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
#
|
1
|
+
# Representations
|
2
2
|
|
3
3
|
Rails helpers, including form builders are great to allow rapid development of applications and views.
|
4
4
|
|
5
5
|
They are procedural in nature and have hard time to adapt to complex models. They also live in a single namespace making it difficult to find which helpers apply to which models.
|
6
6
|
|
7
|
-
|
7
|
+
Representations change syntax to object oriented and model specific.
|
8
8
|
|
9
9
|
## Example usage
|
10
10
|
|
@@ -27,7 +27,7 @@ Rails helpers:
|
|
27
27
|
= f.label(:eye_color_blue, "Blue")
|
28
28
|
= f.submit("Submit")
|
29
29
|
|
30
|
-
|
30
|
+
Representations:
|
31
31
|
|
32
32
|
- r(user).form do
|
33
33
|
login:
|
@@ -42,10 +42,24 @@ Resource Representations
|
|
42
42
|
= p.last_name.text_field
|
43
43
|
= p.eye_color.radio_button('blue')
|
44
44
|
= p.eye_color.radio_button_label('blue', 'Blue')
|
45
|
-
|
45
|
+
##Extensions
|
46
46
|
Representations can be altered. For example to add new method DefaultRepresentation create file app/representations/default_representation.rb with the content:
|
47
|
-
module DefaultRepresentation
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
47
|
+
module DefaultRepresentation
|
48
|
+
def new_method
|
49
|
+
some code
|
50
|
+
end
|
51
|
+
end
|
52
|
+
##Nested attributes
|
53
|
+
- user.children.each do |child|
|
54
|
+
= child.name.label
|
55
|
+
= child.name.text_field
|
56
|
+
= child.delete_checkbox
|
57
|
+
= child.delete_checkbox_label
|
58
|
+
Or even:
|
59
|
+
- user.children.build do |child|
|
60
|
+
= child.name.label
|
61
|
+
= child.name.text_field
|
62
|
+
## Automatic wrapping
|
63
|
+
Create file config/initializers/representations.rb
|
64
|
+
Representations.enable_automatic_wrapping = true
|
65
|
+
if you want Representations to automatically wrap variables from the controller
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
data/init.rb
CHANGED
data/lib/representations.rb
CHANGED
@@ -1,33 +1,48 @@
|
|
1
1
|
module Representations
|
2
|
+
#Enables automatic wrapping
|
3
|
+
#TODO should be disabled until module unloading will be fixed
|
4
|
+
def self.enable_automatic_wrapping=(value)
|
5
|
+
if value
|
6
|
+
ActionView::Base.class_eval do
|
7
|
+
def instance_variable_set_with_r(symbol, obj)
|
8
|
+
load ActiveSupport::Dependencies.search_for_file('representations.rb')
|
9
|
+
obj = Representations.representation_for(obj, self, symbol.to_s[1..-1]) if obj.is_a?(ActiveRecord::Base)
|
10
|
+
instance_variable_set_without_r(symbol, obj) #call to the original method
|
11
|
+
end
|
12
|
+
self.alias_method_chain :instance_variable_set, :r
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
2
16
|
#Creates Representation for object passed as a paremeter, type of the representation
|
3
17
|
#depends on the type of the object
|
4
18
|
def representation_for(object, template, name=nil, parent=nil)
|
5
|
-
representation_class =
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
19
|
+
representation_class =
|
20
|
+
begin
|
21
|
+
if object.is_a?(ActiveRecord::Base)
|
22
|
+
ActiveRecordRepresentation
|
23
|
+
else
|
24
|
+
"Representations::#{object.class.to_s.demodulize}Representation".constantize
|
25
|
+
end
|
26
|
+
rescue
|
27
|
+
AssociationsRepresentation if object.ancestors.include?(ActiveRecord::Associations) rescue DefaultRepresentation
|
28
|
+
end
|
10
29
|
representation_class.new(object, template, name, parent)
|
11
30
|
end
|
12
31
|
|
13
32
|
module_function :representation_for
|
14
|
-
|
15
33
|
class Representation
|
16
34
|
|
17
35
|
#value - object for which the representation is created
|
18
|
-
#template - template view
|
36
|
+
#template - template view (needed because some ActionView::Base methods are private)
|
19
37
|
#name - the actuall name of the method that was called on the object's parent that is being initialize
|
38
|
+
#parent - Representation object which contains the object that is being initialize
|
20
39
|
def initialize(value, template, name=nil, parent=nil)
|
21
40
|
@value = value
|
22
41
|
@name = name
|
23
42
|
@template = template
|
24
43
|
@parent = parent
|
25
|
-
|
26
|
-
|
27
|
-
rescue
|
28
|
-
puts "::#{self.class.to_s.demodulize} not defined"
|
29
|
-
#procced then (user did not provide file for customisation of the class)
|
30
|
-
end
|
44
|
+
#extend class if user provided appropriate file (look at the files app/representations/*_representation.rb)
|
45
|
+
self.send(:extend, "::#{self.class.to_s.demodulize}".constantize) rescue Rails.logger.info "No extension defined for ::#{self.class.to_s}"
|
31
46
|
end
|
32
47
|
|
33
48
|
def id
|
@@ -40,7 +55,7 @@ module Representations
|
|
40
55
|
#returns html label tag for the representation
|
41
56
|
def label(value = nil, html_options = {})
|
42
57
|
tree = get_parents_tree
|
43
|
-
for_attr_value = tree.join('_')
|
58
|
+
for_attr_value = tree.collect{ |x| x[0] }.join('_')
|
44
59
|
tags = get_tags(html_options, {:for => for_attr_value})
|
45
60
|
value = ERB::Util::h(@name.humanize) if value.nil?
|
46
61
|
%Q{<label #{tags}>#{value}</label>}
|
@@ -51,31 +66,40 @@ module Representations
|
|
51
66
|
def with_block(&block)
|
52
67
|
yield self if block_given?
|
53
68
|
end
|
54
|
-
#Returns array of Represantation objects which are linked together by @parent field
|
69
|
+
#Returns two dimensional array based on the tree of the Represantation objects which are linked together by the @parent field
|
70
|
+
#First element of the array consists of Representation's @name and the second of Representation's class
|
55
71
|
def get_parents_tree
|
56
|
-
|
72
|
+
tree = Array.new
|
73
|
+
tree[0] = []
|
74
|
+
tree[0][0] = @name
|
75
|
+
tree[0][1] = self.class
|
57
76
|
parent = @parent
|
58
|
-
|
59
|
-
|
60
|
-
|
77
|
+
while parent do #iterate parent tree
|
78
|
+
array = []
|
79
|
+
array[0] = parent.instance_variable_get(:@name)
|
80
|
+
array[1] = parent.class
|
81
|
+
tree.unshift(array)
|
61
82
|
parent = parent.instance_variable_get(:@parent)
|
62
|
-
end
|
63
|
-
|
83
|
+
end
|
84
|
+
tree #tree now looks something like this [['user', ActiverRecordRepresentation], ['nick', DefaultRepresentation]]
|
64
85
|
end
|
65
|
-
#Creates value of the html name attribute according to passed tree
|
86
|
+
#Creates value of the html name attribute according to the passed tree
|
66
87
|
def get_html_name_attribute_value(tree)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
88
|
+
first = tree.delete_at(0)
|
89
|
+
root_name = first[0]
|
90
|
+
name = []
|
91
|
+
prev = nil
|
92
|
+
tree.each do |elem|
|
93
|
+
if elem[1] == DefaultRepresentation || elem[1] == TimeWithZoneRepresentation || prev == AssociationsRepresentation
|
94
|
+
name.push "[" + elem[0] + "]"
|
95
|
+
else
|
96
|
+
name.push "[" + elem[0] + "_attributes]"
|
97
|
+
end
|
98
|
+
prev = elem[1]
|
75
99
|
end
|
76
100
|
name.unshift(root_name)
|
77
101
|
end
|
78
|
-
#Returns string created by merging two hashes of html options passed as
|
102
|
+
#Returns string created by merging two hashes of html options passed as the arguments
|
79
103
|
def get_tags(user_options, base_options)
|
80
104
|
options = base_options.merge(user_options)
|
81
105
|
options.stringify_keys!
|
@@ -85,14 +109,11 @@ module Representations
|
|
85
109
|
end
|
86
110
|
|
87
111
|
class DefaultRepresentation < Representation
|
88
|
-
#require_dependency "#{RAILS_ROOT}/app/representations/default_representation"
|
89
|
-
#require "#{RAILS_ROOT}/app/representations/default_representation"
|
90
|
-
#include DefaultRepresentationExt
|
91
112
|
#not tested in the view
|
92
113
|
#Returns string with html check box tag
|
93
114
|
def check_box(checked_value = "1", unchecked_value = "0", html_options = {})
|
94
115
|
tree = get_parents_tree
|
95
|
-
id_attr_value = tree.join('_')
|
116
|
+
id_attr_value = tree.collect{ |x| x[0] }.join('_')
|
96
117
|
name_attr_value = get_html_name_attribute_value(tree)
|
97
118
|
tags = get_tags(html_options, {:value => checked_value, :id => id_attr_value, :name=>name_attr_value})
|
98
119
|
%Q{<input type="checkbox" #{tags}/>\n<input type="hidden" value="#{unchecked_value}" id="#{id_attr_value}" name="#{name_attr_value}"/>}
|
@@ -101,7 +122,7 @@ module Representations
|
|
101
122
|
#Returns string with html file field tag
|
102
123
|
def file_field(html_options = {})
|
103
124
|
tree = get_parents_tree
|
104
|
-
id_attr_value = tree.join('_')
|
125
|
+
id_attr_value = tree.collect{ |x| x[0] }.join('_')
|
105
126
|
tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
|
106
127
|
%Q{<input type="file" #{tags}/>}
|
107
128
|
end
|
@@ -109,36 +130,35 @@ module Representations
|
|
109
130
|
#Returns string with html hidden input tag
|
110
131
|
def hidden_field(html_options = {})
|
111
132
|
tree = get_parents_tree
|
112
|
-
id_attr_value = tree.join('_')
|
133
|
+
id_attr_value = tree.collect{ |x| x[0] }.join('_')
|
113
134
|
tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
|
114
135
|
%Q{<input type="hidden" #{tags}/>}
|
115
136
|
end
|
116
137
|
#Returns string with html text input tag
|
117
138
|
def text_field(html_options = {})
|
118
|
-
puts "#{RAILS_ROOT}/app/representations/default_representation"
|
119
139
|
tree = get_parents_tree
|
120
|
-
id_attr_value = tree.join('_')
|
140
|
+
id_attr_value = tree.collect{ |x| x[0] }.join('_')
|
121
141
|
tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
|
122
142
|
%Q{<input type="text" #{tags}/>}
|
123
143
|
end
|
124
144
|
#Returns string with html text area tag
|
125
145
|
def text_area(html_options = {})
|
126
146
|
tree = get_parents_tree
|
127
|
-
id_attr_value = tree.join('_')
|
147
|
+
id_attr_value = tree.collect{ |x| x[0] }.join('_')
|
128
148
|
tags = get_tags(html_options, {:id => id_attr_value, :name => get_html_name_attribute_value(tree)})
|
129
149
|
%Q{<textarea #{tags}>\n#{to_s}\n</textarea>}
|
130
150
|
end
|
131
151
|
#Returns string with html password tag
|
132
152
|
def password_field(html_options = {})
|
133
153
|
tree = get_parents_tree
|
134
|
-
id_attr_value = tree.join('_')
|
154
|
+
id_attr_value = tree.collect{ |x| x[0] }.join('_')
|
135
155
|
tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
|
136
156
|
%Q{<input type="password" #{tags}/>}
|
137
157
|
end
|
138
158
|
#Returns string with html radio button tag
|
139
159
|
def radio_button(value, html_options = {})
|
140
160
|
tree = get_parents_tree
|
141
|
-
id_attr_value = tree.join('_') + "_#{value}"
|
161
|
+
id_attr_value = tree.collect{ |x| x[0] }.join('_') + "_#{value}"
|
142
162
|
name_attr_value = get_html_name_attribute_value(tree)
|
143
163
|
tags = get_tags(html_options, {:name => name_attr_value, :value=>value, :id=>id_attr_value, :checked=>"#{@value.capitalize==value.capitalize}"})
|
144
164
|
%Q{<input type="radio" #{tags}/>}
|
@@ -146,68 +166,115 @@ module Representations
|
|
146
166
|
#Returns string with html label tag with for attribute set to the radio button of this object
|
147
167
|
def radio_button_label(radio_button_value, value = nil, html_options = {})
|
148
168
|
tree = get_parents_tree
|
149
|
-
for_attr_value = tree.join('_') + "_#{radio_button_value}"
|
169
|
+
for_attr_value = tree.collect{ |x| x[0] }.join('_') + "_#{radio_button_value}"
|
150
170
|
value = radio_button_value.capitalize if value.nil?
|
151
171
|
tags = get_tags(html_options, {:for => for_attr_value})
|
152
172
|
%Q{<label #{tags}>#{ERB::Util::h(value)}</label>}
|
153
173
|
end
|
174
|
+
end
|
175
|
+
#Representation for objects which are nil
|
176
|
+
class NilClassRepresentation < Representation
|
177
|
+
#Returns self so the calls:
|
178
|
+
#nil_object.not_defined_method.another_not_defined_method
|
179
|
+
#want raise an error
|
180
|
+
def method_missing(method_name, *args)
|
181
|
+
return self
|
154
182
|
end
|
155
|
-
#
|
156
|
-
|
157
|
-
#Returns self so the calls:
|
158
|
-
#nil_object.not_defined_method.another_not_defined_method
|
159
|
-
#want raise an error
|
160
|
-
def method_missing(method_name, *args)
|
161
|
-
return self
|
162
|
-
end
|
163
|
-
#Passed block shouldn't be called
|
164
|
-
def with_block(&block)
|
165
|
-
end
|
166
|
-
#Returns blank string
|
167
|
-
def to_s
|
168
|
-
return ''
|
169
|
-
end
|
183
|
+
#Passed block shouldn't be called
|
184
|
+
def with_block(&block)
|
170
185
|
end
|
171
|
-
#
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
186
|
+
#Returns blank string
|
187
|
+
def to_s
|
188
|
+
return ''
|
189
|
+
end
|
190
|
+
end
|
191
|
+
#Representation for ActiveRecord::Base object's
|
192
|
+
class ActiveRecordRepresentation < Representation
|
193
|
+
#Form builder
|
194
|
+
def form(&block)
|
195
|
+
raise "You need to provide block to form representation" unless block_given?
|
196
|
+
content = @template.capture(self, &block)
|
197
|
+
@template.concat(@template.form_tag(@value))
|
198
|
+
@template.concat(content)
|
199
|
+
@template.concat("</form>")
|
200
|
+
self
|
201
|
+
end
|
202
|
+
#Forwards ActiveRecord invocation and wraps result in appropriate Representation
|
203
|
+
#Suppose that User extends ActiveRecord::Base :
|
204
|
+
#ar_user = User.new
|
205
|
+
#ar_user.nick = 'foo'
|
206
|
+
#user = r(ar_user) #user is now ActiveRecordRepresentation
|
207
|
+
#user.nick.text_field #method_missing will be called on user with method_name = 'nick' in which new method for user will be created and will be called. The newly created method will create a new DefaultRepresentation with @value set to the string 'foo'. Next the text_field will be called on the newly created DefaultRepresentation
|
208
|
+
def method_missing(method_name, *args, &block)
|
209
|
+
method = <<-EOF
|
191
210
|
def #{method_name}(*args, &block)
|
192
211
|
@__#{method_name} ||= Representations.representation_for(@value.#{method_name}, @template, "#{method_name}", self)
|
193
212
|
@__#{method_name}.with_block(&block)
|
194
213
|
@__#{method_name} if block.nil?
|
195
214
|
end
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
215
|
+
EOF
|
216
|
+
::Representations::ActiveRecordRepresentation.class_eval(method, __FILE__, __LINE__)
|
217
|
+
self.__send__(method_name, &block)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
#Representation for TimeWithZone object
|
221
|
+
class TimeWithZoneRepresentation < Representation
|
222
|
+
def select(passed_options = {}, html_options = {})
|
223
|
+
options = {:defaults => {:day => @value.day, :month => @value.month, :year => @value.year}}
|
224
|
+
options.merge!(passed_options)
|
225
|
+
tree = get_parents_tree
|
226
|
+
name = get_html_name_attribute_value(tree)
|
227
|
+
@template.date_select(name, @name, options, html_options)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
#Representation for Collections
|
231
|
+
class AssociationsRepresentation < Representation
|
232
|
+
#initilize @num variable
|
233
|
+
def initialize(object, template, name, parent)
|
234
|
+
super
|
235
|
+
@num = 0
|
236
|
+
end
|
237
|
+
#Creates Representation for every object in the Array and invokes passed block with this Representation as the argument
|
238
|
+
def each
|
239
|
+
@value.each do |object|
|
240
|
+
representation_object = Representations.representation_for(object, @template, object.id.to_s, self)
|
241
|
+
yield representation_object
|
200
242
|
end
|
201
243
|
end
|
202
|
-
#
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
244
|
+
#Creates new object in the collection and input fields for it defined in the passed block
|
245
|
+
def build
|
246
|
+
new_object = @value.build
|
247
|
+
representation_object = AssociationsRepresentation::NewRecordRepresentation.new(new_object, @template, 'new_' + num.to_s, self)
|
248
|
+
yield representation_object
|
249
|
+
end
|
250
|
+
private
|
251
|
+
attr_reader :num
|
252
|
+
#Used for generating unique @name for ArrayRepresentation::NewRecordRepresentation
|
253
|
+
def num
|
254
|
+
@num += 1
|
255
|
+
end
|
256
|
+
#Representation that wraps newly created ActiveRecord::Base that will be added to some collection
|
257
|
+
class NewRecordRepresentation < Representation
|
258
|
+
#Creates new method which wraps call for ActionRecord
|
259
|
+
#New method returns Representation which represents datatype in the appropriate column
|
260
|
+
def method_missing(method_name_symbol, *args, &block)
|
261
|
+
method_name = method_name_symbol.to_s
|
262
|
+
representation_class = case @value.class.columns_hash[method_name].type
|
263
|
+
when :string
|
264
|
+
"DefaultRepresentation"
|
265
|
+
when :date
|
266
|
+
"TimeWithZoneRepresentation"
|
267
|
+
end
|
268
|
+
method = <<-EOF
|
269
|
+
def #{method_name}(*args, &block)
|
270
|
+
@__#{method_name} ||= #{representation_class}.new(@value.#{method_name}, @template, "#{method_name}", self)
|
271
|
+
@__#{method_name}.with_block(&block)
|
272
|
+
@__#{method_name} if block.nil?
|
273
|
+
end
|
274
|
+
EOF
|
275
|
+
::Representations::AssociationsRepresentation::NewRecordRepresentation.class_eval(method, __FILE__, __LINE__)
|
276
|
+
self.__send__(method_name_symbol, &block)
|
211
277
|
end
|
212
278
|
end
|
213
279
|
end
|
280
|
+
end
|
data/lib/view_helpers.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
module Representations
|
2
2
|
module ViewHelpers
|
3
3
|
def r(model)
|
4
|
-
r = Representations.representation_for(model, self, model
|
4
|
+
r = Representations.representation_for(model, self, find_variables_name(model))
|
5
5
|
yield r if block_given?
|
6
6
|
r
|
7
7
|
end
|
8
|
+
private
|
9
|
+
def find_variables_name(object)
|
10
|
+
self.instance_variables.each do |name|
|
11
|
+
return name[1..-1] if instance_variable_get(name) == object
|
12
|
+
end
|
13
|
+
end
|
8
14
|
end
|
9
15
|
end
|
data/representations.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{representations}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["\305\201ukasz Piestrzeniewicz", "Adam Sokolnicki"]
|
12
|
-
s.date = %q{2009-10-
|
12
|
+
s.date = %q{2009-10-24}
|
13
13
|
s.description = %q{Rails helpers, including form builders are great to allow rapid development of applications and views.
|
14
14
|
They are procedural in nature and have hard time to adapt to complex models. They also live in a single namespace making it difficult to find which helpers apply to which models.
|
15
15
|
Resource representations change syntax to object oriented and model specific.}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: representations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "\xC5\x81ukasz Piestrzeniewicz"
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-10-
|
13
|
+
date: 2009-10-24 00:00:00 +02:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|