hobo 0.8.10 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGES.txt +126 -2
  2. data/Rakefile +4 -1
  3. data/bin/hobo +1 -1
  4. data/doctest/scopes.rdoctest +11 -4
  5. data/dryml_generators/rapid/cards.dryml.erb +8 -2
  6. data/dryml_generators/rapid/forms.dryml.erb +5 -4
  7. data/dryml_generators/rapid/pages.dryml.erb +150 -65
  8. data/lib/hobo.rb +1 -1
  9. data/lib/hobo/accessible_associations.rb +2 -0
  10. data/lib/hobo/authentication_support.rb +1 -1
  11. data/lib/hobo/controller.rb +11 -3
  12. data/lib/hobo/dryml/dryml_doc.rb +1 -1
  13. data/lib/hobo/fake_initializer.rb +14 -0
  14. data/lib/hobo/hobo_helper.rb +94 -6
  15. data/lib/hobo/lifecycles.rb +17 -2
  16. data/lib/hobo/lifecycles/lifecycle.rb +1 -1
  17. data/lib/hobo/lifecycles/transition.rb +12 -4
  18. data/lib/hobo/model.rb +25 -22
  19. data/lib/hobo/model_controller.rb +42 -37
  20. data/lib/hobo/model_router.rb +11 -7
  21. data/lib/hobo/permissions.rb +12 -10
  22. data/lib/hobo/permissions/associations.rb +1 -1
  23. data/lib/hobo/static_tags +21 -0
  24. data/lib/hobo/user.rb +7 -3
  25. data/lib/hobo/user_controller.rb +7 -7
  26. data/lib/hobo/view_hints.rb +10 -3
  27. data/rails_generators/hobo/USAGE +4 -0
  28. data/rails_generators/hobo_admin_site/USAGE +16 -0
  29. data/rails_generators/hobo_front_controller/hobo_front_controller_generator.rb +11 -2
  30. data/rails_generators/hobo_front_controller/templates/controller.rb +6 -0
  31. data/rails_generators/hobo_front_controller/templates/summary.dryml +103 -0
  32. data/rails_generators/hobo_model_resource/USAGE +38 -0
  33. data/rails_generators/hobo_rapid/USAGE +3 -0
  34. data/rails_generators/hobo_rapid/templates/hobo-rapid.js +7 -3
  35. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +4 -0
  36. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +5 -0
  37. data/rails_generators/hobo_subsite/USAGE +16 -0
  38. data/rails_generators/hobo_subsite/hobo_subsite_generator.rb +1 -1
  39. data/rails_generators/hobo_user_controller/templates/controller.rb +2 -2
  40. data/rails_generators/hobo_user_model/templates/model.rb +6 -1
  41. data/taglibs/rapid.dryml +1 -0
  42. data/taglibs/rapid_core.dryml +4 -4
  43. data/taglibs/rapid_forms.dryml +29 -21
  44. data/taglibs/rapid_generics.dryml +3 -1
  45. data/taglibs/rapid_lifecycles.dryml +14 -9
  46. data/taglibs/rapid_navigation.dryml +1 -1
  47. data/taglibs/rapid_plus.dryml +1 -0
  48. data/taglibs/rapid_summary.dryml +300 -0
  49. data/taglibs/rapid_support.dryml +1 -1
  50. data/taglibs/rapid_user_pages.dryml +21 -19
  51. data/test/permissions/test_permissions.rb +1 -1
  52. metadata +12 -4
@@ -0,0 +1,38 @@
1
+ Description:
2
+ The model generator creates stubs for a new model, corresponding
3
+ controller and it's views.
4
+
5
+ The generator takes a model name as its argument. The model name
6
+ may be given in CamelCase or under_score and should not be
7
+ suffixed with anything.
8
+
9
+ As additional parameters, the generator will take attribute pairs
10
+ described by name and type. These attributes will be used to
11
+ prepopulate the migration to create the table for the model and
12
+ give you a set of predefined fixture. You don't have to think up
13
+ all attributes up front, but it's a good idea of adding just the
14
+ baseline of what's needed to start really working with the
15
+ resource.
16
+
17
+ The generator creates a model class in app/models, a test suite in
18
+ test/unit and test fixtures in test/fixtures/singular_name.yml.
19
+ The generator creates a controller class in app/controllers with
20
+ view templates in app/views/controller_name, a helper class in
21
+ app/helpers, and a functional test suite in test/functional.
22
+
23
+ Examples:
24
+ ./script/generate hobo_model_resource account
25
+
26
+ This will create an Account model:
27
+ Model: app/models/account.rb
28
+ Controller: app/controllers/accounts_controller.rb
29
+ Helper: app/helpers/accounts_helper.rb
30
+ Views: app/views/accounts
31
+ ViewHints: app/viewhints/account_hints.rb
32
+ Test: test/unit/account_test.rb
33
+ Test: test/functions/accounts_controller_test.rb
34
+ Fixtures: test/fixtures/accounts.yml
35
+
36
+ ./script/generate hobo_model_resource post title:string created_on:date body:text published:boolean
37
+
38
+ Creates post model, controller & views with predefined attributes.
@@ -0,0 +1,3 @@
1
+ Description:
2
+
3
+ Adds support for Rapid to your application.
@@ -655,7 +655,9 @@ SelectManyInput = Behavior.create({
655
655
  this.element.down('.items').appendChild(newItem);
656
656
  newItem.down('span').innerHTML = selected.innerHTML
657
657
  this.itemAdded(newItem, selected)
658
- selected.disabled = true
658
+ var optgroup = new Element("optgroup", {alt:selected.value, label:selected.text})
659
+ optgroup.addClassName("disabled-option")
660
+ selected.replace(optgroup)
659
661
  select.value = ""
660
662
  Event.addBehavior.reload()
661
663
  this.element.fire("rapid:add", { element: newItem })
@@ -678,8 +680,10 @@ SelectManyInput = Behavior.create({
678
680
  element.fire("rapid:change", { element: el })
679
681
  } } )
680
682
  var label = el.down('span').innerHTML
681
- var option = $A(element.getElementsByTagName('option')).find(function(o) { return o.innerHTML == label })
682
- option.disabled = false
683
+ var optgroup = element.down("optgroup[label="+label+"]")
684
+ var option = new Element("option", {value:optgroup.readAttribute("alt")})
685
+ option.innerHTML = optgroup.readAttribute("label")
686
+ optgroup.replace(option)
683
687
  },
684
688
 
685
689
  itemAdded: function(item, option) {
@@ -321,3 +321,7 @@ ul.input-many div.buttons {float:left; margin-left:10px;}
321
321
  ul.check-many { list-style-type: none; margin-left: 0px;}
322
322
  ul.check-many li input { vertical-align: -20%;}
323
323
 
324
+ /* rapid-summary */
325
+ table.app-summary { border: 1px solid; border-collapse: collapse; }
326
+ table.app-summary td { padding: 2px; border: 1px dotted #bbb; }
327
+ table.app-summary th { padding: 2px; border-bottom: 1px solid; background: #acd3e6; }
@@ -92,4 +92,9 @@ select.dev-user-changer:hover { opacity: 1; }
92
92
 
93
93
  .part-wrapper {
94
94
  display: inline; /* don't mess up layout when wrapping something */
95
+ }
96
+
97
+ optgroup.disabled-option {
98
+ color: #ccc;
99
+ height: 1em;
95
100
  }
@@ -0,0 +1,16 @@
1
+ Description:
2
+
3
+ Creates a subsite, a namespaced section of your application.
4
+
5
+ The subsite will use app/views/taglibs/<subsite_name>_site.dryml
6
+ instead of app/views/taglibs/appplication.dryml. This allows you
7
+ to easily set different themes and choose different CSS files for
8
+ the subsite.
9
+
10
+ Controllers for the subsite are created in
11
+ app/controllers/<subsite_name>/ and views are also in their own
12
+ subdirectory. This allows you to have two different controllers
13
+ and two different sets of views for the same model.
14
+
15
+ The difference between hobo_admin_site and hobo_subsite is that
16
+ hobo_admin_site limits the subsite to use by administrators only.
@@ -53,7 +53,7 @@ class HoboSubsiteGenerator < Rails::Generator::NamedBase
53
53
 
54
54
  protected
55
55
  def banner
56
- "Usage: #{$0} #{spec.name} [--make-front-site | --no-front-site]"
56
+ "Usage: #{$0} #{spec.name} <subsite_name> [--make-front-site | --no-front-site]"
57
57
  end
58
58
 
59
59
 
@@ -12,7 +12,7 @@ class <%= class_name %>Controller < ApplicationController
12
12
  this.password = this.password_confirmation = nil # don't trigger password change validations
13
13
  this.state = 'active'
14
14
  this.save
15
- flash[:notice] = "You are now the site administrator"
15
+ flash[:notice] = ht("hobo.messages.you_are_site_admin", :default=>"You are now the site administrator")
16
16
  redirect_to home_page
17
17
  end
18
18
  end
@@ -21,7 +21,7 @@ class <%= class_name %>Controller < ApplicationController
21
21
  def do_accept_invitation
22
22
  do_transition_action :accept_invitation do
23
23
  self.current_user = this
24
- flash[:notice] = "You have signed up"
24
+ flash[:notice] = ht("hobo.messages.you_signed_up", :default=>"You have signed up")
25
25
  end
26
26
  end
27
27
  <% end -%>
@@ -9,12 +9,17 @@ class <%= class_name %> < ActiveRecord::Base
9
9
  timestamps
10
10
  end
11
11
 
12
+ validates_presence_of :name
13
+
12
14
  # This gives admin rights to the first sign-up.
13
15
  # Just remove it if you don't want that
14
16
  before_create { |user| user.administrator = true if !Rails.env.test? && count == 0 }
15
17
 
16
18
  <% if invite_only? -%>
17
- validates_confirmation_of :password, :if => "User.count == 0"
19
+ def new_password_required_with_invite_only?
20
+ new_password_required_without_invite_only? || User.count==0
21
+ end
22
+ alias_method_chain :new_password_required?, :invite_only
18
23
  <% end -%>
19
24
 
20
25
  # --- Signup lifecycle --- #
@@ -18,3 +18,4 @@ The Rapid tag library makes web development go fast. The Rapid tag library is yo
18
18
  <include src="rapid_plus"/>
19
19
  <include src="rapid_generics"/>
20
20
  <include src="rapid_lifecycles"/>
21
+ <include src="rapid_summary"/>
@@ -112,7 +112,7 @@ This will use `<input/>` as the tag in each table cell instead of `<view/>`
112
112
  <td param="#{this_field.to_s.sub('?', '').gsub('.', '-')}-view"><call-tag tag="&field_tag"/></td>
113
113
  </with-fields>
114
114
  <td class="controls" param="controls" if="&all_parameters[:controls]">
115
- <a param="edit-link" action="edit" if="&can_edit?">Edit</a>
115
+ <a param="edit-link" action="edit" if="&can_edit?"><ht key="hobo.action.edit">Edit</ht></a>
116
116
  <delete-button param/>
117
117
  </td>
118
118
  </if>
@@ -517,11 +517,11 @@ The label can be customised using the `label` attribute, e.g.
517
517
  c = this.try.to_int || this.try.total_entries || (this.try.loaded? && this.try.length) || this.try.count || this.try.length
518
518
 
519
519
  label ||= if this.is_a?(Class)
520
- this.name
520
+ this.view_hints.model_name
521
521
  elsif (attr = this.try.origin_attribute)
522
- attr.to_s.singularize
522
+ this.member_class.view_hints.field_name(attr).singularize
523
523
  else
524
- this.member_class.name
524
+ this.member_class.view_hints.model_name
525
525
  end.titleize
526
526
 
527
527
  label = label.downcase if lowercase
@@ -32,7 +32,7 @@ Several of the tags in this taglib support the following set of ajax attributes:
32
32
  The following attributes are also supported by all the ajax tags. Set them to fragments of javascript to have that script
33
33
  executed at various points in the ajax request cycle:
34
34
 
35
- - before: script to run befofre the request
35
+ - before: script to run before the request
36
36
 
37
37
  - success: script to run on successful completion of the request
38
38
 
@@ -183,7 +183,7 @@ AJAX based submission can be enabled by simply adding an `update` attribute. e.g
183
183
  page_path = if (request.post? || request.put?) && params[:page_path]
184
184
  params[:page_path]
185
185
  else
186
- view_name.sub(Hobo::Dryml::EMPTY_PAGE, params[:action])
186
+ view_name.sub(Hobo::Dryml::EMPTY_PAGE, params[:action] || '')
187
187
  end
188
188
  page_path_hidden = hidden_field_tag("page_path", page_path)
189
189
  end
@@ -319,7 +319,7 @@ edit collections a `Category` model in your application:
319
319
  <def tag="input" for="boolean" attrs="name">
320
320
  <%= unless attributes[:disabled]
321
321
  cb_tag = check_box_tag(name, '1', this, attributes)
322
- cb_hidden_tag = hidden_field_tag(name, '0')
322
+ cb_hidden_tag = hidden_field_tag(name, '0', :id => nil)
323
323
  HoboSupport::RAILS_AT_LEAST_23 ? cb_hidden_tag + cb_tag : cb_tag + cb_hidden_tag
324
324
  end %>
325
325
  </def>
@@ -380,8 +380,6 @@ The menus default to the current time if the current value is nil.
380
380
  <% if ! order.nil?
381
381
  order = comma_split(order).*.to_sym
382
382
  attributes.merge!(:order => order)
383
- require 'ruby-debug'
384
- debugger
385
383
  end -%>
386
384
  <%= select_datetime(this || current_time, attributes.merge(:prefix => param_name_for_this)) %>
387
385
  </def>
@@ -536,8 +534,8 @@ All the standard ajax attributes *except the callbacks* are supported (see the m
536
534
  else
537
535
  { :type => "button" }
538
536
  end)
539
- label ||= "Remove"
540
- confirm = "Are you sure?" if confirm.nil?
537
+ label ||= ht("hobo.actions.remove", :default=>"Remove")
538
+ confirm = ht("hobo.messages.confirm", :default=>"Are you sure?") if confirm.nil?
541
539
 
542
540
  add_classes!(attributes,
543
541
  image ? "image-button" : "button",
@@ -584,7 +582,7 @@ All of the standard ajax attributes are supported (see the main taglib documenti
584
582
  new = class_or_assoc.new(fields)
585
583
  new.set_creator(current_user)
586
584
  if can_create?(new)
587
- label ||= "New #{new.class.name.titleize}"
585
+ label ||= ht("#{new.class.name.pluralize.underscore}.actions.new", :default=>"New #{new.class.name.titleize}")
588
586
  ajax_attributes = { :message => message }
589
587
  class_name = new.class.name.underscore
590
588
  ajax_attributes[:params] = { class_name => fields } unless fields.empty?
@@ -601,7 +599,7 @@ This is the default input that Rapid uses for `belongs_to` associations. The men
601
599
 
602
600
  ### Attributes
603
601
 
604
- - `include-none` - whether to include a 'none' option (i.e. set the foreign key to null). Defaults to false
602
+ - `include-none` - whether to include a 'none' option (i.e. set the foreign key to null). If this value is not supplied, the default is "true" if the current value is nil; otherwise the default is "false". One implication of this is that the default may change when the form is re-rendered due to a validation failure. Setting this value explicitly is recommended.
605
603
  - `blank-message` - the message for the 'none' option. Defaults to "(No `<model-name>`)", e.g. "(No Product)"
606
604
  - `options` - an array of records to include in the menu. Defaults to the all the records in the target table that match any `:conditions` declared on the `belongs_to` (subject to `limit`)
607
605
  - `limit` - if `options` is not specified, this limits the number of records. Default: 100
@@ -615,7 +613,7 @@ For situations where there are too many target records to practically include in
615
613
  <def tag="select-one" attrs="include-none, blank-message, options, sort, limit, text-method"><%
616
614
  raise HoboError.new("Not allowed to edit #{this_field}") if !attributes[:disabled] && !can_edit?
617
615
 
618
- blank_message ||= "(No #{this_type.name.to_s.titleize})"
616
+ blank_message ||= ht("#{this_type.name.underscore}.message.no", :default=>"(No #{this_type.view_hints.model_name})")
619
617
  limit ||= 100
620
618
 
621
619
  options ||= begin
@@ -682,7 +680,7 @@ Here's a more complex example. This used to be a part of [agility](http://cookb
682
680
  end
683
681
  end
684
682
 
685
- 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.
683
+ 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.
686
684
 
687
685
  <name-one:user complete-target="&@project" completer="new_member_name"/>
688
686
 
@@ -751,13 +749,14 @@ To use this tag, the model of the items the user is chosing *must* have unique n
751
749
  name ||= param_name_for_this
752
750
 
753
751
  values = this
752
+ remove_label ||= ht("hobo.actions.remove", :default=>'Remove')
754
753
  -%>
755
754
  <div class="input select-many" merge-attrs>
756
755
  <div style="display:none" class="item-proto">
757
756
  <div class="item" param="proto-item">
758
757
  <span></span>
759
758
  <input type="hidden" name="#{name}[]" param="proto-hidden"/>
760
- <input type="button" class="remove-item" value="#{remove_label || 'Remove'}" param="proto-remove-button"/>
759
+ <input type="button" class="remove-item" value="#{remove_label}" param="proto-remove-button"/>
761
760
  </div>
762
761
  </div>
763
762
  <div class="items">
@@ -765,14 +764,20 @@ To use this tag, the model of the items the user is chosing *must* have unique n
765
764
  <span><%= h this.to_s %></span>
766
765
  <input type="hidden" name="#{name}[]" value="@#{h this.id}" disabled="&disabled"
767
766
  param="hidden"/>
768
- <input type="button" class="remove-item" value="#{remove_label || 'Remove'}" disabled="&disabled"
767
+ <input type="button" class="remove-item" value="#{remove_label}" disabled="&disabled"
769
768
  param="remove-button"/>
770
769
  </div>
771
770
  </div>
772
771
  <select merge-attrs="&{:disabled => disabled}">
773
772
  <option value=""><prompt/></option>
774
- <option repeat="&options.sort_by {|x| x.to_s.downcase}" value="@#{this.id}"
775
- merge-attrs="&{:disabled => 'true'} if this.in?(values)"><%= h this.to_s %></option>
773
+ <repeat with="&options">
774
+ <if test="&this.in?(values)">
775
+ <optgroup class="disabled-option" label="#{h this.to_s}" alt="@#{this.id}">&nbsp;</optgroup>
776
+ </if>
777
+ <else>
778
+ <option value="@#{this.id}"><%= h this.to_s %></option>
779
+ </else>
780
+ </repeat>
776
781
  </select>
777
782
  </div>
778
783
  </def>
@@ -814,7 +819,7 @@ Use the `uri` option to specify a redirect location:
814
819
 
815
820
  <!-- A simple wrapper around the `<select>` tag and `options_for_select` helper
816
821
 
817
- ### Attributes
822
+ ### Attributes
818
823
 
819
824
  - `options` - an array of options suitable to be passed to the Rails `options_for_select` helper.
820
825
  - `selected` - the value (from the `options` array) that should be initially selected. Defaults to `this`
@@ -867,7 +872,7 @@ Use the `uri` option to specify a redirect location:
867
872
 
868
873
  This tag is very different from tags like `<select-many>` and `<check-many>` in that:
869
874
 
870
- - Those tags are used to *chose existing records* to include in the assocaition, while `<input-many>` is used to actually create or edit the records in the association.
875
+ - Those tags are used to *chose existing records* to include in the association, while `<input-many>` is used to actually create or edit the records in the association.
871
876
 
872
877
  ### Example
873
878
 
@@ -880,9 +885,11 @@ The body of the tag will be repeated for each of the current records in the coll
880
885
  ### Attributes
881
886
 
882
887
  - 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>`
888
+
889
+ - skip: Passed through to the `<field-list>`. If not specified, it defaults to the parent association.
883
890
 
884
891
  -->
885
- <def tag="input-many" attrs="fields" polymorphic>
892
+ <def tag="input-many" attrs="fields,skip" polymorphic>
886
893
  <set empty="&this.empty?"/>
887
894
  <ul class="input-many #{this_field.dasherize} #{css_data :input_many_prefix, param_name_for_this}">
888
895
  <li repeat class="#{'record-with-errors' unless this.errors.empty?}">
@@ -897,9 +904,10 @@ The body of the tag will be repeated for each of the current records in the coll
897
904
  </div>
898
905
  </li>
899
906
  <li if="&empty">
907
+ <% 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 %>
900
908
  <fake-field-context fake-field="0" context="&this.try.new_candidate || this.member_class.new">
901
909
  <div class="input-many-item" param="default">
902
- <field-list merge-attrs="fields"/>
910
+ <field-list merge-attrs="fields" skip="&skip"/>
903
911
  </div>
904
912
  </fake-field-context>
905
913
  <div class="buttons">
@@ -928,8 +936,8 @@ The body of the tag will be repeated for each of the current records in the coll
928
936
  <!-- Renders the common "or (Cancel)" for a form. Attributes are merged into the link (`<a>Cancel</a>`), making it easy to customise the destination of the cancel link. By default it will link to `this` or `this.class`.
929
937
  -->
930
938
  <def tag="or-cancel">
931
- <if test="&linkable?">or <a merge-attrs>Cancel</a></if>
939
+ <if test="&linkable?"><ht key="hobo.support.or">or</ht> <a merge-attrs><ht key="hobo.actions.cancel">Cancel</ht></a></if>
932
940
  <else>
933
- <if test="&linkable?(this.class)">or <a to="&this.class" merge-attrs>Cancel</a></if>
941
+ <if test="&linkable?(this.class)"><ht key="hobo.support.or">or</ht> <a to="&this.class" merge-attrs><ht key="hobo.actions.cancel">Cancel</ht></a></if>
934
942
  </else>
935
943
  </def>
@@ -19,7 +19,9 @@
19
19
  -->
20
20
  <def tag="empty-collection-message">
21
21
  <div class="empty-collection-message" style="#{'display:none' if !this.empty?}" param="default">
22
- No <collection-name lowercase/> to display
22
+ <ht key="#{type_name.downcase.sub(' ', '_').pluralize}.collection.empty_message">
23
+ No <collection-name lowercase/> to display
24
+ </ht>
23
25
  </div>
24
26
  </def>
25
27
 
@@ -10,15 +10,20 @@
10
10
 
11
11
  All of the [standard ajax attributes](/api_taglibs/rapid_forms) are also supported.
12
12
  -->
13
- <def tag="transition-button" attrs="transition, update, label"><%=
14
- transition = transition.name unless transition.is_a?(String)
13
+ <def tag="transition-button" attrs="transition, update, label"><%=
14
+ if transition.is_a?(String)
15
+ transition = this.lifecycle.find_transition(transition, current_user)
16
+ end
17
+ transition_name = transition.name
18
+ has_params = !transition.options[:params].blank?
15
19
  ajax_attributes, html_attributes = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
16
20
 
17
- html_attributes[:method] = :put
18
- url = object_url(this, transition, :method => :put)
19
- add_classes!(html_attributes, "transition-button #{transition}-button")
20
- label ||= transition.to_s.titleize
21
- if update || !ajax_attributes.empty?
21
+ html_attributes[:method] ||= has_params ? :get : :put
22
+ add_classes!(html_attributes, "transition-button #{transition_name}-button")
23
+ label ||= transition_name.to_s.titleize
24
+ url = object_url(this, transition_name, :method => html_attributes[:method])
25
+
26
+ if (update || !ajax_attributes.empty?) && !has_params
22
27
  ajax_attributes[:message] ||= label
23
28
  func = ajax_updater(url, update, ajax_attributes)
24
29
  html_attributes.update(:onclick => "var e = this; " + func, :type =>'button', :value => label)
@@ -28,10 +33,10 @@ All of the [standard ajax attributes](/api_taglibs/rapid_forms) are also support
28
33
  end
29
34
  %>
30
35
  </def>
31
-
36
+
32
37
 
33
38
  <!-- Renders a div containing transition buttons for every transition available to the current user.
34
-
39
+
35
40
  For example, you could use this on a `Friendship` card: the person invited to have friendship would automatically see 'Accept' and 'Decline' buttons, while the person initiating the invite would see 'Retract'.
36
41
  -->
37
42
  <def tag="transition-buttons">
@@ -85,5 +85,5 @@ This is a simple tag - just look at the source if you need to know more detail.
85
85
 
86
86
  <!--- A simple wrapper around the `will_paginate` helper. All options to `will_paginate` are available as attributes -->
87
87
  <def tag="page-nav">
88
- <%= will_paginate this, attributes.symbolize_keys.reverse_merge(:inner_window => 2, :previous_label => "&laquo; Prev") %>
88
+ <%= will_paginate this, attributes.symbolize_keys.reverse_merge(:inner_window => 2, :previous_label => translate("hobo.actions.previous", :default=>"&laquo; Prev"), :next_label =>translate("hobo.actions.next", :default=>"Next &raquo;")) %>
89
89
  </def>
@@ -103,6 +103,7 @@ This tag assumes the controller has a `reorder` action. This action is added aut
103
103
  <h3 param="heading"><%= name.titleize %></h3>
104
104
  <a param="more">More <type-name plural lowercase/>...</a>
105
105
  <collection param/>
106
+ <a action="new" if="&can_create?(this.new)" param="new-link">New <%= this.member_class.view_hints.model_name %></a>
106
107
  </section>
107
108
  </def>
108
109
 
@@ -0,0 +1,300 @@
1
+ <!-- These are a collection of tags that allow a application outline or summary to be created -->
2
+
3
+ <!-- Rails.version -->
4
+ <def tag="rails-version">
5
+ <%= Rails.version -%>
6
+ </def>
7
+
8
+ <!-- returns "vendor" or "gem" -->
9
+ <def tag="rails-location">
10
+ <%= Rails.vendor_rails? ? "vendor" : "gem" -%>
11
+ </def>
12
+
13
+ <!-- `RAILS_ROOT` -->
14
+ <def tag="rails-root">
15
+ <%= RAILS_ROOT -%>
16
+ </def>
17
+
18
+ <!-- `RAILS_ENV` -->
19
+ <def tag="rails-env">
20
+ <%= RAILS_ENV -%>
21
+ </def>
22
+
23
+ <!-- Which change management system is in use: "git" "subversion" "other" -->
24
+ <def tag="cms-method">
25
+ <%= if File.exist?("#{RAILS_ROOT}/.git")
26
+ "git"
27
+ elsif File.exist?("#{RAILS_ROOT}/.svn")
28
+ "subversion"
29
+ else
30
+ "other"
31
+ end -%>
32
+ </def>
33
+
34
+ <!-- the git branch currently in use -->
35
+ <def tag="git-branch">
36
+ <%= File.basename(`git symbolic-ref HEAD`) -%>
37
+ </def>
38
+
39
+ <!-- the git version currently in use -->
40
+ <def tag="git-version" attrs="dir">
41
+ <% dir=RAILS_ROOT if dir.blank? -%>
42
+ <%= Dir.chdir(dir) {`git rev-list "HEAD^..HEAD" --abbrev-commit --abbrev=7`} -%>
43
+ </def>
44
+
45
+ <!-- returns 'clean' if there are no modified files, 'modified' otherwise. -->
46
+ <def tag="git-clean" attrs="dir">
47
+ <% dir=RAILS_ROOT if dir.blank? -%>
48
+ <%= Dir.chdir(dir) {`git ls-files -m`}.blank? ? "clean" : "modified" -%>
49
+ </def>
50
+
51
+ <!-- the time & date of the last commit -->
52
+ <def tag="git-last-commit-time">
53
+ <%= `git log "HEAD^..HEAD" --pretty=format:%cD` -%>
54
+ </def>
55
+
56
+ <!-- calls `git-clean` or `svn-clean` as appropriate. `svn-clean` not yet written. -->
57
+ <def tag="cms-clean">
58
+ <% method = cms_method.strip -%>
59
+ <git-clean if="&method=='git'"/>
60
+ </def>
61
+
62
+ <!-- calls `git-last-commit-time` or `svn-last-commit-time` as appropriate. `svn-last-commit-time` not yet written. -->
63
+ <def tag="cms-last-commit-time">
64
+ <% method = cms_method.strip -%>
65
+ <git-last-commit-time if="&method=='git'"/>
66
+ </def>
67
+
68
+ <!-- calls `git-version` or `svn-version` as appropriate. `svn-version` not yet written. -->
69
+ <def tag="cms-version">
70
+ <% method = cms_method.strip -%>
71
+ <git-version if="&method=='git'"/>
72
+ </def>
73
+
74
+ <!-- calls `git-branch` or `svn-branch` as appropriate. `svn-branch` not yet written. -->
75
+ <def tag="cms-branch">
76
+ <% method = cms_method.strip -%>
77
+ <git-branch if="&method=='git'"/>
78
+ </def>
79
+
80
+ <!-- Hobo::VERSION -->
81
+ <def tag="hobo-version">
82
+ <%= Hobo::VERSION -%>
83
+ </def>
84
+
85
+ <!-- repeats on Rails.configuration.gems, including dependent gems -->
86
+ <def tag="with-gems">
87
+ <% gems = ActiveSupport::OrderedHash.new -%>
88
+ <% Rails.configuration.gems.each do |gem|
89
+ gems[name] ||= gem
90
+ gem.dependencies.each do |dep|
91
+ gems[dep] ||= dep
92
+ end
93
+ end -%>
94
+ <repeat with="&gems">
95
+ <do param="default"/>
96
+ </repeat>
97
+ </def>
98
+
99
+ <!-- inside `<with-gems>`, returns the gem name -->
100
+ <def tag="gem-name">
101
+ <%= this.name -%>
102
+ </def>
103
+
104
+ <def tag="gem-version-requirement">
105
+ <%= this.requirements.map{|l| l.first+l.second.to_s}.join(",") -%>
106
+ </def>
107
+
108
+ <!-- inside `<with-gems>`, returns the version required -->
109
+ <def tag="gem-version-required">
110
+ <gem-version-requirement:version-requirements />
111
+ </def>
112
+
113
+ <!-- inside `<with-gems>`, returns the version -->
114
+ <def tag="gem-version">
115
+ <%= this.specification.version.to_s -%>
116
+ </def>
117
+
118
+ <!-- inside `<with-gems>`, returns 'frozen', 'installed' or 'missing' -->
119
+ <def tag="gem-frozen">
120
+ <%= this.frozen? ? "frozen" : (this.installed? ? "installed" : "missing") -%>
121
+ </def>
122
+
123
+ <!-- inside `<with-gems>`, returns the gem dependencies -->
124
+ <def tag="gem-dependencies">
125
+ <repeat with="&this.dependencies">
126
+ <%= this.name -%><gem-version-requirement:requirement />
127
+ </repeat>
128
+ </def>
129
+
130
+ <!-- repeats on the plugins used by the application -->
131
+ <def tag="with-plugins">
132
+ <% fi = Hobo::FakeInitializer.new(Rails.configuration)
133
+ plugins = Rails.configuration.plugin_loader.new(fi).plugins %>
134
+ <repeat with="&plugins">
135
+ <do param="default" />
136
+ </repeat>
137
+ </def>
138
+
139
+ <!-- within `<with-plugins>`, returns the plugin name -->
140
+ <def tag="plugin-name">
141
+ <%= this.name -%>
142
+ </def>
143
+
144
+ <!-- within `<with-plugins>`, returns the plugin location (directory) -->
145
+ <def tag="plugin-location">
146
+ <%= this.directory -%>
147
+ </def>
148
+
149
+ <def tag="plugin-git-clean">
150
+ <git-clean dir="&this.directory"/>
151
+ </def>
152
+
153
+ <def tag="plugin-git-version">
154
+ <git-version dir="&this.directory"/>
155
+ </def>
156
+
157
+ <!-- within `<with-plugins>`, try and determine the method that was used to install the plugin. Returns "braid", "symlink", "git-submodule" or "other" -->
158
+ <def tag="plugin-method">
159
+ <%=
160
+ braids = File.open("#{RAILS_ROOT}/.braids") {|f| YAML::load(f) } rescue {}
161
+ if !braids[this.directory.gsub(RAILS_ROOT+"/", "")].nil?
162
+ "braid"
163
+ elsif File.lstat(this.directory).symlink?
164
+ "symlink"
165
+ elsif File.exist?(this.directory+"/.git")
166
+ "git-submodule"
167
+ else
168
+ "other"
169
+ end
170
+ -%>
171
+ </def>
172
+
173
+ <!-- within `<with-plugins>`, determine if the plugin has been modified, returning "clean" or "modified". Returns a blank string if this information is not available. Uses `<plugin-git-clean>` or `<plugin-braid-clean>` to do the heavy lifting. -->
174
+ <def tag="plugin-clean">
175
+ <% method = plugin_method.strip -%>
176
+ <plugin-git-clean if="&method=='git-submodule'"/>
177
+ <plugin-braid-clean if="&method=='braid'"/>
178
+ </def>
179
+
180
+ <!-- within `<with-plugins>`, returns the plugin version. Returns a blank string if this information is not available. Uses `<plugin-git-version>` or `<plugin-braid-version>` to do the heavy lifting. -->
181
+ <def tag="plugin-version">
182
+ <% method = plugin_method.strip -%>
183
+ <plugin-git-version if="&method=='git-submodule'"/>
184
+ <plugin-braid-version if="&method=='braid'"/>
185
+ </def>
186
+
187
+ <def tag="plugin-braid-clean">
188
+ <%= diff=`braid diff #{this.directory.gsub(RAILS_ROOT+"/", "")}`
189
+ diff.strip.empty? ? "clean" : "modified" -%>
190
+ </def>
191
+
192
+ <def tag="plugin-braid-version">
193
+ <%= braids = File.open("#{RAILS_ROOT}/.braids") {|f| YAML::load(f) } rescue {}
194
+ b=braids[this.directory.gsub(RAILS_ROOT+"/", "")]
195
+ b['revision'][0...7] if b -%>
196
+ </def>
197
+
198
+ <!-- repeats on the available execution environments, which are usually 'development', 'test' and 'production' -->
199
+ <def tag="with-environments">
200
+ <repeat with="&Rails.configuration.database_configuration.keys">
201
+ <do param="default" />
202
+ </repeat>
203
+ </def>
204
+
205
+ <def tag="environment-name">
206
+ <%= this -%>
207
+ </def>
208
+
209
+ <!-- Given an environment name in the context, return the database adapter -->
210
+ <def tag="database-type">
211
+ <%= Rails.configuration.database_configuration[this]['adapter'] -%>
212
+ </def>
213
+
214
+ <!-- Given an environment name in the context, return the database name -->
215
+ <def tag="database-name">
216
+ <%= Rails.configuration.database_configuration[this]['database'] -%>
217
+ </def>
218
+
219
+ <!-- repeats on available models. Does not return models defined in libraries or plugins. -->
220
+ <def tag="with-models">
221
+ <% # stolen from railroad/model_diagram.rb
222
+
223
+ files = Dir.glob("app/models/**/*.rb")
224
+ models = files.map do |file|
225
+ model_name = (model_path = file.gsub(/^(app\/models\/)([\w_\/\\]+)\.rb/, '\2')).camelize
226
+ # Hack to skip all xxx_related.rb files
227
+ next if /_related/i =~ model_name
228
+
229
+ begin
230
+ model_name.constantize
231
+ rescue LoadError
232
+ oldlen = model_path.length
233
+ model_path.gsub!(/.*[\/\\]/, '')
234
+ model_name = model_path.camelize
235
+ if oldlen > model_path.length
236
+ retry
237
+ end
238
+ next
239
+ rescue NameError
240
+ next
241
+ end
242
+ end
243
+ -%>
244
+ <repeat with="&models.sort_by {|m| m.to_s}">
245
+ <do param="default" />
246
+ </repeat>
247
+ </def>
248
+
249
+ <def tag="model-name">
250
+ <%= this.to_s -%>
251
+ </def>
252
+
253
+ <!-- given a model, returns the table name -->
254
+ <def tag="model-table-name">
255
+ <%= this.try.table_name -%>
256
+ </def>
257
+
258
+ <!-- given a model, repeats on the database columns -->
259
+ <def tag="with-model-columns">
260
+ <repeat with="&this.try.content_columns">
261
+ <do param="default" />
262
+ </repeat>
263
+ </def>
264
+
265
+ <!-- given a column, returns the type -->
266
+ <def tag="model-column-type">
267
+ <%= this.type.to_s -%>
268
+ </def>
269
+
270
+ <!-- given a column, return the name -->
271
+ <def tag="model-column-name">
272
+ <%= this.name -%>
273
+ </def>
274
+
275
+ <!-- given a model, repeats on the associations -->
276
+ <def tag="with-model-associations">
277
+ <repeat with="&this.try.reflect_on_all_associations">
278
+ <do param="default" />
279
+ </repeat>
280
+ </def>
281
+
282
+ <!-- given an association, return the name -->
283
+ <def tag="model-association-name">
284
+ <%= this.name.to_s -%>
285
+ </def>
286
+
287
+ <!-- given an association, return the macro type (`has_many`, etc.) -->
288
+ <def tag="model-association-macro">
289
+ <%= if this.macro.to_s=='has_many' && this.options[:through]
290
+ 'has_many :through'
291
+ else
292
+ this.macro.to_s
293
+ end -%>
294
+ </def>
295
+
296
+ <!-- given an association, return the name -->
297
+ <def tag="model-association-class-name">
298
+ <%= (this.class_name.try.respond_to? 'underscore') ? this.class_name.underscore.singularize.camelize : this.try.class_name -%>
299
+ </def>
300
+