class Gridify::Grid
Constants
- OPS
- OPS_PATTERN
[‘eq’,‘ne’,‘lt’,‘le’,‘gt’,‘ge’,
'bw','bn','in','ni','ew','en','cn','nc']
[‘equal’,‘not equal’, ‘less’, ‘less or equal’,‘greater’,‘greater or equal’,
'begins with','does not begin with','is in','is not in','ends with','does not end with','contains','does not contain']
- STRING_OPS
Attributes
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
non persistent options:
:build_model :only :except
Public Class Methods
todo: change this so klass is optional, decouple from active record
# File lib/gridify/grid.rb, line 14 def initialize( klass, *args, &block ) #debugger options = args.extract_options! # => args.last.is_a?(Hash) ? args.pop : {} assert_exclusive_keys( options, :only, :except) @resource = klass.to_s.tableize.pluralize @name = args.first || :grid # non-persistent options # generate model unless explicitly say no build_model = options.delete(:build_model) == false ? false : true only = options.delete(:only) except = options.delete(:except) col_presets = options.delete(:columns) # assign options update options # build columns from ActiveRecord model (klass) if klass.present? && build_model @model = build_columns klass, only, except, col_presets end instance_eval(&block) if block #(note instance_eval cannot access things outside its scope; otherwise do this: #yield self if block_given? end
Public Instance Methods
# File lib/gridify/grid_options.rb, line 159 def arranger_options(type) #read-only (arranger[type] if arranger.is_a?(Hash)) || {} end
# File lib/gridify/grid_options.rb, line 151 def arranger_type #read-only if arranger.is_a?(Hash) arranger.keys else Array(arranger) end end
# File lib/gridify/grid.rb, line 47 def column( name, options={} ) name = name.to_s klass = resource.classify.constantize # TODO: set edit options based on ar_column # TODO: edit # TODO: handle file input types # TODO: custom input types if col = columns_hash[name] # update an existing column col.update options elsif ar = klass.columns.detect {|c| c.name==name} #debugger # create column from database schema edit = editable && # only edit accessible attributes (klass.accessible_attributes.nil? || klass.accessible_attributes.include?(ar.name)) args = { :ar_column => ar, :name => ar.name, :value_type => ar.type, :searchable => searchable, :sortable => sortable, :editable => edit }.merge(options) columns << GridColumn.new( args) else # create column from scratch args = { :name => name, :value_type => :string, :searchable => searchable, :sortable => sortable, :editable => edit }.merge(options) columns << GridColumn.new( args) end end
# File lib/gridify/grid.rb, line 91 def column_model columns.collect {|col| col.properties } end
# File lib/gridify/grid.rb, line 87 def column_names columns.collect {|col| col.name.titleize } end
normally we need to keep columns an ordered array, sometimes its convenient to have a hash
# File lib/gridify/grid.rb, line 96 def columns_hash columns.inject({}) {|h, col| h[col.name] = col; h } end
return find args (scope) for current settings
# File lib/gridify/grid_finder.rb, line 26 def current_scope #debugger find_args = {} if sort_by.present? && col = columns_hash[sort_by] if case_sensitive || !([:string, :text].include?(col.value_type)) find_args[:order] = "#{sort_by} #{sort_order}" else find_args[:order] = "upper(#{sort_by}) #{sort_order}" end end if rows_per_page.present? && rows_per_page > 0 find_args[:limit] = rows_per_page offset = (current_page.to_i-1) * rows_per_page if current_page.present? find_args[:offset] = offset if offset && offset > 0 end cond = rules_to_conditions find_args[:conditions] = cond unless cond.blank? find_args end
# File lib/gridify/grid_finder.rb, line 54 def encode_records( records, total_count=nil ) #debugger klass = resource.classify.constantize total_count ||= klass.count total_pages = total_count / rows_per_page + 1 #TODO: :only => [attributes], :methods => [virtual attributes] case data_type when :xml then xml = records.to_xml( :skip_types => true, :dasherize => false ) do |xml| if rows_per_page > 0 xml.page current_page xml.total_pages total_pages xml.total_records total_count end end when :json then #debugger data = { resource => records } if rows_per_page > 0 data.merge!( :page => current_page, :total_pages => total_pages, :total_records => total_count ) end save = ActiveRecord::Base.include_root_in_json ActiveRecord::Base.include_root_in_json = false json = data.to_json ActiveRecord::Base.include_root_in_json = save json #others... else #nop ? records.to_s end end
# File lib/gridify/grid_options.rb, line 252 def error_handler_return_value error_handler ? error_handler : 'true;' end
# File lib/gridify/grid_finder.rb, line 46 def find( params ) #debugger update_from_params params find_args = current_scope klass = resource.classify.constantize records = klass.send( finder, :all, find_args ) end
# File lib/gridify/grid_finder.rb, line 93 def find_and_encode( params ) encode_records( find( params ) ) end
grid doesnt nest attributes inside the resource could change this behavior in jqGrid, see grid.postext.js ?
http://www.trirand.com/jqgridwiki/doku.php?id=wiki:post_data_module
# File lib/gridify/grid_finder.rb, line 101 def member_params( params ) params.inject({}) {|h, (name, value)| h[name] = value if columns_hash[name]; h } end
generate the grid javascript for a view options:
:script => true generates <script> tag (true) :ready => true generates jquery ready function (true)
# File lib/gridify/grid_view.rb, line 11 def to_javascript( options={} ) options = { :script => true, :ready => true }.merge(options) s = '' if options[:script] s << %Q^<script type="text/javascript">^ end s << js_helpers if options[:ready] s << %Q^jQuery(document).ready(function(){^ end s << jqgrid_javascript(options) if options[:ready] s << %Q^});^ end if options[:script] s << %Q^</script>^ end s end
# File lib/gridify/grid_view.rb, line 37 def to_json jqgrid_properties.to_json_with_js end
alias :to_s, :to_javascript
# File lib/gridify/grid_view.rb, line 42 def to_s( options={} ) to_javascript( options ) end
# File lib/gridify/grid.rb, line 42 def update( options ) options.each {|atr, val| send( "#{atr}=", val )} # exception "invalid option..." end
finds records based on request params e.g. params from jqGrid
:_search do search (true/false) ["false"] :sidx sort index (column to search on) [""] :sord sort direction (desc/asc) ["asc"] :nd ? :rows number of items to get ["20"] :page page number (starts at 1) ["1"]
# File lib/gridify/grid_finder.rb, line 16 def update_from_params( params ) params.symbolize_keys! params_to_rules params self.sort_by = params[:sidx] if params[:sidx] self.sort_order = params[:sord] if params[:sord] self.current_page = params[:page].to_i if params[:page] self.rows_per_page = params[:rows].to_i if params[:rows] end
Protected Instance Methods
generate list of columns based on AR model option: :only or :except
:col_options hash of hash of preset values for columns (eg from cookie) { :title => {:width => 98}}
# File lib/gridify/grid.rb, line 115 def build_columns( klass, only, except, presets ) #debugger # stringify only = Array(only).map {|s| s.to_s } except = Array(except).map {|s| s.to_s } presets ||= {} presets.stringify_keys! self.columns = klass.columns.collect do |ar| #debugger next if only.present? && !only.include?(ar.name) next if except.present? && except.include?(ar.name) is_key = (ar.name=='id') edit = editable && !is_key && # only edit accessible attributes (klass.accessible_attributes.nil? || klass.accessible_attributes.include?(ar.name)) args = { :ar_column => ar, :name => ar.name, :value_type => ar.type, :key => is_key, :hidden => is_key, :searchable => searchable, :sortable => sortable, :editable => edit } # create column with default args merged with options given for this column GridColumn.new args.merge( presets[ar.name]||{} ) end.compact end
# File lib/gridify/grid_view.rb, line 192 def jqgrid_javascript( options={} ) s = '' if table_to_grid s << %Q^ tableToGrid("##{dom_id}", #{to_json});^ s << %Q^ grid = jQuery("##{dom_id}") ^ else s << %Q^ grid = jQuery("##{dom_id}").jqGrid(#{to_json})^ end # tag the grid as fluid so we can find it on resize events if width_fit == :fluid s << %Q^ .addClass("fluid")^ end # override tableToGrid colmodel options as needed (sortable) #s << %Q^ .jqGrid('setColProp','Title',{sortable: false})^ # resize method if resizable s << %Q^ .jqGrid('gridResize', #{resizable.to_json})^ end # pager buttons (navGrid) if pager nav_params = { 'edit' => edit_button.present?, 'add' => add_button.present?, 'del' => delete_button.present?, 'search' => search_button.present? || search_advanced.present?, 'view' => view_button.present?, 'refresh' => refresh_button.present? }.merge(jqgrid_nav_options||{}) s << %Q^ .navGrid('##{pager}', #{nav_params.to_json}, #{edit_button_options.to_json_with_js}, #{add_button_options.to_json_with_js}, #{delete_button_options.to_json_with_js}, #{search_button_options.to_json_with_js}, #{view_button_options.to_json_with_js} )^ end if arranger_type.include?(:hide_show) s << %Q^ .jqGrid('navButtonAdd','##{pager}',{ caption: "Columns", title: "Hide/Show Columns", onClickButton : function (){ jQuery("##{dom_id}").jqGrid('setColumns', #{arranger_options(:hide_show).to_json_with_js} ); } })^ end if arranger_type.include?(:chooser) # hackey way to build the string but gets it done chooser_code = %Q^ function (){ jQuery('##{dom_id}').jqGrid('columnChooser', { done : function (perm) { if (perm) { this.jqGrid('remapColumns', perm, true); var gwdth = this.jqGrid('getGridParam','width'); this.jqGrid('setGridWidth',gwdth); } } })}^ chooser_opts = { 'caption' => 'Columns', 'title' => 'Arrange Columns', 'onClickButton' => 'chooser_code' }.merge(arranger_options(:chooser)) s << %Q^ .jqGrid('navButtonAdd','##{pager}', #{chooser_opts.to_json.gsub('"chooser_code"', chooser_code)} )^ end if search_toolbar # I wish we could put this in the header rather than the pager s << %Q^ .jqGrid('navButtonAdd',"##{pager}", { caption:"Toggle", title:"Toggle Search Toolbar", buttonicon: 'ui-icon-pin-s', onClickButton: function(){ grid[0].toggleToolbar() } }) .jqGrid('navButtonAdd',"##{pager}", { caption:"Clear", title:"Clear Search", buttonicon: 'ui-icon-refresh', onClickButton: function(){ grid[0].clearToolbar() } }) .jqGrid('filterToolbar')^ end # TODO: built in event handlers, eg # loadError # onSelectRow, onDblClickRow, onRightClickRow etc s << '; ' unless search_toolbar == :visible s << %Q^ grid[0].toggleToolbar(); ^ end # # keep page controls centered (jqgrid bug) [eg appears when :width_fit => :scroll] # s << %Q^ $("##{pager}_left").css("width", "auto"); ^ s end
generate the jqGrid initial values in json
maps our attributes to jqGrid options; omit values when same as jqGrid defaults
# File lib/gridify/grid_view.rb, line 99 def jqgrid_properties vals = {} # data and request options vals['url'] = url if url vals['restful'] = true if restful vals['postData'] = { :grid => name } #identify which grid making the request # vals['colNames'] = column_names if columns.present? vals['colModel'] = column_model if columns.present? vals['datatype'] = data_type if data_type if data_format.present? if data_type == :xml vals['xmlReader'] = data_format elsif data_type == :json vals['jsonReader'] = data_format end end vals['loadonce'] = load_once if load_once vals['sortname'] = sort_by if sort_by vals['sortorder'] = sort_order if sort_order && sort_by vals['rowNum'] = rows_per_page if rows_per_page vals['page'] = current_page if current_page # grid options vals['height'] = height if height vals['gridview'] = true # faster views, NOTE theres cases when this needs to be disabled case width_fit when :fitted then #vals[:autowidth] = false #default #vals[:shrinkToFit] = true #default vals['forceFit'] = true vals['width'] = width if width when :scroll then #vals[:autowidth] = false #default vals['shrinkToFit'] = false #vals['forceFit'] = #ignored by jqGrid vals['width'] = width if width else #when :fluid vals['autowidth'] = true #vals['shrinkToFit'] = true #default vals['forceFit'] = true #vals['width'] = is ignored vals['resizeStop'] = 'javascript: gridify_fluid_recalc_width' end vals['sortable'] = true if arranger_type.include?(:sortable) # header layer vals['caption'] = title if title vals['hidegrid'] = false unless collapsible vals['hiddengrid'] = true if collapsed # row formatting vals['altrows'] = true if alt_rows vals['altclass'] = alt_rows if alt_rows.is_a?(String) vals['rowNumbers'] = true if row_numbers vals['rownumWidth'] = row_numbers if row_numbers.is_a?(Numeric) if select_rows.present? vals['scrollrows'] = true #handler... else vals['hoverrows'] = false vals['beforeSelectRow'] = "javascript: function(){ false; }" end # pager layer if pager vals['pager'] = "##{pager}" vals['viewrecords'] = true # display total records in the query (eg "1 - 10 of 25") vals['rowList'] = paging_choices if paging_controls.is_a?(Hash) # allow override of jqGrid pager options vals.merge!(paging_controls) elsif !paging_controls vals['rowList'] = [] vals['pgbuttons'] = false vals['pginput'] = false vals['recordtext'] = "{2} records" end end # allow override of native jqGrid options vals.merge(jqgrid_options) end
# File lib/gridify/grid_view.rb, line 287 def js_helpers # just move this into appliaction.js? # gridify_fluid_recalc_width: allow grid resize on window resize events # recalculate width of grid based on parent container; handles multiple grids # ref: http://www.trirand.com/blog/?page_id=393/feature-request/Resizable%20grid/ # afterSubmit: display error message in response %Q^ function gridify_fluid_recalc_width(){ if (grids = jQuery('.fluid.ui-jqgrid-btable:visible')) { grids.each(function(index) { gridId = jQuery(this).attr('id'); gridParentWidth = jQuery('#gbox_' + gridId).parent().width(); jQuery('#' + gridId).setGridWidth(gridParentWidth); }); } }; jQuery(window).bind('resize', gridify_fluid_recalc_width); function gridify_action_error_handler(r, data, action){ if (r.responseText != '') { return [false, r.responseText]; } else { return true; } } ^ end
lets options be true or a hash, merges into defaults and returns a hash
# File lib/gridify/grid_view.rb, line 330 def merge_options_defaults( options, defaults={} ) if options defaults.merge( options==true ? {} : options) else {} end end
params => {“groupOp”=>“AND”,
"rules"=>[{"data"=>"b", "op"=>"ge", "field"=>"title"}, {"data"=>"f", "op"=>"le", "field"=>"title"}] }
# File lib/gridify/grid_finder.rb, line 137 def params_to_rules( params ) #debugger if params[:_search]=='true' || params[:_search]==true if params[:filters] # advanced search filters = ActiveSupport::JSON.decode( params[:filters] ) self.search_rules = filters['rules'] self.search_rules_op = filters['groupOp'] elsif params[:searchField] # simple search self.search_rules = [{ "field" => params[:searchField], "op" => params[:searchOper], "data" => params[:searchString]}] else # toolbar search self.search_rules = [] self.search_rules_op = :and columns.each do |col| name = col.name data = params[name.to_sym] self.search_rules << { "field" => name, "op" => "cn", "data" => data } if data end end end search_rules end
# File lib/gridify/grid_finder.rb, line 162 def rules_to_conditions # note: ignoring case_sensitive as an option, ActiveRecord find is insensitive by default (have to model the db to let it be case sensitive?) return nil if search_rules.blank? cond = nil expr = '' vals = [] search_rules.each do |rule| expr << " #{search_rules_op} " unless expr.blank? if op = OPS[rule['op']] expr << "#{rule['field']} #{op} ?" vals << rule['data'] elsif op = STRING_OPS[rule['op']] expr << "#{rule['field']} #{op} ?" vals << OPS_PATTERN[rule['op']].gsub('?', rule['data']) end end cond = [ expr ] + vals end