puffer 0.0.29 → 0.0.30

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/Gemfile +1 -0
  2. data/Gemfile.lock +56 -52
  3. data/README.md +51 -43
  4. data/VERSION +1 -1
  5. data/app/assets/javascripts/puffer/application.js +3 -1
  6. data/app/assets/stylesheets/puffer/application.css +2 -0
  7. data/app/assets/stylesheets/puffer/puffer.css +5 -9
  8. data/app/components/base_component.rb +0 -1
  9. data/app/components/boolean/index.html.erb +1 -1
  10. data/app/components/boolean_component.rb +1 -1
  11. data/app/components/references_many/index.html.erb +1 -0
  12. data/app/components/references_many_component.rb +8 -0
  13. data/app/components/references_one_component.rb +6 -1
  14. data/app/components/select/filter.html.erb +2 -0
  15. data/app/components/select/form.html.erb +1 -1
  16. data/app/components/select_component.rb +13 -2
  17. data/app/controllers/admin/puffer_users_controller.rb +5 -1
  18. data/app/controllers/admin/sessions_controller.rb +4 -1
  19. data/app/controllers/puffer/base.rb +1 -1
  20. data/app/controllers/puffer/sessions_base.rb +3 -0
  21. data/app/controllers/puffer/tree_base.rb +4 -4
  22. data/app/helpers/puffer_helper.rb +16 -15
  23. data/app/models/puffer/puffer_user.rb +11 -10
  24. data/app/models/puffer_user.rb +14 -2
  25. data/app/views/layouts/puffer.html.erb +16 -17
  26. data/app/views/layouts/puffer_base.html.erb +1 -2
  27. data/app/views/layouts/puffer_dashboard.html.erb +2 -2
  28. data/app/views/puffer/base/edit.html.erb +12 -3
  29. data/app/views/puffer/base/new.html.erb +12 -3
  30. data/app/views/puffer/dashboard_base/index.html.erb +5 -5
  31. data/lib/generators/puffer/component/component_generator.rb +0 -1
  32. data/lib/generators/puffer/controller/controller_generator.rb +1 -1
  33. data/lib/generators/puffer/controller/templates/controller.rb +8 -0
  34. data/lib/puffer.rb +77 -13
  35. data/lib/puffer/component.rb +14 -9
  36. data/lib/puffer/controller/auth.rb +41 -2
  37. data/lib/puffer/controller/config.rb +1 -1
  38. data/lib/puffer/controller/mutate.rb +15 -6
  39. data/lib/puffer/extensions/core.rb +4 -0
  40. data/lib/puffer/extensions/directive_processor.rb +36 -0
  41. data/lib/puffer/extensions/form.rb +1 -1
  42. data/lib/puffer/extensions/mapper.rb +74 -149
  43. data/lib/puffer/field.rb +2 -4
  44. data/lib/puffer/field_set.rb +3 -15
  45. data/lib/puffer/filters.rb +105 -0
  46. data/lib/puffer/orm_adapter/active_record.rb +33 -0
  47. data/lib/puffer/orm_adapter/base.rb +4 -0
  48. data/lib/puffer/orm_adapter/mongoid.rb +29 -0
  49. data/lib/puffer/resource.rb +63 -62
  50. data/lib/puffer/resource/node.rb +71 -0
  51. data/lib/puffer/resource/routing.rb +4 -4
  52. data/lib/puffer/resource/tree.rb +36 -0
  53. data/puffer.gemspec +28 -8
  54. data/spec/dummy/app/controllers/admin/posts_controller.rb +1 -1
  55. data/spec/dummy/app/controllers/admin/users_controller.rb +1 -0
  56. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  57. data/spec/dummy/app/controllers/orms/active_record_orm_primals_controller.rb +36 -0
  58. data/spec/dummy/app/controllers/orms/mongoid_orm_primals_controller.rb +42 -0
  59. data/spec/dummy/app/models/active_record_orm.rb +5 -0
  60. data/spec/dummy/app/models/active_record_orm/primal.rb +2 -0
  61. data/spec/dummy/app/models/{mongoid_test.rb → mongoid_orm.rb} +1 -1
  62. data/spec/dummy/app/models/mongoid_orm/primal.rb +46 -0
  63. data/spec/dummy/config/environments/development.rb +3 -0
  64. data/spec/dummy/config/routes.rb +2 -1
  65. data/spec/dummy/db/migrate/20110930183902_create_active_record_orm_primals.rb +17 -0
  66. data/spec/dummy/db/schema.rb +15 -1
  67. data/spec/fabricators/active_record_orm/primal.rb +13 -0
  68. data/spec/fabricators/mongoid_orm/primal.rb +12 -0
  69. data/spec/generators/puffer/component/component_generator_spec.rb +32 -0
  70. data/spec/generators/puffer/controller/controller_generator_spec.rb +31 -0
  71. data/spec/helpers/puffer_helper_spec.rb +62 -0
  72. data/spec/lib/filters_spec.rb +21 -0
  73. data/spec/lib/orm_adapter/active_record_spec.rb +33 -0
  74. data/spec/lib/orm_adapter/base_shared.rb +97 -0
  75. data/spec/lib/orm_adapter/mongoid_spec.rb +46 -0
  76. data/spec/lib/resource/routing_spec.rb +5 -5
  77. data/spec/lib/resource/tree_spec.rb +31 -0
  78. data/spec/lib/resource_spec.rb +36 -37
  79. data/spec/spec_helper.rb +3 -2
  80. metadata +71 -43
  81. data/app/views/puffer/base/_form.html.erb +0 -11
  82. data/lib/generators/puffer/component/templates/component_spec.rb +0 -19
  83. data/lib/puffer/resource/scoping.rb +0 -19
  84. data/spec/dummy/app/controllers/orms/mongoid_tests_controller.rb +0 -19
  85. data/spec/lib/params_spec.rb +0 -120
@@ -5,8 +5,13 @@ class ReferencesOneComponent < Puffer::Component::Base
5
5
  end
6
6
 
7
7
  def choose
8
- @records = field.reflection.klass.includes(field.children.includes).where(field.children.searches(params[:search])).page(params[:page])
8
+ #@records = field.reflection.klass.includes(field.children.includes).where(field.children.searches(params[:search])).page(params[:page])
9
+ @records = field.reflection.klass.to_adapter.filter(field.reflection.klass, field.children, :search => params[:puffer_search]).page(params[:page])
9
10
  render
10
11
  end
11
12
 
13
+ def filter
14
+
15
+ end
16
+
12
17
  end
@@ -0,0 +1,2 @@
1
+ <dt><%= opts[:form].label field.field_name %></dt>
2
+ <dd><%= opts[:form].select field.field_name, @options, :include_blank => true %></dd>
@@ -1,5 +1,5 @@
1
1
  <% content_for :input do %>
2
- <%= opts[:form].select field, @options, {:include_blank => field.options[:include_blank]}, field.input_options %>
2
+ <%= opts[:form].select field, @options, {:include_blank => field.options.key?(:include_blank) ? field.options[:include_blank] : true }, field.input_options %>
3
3
  <% end %>
4
4
 
5
5
  <%= render :file => 'base/form' %>
@@ -1,7 +1,19 @@
1
1
  class SelectComponent < BaseComponent
2
2
 
3
3
  def form
4
- @options = case field.options[:select]
4
+ @options = select_options
5
+ super
6
+ end
7
+
8
+ def filter
9
+ @options = select_options
10
+ render
11
+ end
12
+
13
+ private
14
+
15
+ def select_options
16
+ case field.options[:select]
5
17
  when Symbol then
6
18
  parent_controller.view_context.send field.options[:select]
7
19
  when Proc then
@@ -9,7 +21,6 @@ class SelectComponent < BaseComponent
9
21
  else
10
22
  field.options[:select]
11
23
  end
12
- super
13
24
  end
14
25
 
15
26
  end
@@ -1,3 +1,7 @@
1
- class Admin::PufferUsersController < Puffer::PufferUsersBase
1
+ require 'puffer_user'
2
2
 
3
+ if defined?(PufferUser)
4
+ class Admin::PufferUsersController < Puffer::PufferUsersBase
5
+
6
+ end
3
7
  end
@@ -1,3 +1,6 @@
1
+ # Implemented basic rails auth with custom PufferUser model.
2
+ # Admin::SessionsController could be redefined in application.
3
+ # See Puffer::SessionsBase docs for additional info.
1
4
  class Admin::SessionsController < Puffer::SessionsBase
2
5
 
3
6
  def new
@@ -5,7 +8,7 @@ class Admin::SessionsController < Puffer::SessionsBase
5
8
  end
6
9
 
7
10
  def create
8
- @record = PufferUser.find_by_email(params[:puffer_user][:email])
11
+ @record = PufferUser.to_adapter.find_first(:conditions => {:email => params[:puffer_user][:email]})
9
12
  if @record && @record.authenticate(params[:puffer_user][:password])
10
13
  session[:puffer_user_id] = @record.id
11
14
  redirect_to admin_root_url
@@ -12,7 +12,7 @@ module Puffer
12
12
  respond_to :html, :js
13
13
 
14
14
  def index
15
- @records = resource.collection
15
+ @records = resource.collection.page(params[:page])
16
16
  end
17
17
 
18
18
  def show
@@ -1,3 +1,6 @@
1
+ # Puffer::SessionsBase is a base class for any Admin::SessionsController
2
+ # implementation by default implemented basic auth through PufferUser.
3
+ # See Puffer::SessionsDeviseBase for alternative implementation example.
1
4
  class Puffer::SessionsBase < ApplicationController
2
5
  unloadable
3
6
  pufferize!
@@ -7,8 +7,8 @@ module Puffer
7
7
  define_fieldset :tree, :fallbacks => :index
8
8
 
9
9
  def index
10
- return super if params[:search]
11
- @records = resource.collection_scope.includes(resource.includes)
10
+ return super if puffer_filters.any?
11
+ @records = resource.collection_scope
12
12
  if session[:expanded].present?
13
13
  @records = @records.where(["depth in (0, 1) or parent_id in (#{session[:expanded].join(', ')})"]).arrange
14
14
  else
@@ -34,7 +34,7 @@ module Puffer
34
34
  session[:expanded] ||= []
35
35
  session[:expanded].push params[:id] if @parent
36
36
  session[:expanded].uniq!
37
- @records = @parent.self_and_descendants.where(:parent_id => [@parent.parent_id] + session[:expanded]).includes(resource.includes).arrange
37
+ @records = resource.model.to_adapter.filter(@parent.self_and_descendants.where(:parent_id => [@parent.parent_id] + session[:expanded]), tree_fields).arrange
38
38
  render 'toggle'
39
39
  end
40
40
 
@@ -42,7 +42,7 @@ module Puffer
42
42
  @parent = resource.member
43
43
  session[:expanded] ||= []
44
44
  session[:expanded].delete params[:id]
45
- @records = resource.collection_scope.where(:id => [@parent.id]).includes(resource.includes).arrange
45
+ @records = resource.model.to_adapter.filter(resource.collection_scope.where(:id => [@parent.id]), tree_fields).arrange
46
46
  render 'toggle'
47
47
  end
48
48
 
@@ -1,25 +1,26 @@
1
1
  module PufferHelper
2
2
 
3
- def puffer_namespaces
4
- Rails.application.routes.puffer.keys.each do |prefix|
5
- yield prefix.to_s.humanize, send("#{prefix}_root_path"), prefix == namespace
3
+ def puffer_scopes_navigation
4
+ Rails.application.routes.resources_tree.map(&:scope).uniq.each do |scope|
5
+ yield scope, send("#{scope}_root_path"), scope == puffer_namespace
6
6
  end
7
7
  end
8
8
 
9
- def puffer_navigation
10
- Rails.application.routes.puffer[namespace].values.map(&:first).each do |controller|
11
- title = controller.configuration.group.to_s.humanize
12
- path = polymorphic_url [namespace, controller.model]
13
- current = configuration.group && resource.root.controller.configuration.group == controller.configuration.group
14
- yield title, path, current
9
+ def puffer_groups_navigation namespace = puffer_namespace
10
+ Rails.application.routes.resources_tree.roots.select {|node| node.controller.model && node.scope == namespace}.uniq_by(&:group).each do |resource_node|
11
+ path = send("#{resource_node.url_string}_path")
12
+ current = resource.resource_node ? resource.root.resource_node.group == resource_node.group : false
13
+
14
+ yield resource_node.group, path, current
15
15
  end
16
16
  end
17
17
 
18
- def sidebar_puffer_navigation
19
- (Rails.application.routes.puffer[namespace][configuration.group] || []).each do |controller|
20
- title = controller.model.model_name.human
21
- path = polymorphic_url [namespace, controller.model]
22
- current = controller.controller_name == resource.root.controller_name
18
+ def puffer_resources_navigation namespace = puffer_namespace, group = configuration.group
19
+ Rails.application.routes.resources_tree.roots.select {|node| node.controller.model && node.scope == namespace && node.group == group}.each do |resource_node|
20
+ title = resource_node.controller.model.model_name.human
21
+ path = send("#{resource_node.url_string}_path")
22
+ current = resource.resource_node ? resource.root.resource_node == resource_node : false
23
+
23
24
  yield title, path, current
24
25
  end
25
26
  end
@@ -41,7 +42,7 @@ module PufferHelper
41
42
  res = record.call_chain(field.to_s)
42
43
  end
43
44
  unless field.native?
44
- url = edit_polymorphic_path [resource.namespace, record.call_chain(field.path)] rescue nil
45
+ url = edit_polymorphic_path [resource.scope, record.call_chain(field.path)] rescue nil
45
46
  res = link_to res, url if url
46
47
  end
47
48
  res
@@ -1,24 +1,25 @@
1
- class Puffer::PufferUser < ActiveRecord::Base
2
- self.abstract_class = true
1
+ module Puffer::PufferUser
2
+ extend ActiveSupport::Concern
3
+ include ActiveModel::SecurePassword
3
4
 
4
- attr_protected :password_digest
5
-
6
- has_secure_password
5
+ included do
6
+ attr_protected :password_digest
7
+ has_secure_password
7
8
 
8
- validates :email, :uniqueness => true, :presence => true
9
- validates :password, :presence => true, :length => {:minimum => 6}, :on => :create
9
+ validates :email, :uniqueness => true, :presence => true
10
+ validates :password, :presence => true, :length => { :minimum => 6 }, :on => :create
11
+ end
10
12
 
11
- def roles= value
13
+ def roles= value = []
12
14
  value = value.split(',').map(&:strip).map(&:presence) if value.is_a?(String)
13
15
  write_attribute(:roles, value.join(', '))
14
16
  end
15
17
 
16
18
  def roles_array
17
- roles.split(',').map(&:strip).map(&:presence)
19
+ roles.to_s.split(',').map(&:strip).map(&:presence)
18
20
  end
19
21
 
20
22
  def has_role? role
21
23
  roles_array.include?(role.to_s)
22
24
  end
23
-
24
25
  end
@@ -1,3 +1,15 @@
1
- class PufferUser < Puffer::PufferUser
1
+ if defined?(Mongoid::Document)
2
+ class PufferUser
3
+ include Mongoid::Document
2
4
 
3
- end
5
+ field :email, :type => String
6
+ field :password_digest, :type => String
7
+ field :roles, :type => String
8
+
9
+ include Puffer::PufferUser
10
+ end
11
+ elsif defined?(ActiveRecord::Base) && ActiveRecord::Base.connection.table_exists?('puffer_users')
12
+ class PufferUser < ActiveRecord::Base
13
+ include Puffer::PufferUser
14
+ end
15
+ end
@@ -1,12 +1,12 @@
1
1
  <% content_for :header do %>
2
2
  <ul class="namespaces">
3
- <% puffer_namespaces do |title, path, current| %>
4
- <li<%= raw(current ? ' class="selected"' : '') %>><%= link_to title, path %></li>
3
+ <% puffer_scopes_navigation do |title, path, current| %>
4
+ <li<%= raw(current ? ' class="selected"' : '') %>><%= link_to title.to_s.humanize, path %></li>
5
5
  <% end %>
6
6
  </ul>
7
7
  <ul class="navigation">
8
- <% puffer_navigation do |title, path, current| %>
9
- <li<%= raw(current ? ' class="selected"' : '') %>><%= link_to title, path %></li>
8
+ <% puffer_groups_navigation do |title, path, current| %>
9
+ <li<%= raw(current ? ' class="selected"' : '') %>><%= link_to title.to_s.humanize, path %></li>
10
10
  <% end %>
11
11
  </ul>
12
12
  <% end %>
@@ -15,8 +15,8 @@
15
15
  <div class="columns">
16
16
  <div class="column sidebar">
17
17
  <div class="padder">
18
- <ul class="navigation">
19
- <% sidebar_puffer_navigation do |title, path, current| %>
18
+ <ul class="navigation">
19
+ <% puffer_resources_navigation do |title, path, current| %>
20
20
  <li<%= raw(current ? ' class="selected"' : '') %>>
21
21
  <%= link_to title, path %>
22
22
  <% if current %>
@@ -34,19 +34,18 @@
34
34
  <% end %>
35
35
  </p>
36
36
  </dd>
37
- <dt>Search</dt>
38
- <dd>
39
- <%= form_tag resource.collection_path, {:method => :get} do %>
40
- <%= text_field_tag :search, params[:search] %>
41
- <%= submit_tag 'Search' %>
42
37
 
43
- <% if false %>
44
- <% if resource_session[:search].present? %>
45
- <%= link_to 'clear', resource.collection_path %>
46
- <% end %>
47
- <% end %>
38
+ <%= form_for puffer_filters, :url => resource.collection_path, :method => :get do |f| %>
39
+ <dt><%= f.label :puffer_search %></dt>
40
+ <dd><%= f.text_field :puffer_search %></dd>
41
+
42
+ <% index_fields.columns.each do |field| %>
43
+ <%= field.render controller, :filter, :form => f %>
48
44
  <% end %>
49
- </dd>
45
+
46
+ <dd><%= submit_tag 'Search', :name => nil %></dd>
47
+ <% end %>
48
+
50
49
  </dl>
51
50
  </div>
52
51
  <% end %>
@@ -6,8 +6,7 @@
6
6
  <%= csrf_meta_tag %>
7
7
  <%= stylesheet_link_tag "puffer/application" %>
8
8
  <%= javascript_include_tag "puffer/application" %>
9
- <%= yield :stylesheets %>
10
- <%= yield :javascripts %>
9
+ <%= yield :assets %>
11
10
  </head>
12
11
  <body>
13
12
  <div class="body">
@@ -1,7 +1,7 @@
1
1
  <% content_for :header do %>
2
2
  <ul class="namespaces">
3
- <% puffer_namespaces do |title, path, current| %>
4
- <li<%= raw(current ? ' class="selected"' : '') %>><%= link_to title, path %></li>
3
+ <% puffer_scopes_navigation do |title, path, current| %>
4
+ <li<%= raw(current ? ' class="selected"' : '') %>><%= link_to title.to_s.humanize, path %></li>
5
5
  <% end %>
6
6
  </ul>
7
7
  <% end %>
@@ -1,6 +1,15 @@
1
1
  <% @title = "Edit #{resource.human}" %>
2
2
  <h1><%= @title %></h1>
3
- <%= form_for record, :url => resource.member_path, :html => {:multipart => true} do |f| %>
4
- <%= render :partial => 'form', :locals => { :f => f, :action => 'edit' } %>
5
- <% end %>
3
+ <%= form_for record, :url => resource.member_path do |f| %>
4
+ <ul class="form">
5
+ <% update_fields.each do |field| -%>
6
+ <li><%= field.render controller, :update, :form => f, :record => f.object %></li>
7
+ <% end -%>
8
+ </ul>
6
9
 
10
+ <div class="buttons">
11
+ <%= f.submit t('puffer.save') %>
12
+ <%= f.submit t('puffer.save_and_exit') %>
13
+ <%= link_to 'cancel', (request.referer || resource.collectin_path) %>
14
+ </div>
15
+ <% end %>
@@ -1,6 +1,15 @@
1
1
  <% @title = "New #{resource.human}" %>
2
2
  <h1><%= @title %></h1>
3
- <%= form_for record, :url => resource.collection_path, :html => {:multipart => true} do |f| %>
4
- <%= render :partial => 'form', :locals => { :f => f, :action => 'new' } %>
5
- <% end %>
3
+ <%= form_for record, :url => resource.collection_path do |f| %>
4
+ <ul class="form">
5
+ <% create_fields.each do |field| -%>
6
+ <li><%= field.render controller, :create, :form => f, :record => f.object %></li>
7
+ <% end -%>
8
+ </ul>
6
9
 
10
+ <div class="buttons">
11
+ <%= f.submit t('puffer.save') %>
12
+ <%= f.submit t('puffer.save_and_exit') %>
13
+ <%= link_to 'cancel', (request.referer || resource.collectin_path) %>
14
+ </div>
15
+ <% end %>
@@ -1,13 +1,13 @@
1
- <% Rails.application.routes.puffer.each do |(namespace, groups)| %>
2
- <h2><%= namespace.to_s.humanize %></h2>
1
+ <% puffer_scopes_navigation do |scope| %>
2
+ <h2><%= scope.to_s.humanize %></h2>
3
3
  <ul class="navigation">
4
- <% groups.each do |(group, controllers)| %>
4
+ <% puffer_groups_navigation scope do |group| %>
5
5
  <li>
6
6
  <span><%= group.to_s.humanize %></span>
7
7
  <ul class="additional">
8
- <% controllers.each do |controller| %>
8
+ <% puffer_resources_navigation scope, group do |resource, url| %>
9
9
  <li>
10
- <%= link_to controller.model.model_name.human, polymorphic_url([namespace, controller.model]) %>
10
+ <%= link_to resource, url %>
11
11
  </li>
12
12
  <% end %>
13
13
  </ul>
@@ -5,7 +5,6 @@ class Puffer::ComponentGenerator < Rails::Generators::NamedBase
5
5
  @name = name.underscore
6
6
 
7
7
  template 'component.rb', "app/components/#{@name}_component.rb"
8
- template 'component_spec.rb', "spec/app/components/#{@name}_component_spec.rb"
9
8
  end
10
9
 
11
10
  def generate_views
@@ -15,7 +15,7 @@ private
15
15
  end
16
16
 
17
17
  def attributes
18
- @model_name.constantize.columns.map(&:name)
18
+ @model_name.constantize.to_adapter.column_names
19
19
  end
20
20
 
21
21
  end
@@ -6,13 +6,21 @@ class <%= controller_name %>Controller < Puffer::Base
6
6
 
7
7
  index do
8
8
  <% attributes.each do |attribute| -%>
9
+ <% if %w(_id id _type type created_at updated_at).include?(attribute.to_s) -%>
10
+ # field :<%= attribute %>
11
+ <% else -%>
9
12
  field :<%= attribute %>
13
+ <% end -%>
10
14
  <% end -%>
11
15
  end
12
16
 
13
17
  form do
14
18
  <% attributes.each do |attribute| -%>
19
+ <% if %w(_id id _type type created_at updated_at).include?(attribute.to_s) -%>
20
+ # field :<%= attribute %>
21
+ <% else -%>
15
22
  field :<%= attribute %>
23
+ <% end -%>
16
24
  <% end -%>
17
25
  end
18
26
 
@@ -11,11 +11,18 @@ require 'puffer/extensions/controller'
11
11
  require 'puffer/extensions/core'
12
12
  require 'puffer/extensions/mapper'
13
13
  require 'puffer/extensions/form'
14
+ require 'puffer/extensions/directive_processor'
14
15
  require 'puffer/extensions/engine'
15
16
  require 'puffer/engine'
16
17
 
17
18
  module Puffer
18
19
 
20
+ class PufferError < StandardError
21
+ end
22
+
23
+ class ComponentMissing < PufferError
24
+ end
25
+
19
26
  module Controller
20
27
  autoload :Action, 'puffer/controller/actions'
21
28
  autoload :MemberAction, 'puffer/controller/actions'
@@ -24,24 +31,81 @@ module Puffer
24
31
 
25
32
  module Component
26
33
  autoload :Base, 'puffer/component'
34
+ end
27
35
 
28
- mattr_accessor :_mappings
29
- self._mappings = {}
36
+ # Puffer has two types of mappings. If maps <tt>field.type</tt> to component
37
+ # class and also maps field attributes to <tt>field.type</tt>
38
+ mattr_accessor :_component_mappings
39
+ self._component_mappings = {}
40
+
41
+ # Maps <tt>field.type</tt> to component class
42
+ #
43
+ # ex:
44
+ #
45
+ # Puffer.map_component :ckeditor, :rich, :text, :to => CkeditorComponent
46
+ #
47
+ # this declaration maps even text fields to use <tt>CkeditorComponent</tt> for
48
+ # rendering
49
+ def self.map_component *args
50
+ to = args.extract_options![:to]
51
+ args.each { |type| _component_mappings[type.to_sym] = to }
52
+ end
53
+
54
+ def self.component_for field
55
+ type = field
56
+ type = field.type if field.respond_to? :type
57
+ (_component_mappings[type.to_sym] || "#{type}_component").to_s.camelize.constantize
58
+ rescue NameError
59
+ raise ComponentMissing, "Missing `#{type}` component for `#{field}` field. Please use Puffer.map_component binding or specify field type manually"
60
+ end
61
+
62
+ map_component :belongs_to, :has_one, :to => :ReferencesOneComponent
63
+ map_component :has_many, :has_and_belongs_to_many, :to => :ReferencesManyComponent
64
+ map_component :date, :time, :datetime, :date_time, :timestamp, :to => :DateTimeComponent
65
+ map_component :integer, :float, :decimal, :big_decimal, :to => :StringComponent
66
+ map_component :"mongoid/fields/serializable/object", :"bson/object_id", :symbol, :array, :hash, :set, :range, :to => :StringComponent
30
67
 
31
- def self.map_component *args
32
- to = args.extract_options![:to]
33
- args.each { |type| _mappings[type.to_sym] = to }
34
- end
35
68
 
36
- def self.component_for type
37
- (_mappings[type.to_sym] || "#{type}_component").to_s.camelize.constantize
69
+
70
+
71
+ mattr_accessor :_field_type_customs
72
+ self._field_type_customs = []
73
+
74
+
75
+ # Appends or prepends custom type.
76
+ #
77
+ # ex:
78
+ #
79
+ # Puffer.append_custom_field_type :paperclip do |field|
80
+ # field.model.respond_to?(:attachment_definitions)
81
+ # && field.model.attachment_definitions.key?(:field.field_name.to_sym)
82
+ # end
83
+ def self.prepend_custom_field_type custom_type, &block
84
+ _field_type_customs.shift [custom_type, block]
85
+ end
86
+
87
+ def self.append_custom_field_type custom_type, &block
88
+ _field_type_customs.push [custom_type, block]
89
+ end
90
+
91
+ def self.field_type_for field
92
+ custom_type = swallow_nil{_field_type_customs.detect {|(type, block)| block.call(field) }.first}
93
+ case custom_type
94
+ when Proc then
95
+ custom_type.call(field)
96
+ else
97
+ custom_type
38
98
  end
99
+ end
39
100
 
40
- map_component :belongs_to, :has_one, :to => :ReferencesOneComponent
41
- map_component :has_many, :has_and_belongs_to_many, :to => :ReferencesManyComponent
42
- map_component :date, :time, :datetime, :timestamp, :to => :DateTimeComponent
43
- map_component :integer, :decimal, :to => :StringComponent
44
- map_component :array, :decimal, :to => :StringComponent
101
+ append_custom_field_type :select do |field|
102
+ field.options.key? :select
103
+ end
104
+ append_custom_field_type :password do |field|
105
+ field.name =~ /password/
106
+ end
107
+ append_custom_field_type(proc {|type| type.reflection.macro}) do |field|
108
+ field.reflection
45
109
  end
46
110
 
47
111
  end