hobo_rapid 1.4.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +5 -0
- data/VERSION +1 -0
- data/app/controllers/dev_controller.rb +25 -0
- data/app/helpers/hobo_rapid_helper.rb +197 -0
- data/hobo_rapid.gemspec +26 -0
- data/lib/hobo_rapid/railtie.rb +6 -0
- data/lib/hobo_rapid.rb +13 -0
- data/taglibs/buttons/buttons.dryml +1 -0
- data/taglibs/buttons/create_button.dryml +40 -0
- data/taglibs/buttons/delete-button.dryml +75 -0
- data/taglibs/buttons/remote_method_button.dryml +34 -0
- data/taglibs/buttons/transition_button.dryml +71 -0
- data/taglibs/buttons/transition_link.dryml +22 -0
- data/taglibs/buttons/update_button.dryml +29 -0
- data/taglibs/cache/cache.dryml +1 -0
- data/taglibs/cache/nested_cache.dryml +332 -0
- data/taglibs/cards/card.dryml +7 -0
- data/taglibs/cards/cards.dryml +1 -0
- data/taglibs/cards/search_card.dryml +4 -0
- data/taglibs/editors/click_editor.dryml +60 -0
- data/taglibs/editors/editors.dryml +27 -0
- data/taglibs/editors/live_editor.dryml +37 -0
- data/taglibs/forms/error_messages.dryml +21 -0
- data/taglibs/forms/form.dryml +101 -0
- data/taglibs/forms/formlet.dryml +48 -0
- data/taglibs/forms/forms.dryml +5 -0
- data/taglibs/forms/submit.dryml +14 -0
- data/taglibs/hobo_rapid.dryml +14 -0
- data/taglibs/html/a.dryml +156 -0
- data/taglibs/html/aside.dryml +5 -0
- data/taglibs/html/doctype.dryml +39 -0
- data/taglibs/html/empty_tag.dryml +23 -0
- data/taglibs/html/footer.dryml +5 -0
- data/taglibs/html/header.dryml +5 -0
- data/taglibs/html/html.dryml +18 -0
- data/taglibs/html/if_ie.dryml +11 -0
- data/taglibs/html/image.dryml +11 -0
- data/taglibs/html/javascript.dryml +12 -0
- data/taglibs/html/section.dryml +12 -0
- data/taglibs/html/section_group.dryml +11 -0
- data/taglibs/html/stylesheet.dryml +5 -0
- data/taglibs/html/table.dryml +174 -0
- data/taglibs/i18n/ht.dryml +48 -0
- data/taglibs/i18n/human_attribute_name +18 -0
- data/taglibs/i18n/human_collection_name.dryml +69 -0
- data/taglibs/i18n/i18n.dryml +1 -0
- data/taglibs/i18n/model_name_human.dryml +16 -0
- data/taglibs/i18n/t.dryml +12 -0
- data/taglibs/inputs/after_submit.dryml +32 -0
- data/taglibs/inputs/check_many.dryml +25 -0
- data/taglibs/inputs/collection_input.dryml +10 -0
- data/taglibs/inputs/hidden_field.dryml +48 -0
- data/taglibs/inputs/hot_input.dryml +50 -0
- data/taglibs/inputs/input.dryml +76 -0
- data/taglibs/inputs/input_all.dryml +14 -0
- data/taglibs/inputs/input_for.dryml +38 -0
- data/taglibs/inputs/input_for_date.dryml +86 -0
- data/taglibs/inputs/input_for_enum_string.dryml +26 -0
- data/taglibs/inputs/input_many.dryml +131 -0
- data/taglibs/inputs/inputs.dryml +1 -0
- data/taglibs/inputs/name_one.dryml +74 -0
- data/taglibs/inputs/or_cancel.dryml +8 -0
- data/taglibs/inputs/select_input.dryml +13 -0
- data/taglibs/inputs/select_many.dryml +58 -0
- data/taglibs/inputs/select_menu.dryml +23 -0
- data/taglibs/inputs/select_one.dryml +46 -0
- data/taglibs/inputs/sortable_input_many.dryml +31 -0
- data/taglibs/inputs/sti_type_input.dryml +6 -0
- data/taglibs/lists/collection.dryml +26 -0
- data/taglibs/lists/empty_collection_message.dryml +13 -0
- data/taglibs/lists/feckless_fieldset.dryml +94 -0
- data/taglibs/lists/field_list.dryml +21 -0
- data/taglibs/lists/field_list_v1.dryml +64 -0
- data/taglibs/lists/labelled_item_list.dryml +11 -0
- data/taglibs/lists/lists.dryml +2 -0
- data/taglibs/lists/with_fields.dryml +98 -0
- data/taglibs/pages/account.dryml +30 -0
- data/taglibs/pages/account_disabled.dryml +19 -0
- data/taglibs/pages/flash_message.dryml +23 -0
- data/taglibs/pages/forgot_password.dryml +62 -0
- data/taglibs/pages/login.dryml +57 -0
- data/taglibs/pages/not_found_page.dryml +18 -0
- data/taglibs/pages/page_nav.dryml +17 -0
- data/taglibs/pages/pages.dryml +12 -0
- data/taglibs/pages/permission_denied_page.dryml +24 -0
- data/taglibs/plus/collection_preview.dryml +23 -0
- data/taglibs/plus/filter_menu.dryml +103 -0
- data/taglibs/plus/gravatar.dryml +12 -0
- data/taglibs/plus/live_search.dryml +40 -0
- data/taglibs/plus/plus.dryml +1 -0
- data/taglibs/plus/sortable_collection.dryml +36 -0
- data/taglibs/plus/table_plus.dryml +63 -0
- data/taglibs/views/a_or_an.dryml +10 -0
- data/taglibs/views/collection_name.dryml +28 -0
- data/taglibs/views/comma_list.dryml +2 -0
- data/taglibs/views/count.dryml +99 -0
- data/taglibs/views/links_for_collection.dryml +2 -0
- data/taglibs/views/name.dryml +30 -0
- data/taglibs/views/nil_view.dryml +10 -0
- data/taglibs/views/record_flags.dryml +5 -0
- data/taglibs/views/type_name.dryml +24 -0
- data/taglibs/views/view.dryml +79 -0
- data/taglibs/views/view_for.dryml +42 -0
- data/taglibs/views/views.dryml +1 -0
- data/taglibs/views/you.dryml +150 -0
- data/vendor/assets/javascripts/hobo_rapid.js +1 -0
- data/vendor/assets/stylesheets/feckless-fieldset.css +40 -0
- data/vendor/assets/stylesheets/hobo-rapid.css +94 -0
- data/vendor/assets/stylesheets/hobo_rapid.css +1 -0
- metadata +174 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
<!-- Wrapper around ActiveModel::Name#human
|
2
|
+
|
3
|
+
#### Attributes
|
4
|
+
|
5
|
+
- model - (optional) should be a model class or a record object (default to this)
|
6
|
+
- count - (optional) used to pick the inflected string for the model. It should be an integer.
|
7
|
+
-->
|
8
|
+
<def tag="model-name-human" attrs="model, count"><%=
|
9
|
+
model ||= this
|
10
|
+
model = model.class unless model.kind_of? Class
|
11
|
+
# prepare symbolized attributes for merging
|
12
|
+
attrs = {}
|
13
|
+
all_attributes.each_pair{|k,v| attrs[k.to_sym] = v}
|
14
|
+
model.model_name.human( attrs )
|
15
|
+
%>
|
16
|
+
</def>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<!-- Simple wrapper around I18n.t.
|
2
|
+
|
3
|
+
The tag content is used as the :default option. It is overridden by an explicit 'default' attribute.
|
4
|
+
There is a default :count => 1.
|
5
|
+
|
6
|
+
### Attributes
|
7
|
+
|
8
|
+
- key: the key to lookup
|
9
|
+
- all the attributes accepted by the wrapped method -->
|
10
|
+
<fakedef tag="t" attrs="key">
|
11
|
+
<!-- actually defined in i18n.rb -->
|
12
|
+
</fakedef>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<!--
|
2
|
+
Used inside a form to specify where to redirect after successful submission. This works by inserting a hidden field called `after_submit` which is used by Hobo if present to perform a redirect after the form submission.
|
3
|
+
|
4
|
+
### Usage
|
5
|
+
|
6
|
+
Use the `stay-here` attribute to remain on the current page:
|
7
|
+
|
8
|
+
<form>
|
9
|
+
<after-submit stay-here/>
|
10
|
+
...
|
11
|
+
</form>
|
12
|
+
|
13
|
+
Use the `go-back` option to return to the page in `session[:previous_uri]`:
|
14
|
+
|
15
|
+
<form>
|
16
|
+
<after-submit go-back/>
|
17
|
+
...
|
18
|
+
</form>
|
19
|
+
|
20
|
+
Use the `uri` option to specify a redirect location:
|
21
|
+
|
22
|
+
<form>
|
23
|
+
<after-submit uri="/admin"/>
|
24
|
+
...
|
25
|
+
</form>
|
26
|
+
-->
|
27
|
+
<def tag="after-submit" attrs="uri, stay-here, go-back"><%
|
28
|
+
uri = "stay-here" if stay_here
|
29
|
+
uri = session[:previous_uri] if go_back
|
30
|
+
-%>
|
31
|
+
<input type="hidden" value="¶ms[:after_submit] || uri" name="after_submit" if="&uri"/>
|
32
|
+
</def>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<!-- Renders a `<ul>` list of checkboxes, one for each of the potential targt in a `has_many` association. The user can check the items they wish to have associated. A typical use might be selecting categories for a blog post.
|
2
|
+
|
3
|
+
### Attributes
|
4
|
+
|
5
|
+
- `options` - an array of models that may be added to the collection
|
6
|
+
- `disabled` - if true, sets the disabled flag on all check boxes.
|
7
|
+
|
8
|
+
-->
|
9
|
+
<def tag="check-many" attrs="options, disabled"><%
|
10
|
+
collection = this
|
11
|
+
param_name = param_name_for_this
|
12
|
+
options ||= begin
|
13
|
+
conditions = ActiveRecord::Associations::BelongsToAssociation.new(this_parent, this_field_reflection).send(:conditions)
|
14
|
+
this_field_reflection.klass.all(:conditions => conditions, :limit => 100).select {|x| can_view?(x)}
|
15
|
+
end
|
16
|
+
-%>
|
17
|
+
<ul class="check-many" param="default" merge-attrs>
|
18
|
+
<input type="hidden" name="#{param_name}[]" value=""/><% # ensure all items are removed when nothing checked
|
19
|
+
%>
|
20
|
+
<li repeat="&options" param>
|
21
|
+
<input id="#{dom_id(this, :check_many)}" type="checkbox" name="#{param_name}[]" value="@#{this.id}" checked="&this.in?(collection)" disabled="&disabled"/>
|
22
|
+
<label for="#{dom_id(this, :check_many)}"><name param/></label>
|
23
|
+
</li>
|
24
|
+
</ul>
|
25
|
+
</def>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<!-- This tag is called by `<input>` when the context is a `has_many :through` collection. By default a `<select-many>`
|
2
|
+
is used, but this can be customised on a per-type basis. For example, say you would like the `<check-many>` tag used to
|
3
|
+
edit collections a `Category` model in your application:
|
4
|
+
|
5
|
+
<def tag="collection-input" for="Category"><check-many merge/></def>
|
6
|
+
-->
|
7
|
+
<def tag="collection-input" polymorphic></def>
|
8
|
+
|
9
|
+
<!-- The default `<collection-input>` - calls `<select-many>` -->
|
10
|
+
<def tag="collection-input" for="ActiveRecord::Base"><select-many merge/></def>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<%# creates hidden inputs for the specified fields.
|
2
|
+
|
3
|
+
### Attributes
|
4
|
+
|
5
|
+
- `fields`: the fields to create hidden inputs for. If neither fields nor for-query-string is specified, all fields except for `type`, `created_at` and `updated_at` are used.
|
6
|
+
|
7
|
+
- `for-query-string`: creates hidden inputs from the query string
|
8
|
+
parameters of our original request. AJAX parameters are skipped.
|
9
|
+
%>
|
10
|
+
<def tag="hidden-fields" attrs="fields, skip, for-query-string"><%=
|
11
|
+
pairs = if for_query_string
|
12
|
+
if for_query_string.blank? || for_query_string==true
|
13
|
+
query_parameters_filtered
|
14
|
+
else
|
15
|
+
query_parameters_filtered(:only => comma_split(for_query_string))
|
16
|
+
end
|
17
|
+
else
|
18
|
+
hiddens = case fields
|
19
|
+
when '*', nil
|
20
|
+
this.class.column_names - ['type', 'created_at', 'updated_at']
|
21
|
+
else
|
22
|
+
comma_split(fields)
|
23
|
+
end
|
24
|
+
hiddens.map do |field|
|
25
|
+
val = this.send(field)
|
26
|
+
param_name = param_name_for(form_field_path + [field])
|
27
|
+
[param_name, val] unless val.nil? ||
|
28
|
+
field.to_sym.in?(this.class.attr_protected) ||
|
29
|
+
(this.new_record? && val == this.class.column(field).default)
|
30
|
+
end.compact
|
31
|
+
end
|
32
|
+
skip = comma_split skip
|
33
|
+
pairs.to_a.reject { |p| p.first.in?(skip) }.map { |n, v|
|
34
|
+
hidden_field_tag(n, v.to_s) if v && n.not_in?(scope.form_field_names)
|
35
|
+
}.compact.safe_join("\n".html_safe)
|
36
|
+
%></def>
|
37
|
+
|
38
|
+
|
39
|
+
<%# a simple wrapper around hidden_field_tag.
|
40
|
+
%>
|
41
|
+
<def tag="hidden-field">
|
42
|
+
<%= hidden_field_tag(param_name_for_this, this, deunderscore_attributes(attributes)) %>
|
43
|
+
</def>
|
44
|
+
|
45
|
+
<!-- Renders an `<input type='hidden'>` for the `id` field of the current context -->
|
46
|
+
<def tag="hidden-id-field">
|
47
|
+
<if:id><input type="hidden" name="#{param_name_for_this}" value="#{this}" /></if>
|
48
|
+
</def>
|
@@ -0,0 +1,50 @@
|
|
1
|
+
<!--
|
2
|
+
This tag wraps an input with some ajax that refreshes a part when the input is changed.
|
3
|
+
|
4
|
+
An example of when this is useful is when a form is rendered differently depending on what country is selected. In this example, we'll change the label of the region to state or province or whatever is appropriate for the country.
|
5
|
+
|
6
|
+
<field-list: replace>
|
7
|
+
<do part="shipping">
|
8
|
+
<field-list fields="address,city,region,country,postal_code">
|
9
|
+
<country-view:><hot-input ajax /><country-view:>
|
10
|
+
<region-label:><%= this_parent.country._?.region_label %><region-label:>
|
11
|
+
<field-list>
|
12
|
+
</do>
|
13
|
+
<field-list:>
|
14
|
+
|
15
|
+
The actual input is exposed as the default parameter, so it can be customized:
|
16
|
+
|
17
|
+
<hot-input ajax selector="select">
|
18
|
+
<combobox>
|
19
|
+
<select-one limit="5000"/>
|
20
|
+
</combobox>
|
21
|
+
</hot-input>
|
22
|
+
|
23
|
+
Note also the selector option, which is useful for complex, combined inputs like a combobox. A single change in a complex input may actually result in change events being fired on multiple fundamental HTML elements, so we use the selector to only watch a single input.
|
24
|
+
|
25
|
+
The hot-input must be inside a form, and the form's context must be the same as the page's context. You can work around this limitation by using custom controller code.
|
26
|
+
|
27
|
+
### Attributes
|
28
|
+
|
29
|
+
This tag supports the [standard form AJAX attributes](/api_taglibs/rapid_forms), such as update, message, spinner-next-to, etc.
|
30
|
+
|
31
|
+
In particular, the part to update *must* be specified using either the `ajax`, `update` or `updates` attribute.
|
32
|
+
|
33
|
+
* path: the path to send the AJAX request to. If not specified, the current page path is used.
|
34
|
+
* method: the HTTP method for the request. Defaults to 'GET'.
|
35
|
+
* events: the javascript event(s) to trigger on. Default is 'change'.
|
36
|
+
* selector: filter for the events. Default is null.
|
37
|
+
|
38
|
+
Note that `event` and `selector` are passed straight to `$.on()` See [its documentation](http://api.jquery.com/on/) for more details.
|
39
|
+
|
40
|
+
-->
|
41
|
+
<def tag="hot-input" attrs="path, method, events, selector" >
|
42
|
+
<%
|
43
|
+
attributes['message'] ||= 'Loading...'
|
44
|
+
ajax_attrs, html_attrs = attributes.partition_hash(HoboRapidHelper::AJAX_ATTRS)
|
45
|
+
add_data_rapid!(html_attrs, "hot-input", :ajax_attrs => ajax_attrs, :path => path || request.path, :method => method || 'GET', :events => events || 'change', :selector => selector)
|
46
|
+
%>
|
47
|
+
<span class="hot-input" param="default" merge-attrs="&html_attrs">
|
48
|
+
<input merge param />
|
49
|
+
</span>
|
50
|
+
</def>
|
@@ -0,0 +1,76 @@
|
|
1
|
+
<!--
|
2
|
+
Provides an editable control tailored to the type of the object in context. `<input>` tags should be used within a
|
3
|
+
`<form>`. `<input>` is a _polymorphic_ tag which means that there are a variety of definitions, each one written for a
|
4
|
+
particular type. For example there are inputs for `text`, `boolean`, `password`, `date`, `datetime`, `integer`,
|
5
|
+
`float`, `string` and more.
|
6
|
+
|
7
|
+
### Usage
|
8
|
+
|
9
|
+
The tag behaves as a regular HTML input if the type attribute is given:
|
10
|
+
|
11
|
+
<input type="text" name="my_input"/> -> Output is exactly as provided, untouched by Rapid
|
12
|
+
|
13
|
+
If no type attribute is given then the _context_ is used. For example if the context is a blog post:
|
14
|
+
|
15
|
+
<input:title/> ->
|
16
|
+
<input id="blog_post[name]" class="string blog-post-name" type="text" value="My Blog Post" name="blog_post[name]"/>
|
17
|
+
|
18
|
+
<input:created_at/> ->
|
19
|
+
<select id="blog_post_created_at_year" name="blog_post[created_at][year]">...</select>
|
20
|
+
<select id="blog_post_created_at_month" name="blog_post[created_at][month]">...</select>
|
21
|
+
<select id="blog_post_created_at_day" name="blog_post[created_at][day]">...</select>
|
22
|
+
|
23
|
+
<input:description/> ->
|
24
|
+
<textarea class="text blog-post-description" id="blog_post[description]" name="blog_post[description]">...</textarea>
|
25
|
+
|
26
|
+
If the context is a `belongs_to` association, the `<select-one>` tag is used.
|
27
|
+
|
28
|
+
If the context is a `has_many :through` association, the polymorphic `<collection-input>` tag is used.
|
29
|
+
|
30
|
+
### Attributes
|
31
|
+
|
32
|
+
- no-edit: control what happens if `can_edit?` is false. Can be one of:
|
33
|
+
|
34
|
+
- view: render the current value using the `<view>` tag
|
35
|
+
- disable: render the input as normal, but add HTML's `disabled` attribute
|
36
|
+
- skip: render nothing at all
|
37
|
+
- ignore: render the input normally. That is, don't even perform the edit check.
|
38
|
+
-->
|
39
|
+
<def tag="input" attrs="no-edit"><%=
|
40
|
+
if attributes[:type]
|
41
|
+
element :input, deunderscore_attributes(attributes), nil, true, true
|
42
|
+
else
|
43
|
+
no_edit ||= :view
|
44
|
+
no_edit = no_edit.to_sym
|
45
|
+
no_edit_permission = !can_edit? unless no_edit == :ignore
|
46
|
+
if no_edit_permission && no_edit == :view
|
47
|
+
view
|
48
|
+
elsif no_edit_permission && no_edit == :skip
|
49
|
+
""
|
50
|
+
else
|
51
|
+
attrs = add_classes(attributes, type_id.dasherize, type_and_field.dasherize)
|
52
|
+
attrs[:name] ||= param_name_for_this
|
53
|
+
attrs[:disabled] = true if no_edit_permission && no_edit == :disable
|
54
|
+
the_input = if (refl = this_field_reflection)
|
55
|
+
if refl.macro == :belongs_to
|
56
|
+
call_polymorphic_tag('input', attrs) or select_one(attrs)
|
57
|
+
elsif refl.macro == :has_many
|
58
|
+
if refl.options[:through]
|
59
|
+
collection_input(attrs)
|
60
|
+
else
|
61
|
+
input_many(attrs)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
else
|
65
|
+
call_polymorphic_tag('input', attrs) or
|
66
|
+
(call_polymorphic_tag('input', HoboFields.to_class(this_type::COLUMN_TYPE), attrs) if defined?(this_type::COLUMN_TYPE)) or
|
67
|
+
raise Hobo::Error, ("No input tag for #{this_field}:#{this_type} (this=#{this.inspect})")
|
68
|
+
end
|
69
|
+
unless this_parent.errors[this_field].empty?
|
70
|
+
"<span class='field-with-errors'>#{the_input}</span>".html_safe
|
71
|
+
else
|
72
|
+
the_input
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
%></def>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!-- Renders a sub-section of a form with fields for every record in a `has_many` association. This is similar to `<input-many>` except there is no ability to add and remove items (i.e. no (+) and (-) buttons).
|
2
|
+
-->
|
3
|
+
<def tag="input-all">
|
4
|
+
<% association_fkey = this_field_reflection.foreign_key -%>
|
5
|
+
<ul class="input-all #{this_field.dasherize}">
|
6
|
+
<li repeat class="#{'record-with-errors' unless this.errors.empty?}">
|
7
|
+
<set-scoped form-field-names="&[]">
|
8
|
+
<hidden-id-field/>
|
9
|
+
<do param="default"/>
|
10
|
+
<hidden-fields skip="&association_fkey"/>
|
11
|
+
</set-scoped>
|
12
|
+
</li>
|
13
|
+
</ul>
|
14
|
+
</def>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<!-- A `<textarea>` input -->
|
2
|
+
<def tag="input" for="text" attrs="name">
|
3
|
+
<%= text_area_tag(name, this, deunderscore_attributes(attributes)) %>
|
4
|
+
</def>
|
5
|
+
|
6
|
+
<!-- A checkbox plus a hidden-field. The hidden field trick comes from Rails - it means that when the checkbox is not checked, the parameter name is still submitted, with a '0' value (the value is '1' when the checkbox is checked) -->
|
7
|
+
<def tag="input" for="boolean" attrs="name">
|
8
|
+
<%= unless attributes[:disabled]
|
9
|
+
cb_tag = check_box_tag(name, '1', this, deunderscore_attributes(attributes))
|
10
|
+
cb_hidden_tag = hidden_field_tag(name, '0', :id => nil)
|
11
|
+
cb_hidden_tag + cb_tag
|
12
|
+
end %>
|
13
|
+
</def>
|
14
|
+
|
15
|
+
<!-- A password input - `<input type='password'>` -->
|
16
|
+
<def tag="input" for="password" attrs="name">
|
17
|
+
<%= password_field_tag(name, this, deunderscore_attributes(attributes)) %>
|
18
|
+
</def>
|
19
|
+
|
20
|
+
<!-- An `<input type='text'>` input. -->
|
21
|
+
<def tag="input" for="integer" attrs="name">
|
22
|
+
<%= text_field_tag(name, this, deunderscore_attributes(attributes)) %>
|
23
|
+
</def>
|
24
|
+
|
25
|
+
<!-- An `<input type='text'>` input. -->
|
26
|
+
<def tag="input" for="BigDecimal" attrs="name">
|
27
|
+
<%= text_field_tag(name, this, deunderscore_attributes(attributes)) %>
|
28
|
+
</def>
|
29
|
+
|
30
|
+
<!-- An `<input type='text'>` input. -->
|
31
|
+
<def tag="input" for="float" attrs="name">
|
32
|
+
<%= text_field_tag(name, this, deunderscore_attributes(attributes)) %>
|
33
|
+
</def>
|
34
|
+
|
35
|
+
<!-- An `<input type='text'>` input. -->
|
36
|
+
<def tag="input" for="string" attrs="name">
|
37
|
+
<%= text_field_tag(name, this, deunderscore_attributes(attributes)) %>
|
38
|
+
</def>
|
@@ -0,0 +1,86 @@
|
|
1
|
+
<!-- A date picker, using the `select_date` helper from Rails
|
2
|
+
|
3
|
+
### Attributes
|
4
|
+
|
5
|
+
- all the options of select_date and date_select are passed to the select_date Rails
|
6
|
+
helper
|
7
|
+
|
8
|
+
All the other attributes are passed to the `select_date` helper as the html-options hash.
|
9
|
+
|
10
|
+
The menus default to the current date if the current value is nil.
|
11
|
+
|
12
|
+
Examples:
|
13
|
+
|
14
|
+
- override the input for date tag in a form
|
15
|
+
|
16
|
+
<my-special-date-view:>
|
17
|
+
<select-date start-year="&1940" order="day,month,year" />
|
18
|
+
</my-special-date-view:>
|
19
|
+
|
20
|
+
- override the input tag application-wide
|
21
|
+
|
22
|
+
<def tag='input' for='date'>
|
23
|
+
<select-date merge order="day,month,year" start-year="&1940" />
|
24
|
+
</def>
|
25
|
+
|
26
|
+
-->
|
27
|
+
<def tag="select-date" attrs="use-month-numbers, use-short-month, add-month-numbers, use-month-names, date-separator, start-year, end-year, discard-day, discard-month, discard-year, order, include-blank, default, disabled, prompt, prefix">
|
28
|
+
<% order = order.nil? ? [:year, :month, :day] : comma_split(order).*.to_sym -%>
|
29
|
+
<%= select_date(this || current_time,
|
30
|
+
(all_attributes - attributes.keys).reverse_merge(:prefix => param_name_for_this).merge(:order => order),
|
31
|
+
attributes - [:name]) %>
|
32
|
+
</def>
|
33
|
+
|
34
|
+
|
35
|
+
<%# Selects the `<datepicker>` as the default input for date. To
|
36
|
+
choose the older `<select-date>` input, add this to your
|
37
|
+
application.dryml:
|
38
|
+
|
39
|
+
<def tag='input' for='date'>
|
40
|
+
<select-date merge/>
|
41
|
+
</def>
|
42
|
+
%>
|
43
|
+
<def tag="input" for="Date">
|
44
|
+
<datepicker merge/>
|
45
|
+
</def>
|
46
|
+
|
47
|
+
<!-- A date/time picker, using the `select_time` helper from Rails
|
48
|
+
|
49
|
+
### Attributes
|
50
|
+
|
51
|
+
- include-seconds, time-separator, prompt and prefix are passed to the select_time helper as options
|
52
|
+
|
53
|
+
All the other attributes are passed to the `select_time` helper as the html-options hash.
|
54
|
+
|
55
|
+
The menus default to the current time if the current value is nil.
|
56
|
+
|
57
|
+
-->
|
58
|
+
<def tag="input" for="time" attrs="include-seconds, time-separator, prompt, prefix">
|
59
|
+
<%= select_time( this || current_time,
|
60
|
+
(all_attributes - attributes.keys).reverse_merge(:prefix => param_name_for_this),
|
61
|
+
attributes - [:name] ) %>
|
62
|
+
</def>
|
63
|
+
|
64
|
+
|
65
|
+
<!-- A date/time picker, using the `select_datetime` helper from Rails
|
66
|
+
|
67
|
+
### Attributes
|
68
|
+
|
69
|
+
- order: The order of the year, month and date menus. A comma separated string or an array. Default: "year, month,
|
70
|
+
day, hour, minute"
|
71
|
+
- date-separator, discard-type, prompt and prefix are passed to the select_date helper as options
|
72
|
+
|
73
|
+
All the other attributes are passed to the `select_date` helper as the html-options hash.
|
74
|
+
|
75
|
+
The menus default to the current time if the current value is nil.
|
76
|
+
|
77
|
+
-->
|
78
|
+
<def tag="input" for="datetime" attrs="order, date-separator, discard-type, prompt, prefix">
|
79
|
+
<% if ! order.nil?
|
80
|
+
order = comma_split(order).*.to_sym
|
81
|
+
attributes.merge!(:order => order)
|
82
|
+
end -%>
|
83
|
+
<%= select_datetime(this || current_time,
|
84
|
+
(all_attributes - attributes.keys).reverse_merge(:prefix => param_name_for_this).merge(:order => order),
|
85
|
+
attributes - [:name] ) %>
|
86
|
+
</def>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<!-- A `<select>` menu containing the values of an 'enum string'.
|
2
|
+
|
3
|
+
### Attributes
|
4
|
+
|
5
|
+
- `labels` - A hash that gives custom labels for the values of the enum.
|
6
|
+
Any values that do not have corresponding keys in this hash will get `value.titleize` as the label.
|
7
|
+
- `titleize` - Set to false to have the value itself (rather than `value.titleize`) be the default label. Default: true
|
8
|
+
- `first-option` - a string to be used for an extra option in the first position. E.g. "Please choose..."
|
9
|
+
- `first-value` - the value to be used with the `first-option`. Typically not used, meaning the option has a blank value.
|
10
|
+
|
11
|
+
-->
|
12
|
+
<def tag="input" for="HoboFields::Types::EnumString" attrs="labels, titleize, first-option, first-value"><%
|
13
|
+
labels ||= {}
|
14
|
+
labels = HashWithIndifferentAccess.new(labels)
|
15
|
+
titleize = true if titleize.nil?
|
16
|
+
options = this_type.values.map {|v|
|
17
|
+
[I18n.t("activerecord.attributes.#{this_parent.class.to_s.downcase}/#{this_field}s.#{v}",
|
18
|
+
:default => titleize ? v.titleize : v),
|
19
|
+
v]
|
20
|
+
}
|
21
|
+
%>
|
22
|
+
<select name="#{param_name_for_this}" merge-attrs>
|
23
|
+
<option value="#{first_value}" unless="&first_option.nil?"><first-option/></option>
|
24
|
+
<%= options_for_select(options, this) %>
|
25
|
+
</select>
|
26
|
+
</def>
|
@@ -0,0 +1,131 @@
|
|
1
|
+
<!-- Creates a sub-section of the form which the user can repeat using (+) and (-) buttons, in order to allow an entire `has_many` collection to be created/edited in a single form.
|
2
|
+
|
3
|
+
This tag is very different from tags like `<select-many>` and `<check-many>` in that:
|
4
|
+
|
5
|
+
- Those tags are used to *choose existing records* to include in the association, while `<input-many>` is used to actually create or edit the records in the association.
|
6
|
+
|
7
|
+
### Example
|
8
|
+
|
9
|
+
Say you are creating a new `Category` in your online shop, and you want to create some initial products *in the same form*, you can add the following to your form:
|
10
|
+
|
11
|
+
<input-many:products fields="name, price"/>
|
12
|
+
|
13
|
+
which is the same as
|
14
|
+
|
15
|
+
<input-many:products><field-list fields="name, price"/></input-many>
|
16
|
+
|
17
|
+
The body of the tag will be repeated for each of the current records in the collection, or will just appear once (with blank fields) if the colleciton is empty.
|
18
|
+
|
19
|
+
### Attributes
|
20
|
+
|
21
|
+
- fields: If you do not specify any content for the input-many, a `<field-list>` is rendered. This attribute is passed through to the `<field-list>`
|
22
|
+
|
23
|
+
- skip: Passed through to the `<field-list>`. If not specified, it defaults to the parent association.
|
24
|
+
|
25
|
+
- `minimum`: the minimum number of items in the collection. Currently only '0' and '1' are supported values. The default is '0'.
|
26
|
+
|
27
|
+
- `template`: the default values for new items. Normally this functionality is better provided by Model.new, but it's here if you need it.
|
28
|
+
|
29
|
+
- `add-hook`, `remove-hook`: javascript hooks which work similarly to the events described below.
|
30
|
+
|
31
|
+
### Events
|
32
|
+
|
33
|
+
- `rapid:add`: fired after the element is inserted
|
34
|
+
|
35
|
+
- `rapid:remove`: fired before the element is inserted. Returning false will cancel the removal.
|
36
|
+
|
37
|
+
- `rapid:change`: fired after an element has been removed or inserted.
|
38
|
+
|
39
|
+
- `hide`, `show`: passed to jquery-ui's hide and show functions.
|
40
|
+
|
41
|
+
Example javascript:
|
42
|
+
|
43
|
+
var last_added;
|
44
|
+
var last_removed;
|
45
|
+
$(document).ready(function() {
|
46
|
+
$('.stories').on('rapid:add', function(ev) {
|
47
|
+
last_added = this;
|
48
|
+
});
|
49
|
+
$('.stories').on('rapid:remove', function(ev) {
|
50
|
+
last_removed = this;
|
51
|
+
if(!confirm("really?")) return false;
|
52
|
+
});
|
53
|
+
});
|
54
|
+
|
55
|
+
### Example
|
56
|
+
|
57
|
+
Say you are creating a new `Category` in your online shop, and you want to create some initial products *in the same form*, you can add the following to your form:
|
58
|
+
|
59
|
+
<input-many:products fields="name, price" />
|
60
|
+
|
61
|
+
You'll often want to provide the `item` parameter:
|
62
|
+
|
63
|
+
<input-many:products><item:><field-list fields="name, price" /></item:></input-many>
|
64
|
+
|
65
|
+
A fully worked up example of nested input-many's may be found in [agility/jquery-test](http://github.com/tablatom/agility/blob/jquery-test/app/views/projects/nested_has_many_test.dryml)
|
66
|
+
-->
|
67
|
+
<def tag="input-many" attrs="minimum, fields, skip, more-skip, template, add-hook, remove-hook, hide, show" polymorphic >
|
68
|
+
<%
|
69
|
+
# helper function to create id's on buttons to facilitate testing
|
70
|
+
def underize(s)
|
71
|
+
s.gsub(/\[/,"_").gsub(/\]/,"")
|
72
|
+
end
|
73
|
+
%>
|
74
|
+
<set empty="&this.empty?"/>
|
75
|
+
<% template ||= this.try.new_candidate || this.member_class.new -%>
|
76
|
+
<% minimum ||= 0 ; minimum = minimum.to_i -%>
|
77
|
+
<% skip ||= this.proxy_reflection.klass.reflect_on_all_associations.detect {|p| p.primary_key_name==this.proxy_reflection.primary_key_name}.try.name.to_s if this.respond_to? :proxy_reflection -%>
|
78
|
+
<% skip += ",#{more_skip}" if more_skip -%>
|
79
|
+
<% js_attrs = all_attributes.slice(:minimum, :add_hook, :remove_hook, :hide, :show) %>
|
80
|
+
<% js_attrs[:prefix] = param_name_for_this %>
|
81
|
+
<ul class="input-many #{this_field.dasherize}" data-rapid="#{data_rapid('input-many', js_attrs)}" merge-attrs>
|
82
|
+
<fake-field-context fake-field="-1" context="&template">
|
83
|
+
<li class="input-many-li input-many-template" id="#{underize param_name_for_this}">
|
84
|
+
<div class="input-many-item" param="default">
|
85
|
+
<field-list param merge-attrs="fields" skip="&skip" />
|
86
|
+
</div>
|
87
|
+
<div class="buttons">
|
88
|
+
<button param="remove-item" id="#{underize param_name_for_this}_remove">-</button>
|
89
|
+
<button param="add-item" id="#{underize param_name_for_this}_add">+</button>
|
90
|
+
</div>
|
91
|
+
</li>
|
92
|
+
</fake-field-context>
|
93
|
+
<li class="input-many-li empty #{'hidden' unless this.empty? and minimum==0}" id="#{underize param_name_for_this}_-1_empty">
|
94
|
+
<!-- HACK way to signal an empty collection to the controller -->
|
95
|
+
<input type="hidden" class="empty-input" id="#{underize param_name_for_this}" name="#{param_name_for_this}" value="" disabled="&(!this.empty? || minimum>0)" />
|
96
|
+
<fake-field-context fake-field="-1" context="&template">
|
97
|
+
<div param="empty-message">
|
98
|
+
<ht key="#{this.class.to_s.underscore}.collection.empty_message">
|
99
|
+
No <%= this.class.name.titleize.downcase.pluralize %>.
|
100
|
+
</ht>
|
101
|
+
</div>
|
102
|
+
<div class="buttons">
|
103
|
+
<button param="remove-item" class="hidden" id="#{underize param_name_for_this}_remove">-</button>
|
104
|
+
<button param="add-item" id="#{underize param_name_for_this}_add">+</button>
|
105
|
+
</div>
|
106
|
+
</fake-field-context>
|
107
|
+
</li>
|
108
|
+
<fake-field-context fake-field="0" context="&template">
|
109
|
+
<li class="input-many-li" if="&(this_parent.empty? && minimum>0)" id="#{underize param_name_for_this}">
|
110
|
+
<div class="input-many-item" param="default">
|
111
|
+
<field-list param merge-attrs="fields" skip="&skip" />
|
112
|
+
</div>
|
113
|
+
<div class="buttons">
|
114
|
+
<button param="remove-item" class="hidden" id="#{underize param_name_for_this}_remove">-</button>
|
115
|
+
<button param="add-item" id="#{underize param_name_for_this}_add">+</button>
|
116
|
+
</div>
|
117
|
+
</li>
|
118
|
+
</fake-field-context>
|
119
|
+
<li repeat class="input-many-li #{'record-with-errors' unless this.errors.empty?}" id="#{underize param_name_for_this}">
|
120
|
+
<error-messages without-heading class="sub-record"/>
|
121
|
+
<hidden-id-field/>
|
122
|
+
<div class="input-many-item" param="default">
|
123
|
+
<field-list param merge-attrs="fields" skip="&skip" />
|
124
|
+
</div>
|
125
|
+
<div class="buttons">
|
126
|
+
<button param="remove-item" class="#{'hidden' if this_parent.length<=minimum}" id="#{underize param_name_for_this}_remove">-</button>
|
127
|
+
<button param="add-item" class="#{'hidden' if not last_item?}" id="#{underize param_name_for_this}_add">+</button>
|
128
|
+
</div>
|
129
|
+
</li>
|
130
|
+
</ul>
|
131
|
+
</def>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%# Simple or complicated input widgets for use inside forms %>
|
@@ -0,0 +1,74 @@
|
|
1
|
+
<!-- An `<input type="text">` with auto-completion. Allows the user to chose the target of a `belongs_to` association by name.
|
2
|
+
|
3
|
+
This tag relies on an autocompleter being defined in a controller. A simple example:
|
4
|
+
|
5
|
+
<form with="&ProjectMembership.new">
|
6
|
+
<name-one:user>
|
7
|
+
</form>
|
8
|
+
|
9
|
+
class ProjectMembership < ActiveRecord::Base
|
10
|
+
hobo_model
|
11
|
+
belongs_to :user
|
12
|
+
end
|
13
|
+
|
14
|
+
class User < ActiveRecord::Base
|
15
|
+
hobo_user_model
|
16
|
+
has_many :project_memberships, :accessible => true, :dependent => :destroy
|
17
|
+
end
|
18
|
+
|
19
|
+
class UsersController < ApplicationController
|
20
|
+
autocomplete
|
21
|
+
end
|
22
|
+
|
23
|
+
The route used by the autocompleter looks something like `/users/complete_name`. The first part of this route is specified by the `complete-target` attribute, and the second part is specified by the `completer` attribute.
|
24
|
+
|
25
|
+
`complete-target` specifies the controller for the route. It can be specified by either supplying a model class or a model. If a model is supplied, the id of the model is passed as a parameter to the controller. (`?id=7`, for example) The default for this attribute is the class of the context. In other words, the class that contains the `has_many / has_one`, not the class with the `belongs_to`.
|
26
|
+
|
27
|
+
`completer` specifies the action for the route. `name-one` prepends `complete_` to the value given here. This should be exactly the same as the first parameter to `autocomplete` in your controller. As an example: `autocomplete :email_address` would correspond to `completer="email_address"`. The default for this attribute is the name field for the model being searched, which is usually `name`, but not always.
|
28
|
+
|
29
|
+
The query string is passed to the controller in the `query` parameter. (`?query=hello` for example).
|
30
|
+
|
31
|
+
For more information on how to customize the controller, see the [controller manual](http://cookbook.hobocentral.net/manual/controllers#autocompleters)
|
32
|
+
|
33
|
+
Here's a more complex example. This used to be a part of [agility](http://cookbook.hobocentral.net/tutorials/agility) until it was simplified.
|
34
|
+
|
35
|
+
class ProjectsController < ApplicationController
|
36
|
+
autocomplete :new_member_name do
|
37
|
+
project = find_instance
|
38
|
+
hobo_completions :name, User.without_project(project).is_not(project.owner)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Note that this was added to the projects controller, rather than the users controller as in the first example. You can read this as: create an auto-complete action called `new_member_name` that finds users that are not already members of the project, and not the owner of the project, and completes the :name field.
|
43
|
+
|
44
|
+
<name-one:user complete-target="&@project" completer="new_member_name"/>
|
45
|
+
|
46
|
+
We're using an object as the complete-target rather than a class. This allows the `find_instance` in our controller action to function.
|
47
|
+
|
48
|
+
There's another example of `<name-one>` use in the [recipes](http://cookbook.hobocentral.net/recipes/36-using-a-name-one-one-a).
|
49
|
+
|
50
|
+
### Attributes:
|
51
|
+
|
52
|
+
- `complete-target`, `completer`: see above
|
53
|
+
- `min-chars`: The minimum number of characters that must be entered in the input field before an Ajax request is made.
|
54
|
+
- `nil-value`: If there is no current value, this text will appear greyed out inside the control, and will disappear on focus.
|
55
|
+
|
56
|
+
### Note:
|
57
|
+
|
58
|
+
If you wish to set `min-chars` to 0, you will require this [patch to controls.js](http://github.com/bryanlarsen/scriptaculous/commit/3915b7b). 'controls.js' was added to your project via the rails generator, not via Hobo.
|
59
|
+
|
60
|
+
-->
|
61
|
+
<def tag="name-one" attrs="complete-target, completer, min-chars, nil-value"><%
|
62
|
+
complete_target ||= this_field_reflection.klass
|
63
|
+
completer ||= (complete_target.is_a?(Class) ? complete_target : complete_target.class).name_attribute
|
64
|
+
min_chars ||= 1
|
65
|
+
value = name(:no_wrapper => true, :if_present => true)
|
66
|
+
-%>
|
67
|
+
<wrap tag="span" class="field-with-errors" when="&!this_parent.nil? && !this_parent.errors[this_field].empty?">
|
68
|
+
<input type="text" name="#{param_name_for_this}"
|
69
|
+
class="autocompleter #{type_and_field._?.dasherize} #{css_data :complete_on, typed_id(complete_target), completer} #{css_data :min_chars, min_chars} #{'nil-value' if value==''}"
|
70
|
+
value="#{value=='' ? nil_value : value}"
|
71
|
+
merge-attrs/>
|
72
|
+
<div class="completions-popup" style="display:none"></div>
|
73
|
+
</wrap>
|
74
|
+
</def>
|