transit 0.0.1 → 0.0.2

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.
Files changed (186) hide show
  1. data/Gemfile +19 -11
  2. data/app/assets/images/transit/icon24x24.png +0 -0
  3. data/app/assets/images/transit/icon24x24_files.png +0 -0
  4. data/app/assets/images/transit/jplayer.swf +0 -0
  5. data/app/assets/images/transit/uploadify.swf +0 -0
  6. data/app/assets/images/transit/video_player.swf +0 -0
  7. data/app/assets/javascripts/jqtools/expose.js +224 -0
  8. data/app/assets/javascripts/jqtools/flashembed.js +300 -0
  9. data/app/assets/javascripts/jqtools/overlay.js +294 -0
  10. data/app/assets/javascripts/jqtools/scrollable.js +3 -0
  11. data/app/assets/javascripts/jqtools/scrollable/autoscroll.js +81 -0
  12. data/app/assets/javascripts/jqtools/scrollable/base.js +335 -0
  13. data/app/assets/javascripts/jqtools/scrollable/navigator.js +139 -0
  14. data/app/assets/javascripts/jqtools/validator.js +590 -0
  15. data/app/assets/javascripts/jqueryui/accordion.js +611 -0
  16. data/app/assets/javascripts/jqueryui/autocomplete.js +612 -0
  17. data/app/assets/javascripts/jqueryui/button.js +388 -0
  18. data/app/assets/javascripts/jqueryui/core.js +312 -0
  19. data/app/assets/javascripts/jqueryui/datepicker.js +1791 -0
  20. data/app/assets/javascripts/jqueryui/dialog.js +878 -0
  21. data/app/assets/javascripts/jqueryui/draggable.js +815 -0
  22. data/app/assets/javascripts/jqueryui/droppable.js +285 -0
  23. data/app/assets/javascripts/jqueryui/effects/blind.js +49 -0
  24. data/app/assets/javascripts/jqueryui/effects/bounce.js +78 -0
  25. data/app/assets/javascripts/jqueryui/effects/clip.js +54 -0
  26. data/app/assets/javascripts/jqueryui/effects/core.js +746 -0
  27. data/app/assets/javascripts/jqueryui/effects/drop.js +50 -0
  28. data/app/assets/javascripts/jqueryui/effects/explode.js +79 -0
  29. data/app/assets/javascripts/jqueryui/effects/fade.js +32 -0
  30. data/app/assets/javascripts/jqueryui/effects/fold.js +56 -0
  31. data/app/assets/javascripts/jqueryui/effects/highlight.js +50 -0
  32. data/app/assets/javascripts/jqueryui/effects/pulsate.js +51 -0
  33. data/app/assets/javascripts/jqueryui/effects/scale.js +178 -0
  34. data/app/assets/javascripts/jqueryui/effects/shake.js +57 -0
  35. data/app/assets/javascripts/jqueryui/effects/slide.js +50 -0
  36. data/app/assets/javascripts/jqueryui/effects/transfer.js +45 -0
  37. data/app/assets/javascripts/jqueryui/mouse.js +160 -0
  38. data/app/assets/javascripts/jqueryui/position.js +252 -0
  39. data/app/assets/javascripts/jqueryui/progressbar.js +109 -0
  40. data/app/assets/javascripts/jqueryui/resizable.js +814 -0
  41. data/app/assets/javascripts/jqueryui/selectable.js +266 -0
  42. data/app/assets/javascripts/jqueryui/slider.js +666 -0
  43. data/app/assets/javascripts/jqueryui/sortable.js +1077 -0
  44. data/app/assets/javascripts/jqueryui/tabs.js +758 -0
  45. data/app/assets/javascripts/jqueryui/widget.js +262 -0
  46. data/app/assets/javascripts/libs/backbone.js +1152 -0
  47. data/app/assets/javascripts/libs/cookie.js +89 -0
  48. data/app/assets/javascripts/libs/fileinput.js +130 -0
  49. data/app/assets/javascripts/libs/jplayer.js +1768 -0
  50. data/app/assets/javascripts/libs/proper.js +541 -0
  51. data/app/assets/javascripts/libs/sanitize.js +282 -0
  52. data/app/assets/javascripts/libs/selecttolist.js +75 -0
  53. data/app/assets/javascripts/libs/underscore.js +807 -0
  54. data/app/assets/javascripts/libs/uploadify.js +677 -0
  55. data/app/assets/javascripts/libs/wymeditor.js +9538 -0
  56. data/app/assets/javascripts/transit.js +4 -0
  57. data/app/assets/javascripts/transit/admin.js +22 -0
  58. data/app/assets/javascripts/transit/admin/contexts.js +52 -0
  59. data/app/assets/javascripts/transit/admin/fields.js +36 -0
  60. data/app/assets/javascripts/transit/admin/upload.js +109 -0
  61. data/app/assets/javascripts/transit/config.js.erb +101 -0
  62. data/app/assets/javascripts/transit/contexts/audio.js +39 -0
  63. data/app/assets/javascripts/transit/contexts/video.js +79 -0
  64. data/app/assets/javascripts/transit/core.js +171 -0
  65. data/app/assets/javascripts/transit/frontend.js +3 -0
  66. data/app/assets/javascripts/transit/lib/base64.js +120 -0
  67. data/app/assets/javascripts/transit/lib/editor.js +177 -0
  68. data/app/assets/javascripts/transit/views/audio_player.jst +22 -0
  69. data/app/assets/javascripts/transit/views/editor_toolbar.jst +12 -0
  70. data/app/assets/javascripts/transit/views/file_upload.jst +5 -0
  71. data/app/assets/javascripts/transit/views/video_player.jst +20 -0
  72. data/app/assets/javascripts/transit/views/wym_box.jst +4 -0
  73. data/app/assets/javascripts/transit/views/wym_iframe.jst +3 -0
  74. data/app/assets/stylesheets/transit.css.scss.erb +42 -0
  75. data/app/assets/stylesheets/transit/forms.css.scss +66 -0
  76. data/app/assets/stylesheets/transit/media/audio.css.scss +65 -0
  77. data/app/assets/stylesheets/transit/media/video.css.scss +30 -0
  78. data/app/assets/stylesheets/transit/panel.css.scss +100 -0
  79. data/app/assets/stylesheets/transit/ui.css.scss +507 -0
  80. data/app/controllers/pages_controller.rb +3 -0
  81. data/app/controllers/posts_controller.rb +3 -0
  82. data/app/controllers/transit/assets_controller.rb +38 -0
  83. data/app/controllers/transit/contexts_controller.rb +12 -9
  84. data/app/controllers/transit/pages_controller.rb +26 -0
  85. data/app/controllers/transit/posts_controller.rb +31 -0
  86. data/app/controllers/transit/topics_controller.rb +5 -0
  87. data/app/controllers/transit_controller.rb +16 -0
  88. data/app/helpers/transit/admin_helper.rb +43 -0
  89. data/app/helpers/transit/form_helper.rb +17 -0
  90. data/app/helpers/transit/package_helper.rb +41 -0
  91. data/app/helpers/transit/pagination_helper.rb +58 -0
  92. data/app/helpers/transit_helper.rb +42 -0
  93. data/app/models/comment.rb +37 -0
  94. data/app/models/contexts/audio.rb +12 -0
  95. data/app/models/{text.rb → contexts/text.rb} +0 -0
  96. data/app/models/contexts/video.rb +24 -0
  97. data/app/models/topic.rb +19 -0
  98. data/app/models/transit/asset.rb +68 -0
  99. data/{lib → app/models}/transit/context.rb +29 -0
  100. data/app/views/contexts/_text.html.erb +1 -1
  101. data/app/views/posts/index.rss.builder +18 -0
  102. data/app/views/transit/assets/_file.html.erb +4 -0
  103. data/app/views/transit/assets/_image.html.erb +13 -0
  104. data/app/views/transit/assets/create.js.erb +8 -0
  105. data/app/views/transit/assets/destroy.js.erb +3 -0
  106. data/app/views/transit/assets/manage.html.erb +20 -0
  107. data/app/views/transit/contexts/_audio.html.erb +18 -0
  108. data/app/views/transit/contexts/_text.html.erb +6 -0
  109. data/app/views/transit/contexts/_video.html.erb +13 -0
  110. data/app/views/transit/contexts/destroy.js.erb +1 -0
  111. data/app/views/transit/contexts/index.html.erb +5 -0
  112. data/app/views/transit/contexts/new.js.erb +7 -0
  113. data/app/views/transit/contexts/show.html.erb +8 -0
  114. data/app/views/transit/index.html.erb +26 -0
  115. data/app/views/transit/index.js.erb +1 -0
  116. data/app/views/transit/interface/post_panel.html.erb +96 -0
  117. data/app/views/transit/pages/_table.html.erb +7 -0
  118. data/app/views/transit/pages/edit.html.erb +17 -0
  119. data/app/views/transit/pages/index.html.erb +17 -0
  120. data/app/views/transit/pages/update.js.erb +1 -0
  121. data/app/views/transit/posts/_form.html.erb +49 -0
  122. data/app/views/transit/posts/edit.html.erb +14 -0
  123. data/app/views/transit/posts/new.html.erb +21 -0
  124. data/app/views/transit/table.html.erb +13 -0
  125. data/app/views/transit/table.js.erb +8 -0
  126. data/app/views/transit/topics/manage.html.erb +28 -0
  127. data/config/locales/en.yml +22 -0
  128. data/config/routes.rb +3 -3
  129. data/lib/transit.rb +51 -17
  130. data/lib/transit/admin.rb +85 -0
  131. data/lib/transit/builders/form_builder.rb +319 -0
  132. data/lib/transit/builders/jst_builder.rb +38 -0
  133. data/lib/transit/builders/package_builder.rb +45 -0
  134. data/lib/transit/config.rb +20 -0
  135. data/lib/transit/controller/generator.rb +42 -0
  136. data/lib/transit/controller/responder.rb +34 -0
  137. data/lib/transit/core_ext.rb +18 -0
  138. data/lib/transit/errors/resource_not_found.rb +6 -0
  139. data/lib/transit/model/assets.rb +14 -0
  140. data/lib/transit/model/attachments.rb +55 -0
  141. data/lib/transit/model/auto_increment.rb +22 -0
  142. data/lib/transit/model/base.rb +56 -0
  143. data/lib/transit/model/comments.rb +19 -0
  144. data/lib/transit/model/hooks.rb +38 -0
  145. data/lib/transit/model/owners.rb +14 -0
  146. data/lib/transit/model/paginator.rb +92 -0
  147. data/lib/transit/model/topics.rb +14 -0
  148. data/lib/transit/package/page.rb +20 -12
  149. data/lib/transit/package/post.rb +87 -33
  150. data/lib/transit/package/post/validations.rb +14 -0
  151. data/lib/transit/rails/engine.rb +29 -13
  152. data/lib/transit/rails/railtie.rb +31 -0
  153. data/lib/transit/rails/routing.rb +11 -6
  154. data/lib/transit/services.rb +13 -0
  155. data/lib/transit/services/base.rb +14 -0
  156. data/lib/transit/services/facebook.rb +13 -0
  157. data/lib/transit/services/ted.rb +10 -0
  158. data/lib/transit/services/twitter.rb +13 -0
  159. data/lib/transit/services/vimeo.rb +10 -0
  160. data/lib/transit/services/you_tube.rb +12 -0
  161. data/lib/transit/version.rb +1 -1
  162. metadata +178 -31
  163. data/app/assets/stylesheets/includes/_compat.scss +0 -24
  164. data/app/assets/stylesheets/includes/_defaults.scss +0 -99
  165. data/app/assets/stylesheets/includes/_global.scss +0 -16
  166. data/app/assets/stylesheets/includes/_imports.scss +0 -27
  167. data/app/assets/stylesheets/includes/_mixins.scss +0 -38
  168. data/app/assets/stylesheets/includes/_setup.scss +0 -85
  169. data/app/assets/stylesheets/layout.css.scss +0 -29
  170. data/app/assets/stylesheets/transit.css +0 -3
  171. data/app/controllers/application_controller.rb +0 -5
  172. data/app/controllers/transit/index_controller.rb +0 -7
  173. data/app/controllers/transit/packages_controller.rb +0 -64
  174. data/app/controllers/transit/transit_controller.rb +0 -4
  175. data/app/helpers/routing_helpers.rb +0 -7
  176. data/app/models/audio.rb +0 -4
  177. data/app/models/package_asset.rb +0 -11
  178. data/app/models/video.rb +0 -8
  179. data/app/views/contexts/_audio.html.erb +0 -1
  180. data/app/views/contexts/_video.html.erb +0 -1
  181. data/app/views/layouts/transit.html.erb +0 -31
  182. data/app/views/transit/index/index.html.erb +0 -0
  183. data/lib/transit/helpers/controller_helpers.rb +0 -40
  184. data/lib/transit/helpers/model_helpers.rb +0 -26
  185. data/lib/transit/package.rb +0 -25
  186. data/lib/transit/package/base.rb +0 -49
@@ -0,0 +1,17 @@
1
+ <%= page_id('transit_index') %>
2
+ <%= page_title("Transit: Manage Pages") %>
3
+ <h1>Manage Pages</h1>
4
+ <%= paginate(@pages, :remote => true) %>
5
+ <table border="0" id="listing">
6
+ <thead>
7
+ <tr>
8
+ <th>Name</th>
9
+ <th>URL</th>
10
+ <th>Edit</th>
11
+ </tr>
12
+ </thead>
13
+ <tbody>
14
+ <%= render :partial => 'table' %>
15
+ </tbody>
16
+ </table>
17
+ <%= paginate(@pages, :remote => true) %>
@@ -0,0 +1 @@
1
+ success
@@ -0,0 +1,49 @@
1
+ <%= form_for resource, as: :post, validate: true do |f| %>
2
+ <%= transit_toolbar(resource) do %>
3
+ <ol>
4
+ <% unless resource.new_record %>
5
+ <li class="menu">
6
+ <span class='ui-icon ui-icon-circle-plus'></span>
7
+ Add a New Item
8
+ <ol class="ui-state-hover">
9
+ <% Transit.contexts.map(&:to_s).each do |type| %>
10
+ <li class="icon_<%= type.underscore %>">
11
+ <%= link_to type, new_polymorphic_path([resource, Transit::Context], type: type, form_name: 'post'), :remote => true %>
12
+ </li>
13
+ <% end %>
14
+ </ol>
15
+ </li>
16
+ <% end %>
17
+ <li class="multifield post_date menu" id="datepicker_target">
18
+ <span class='ui-icon ui-icon-calendar'></span>
19
+ Post Date
20
+ <span id="post_date_selects"><%= f.date_select :post_date, { order: [:month, :day, :year], use_short_month: true } %></span>
21
+ </li>
22
+ <% unless resource.new_record? %>
23
+ <li>
24
+ <%= f.check_box :published, class: 'toggle_button', data: { 'ui-icon' => 'ui-icon-star' } %>
25
+ <%= f.label :published %>
26
+ </li>
27
+ <li>
28
+ <%= toolbar_button('Delete This Post', polymorphic_path(resource), method: :delete, class: 'ui-icon-red', icon: 'closethick') %>
29
+ </li>
30
+ <% end %>
31
+ </ol>
32
+ <%= f.hidden_field(:user, value: current_user.id) if resource.delivers?(:owner) %>
33
+ <% end %>
34
+
35
+ <div id="post">
36
+ <%= render partial: 'transit/posts/fields/title', locals: { form: f } %>
37
+ <%= render template: 'transit/contexts/index', locals: { form: f } %>
38
+ </div>
39
+ <div id='edit_post_sidebar'>
40
+ <% if resource.delivers?(:topics) %>
41
+ <%= render(partial: 'transit/topics/model', locals: { form: f }) %>
42
+ <% end %>
43
+ <% if resource.delivers?(:assets) %>
44
+ <%= render(partial: 'transit/assets/model', locals: { form: f }) %>
45
+ <% end %>
46
+
47
+ <%= f.button %>
48
+ <% end %>
49
+ <%= text_field_tag :datepicker_holder, resource.post_date.strftime('%Y-%m-%d'), rel: 'post_post_date', data: { 'calendar-link' => "#post_date_menu" } %>
@@ -0,0 +1,14 @@
1
+ <%= page_id("#{resource_collection_name}_show") %>
2
+ <%= page_title("Edit Post #{resource.title}") %>
3
+ <h2 class='main_heading'>Edit Post</h2>
4
+ <%= form_for(resource, as: :post, html: { remote: true }) do |f| %>
5
+ <div id='edit_post_form'>
6
+ <div class="post">
7
+ <h1 class='transit-editable' rel="post_title"><%= resource.title %></h1>
8
+ <p class='timestamp'>Posted <%= resource.timestamp %></p>
9
+ <%= render(template: 'transit/contexts/index', locals: { form: f }) %>
10
+ </div>
11
+ </div>
12
+ <%= render(template: 'transit/interface/post_panel', locals: { form: f }) %>
13
+ <%= f.hidden_field :title %>
14
+ <% end %>
@@ -0,0 +1,21 @@
1
+ <%= page_id("#{resource_collection_name}_show") %>
2
+ <%= page_title("Create a New Post") %>
3
+ <h2 class='main_heading'>New Post</h2>
4
+ <%= form_for(resource, as: :post, html: { remote: true }) do |f| %>
5
+ <div id='edit_post_form'>
6
+ <div class="post">
7
+ <h1 class='transit-editable' rel="post_title">Add your title here</h1>
8
+ <%= f.label :teaser, 'Teaser (optional)', style: 'padding-top:1em; display:block;' %>
9
+ <%= f.text_area :teaser, id: 'new_post_teaser' %>
10
+ <p id='new_post_help_note'>
11
+ Once you click Create Post! below you will be able to edit things like files/images and the overall content
12
+ that will be displayed in your post.
13
+ </p>
14
+ </div>
15
+ </div>
16
+ <%= render(template: 'transit/interface/post_panel', locals: { form: f }) %>
17
+ <%= f.hidden_field :title %>
18
+ <p style="clear:both">
19
+ <%= f.button 'Create Post!', class: 'green' %>
20
+ </p>
21
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <% collection.each do |item| %>
2
+ <tr<%= cycle(""," class='even'") %>>
3
+ <% resource_class.admin_options.column_hash.each do |name, value| %>
4
+ <% if value.is_a?(Array) %>
5
+ <td><%= value.last.call(item) %></td>
6
+ <% else %>
7
+ <td><%= item.send(:"#{name.to_s}") %></td>
8
+ <% end %>
9
+ <% end %>
10
+ <td><%= link_to 'Edit', transit.edit_polymorphic_path(item) %></td>
11
+ <td><%= link_to 'Delete', transit.polymorphic_path(item), method: :delete, confirm: 'Are you sure?' %></td>
12
+ </tr>
13
+ <% end %>
@@ -0,0 +1,8 @@
1
+ $("#listing tbody").slideUp('fast',
2
+ function(){
3
+ $(this)
4
+ .empty()
5
+ .html("<%= escape_javascript(render(template: "transit/table.html.erb")) %>")
6
+ .slideDown('fast');
7
+ $('p.pagination').replaceWith("<%= escape_javascript(paginate(collection, :remote => true)) %>");
8
+ });
@@ -0,0 +1,28 @@
1
+ <div id="post_topics">
2
+ <h4>Topics</h4>
3
+ <ol>
4
+ <li class='select_list'>
5
+ <%= select_tag :topics, options_for_select(Topic.for_editing(resource_class, resource).collect{ |t| [t.title, t.id.to_s] }), id: 'all_topics_list' %>
6
+ <%= link_to 'Add', '#', class: 'add_to_list icon_add ui-button', id:'add_topic_link', data: { ui_icon: 'plus' } %>
7
+ </li>
8
+ <li id="topic_selections" rel="<%= "post[topics][]" %>">
9
+ <ul>
10
+ <% form.object.topics.each do |topic| %>
11
+ <li rel="<%= topic.id.to_s %>">
12
+ <%= topic.title %>
13
+ <%= hidden_field_tag "post[topics][]", topic.id.to_s %>
14
+ <%= link_to 'Remove', '#', class: 'icon_delete remove_from_list' %>
15
+ </li>
16
+ <% end %>
17
+ </ul>
18
+ </li>
19
+ </ol>
20
+ <script type="text/javascript" charset="utf-8">
21
+ //<![CDATA[
22
+ jQuery('#all_topics_list').selectToList({
23
+ target:'#topic_selections ul',
24
+ name: jQuery('#topic_selections').attr('rel'),
25
+ add: jQuery('#add_topic_link') });
26
+ //]]>
27
+ </script>
28
+ </div>
@@ -0,0 +1,22 @@
1
+ en:
2
+ flash:
3
+ actions:
4
+ create:
5
+ success: '%{resource_name} was successfully created.'
6
+ error: 'Looks like there was a problem with your submission!'
7
+ update:
8
+ success: '%{resource_name} was successfully updated.'
9
+ error: 'Looks like there was a problem with your submission!'
10
+ destroy:
11
+ success: '%{resource_name} was successfully destroyed.'
12
+ error: '%{resource_name} could not be destroyed.'
13
+ transit:
14
+ post_collection: 'Posts'
15
+ blog_collection: 'Blog Posts'
16
+ article_collection: 'Articles'
17
+ post: 'Post'
18
+ blog: 'Blog Post'
19
+ article: 'Article'
20
+
21
+ legends:
22
+ topics: "Topics"
data/config/routes.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  Transit::Engine.routes.draw do
2
- match "packages/:scope_name/:scope_id/contexts/new" => 'contexts#new', :as => :new_package_context, :via => [:get]
3
- match "packages/:scope_name/:scope_id/contexts/:id" => 'contexts#destroy', :as => :package_context, :via => [:delete]
4
- root :to => 'index#index'
2
+ resources :posts
3
+ resources :pages
4
+ resources :package_assets, :controller => 'assets'
5
5
  end
data/lib/transit.rb CHANGED
@@ -1,30 +1,60 @@
1
- require 'rails'
2
1
  require 'active_support'
3
- require 'mongoid'
2
+ require 'transit/core_ext'
4
3
 
5
4
  module Transit
6
5
 
7
- autoload :Package, 'transit/package'
8
- autoload :Context, 'transit/context'
6
+ autoload :Admin, 'transit/admin'
7
+ autoload :Config, 'transit/config'
9
8
 
10
- module Helpers
11
- autoload :ControllerHelpers, 'transit/helpers/controller_helpers'
12
- autoload :ModelHelpers, 'transit/helpers/model_helpers'
9
+ module Package
10
+ autoload :Post, 'transit/package/post'
11
+ autoload :Page, 'transit/package/page'
12
+ end
13
+
14
+ module Model
15
+ autoload :Assets, 'transit/model/assets'
16
+ autoload :Attachments, 'transit/model/attachments'
17
+ autoload :AutoIncrement, 'transit/model/auto_increment'
18
+ autoload :Comments, 'transit/model/comments'
19
+ autoload :Topics, 'transit/model/topics'
20
+ autoload :Base, 'transit/model/base'
21
+ autoload :Hooks, 'transit/model/hooks'
22
+ autoload :Owners, 'transit/model/owners'
23
+ autoload :Paginator, 'transit/model/paginator'
24
+ end
25
+
26
+ module Controller
27
+ autoload :Generator, 'transit/controller/generator'
28
+ autoload :Responder, 'transit/controller/responder'
13
29
  end
14
30
 
15
31
  module Errors
16
- autoload :InvalidContext, 'transit/errors/invalid_context'
32
+ autoload :InvalidContext, 'transit/errors/invalid_context'
33
+ autoload :ResourceNotFound, 'transit/errors/resource_not_found'
17
34
  end
18
35
 
36
+ module Builders
37
+ autoload :JstBuilder, 'transit/builders/jst_builder'
38
+ autoload :FormBuilder, 'transit/builders/form_builder'
39
+ autoload :PackageBuilder, 'transit/builders/package_builder'
40
+ end
41
+
19
42
  DESCRIPTIONS = {}
20
- CONTROLLERS = []
21
43
 
22
- def self.add_controller(klass)
23
- CONTROLLERS << klass.to_s.classify.pluralize
44
+ # Store an array of controller mappings
45
+ mattr_accessor :mappings
46
+ @@mappings = []
47
+
48
+ def self.add_mapping(obj)
49
+ @@mappings << obj
24
50
  end
25
-
51
+
52
+ def self.configure(&block)
53
+ yield Transit::Config
54
+ end
55
+
26
56
  def self.contexts
27
- Transit::Context.subclasses.map(&:to_s).uniq
57
+ ["Text","Video", "Audio"]
28
58
  end
29
59
 
30
60
  def self.track(klass, template)
@@ -36,8 +66,12 @@ module Transit
36
66
  DESCRIPTIONS[template] ||= []
37
67
  end
38
68
 
69
+ def self.superclass_for(template)
70
+ DESCRIPTIONS[template].detect do |klass|
71
+ klass.constantize.superclass === Object
72
+ end
73
+ end
74
+
39
75
  end
40
-
41
- require 'transit/package'
42
- require 'transit/rails/engine'
43
- require 'transit/rails/routing'
76
+ require 'transit/model/hooks'
77
+ require 'transit/rails/engine'
@@ -0,0 +1,85 @@
1
+ module Transit
2
+ ##
3
+ # Manages configuration options for models.
4
+ # When a model is managed via the admin, register it by Transit::Admin.register and
5
+ # passing a configuration block.
6
+ #
7
+ # @example Assign the columns to be displayed on "index"
8
+ # Transit::Admin.register :model_name do |config|
9
+ # config.columns = [:name, :title]
10
+ # end
11
+ #
12
+ module Admin
13
+
14
+ def self.register(klass, &block)
15
+ klass = klass.to_s.classify.constantize
16
+ unless klass.respond_to?(:admin_options)
17
+ klass.send(:class_attribute, :admin_options, instance_writer: false)
18
+ end
19
+ dsl = klass.admin_options ||= DSL.new
20
+ block.call(dsl)
21
+ klass.admin_options = dsl
22
+ end
23
+
24
+ class DSL
25
+
26
+ attr_accessor :columns, :column_hash, :fields
27
+
28
+ ##
29
+ # Specify the columns to be displayed in the admin. At minimum the name of a
30
+ # method is required. For additional options, use hash syntax.
31
+ #
32
+ # @example List a title and post_date column
33
+ # config.columns :title, :post_date
34
+ #
35
+ # @example List a title field, and a post_date field, where post_date uses a proc to describe its value. When called
36
+ # the proc will be passed a reference to the current object.
37
+ #
38
+ # config.columns :title, post_date: lambda{ |obj| obj.created_at.to_formatted_s }
39
+ #
40
+ # @example Overriding the displayed name
41
+ #
42
+ # config.columns :title, timestamp: { as: 'Post Date' }
43
+ #
44
+ def columns(*cols)
45
+ options = cols.extract_options!
46
+ @column_hash = ActiveSupport::OrderedHash.new
47
+ cols.each.each do |col|
48
+ @column_hash[col] = col.to_s.titleize
49
+ end
50
+ options.each do |name, value|
51
+ @column_hash[name] = case value
52
+ when Proc then [name.to_s.titleize, value]
53
+ when Hash
54
+ colname = (value[:as].present? ? value[:as].to_s.titleize : name.to_s.titleize)
55
+ (value[:proc].present? ? [colname, value[:proc]] : colname)
56
+ end
57
+ end
58
+ end
59
+
60
+ ##
61
+ # Specify the form fields used for each attribute. When inheriting posts or pages
62
+ # defaults are already pre-defined and normally only need to be overridden.
63
+ #
64
+ # @example Specify a text_area for the title and teaser of a post
65
+ # config.fields title: :text_area, teaser: :text_area
66
+ #
67
+ def fields(field_hash = {})
68
+ @fields.merge!(field_hash) unless field_hash.empty?
69
+ @fields
70
+ end
71
+
72
+ def initialize
73
+ @fields = ActiveSupport::HashWithIndifferentAccess.new({
74
+ title: :text_field,
75
+ teaser: :text_area,
76
+ post_date: { date_select: { order: [:month, :day, :year], use_short_month: true } },
77
+ image: :file_field,
78
+ photo: :file_field
79
+ })
80
+ end
81
+
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,319 @@
1
+ module Transit
2
+ module Builders
3
+
4
+ ##
5
+ # Base class for creating form builders
6
+ #
7
+ class FormBuilder < ActionView::Helpers::FormBuilder #:nodoc:
8
+
9
+ # Access the template object
10
+ attr_accessor :template
11
+ # Tracks the order in which fields are used in the form, this allows the easy rebuilding of the submitted form
12
+ # data when submitted since normally the hash isn't ordered.
13
+ attr_accessor :field_order
14
+ # Tracks the field currently being "processed"
15
+ attr_accessor :current_field_type
16
+
17
+ # create overrides for custom rendering
18
+ [:email_field, :password_field, :text_field, :text_area, :url_field].each do |method|
19
+ class_eval <<-FUNC, __FILE__, __LINE__ + 1
20
+
21
+ alias :_super_#{method} :#{method}
22
+
23
+ def #{method}(method_name, *args)
24
+ render_field_as_custom(:#{method}, method_name, *args)
25
+ end
26
+ FUNC
27
+ end
28
+
29
+ ##
30
+ # Modified label tag to support adding a 'required' asterisk to the end of the label.
31
+ # Same params as the original implementation
32
+ #
33
+ def label(method, text = nil, options = {}, &block) #:nodoc:
34
+
35
+ options, text = text, nil if text.is_a?(Hash)
36
+ text ||= method.to_s.humanize
37
+
38
+ options.stringify_keys!
39
+ klasses = (options.delete(['class']) || "").split(" ")
40
+ klasses << 'field_with_errors' if errors_on_attribute?(method)
41
+ options['class'] = klasses.join(" ") unless klasses.compact.empty?
42
+
43
+ text = "#{text} <abbr title='Required'>*</abbr>".html_safe if attribute_required?(method) || required_by_option?(options.delete('required'))
44
+ super(method, text, options, &block)
45
+
46
+ end
47
+
48
+ ##
49
+ #
50
+ # Creates a button tag to be used in a form instead of the default input[type=submit]
51
+ # to help make CSS styling easier
52
+ #
53
+ # @param [String] value The text for the button
54
+ # @param [Hash] options HTML options to be passed to the button
55
+ # @option [String] icon If included, adds an image to the button to be used as an icon
56
+ #
57
+ def button(value = nil, options = {})
58
+
59
+ value, options = nil, value if value.is_a?(Hash)
60
+ value ||= submit_default_value
61
+
62
+ value = [image_tag(icon, :class => 'icon'), value].join(' ') if icon = options.delete(:icon)
63
+ klasses = (options.delete(:class) || "").split(" ")
64
+ klasses << "button"
65
+ options['class'] = klasses.join(" ")
66
+ content_tag(:button, value.to_s.html_safe, options.reverse_merge!({ "type" => "submit", "name" => "commit" }))
67
+
68
+ end
69
+
70
+ # Overrides fields_for to make sure the form builder is set properly
71
+ #
72
+ def fields_for(record_or_name_or_array, *args, &block) #:nodoc:
73
+ opts = args.extract_options!
74
+ opts.merge!(:builder => Transit::Builders::FormBuilder)
75
+ args.push(opts)
76
+ super(record_or_name_or_array, *args, &block)
77
+ end
78
+
79
+
80
+ ##
81
+ #
82
+ # Generate a select tag with the 50 US states as options
83
+ #
84
+ # @param [Symbol] method The object method/attribute to be used
85
+ # @param [Hash] options Same as Rails' select options hash
86
+ # @option options [Symbol] :international Include an international option
87
+ # @option options [Symbol] :abbreviate Use an abbreviated version of the state name for the value
88
+ # @param [Hash] html_options Same as Rails' html_options hash
89
+ #
90
+ # @return [String] HTML select tag
91
+ #
92
+ def state_select(method, options = {}, html_options = {})
93
+ abbr = options.delete(:abbreviate)
94
+ abbr = !(abbr.nil? || abbr === false)
95
+ select(method, @template.options_for_select(options_for_state_select(abbr, options.delete(:international)), @object.try(:state)), options, html_options)
96
+ end
97
+
98
+ protected
99
+
100
+ ##
101
+ #
102
+ # Returns a list of US states as an array
103
+ #
104
+ # @param [Boolean] abbreviate Abbreviate the value
105
+ # @param [Boolean, String] incl_international Include an additional state for "International"
106
+ #
107
+ # @return [Array] An array of states
108
+ #
109
+ def options_for_state_select(abbreviate = false, incl_international = false)
110
+ incl_international ||= false
111
+ state_list = [
112
+ ['Alabama', "AL"],['Alaska', "AK"],['Arizona', "AZ"],['Arkansas', "AR"],['California', "CA"],['Colorado', "CO"],
113
+ ['Connecticut', "CT"],['District of Columbia', "DC"],['Delaware', "DE"],['Florida', "FL"],['Georgia', "GA"],
114
+ ['Hawaii', "HI"],['Idaho', "ID"],['Illinois', "IL"],['Indiana', "IN"],['Iowa', "IA"],['Kansas', "KS"],['Kentucky', "KY"],
115
+ ['Louisiana', "LA"],['Maine', "ME"],['Maryland', "MD"],['Massachusetts', "MA"],['Michigan', "MI"],['Minnesota', "MN"],
116
+ ['Mississippi', "MS"],['Missouri', "MO"],['Montana', "MT"],['Nebraska', "NE"],['Nevada', "NV"],['New Hampshire', "NH"],
117
+ ['New Jersey', "NJ"],['New Mexico', "NM"],['New York', "NY"],['North Carolina', "NC"],['North Dakota', "ND"],
118
+ ['Ohio', "OH"],['Oklahoma', "OK"],['Oregon', "OR"],['Pennsylvania', "PA"],['Rhode Island', "RI"],['South Carolina', "SC"],
119
+ ['South Dakota', "SD"],['Tennessee', "TN"],['Texas', "TX"],['Utah', "UT"],['Vermont', "VT"],['Virginia', "VA"],['Washington', "WA"],
120
+ ['West Virginia', "WV"],['Wisconsin', "WI"],['Wyoming', "WY"]
121
+
122
+ ].map do |state|
123
+ (abbreviate ? state : [state.first, state.first])
124
+ end
125
+ state_list << ['International', incl_international] unless incl_international === false
126
+ state_list
127
+ end
128
+
129
+ ##
130
+ #
131
+ # Checks to see if a particular attribute is required, if so, a "required" attribute is added to the field.
132
+ #
133
+ # @param [Symbol] attribute The attribute to check against validators
134
+ # @return [Boolean]
135
+ #
136
+ def attribute_required?(attribute)
137
+ validates_presence?(attribute) || validates_inclusion?(attribute)
138
+ end
139
+
140
+ # Convenience method to use the +content_tag+ method from our template
141
+ #
142
+ def content_tag(tag, content, options = {}, escape = true, &block) #:nodoc:
143
+ @template.content_tag(tag, content, options, escape, &block)
144
+ end
145
+
146
+ ##
147
+ # Checks to see if there are errors for the particular method or attribute
148
+ #
149
+ # @param [Symbol] method The method/attribute to check
150
+ #
151
+ # @return [Boolean]
152
+ #
153
+ def errors_on_attribute?(method)
154
+ return false if @object.nil?
155
+ !(@object.errors.empty? || !@object.errors[method.to_sym].present? || [@object.errors[method.to_sym]].flatten.empty?)
156
+ end
157
+
158
+ ##
159
+ #
160
+ # Checks a passed validator to see if it is required
161
+ # 'borrowed' from Formtastic by Justin French (see https://github.com/justinfrench/formtastic)
162
+ #
163
+ # @param [Hash] options Validator options
164
+ #
165
+ def options_require_validation?(options)
166
+
167
+ allow_blank = options[:allow_blank]
168
+ return !allow_blank unless allow_blank.nil?
169
+ if_condition = !options[:if].nil?
170
+ condition = if_condition ? options[:if] : options[:unless]
171
+
172
+ condition = if condition.respond_to?(:call)
173
+ condition.call(@object)
174
+ elsif condition.is_a?(::Symbol) && @object.respond_to?(condition)
175
+ @object.send(condition)
176
+ else
177
+ condition
178
+ end
179
+ if_condition ? !!condition : !condition
180
+ end
181
+
182
+ ##
183
+ #
184
+ # Checks an options hash to determine if a required method/attribute was overridden manually
185
+ # @param [Hash] options The options hash to check
186
+ #
187
+ #
188
+ def required_by_option?(options)
189
+ req = (options.is_a?(Hash) ? options.stringify_keys[:required] : options)
190
+ !(req.to_s === 'false' || req.nil?)
191
+ end
192
+
193
+ ##
194
+ #
195
+ # Wrapper method used by all form fields to customize the output
196
+ #
197
+ # @param [Symbol] helper_method Original Rails helper method name
198
+ # @param [Symbol] method Object method / symbol used for the element
199
+ # @param [Array] args Array of original arguments passed to the helper
200
+ #
201
+ # @return [String] Rendered HTML tag for the element
202
+ #
203
+ def render_field_as_custom(helper_method, method, *args)
204
+
205
+ @current_field_type = helper_method
206
+
207
+ options = args.extract_options!
208
+ (@field_order ||= []) << method
209
+
210
+ # Add an error class to the field if it has errors
211
+ #
212
+ if errors_on_attribute?(method)
213
+ klasses = (options.delete(:class) || "").split(" ")
214
+ klasses << "field_with_errors"
215
+ options[:class] = klasses.join(" ")
216
+ end
217
+
218
+ # Add a required attribute to the field if it is required
219
+ # Skip if false was passed as the required option
220
+ #
221
+ options[:required] = "required" if attribute_required?(method) && options.delete(:required).to_s != 'false'
222
+ options['data-validates-uniqueness'] = "true" if validates_uniqueness?(method)
223
+
224
+ result = send(:"_super_#{helper_method}", method, *(args << options))
225
+ messages = @object.nil? ? [] : @object.errors[method]
226
+ render_field_with_errors(method, result, messages)
227
+
228
+ end
229
+
230
+ ##
231
+ #
232
+ # Renders the passed +html_tag+ with the custom error_template
233
+ #
234
+ # @param [Symbol] method The method/attribute to check for errors
235
+ # @param [Object, String] html_tag Instance of an input tag or a string
236
+ # @param [Array] messages An array of all error messages to be added to the template
237
+ #
238
+ def render_field_with_errors(method, html_tag, messages)
239
+
240
+ return html_tag unless errors_on_attribute?(method)
241
+
242
+ error_class = 'field_with_errors'
243
+ message_error_class = 'errors_for_field'
244
+ render_binding = binding
245
+ error_template = %{
246
+ <span class="<%= error_class %>">
247
+ <%= html_tag %>
248
+ <span class="<%= message_error_class %>"><%= [messages].flatten.join(",") %></span>
249
+ </span>}
250
+
251
+ renderer = ERB.new(error_template)
252
+ renderer.result(render_binding).to_s.html_safe
253
+ end
254
+
255
+ ##
256
+ #
257
+ # Compiles an array of all validators for a particular attribute
258
+ # @param [Symbol] attribtue The attribute to check
259
+ #
260
+ def validators_for(attribute)
261
+
262
+ return [] if @object.nil?
263
+ return [] unless @object.class.respond_to?(:validators_on)
264
+
265
+ attribute = attribute.to_s.sub(/_id$/, '').to_sym
266
+ @object.class.validators_on(attribute).uniq
267
+
268
+ end
269
+
270
+ ##
271
+ #
272
+ # Convenience method to see if a particular attribute has validators
273
+ # @param [Symbol] attribute The attribute to check
274
+ #
275
+ def validators_for?(attribute)
276
+ !validators_for(attribute).empty?
277
+ end
278
+
279
+ ##
280
+ #
281
+ # Checks for a presence validation on a particular attribute
282
+ # @param [Symbol] attribute The attribute to check
283
+ #
284
+ def validates_presence?(attribute)
285
+ validator_of_type_exists?(validators_for(attribute), :presence)
286
+ end
287
+
288
+ ##
289
+ #
290
+ # Checks for a uniqueness validation on a particular attribute
291
+ # @param [Symbol] attribute The attribute to check
292
+ #
293
+ def validates_uniqueness?(attribute)
294
+ validator_of_type_exists?(validators_for(attribute), :uniqueness, false)
295
+ end
296
+
297
+ ##
298
+ #
299
+ # Checks for inclusion validation on a particular attribute
300
+ # @param [Symbol] attribute The attribute to check
301
+ #
302
+ def validates_inclusion?(attribute)
303
+ validator_of_type_exists?(validators_for(attribute), :inclusion)
304
+ end
305
+
306
+ private
307
+
308
+ def validator_of_type_exists?(validators, kind, check_options = true) #:nodoc: @private
309
+ validators.detect do |validator|
310
+ exists = (validator.kind.to_s == kind.to_s)
311
+ next exists unless (check_options && exists) && validator.options.present?
312
+ options_require_validation?(validator.options)
313
+ end
314
+ end
315
+
316
+ end
317
+
318
+ end
319
+ end