module Fancygrid#:nodoc: class Grid < Fancygrid::Node # Collection of all fancygrid leafs. These are instances of Fancygrid::Node # and define the columns of a table. They may refer to an attribute or a # method or a renderable cell of a model attr_accessor :leafs # The database query that is sent to the database. attr_accessor :query # The current POST or GET parameters. attr_accessor :request_params # The result of the database query. This is the data that is going to be rendered. attr_accessor :dataset # Number of possible matching results. attr_accessor :resultcount # Order and visibility definition for each column attr_accessor :view # Url for the ajax callback. attr_accessor :url # the request type for the ajax callback attr_accessor :ajax_type # enables or disables the sort window attr_accessor :enable_sort_window # The template name that is used to render this grid. attr_accessor :template # Enables or disables the input fields for simple search. attr_accessor :search_visible # Specifies the type of the search. Must be one of "simple" or "complex" attr_accessor :search_type # Specifies a set of enabled search operators attr_accessor :search_operators # Enables or disables the rendering of the top control bar. attr_accessor :hide_top_control # Enables or disables the rendering of the bottom control bar. attr_accessor :hide_bottom_control # Specifies the rendering strategy. May be one of 'table' or 'list' attr_accessor :grid_type # Spcified the select options for per page drop down attr_accessor :per_page_options # Spcified theselected value in the per page drop down attr_accessor :per_page_selection # Specifies the search options for search input fields attr_accessor :search_formats # Initializes the root node of the fancygrid tree. def initialize(name, klass = nil, table_name = nil, params = nil) super(self, nil, name) initialize_node(name, klass, table_name) self.url = nil self.ajax_type = :get self.enable_sort_window = false self.leafs = [] self.dataset = nil self.resultcount = 0 self.query = {} self.request_params = (params || {}) self.grid_type = Fancygrid.default_grid_type self.search_visible = Fancygrid.search_visible self.search_type = Fancygrid.default_search_type self.search_operators = Fancygrid.search_operators self.search_formats = {} if Fancygrid.use_grid_name_as_cells_template self.template = File.join(Fancygrid.cells_template_directory.to_s, name.to_s) else self.template = File.join(Fancygrid.cells_template_directory.to_s, Fancygrid.cells_template.to_s) end self.per_page_options = Fancygrid.default_per_page_options self.per_page_selection = Fancygrid.default_per_page_selection view_opts = self.request_params[:fancygrid] || {} view_opts = view_opts[self.name] self.load_view(view_opts || {}) end # Inserts a given node into the leafs collection. # def insert_node(node) raise "Node must be a leaf" unless node.is_leaf? if (self.view) node.position = self.view.get_node_position(node) node.visible = self.view.get_node_visibility(node) node.search_value = self.view.get_node_search_value(node) end leafs << node end # Sorts the leafs by position attribute # def sort_leafs! leafs.sort! { |a, b| a.position.to_i <=> b.position.to_i } end # Takes the given view hash and aligns the leafs using the passed view definitions # def load_view options options ||= {} options = options[:fancygrid] || options options = options[self.name] || options self.view = Fancygrid::View.new(options) # reorder current leafs new_leafs = self.leafs self.leafs = [] new_leafs.each do |leaf| insert_node(leaf) end end # Yields a query generator which should be used to build a find query # # == Options # The options are the same as in active record finder method # * :conditions - An SQL fragment like “administrator = 1”, ["user_name = ?", username], or ["user_name = :user_name", { :user_name => user_name }] # * :order - An SQL fragment like “created_at DESC, name”. # * :group - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause. # * :having - Combined with :group this can be used to filter the records that a GROUP BY returns. Uses the HAVING SQL-clause. # * :limit - An integer determining the limit on the number of rows that should be returned. # * :offset - An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4. # * :joins - Either an SQL fragment for additional joins like “LEFT JOIN comments ON comments.post_id = id” (rarely needed), named associations in the same form used for the :include option, which will perform an INNER JOIN on the associated table(s), or an array containing a mixture of both strings and named associations. If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table’s columns. Pass :readonly => false to override. # * :include - Names associations that should be loaded alongside. The symbols named refer to already defined associations. See eager loading under Associations. # * :select - By default, this is “*” as in “SELECT * FROM”, but can be changed if you, for example, want to do a join but not include the joined columns. Takes a string with the SELECT SQL fragment (e.g. “id, name”). # * :from - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name of a database view). # * :readonly - Mark the returned records read-only so they cannot be saved or updated. # * :lock - An SQL fragment like “FOR UPDATE” or “LOCK IN SHARE MODE”. :lock => true gives connection’s default exclusive lock, usually “FOR UPDATE”. def find(options={})#:yields: generator raise "calling 'find' twice or after 'data=' is not allowed" unless dataset.nil? # don not process same or equal leafs twice leafs.compact! # get the parameters for this grid instance, they are mapped like this { :fancygrid => { :gird_name => ..options.. }} params = request_params[:fancygrid] || {} params = params[self.name] || {} # build default query hash url_options = { :select => self.leafs.map{ |leaf| leaf.select_name }.compact, :conditions => params[:conditions], :pagination => params[:pagination], :operator => params[:operator] } generator = Fancygrid::QueryGenerator.new(url_options) generator.parse_options(options) yield(generator) if block_given? generator.order(self.view.get_sort_order) self.query = generator.query end # Sets a custom dataset that should be rendered.Blanks out the # callback url so no ajax request will be made. # def data= data leafs.compact! self.dataset = data.to_a self.url = nil end # Runs the current query and caches the resulting data # def query_for_data if self.record_klass < ActiveRecord::Base self.dataset = self.record_klass.find(:all, self.query) count_query = self.query.reject do |k, v| [:limit, :offset, :order].include?(k.to_sym ) end self.resultcount = self.record_klass.count(:all, count_query) elsif self.record_klass < ActiveResource::Base self.dataset = self.record_klass.find(:all, :params => self.query) self.resultcount = self.dataset.delete_at(self.dataset.length - 1).total else raise "Unable to query for data. Supported base classes are 'ActiveRecord::Base' and 'ActiveResource::Base' but '#{self.record_klass}' was given" end self.resultcount = self.resultcount.length if self.resultcount.respond_to?(:length) end # Returns true if the callback url is blank, that is when no ajax # functionality is wanted. def is_static? self.url.blank? end # Determines whether the grid instance is initialized with simle search # def has_simple_search? self.search_type.to_s == "simple" && !self.is_static? end # Determines whether the grid instance is initialized with complex search # def has_complex_search? self.search_type.to_s == "complex" && !self.is_static? end # Determines whether the grid instance displays the top control bar # def has_top_control? !self.hide_top_control && !self.is_static? end # Determines whether the grid instance displays the bottom control bar # def has_bottom_control? !self.hide_bottom_control && !self.is_static? end # Determines whether the grid instance is able to sort columns # def has_sort_window? !self.is_static? && self.enable_sort_window end def enable_state_caching! load_view_proc do |instance| opts = session[:fancygrid] || {} opts[instance.name.to_s] || {} end load_view_proc do |instance, dump| session[:fancygrid] ||= {} session[:fancygrid][instance.name.to_s] = dump end end # If a block is given a new Proc is created for later css evaluation # def css_proc @css_proc = Proc.new if block_given? @css_proc end # Evaluates the css class for a table row by using the passed record and the ccs_proc of this grid # def css_proc_evaluate(record) @css_proc and @css_proc.call(record) or "" end # Gets and sets a proc for loading a dumped view from session, database or whatever place # # == Example # # fancygrid_for :companies do |g| # g.load_view_proc do |instance| # # load a hash from session, fancygrid will use that to initiate its view # session["fancygrid_#{instance.name.to_s}"] || {} # end # end def load_view_proc @load_view_proc = Proc.new if block_given? @load_view_proc end # Evaluates the load_view_proc if available # def load_view_proc_evaluate return load_view_proc.call(self) if load_view_proc.is_a?(Proc) end # Gets and sets a proc for storing a dumped view to session, database or whatever place # # == Example # # fancygrid_for :companies do |g| # g.store_view_proc do |instance, dump| # # store the dump to. The dump comes from the fancygrid view # session["fancygrid_#{instance.name.to_s}"] = dump # end # end def store_view_proc @store_view_proc = Proc.new if block_given? @store_view_proc end # Evaluates the store_view_proc if available # def store_view_proc_evaluate store_view_proc.call(self, self.view.dump) if store_view_proc.is_a?(Proc) end # Yields each leaf that is visible # def each_leaf#:yields: leaf leafs.compact! leafs.each do |leaf| yield leaf end end # Yields each leaf that is visible # def each_visible_leaf#:yields: leaf leafs.compact! leafs.each do |leaf| yield leaf if leaf.visible end end # Yields each leaf that is not visible # def each_hidden_leaf#:yields: leaf leafs.compact! leafs.each do |leaf| yield leaf unless leaf.visible end end # Gets all leafs that are visible and searchable def serachable_leafs leafs.select { |leaf| leaf && leaf.searchable && leaf.visible }.compact end # Yields each fetched record if available # def each_record#:yields: record return unless self.dataset self.dataset.each do |record| yield record end end # Builds the javascript options for the javascript part of fancygrid def js_options { :url => self.url, :ajaxType => self.ajax_type, :name => self.name, :isStatic => self.is_static?, :gridType => self.grid_type, :searchVisible => (self.view.search_visible or self.search_visible), :searchType => self.search_type, :hideTopControl => self.hide_top_control, :hideBottomControl => self.hide_bottom_control, :paginationPage => self.view.get_pagination_page, :paginationPerPage => self.view.get_pagination_per_page, }.to_json end def log(message)#:nodoc: #Rails.logger.debug("[FANCYGRID] #{message.to_s}") end end end