attrtastic 0.2.2 → 0.3.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.
@@ -0,0 +1,333 @@
1
+ require "active_support"
2
+
3
+ module Attrtastic
4
+ class SemanticAttributesBuilder
5
+
6
+ # Only for testing purposes
7
+ attr_reader :record, :template
8
+
9
+ def initialize(record, template)
10
+ @record, @template = record, template
11
+ end
12
+
13
+ ##
14
+ # Creates block of attributes with optional header. Attributes are surrounded with ordered list.
15
+ #
16
+ # @overload attributes(options = {}, &block)
17
+ # Creates attributes list without header, yields block to include each attribute
18
+ #
19
+ # @param [Hash] options Options for formating attributes block
20
+ # @option options [String] :name (nil) Optional header of attributes section
21
+ # @option options [String] :class ('') Name of html class to add to attributes block
22
+ # @option options [String] :header_class ('') Name of html class to add to header
23
+ # @yield Block which can call #attribute to include attribute value
24
+ #
25
+ # @example
26
+ # <%= attr.attributes do %>
27
+ # <%= attr.attribute :name %>
28
+ # <%= attr.attribute :email %>
29
+ # <% end %>
30
+ #
31
+ # @example
32
+ # <%= attr.attributes :name => "User" do %>
33
+ # <%= attr.attribute :name %>
34
+ # <%= attr.attribute :email %>
35
+ # <% end %>
36
+ #
37
+ # @example
38
+ # <%= attr.attributes :for => :user do |user| %>
39
+ # <%= user.attribute :name %>
40
+ # <%= user.attribute :email %>
41
+ # <%= user.attribute :profile do %>
42
+ # <%= link_to h(user.record.name), user_path(user.record) %>
43
+ # <% end %>
44
+ # <% end %>
45
+ #
46
+ # @example
47
+ # <%= attr.attributes :for => @user do |user| %>
48
+ # <%= user.attribute :name %>
49
+ # <%= user.attribute :email %>
50
+ # <%= user.attribute :profile do %>
51
+ # <%= link_to h(@user.name), user_path(@user) %>
52
+ # <% end %>
53
+ # <% end %>
54
+ #
55
+ # @example
56
+ # <%= attr.attributes :for => :posts do |post| %>
57
+ # <%= post.attribute :author %>
58
+ # <%= post.attribute :title %>
59
+ # <% end %>
60
+ #
61
+ # @example
62
+ # <%= attr.attributes :for => @posts do |post| %>
63
+ # <%= post.attribute :author %>
64
+ # <%= post.attribute :title %>
65
+ # <% end %>
66
+ #
67
+ # @overload attributes(header, options = {}, &block)
68
+ # Creates attributes list with header and yields block to include each attribute
69
+ #
70
+ # @param [String] header Header of attributes section
71
+ # @param [Hash] options Options for formating attributes block
72
+ # @option options [String] :class ('') Name of html class to add to attributes block
73
+ # @option options [String] :header_class ('') Name of html class to add to header
74
+ # @option optinos [Symbol,Object] :for Optional new record for new builder
75
+ # passed as argument block. This new record can be symbol of method name for actual
76
+ # record, or any other object which is passed as new record for builder.
77
+ # @yield Block which can call #attribute to include attribute value
78
+ # @yieldparam builder Builder instance holding actual record (retivable via #record)
79
+ #
80
+ # @example
81
+ # <%= attr.attributes "User info" do %>
82
+ # <%= attr.attribute :name" %>
83
+ # <%= attr.attribute :email %>
84
+ # <% end %>
85
+ #
86
+ # @example
87
+ # <%= attr.attributes "User", :for => :user do |user| %>
88
+ # <%= user.attribute :name %>
89
+ # <%= user.attribute :email %>
90
+ # <%= user.attribute :profile do %>
91
+ # <%= link_to h(user.record.name), user_path(user.record) %>
92
+ # <% end %>
93
+ # <% end %>
94
+ #
95
+ # @example
96
+ # <% attr.attributes "User", :for => @user do |user| %>
97
+ # <%= user.attribute :name %>
98
+ # <%= user.attribute :email %>
99
+ # <%= user.attribute :profile do %>
100
+ # <%= link_to h(@user.name), user_path(@user) %>
101
+ # <% end %>
102
+ # <% end %>
103
+ #
104
+ # @example
105
+ # <%= attr.attributes "Post", :for => :posts do |post| %>
106
+ # <%= post.attribute :author %>
107
+ # <%= post.attribute :title %>
108
+ # <% end %>
109
+ #
110
+ # @example
111
+ # <%= attr.attributes "Post", :for => @posts do |post| %>
112
+ # <%= post.attribute :author %>
113
+ # <%= post.attribute :title %>
114
+ # <% end %>
115
+ #
116
+ # @overload attributes(*symbols, options = {})
117
+ # Creates attributes list without header, attributes are given as list of symbols (record properties)
118
+ #
119
+ # @param [Symbol, ...] symbols List of attributes
120
+ # @param [Hash] options Options for formating attributes block
121
+ # @option options [String] :name (nil) Optional header of attributes section
122
+ # @option options [String] :class ('') Name of html class to add to attributes block
123
+ # @option options [String] :header_class ('') Name of html class to add to header
124
+ #
125
+ # @example
126
+ # <%= attr.attributes :name, :email %>
127
+ #
128
+ # @example
129
+ # <%= attr.attributes :name, :email, :for => :author %>
130
+ #
131
+ # @example
132
+ # <%= attr.attributes :name, :email, :for => @user %>
133
+ #
134
+ # @example
135
+ # <%= attr.attributes :title, :for => :posts %>
136
+ #
137
+ # @example
138
+ # <%= attr.attributes :title, :for => @posts %>
139
+ #
140
+ # @overload attributes(header, *symbols, options = {})
141
+ # Creates attributes list with header, attributes are given as list of symbols (record properties)
142
+ #
143
+ # @param [String] header Header of attributes section
144
+ # @param [Symbol, ...] symbols Optional list of attributes
145
+ # @param [Hash] options Options for formating attributes block
146
+ # @option options [String] :class ('') Name of html class to add to attributes block
147
+ # @option options [String] :header_class ('') Name of html class to add to header
148
+ #
149
+ # @example
150
+ # <%= attr.attributes "User info" :name, :email %>
151
+ #
152
+ # @example
153
+ # <%= attr.attributes "Author", :name, :email, :for => :author %>
154
+ #
155
+ # @example
156
+ # <%= attr.attributes "Author", :name, :email, :for => @user %>
157
+ #
158
+ # @example
159
+ # <%= attr.attributes "Post", :title, :for => :posts %>
160
+ #
161
+ # @example
162
+ # <%= attr.attributes "Post", :title, :for => @posts %>
163
+ #
164
+ # @example All together
165
+ # <%= attr.attributes "User info", :name, :email, :class => "user_info", :header_class => "header important" %>
166
+ #
167
+ # @example With block
168
+ # <%= attr.attributes "User info" :class => "user_info", :header_class => "header important" do %>
169
+ # <%= attr.attribute :name %>
170
+ # <%= attr.attribute :email %>
171
+ # <% end %>
172
+ #
173
+ # @see #attribute
174
+ def attributes(*args, &block)
175
+ options = args.extract_options!
176
+ options[:html] ||= {}
177
+
178
+ if args.first and args.first.is_a? String
179
+ options[:name] = args.shift
180
+ end
181
+
182
+ if options[:for].blank?
183
+ attributes_for(record, args, options, &block)
184
+ else
185
+ for_value = if options[:for].is_a? Symbol
186
+ record.send(options[:for])
187
+ else
188
+ options[:for]
189
+ end
190
+
191
+ [*for_value].map do |value|
192
+ value_options = options.clone
193
+ value_options[:html][:class] = [ options[:html][:class], value.class.to_s.underscore ].compact.join(" ")
194
+
195
+ attributes_for(value, args, options, &block)
196
+ end.join
197
+ end
198
+
199
+ end
200
+
201
+ ##
202
+ # Creates list entry for single record attribute
203
+ #
204
+ # @overload attribute(method, options = {})
205
+ # Creates entry for record attribute
206
+ #
207
+ # @param [Symbol] method Attribute name of given record
208
+ # @param [Hash] options Options
209
+ # @option options [Hash] :html ({}) Hash with optional :class, :label_class and :value_class names of class for html
210
+ # @option options [String] :label Label for attribute entry, overrides default label name from symbol
211
+ # @option options [String] :value Value of attribute entry, overrides default value from record
212
+ # @option options [Boolean] :display_empty (false) Indicates if print value of given attribute even if it is blank?
213
+ #
214
+ # @example
215
+ # <%= attr.attribute :name %>
216
+ #
217
+ # @example
218
+ # <%= attr.attribute :name, :label => "Full user name" %>
219
+ #
220
+ # @example
221
+ # <%= attr.attribute :name, :value => @user.full_name %>
222
+ #
223
+ # @overload attribute(method, options = {}, &block)
224
+ # Creates entry for attribute given with block
225
+ #
226
+ # @param [Symbol] method Attribute name of given record
227
+ # @param [Hash] options Options
228
+ # @option options [Hash] :html ({}) Hash with optional :class, :label_class and :value_class names of classes for html
229
+ # @option options [String] :label Label for attribute entry, overrides default label name from symbol
230
+ # @yield Block which is executed in place of value for attribute
231
+ #
232
+ # @example
233
+ # <%= attr.attribute :name do %>
234
+ # <%= link_to @user.full_name, user_path(@user) %>
235
+ #
236
+ # @overload attribute(options = {}, &block)
237
+ # Creates entry for attribute with given block, options[:label] is mandatory in this case.
238
+ #
239
+ # @param [:Hash] options Options
240
+ # @option options [Hash] :html ({}) Hash with optional :class, :label_class and :value_class names of classes for html
241
+ # @option options [String] :label Mandatory label for attribute entry
242
+ # @yield Block which is executed in place of value for attribute
243
+ #
244
+ # @example
245
+ # <%= attr.attribute :label => "User link" do %>
246
+ # <%= link_to @user.full_name, user_path(@user) %>
247
+ #
248
+ # @example
249
+ # <%= attr.attribute :name, :display_empty => true %>
250
+ #
251
+ # @example
252
+ # <%= attr.attribute :label => "User link" do %>
253
+ # <%= link_to @user.full_name, user_path(@user) %>
254
+ #
255
+ def attribute(*args, &block)
256
+ options = args.extract_options!
257
+ options[:html] ||= {}
258
+
259
+ method = args.shift
260
+
261
+ html_label_class = [ "label", options[:html][:label_class] ].compact.join(" ")
262
+ html_value_class = [ "value", options[:html][:value_class] ].compact.join(" ")
263
+ html_class = [ "attribute", options[:html][:class] ].compact.join(" ")
264
+
265
+ label = options.key?(:label) ? options[:label] : label_for_attribute(method)
266
+
267
+ unless block_given?
268
+ value = options.key?(:value) ? options[:value] : value_of_attribute(method)
269
+
270
+ if value.present? or options[:display_empty]
271
+ output = template.tag(:li, {:class => html_class}, true)
272
+ output << template.content_tag(:span, label, :class => html_label_class)
273
+ output << template.content_tag(:span, value, :class => html_value_class)
274
+ output.safe_concat("</li>")
275
+ end
276
+ else
277
+ output = template.tag(:li, {:class => html_class}, true)
278
+ output << template.content_tag(:span, label, :class => html_label_class)
279
+ output << template.tag(:span, {:class => html_value_class}, true)
280
+ output << template.capture(&block)
281
+ output.safe_concat("</span>")
282
+ output.safe_concat("</li>")
283
+ end
284
+ end
285
+
286
+ private
287
+
288
+ def attributes_for(object, methods, options, &block)
289
+ new_builder = self.class.new(object, template)
290
+
291
+ html_class = [ "attributes", options[:html].delete(:class) ].compact.join(" ")
292
+ html_header_class = [ "legend", options[:html].delete(:header_class) ].compact.join(" ")
293
+
294
+ output = template.tag(:div, {:class => html_class}, true)
295
+
296
+ header = options[:name]
297
+
298
+ if header.present?
299
+ output << template.content_tag(:div, header, :class => html_header_class)
300
+ end
301
+
302
+ if block_given?
303
+ output << template.tag(:ol, {}, true)
304
+ output << template.capture(new_builder, &block)
305
+ output.safe_concat("</ol>")
306
+ elsif methods.present?
307
+ output << template.tag(:ol, {}, true)
308
+ methods.each do |method|
309
+ output << new_builder.attribute(method, options)
310
+ end
311
+ output.safe_concat("</ol>")
312
+ end
313
+
314
+ output.safe_concat("</div>")
315
+ end
316
+
317
+ def label_for_attribute(method)
318
+ if record.class.respond_to?(:human_attribute_name)
319
+ record.class.human_attribute_name(method.to_s)
320
+ else
321
+ method.to_s.send(:humanize)
322
+ end
323
+ end
324
+
325
+ def value_of_attribute(method)
326
+ value = record.send(method)
327
+ value_methods = [ :to_label, :display_name, :full_name, :name, :title, :username, :login, :value ]
328
+ value_method = value_methods.find { |m| value.respond_to?(m) } || :to_s
329
+ value.send(value_method)
330
+ end
331
+
332
+ end
333
+ end
@@ -0,0 +1,55 @@
1
+ module Attrtastic
2
+ ##
3
+ # Helper which should be included in ActionView. Adds #semantic_attributes_for
4
+ # method, which helps printing attributes for given record, similar to
5
+ # formtastic's sematnic_form_for
6
+ #
7
+ # @example
8
+ # ActionView::Base.send :include, Attrtastic::SemanticAttributesHelper
9
+ #
10
+ # @example Example of useage
11
+ # <%= semantic_attributes_for @user do |attr| %>
12
+ # <%= attr.attributes "User info" do %>
13
+ # <%= attr.attribute :name %>
14
+ # <%= attr.attribute :email %>
15
+ # <% end %>
16
+ # <%= attr.attributes "User details" do %>
17
+ # <%= attr.attribute :weight %>
18
+ # <%= attr.attribute :height %>
19
+ # <%= attr.attribute :age %>
20
+ # <% end %>
21
+ # <% end %>
22
+ module SemanticAttributesHelper
23
+
24
+ ##
25
+ # Creates attributes for given object
26
+ #
27
+ # @param[ActiveRecord] record AR instance record for which to display attributes
28
+ # @param[Hash] options Opions
29
+ # @option options [Hash] :html ({}) Hash with optional :class html class name for html block
30
+ # @yield [attr] Block which is yield inside of markup
31
+ # @yieldparam [SemanticAttributesBuilder] builder Builder for attributes for given AR record
32
+ #
33
+ # @example
34
+ # <%= semantic_attributes_for @user do |attr| %>
35
+ # <%= attr.attributes do %>
36
+ # <%= attr.attribute :name %>
37
+ # <%= attr.attribute :email %>
38
+ # <% end %>
39
+ # <% end %>
40
+ #
41
+ def semantic_attributes_for(record, options = {}, &block)
42
+ options[:html] ||= {}
43
+
44
+ html_class = [ "attrtastic", record.class.to_s.underscore, options[:html][:class] ].compact.join(" ")
45
+
46
+ output = tag(:div, { :class => html_class}, true)
47
+ if block_given?
48
+ output << capture(SemanticAttributesBuilder.new(record, self), &block)
49
+ end
50
+ output.safe_concat("</div>")
51
+ end
52
+
53
+ end
54
+ end
55
+
@@ -0,0 +1,3 @@
1
+ module Attrtastic
2
+ VERSION = "0.3.0"
3
+ end