# Base methods for CRUD actions # Simply override any methods in your action controller you want to be customised # Don't forget to add: # resources :plural_model_name_here # to your routes.rb file. # Full documentation about CRUD and resources go here: # -> http://caboo.se/doc/classes/ActionController/Resources.html#M003716 # Example (add to your controller): # crudify :foo, {:title_attribute => 'name'} module Refinery module Crud def self.default_options(model_name) singular_name = model_name.to_s class_name = singular_name.camelize plural_name = singular_name.pluralize this_class = class_name.constantize { :title_attribute => "title", :order => ('position ASC' if this_class.table_exists? and this_class.column_names.include?('position')), :conditions => '', :sortable => true, :searchable => true, :include => [], :paging => true, :search_conditions => '', :redirect_to_url => "admin_#{plural_name}_url" } end def self.append_features(base) super base.extend(ClassMethods) end module ClassMethods def crudify(model_name, options = {}) options = ::Refinery::Crud.default_options(model_name).merge(options) singular_name = model_name.to_s class_name = singular_name.camelize plural_name = singular_name.pluralize module_eval %( prepend_before_filter :find_#{singular_name}, :only => [:update, :destroy, :edit, :show] def new @#{singular_name} = #{class_name}.new end def create # if the position field exists, set this object as last object, given the conditions of this class. if #{class_name}.column_names.include?("position") params[:#{singular_name}].merge!({ :position => ((#{class_name}.maximum(:position, :conditions => #{options[:conditions].inspect})||-1) + 1) }) end if (@#{singular_name} = #{class_name}.create(params[:#{singular_name}])).valid? (request.xhr? ? flash.now : flash).notice = t( 'refinery.crudify.created', :what => "'\#{@#{singular_name}.#{options[:title_attribute]}}'" ) unless from_dialog? unless params[:continue_editing] =~ /true|on|1/ redirect_back_or_default(#{options[:redirect_to_url]}) else unless request.xhr? redirect_to :back else render :partial => "/shared/message" end end else render :text => "" end else unless request.xhr? render :action => 'new' else render :partial => "/shared/admin/error_messages", :locals => { :object => @#{singular_name}, :include_object_name => true } end end end def edit # object gets found by find_#{singular_name} function end def update if @#{singular_name}.update_attributes(params[:#{singular_name}]) (request.xhr? ? flash.now : flash).notice = t( 'refinery.crudify.updated', :what => "'\#{@#{singular_name}.#{options[:title_attribute]}}'" ) unless from_dialog? unless params[:continue_editing] =~ /true|on|1/ redirect_back_or_default(#{options[:redirect_to_url]}) else unless request.xhr? redirect_to :back else render :partial => "/shared/message" end end else render :text => "" end else unless request.xhr? render :action => 'edit' else render :partial => "/shared/admin/error_messages", :locals => { :object => @#{singular_name}, :include_object_name => true } end end end def destroy # object gets found by find_#{singular_name} function if @#{singular_name}.destroy flash.notice = t('refinery.crudify.destroyed', :what => "'\#{@#{singular_name}.#{options[:title_attribute]}}'") end redirect_to #{options[:redirect_to_url]} end # Finds one single result based on the id params. def find_#{singular_name} @#{singular_name} = #{class_name}.find(params[:id], :include => #{options[:include].map(&:to_sym).inspect}) end # Find the collection of @#{plural_name} based on the conditions specified into crudify # It will be ordered based on the conditions specified into crudify # And eager loading is applied as specified into crudify. def find_all_#{plural_name}(conditions = #{options[:conditions].inspect}) @#{plural_name} = #{class_name}.where(conditions).includes( #{options[:include].map(&:to_sym).inspect} ).order("#{options[:order]}") end # Paginate a set of @#{plural_name} that may/may not already exist. def paginate_all_#{plural_name} # If we have already found a set then we don't need to again find_all_#{plural_name} if @#{plural_name}.nil? paging_options = {:page => params[:page]} # Seems will_paginate doesn't always use the implicit method. if #{class_name}.methods.map(&:to_sym).include?(:per_page) paging_options.update({:per_page => #{class_name}.per_page}) end @#{plural_name} = @#{plural_name}.paginate(paging_options) end # Returns a weighted set of results based on the query specified by the user. def search_all_#{plural_name} # First find normal results. find_all_#{plural_name}(#{options[:search_conditions].inspect}) # Now get weighted results by running the query against the results already found. @#{plural_name} = @#{plural_name}.with_query(params[:search]) end # Ensure all methods are protected so that they should only be called # from within the current controller. protected :find_#{singular_name}, :find_all_#{plural_name}, :paginate_all_#{plural_name}, :search_all_#{plural_name} ) # Methods that are only included when this controller is searchable. if options[:searchable] if options[:paging] module_eval %( def index search_all_#{plural_name} if searching? paginate_all_#{plural_name} end ) else module_eval %( def index unless searching? find_all_#{plural_name} else search_all_#{plural_name} end end ) end else if options[:paging] module_eval %( def index paginate_all_#{plural_name} end ) else module_eval %( def index find_all_#{plural_name} end ) end end if options[:sortable] module_eval %( def reorder find_all_#{plural_name} end # Based upon http://github.com/matenia/jQuery-Awesome-Nested-Set-Drag-and-Drop def update_positions previous = nil # The list doesn't come to us in the correct order. Frustration. 0.upto((newlist ||= params[:ul]).length - 1) do |index| hash = newlist[index.to_s] moved_item_id = hash['id'].split(/#{singular_name}\\_?/) @current_#{singular_name} = #{class_name}.find_by_id(moved_item_id) if @current_#{singular_name}.respond_to?(:move_to_root) if previous.present? @current_#{singular_name}.move_to_right_of(#{class_name}.find_by_id(previous)) else @current_#{singular_name}.move_to_root end else @current_#{singular_name}.update_attribute(:position, index) end if hash['children'].present? update_child_positions(hash, @current_#{singular_name}) end previous = moved_item_id end #{class_name}.rebuild! if #{class_name}.respond_to?(:rebuild!) render :nothing => true end def update_child_positions(node, #{singular_name}) 0.upto(node['children'].length - 1) do |child_index| child = node['children'][child_index.to_s] child_id = child['id'].split(/#{singular_name}\_?/) child_#{singular_name} = #{class_name}.find_by_id(child_id) child_#{singular_name}.move_to_child_of(#{singular_name}) if child['children'].present? update_child_positions(child, child_#{singular_name}) end end end ) end module_eval %( def self.sortable? #{options[:sortable].to_s} end def self.searchable? #{options[:searchable].to_s} end ) end end end end