Skip to content

Commit

Permalink
Merge branch 'sql_select_option' of https://github.com/iwiznia/datagrid
Browse files Browse the repository at this point in the history
… into iwiznia-sql_select_option
  • Loading branch information
bogdan committed Mar 7, 2014
2 parents 3f14535 + 15d9d66 commit e647a20
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 44 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Expand Up @@ -9,7 +9,7 @@ group :development do


gem "debugger", :platform => "ruby_19"
gem "byebug", :platform => "ruby_20"
gem "byebug", :platform => ["ruby_20", "ruby_21"]
gem "ruby-debug", :platform => "ruby_18"

gem "rspec"
Expand Down
40 changes: 25 additions & 15 deletions lib/datagrid/columns.rb
Expand Up @@ -88,23 +88,24 @@ def columns(*args)
end

# Defines new datagrid column
#
#
# Arguments:
#
# * <tt>name</tt> - column name
# * <tt>query</tt> - a string representing the query to select this column (supports only ActiveRecord)
# * <tt>options</tt> - hash of options
# * <tt>block</tt> - proc to calculate a column value
#
# Available options:
#
#
# * <tt>:html</tt> - determines if current column should be present in html table and how is it formatted
# * <tt>:order</tt> - determines if this column could be sortable and how.
# The value of order is explicitly passed to ORM ordering method.
# * <tt>:order</tt> - determines if this column could be sortable and how.
# The value of order is explicitly passed to ORM ordering method.
# Ex: <tt>"created_at, id"</tt> for ActiveRecord, <tt>[:created_at, :id]</tt> for Mongoid
# * <tt>:order_desc</tt> - determines a descending order for given column
# * <tt>:order_desc</tt> - determines a descending order for given column
# (only in case when <tt>:order</tt> can not be easily reversed by ORM)
# * <tt>:order_by_value</tt> - used in case it is easier to perform ordering at ruby level not on database level.
# Warning: using ruby to order large datasets is very unrecommended.
# * <tt>:order_by_value</tt> - used in case it is easier to perform ordering at ruby level not on database level.
# Warning: using ruby to order large datasets is very unrecommended.
# If set to true - datagrid will use column value to order by this column
# If block is given - datagrid will use value returned from block
# * <tt>:mandatory</tt> - if true, column will never be hidden with #column_names selection
Expand All @@ -113,13 +114,18 @@ def columns(*args)
# * <tt>:after</tt> - determines the position of this column, by adding it after the column passed here
#
# See: https://github.com/bogdan/datagrid/wiki/Columns for examples
def column(name, options = {}, &block)
def column(name, options_or_query = {}, options = {}, &block)
if options_or_query.is_a?(String)
query = options_or_query
else
options = options_or_query
end
check_scope_defined!("Scope should be defined before columns")
block ||= lambda do |model|
model.send(name)
end
position = Datagrid::Utils.extract_position_from_options(columns_array, options)
column = Datagrid::Columns::Column.new(self, name, default_column_options.merge(options), &block)
column = Datagrid::Columns::Column.new(self, name, query, default_column_options.merge(options), &block)
columns_array.insert(position, column)
end

Expand Down Expand Up @@ -148,7 +154,7 @@ def format(value, &block)
end
end
else
# Ruby Object#format exists.
# Ruby Object#format exists.
# We don't want to change the behaviour and overwrite it.
super
end
Expand All @@ -163,6 +169,10 @@ def inherited(child_class) #:nodoc:

module InstanceMethods

def assets
driver.append_column_queries(super, columns.map(&:query).compact)
end

# Returns <tt>Array</tt> of human readable column names. See also "Localization" section
#
# Arguments:
Expand Down Expand Up @@ -212,7 +222,7 @@ def data(*column_names)
self.rows(*column_names).unshift(self.header(*column_names))
end

# Return Array of Hashes where keys are column names and values are column values
# Return Array of Hashes where keys are column names and values are column values
# for each row in filtered datagrid relation.
#
# Example:
Expand Down Expand Up @@ -267,7 +277,7 @@ def to_csv(*column_names)
# Returns all columns selected in grid instance
#
# Examples:
#
#
# MyGrid.new.columns # => all defined columns
# grid = MyGrid.new(:column_names => [:id, :name])
# grid.columns # => id and name columns
Expand Down Expand Up @@ -301,15 +311,15 @@ def column_by_name(name)
# Gives ability to have a different formatting for CSV and HTML column value.
#
# Example:
#
#
# column(:name) do |model|
# format(model.name) do |value|
# content_tag(:strong, value)
# end
# end
#
# column(:company) do |model|
# format(model.company.name) do
# format(model.company.name) do
# render :partial => "company_with_logo", :locals => {:company => model.company }
# end
# end
Expand All @@ -326,7 +336,7 @@ def format(value, &block)
# Allows to access column values
#
# Example:
#
#
# class MyGrid
# scope { User }
# column(:id)
Expand Down
13 changes: 7 additions & 6 deletions lib/datagrid/columns/column.rb
Expand Up @@ -25,9 +25,9 @@ def html_value(context)
end
end

attr_accessor :grid_class, :options, :data_block, :name, :html_block
attr_accessor :grid_class, :options, :data_block, :name, :html_block, :query

def initialize(grid_class, name, options = {}, &block)
def initialize(grid_class, name, query, options = {}, &block)
self.grid_class = grid_class
self.name = name.to_sym
self.options = options
Expand All @@ -40,6 +40,7 @@ def initialize(grid_class, name, options = {}, &block)
self.html_block = options[:html]
end
end
self.query = query
end

def data_value(model, grid)
Expand All @@ -54,7 +55,7 @@ def label
end

def header
self.options[:header] ||
self.options[:header] ||
I18n.translate(self.name, :scope => "datagrid.#{self.grid_class.param_name}.columns", :default => self.name.to_s.humanize )
end

Expand Down Expand Up @@ -84,17 +85,17 @@ def order_by_value?

def order_desc
return nil unless order
self.options[:order_desc]
self.options[:order_desc]
end

def html?
options[:html] != false
end

def data?
self.data_block != nil
end

def mandatory?
!! options[:mandatory]
end
Expand Down
10 changes: 5 additions & 5 deletions lib/datagrid/core.rb
Expand Up @@ -73,7 +73,7 @@ def initialize(attributes = nil, &block)
end
end

# Returns a hash of grid attributes including filter values
# Returns a hash of grid attributes including filter values
# and ordering values
def attributes
result = {}
Expand Down Expand Up @@ -120,17 +120,17 @@ def paginate(*args, &block) # :nodoc:
end

# Redefines scope at instance level
#
#
# class MyGrid
# scope { Article.order('created_at desc') }
# end
#
#
# grid = MyGrid.new
# grid.scope do |scope|
# scope.where(:author_id => current_user.id)
# end
# grid.assets
# # => SELECT * FROM articles WHERE author_id = ?
# grid.assets
# # => SELECT * FROM articles WHERE author_id = ?
# # ORDER BY created_at desc
#
def scope(&block)
Expand Down
10 changes: 9 additions & 1 deletion lib/datagrid/drivers/abstract_driver.rb
Expand Up @@ -53,7 +53,7 @@ def greater_equal(scope, field, value)
def less_equal(scope, field, value)
raise NotImplementedError
end

def has_column?(scope, column_name)
raise NotImplementedError
end
Expand Down Expand Up @@ -82,6 +82,14 @@ def batch_map(scope, batch_size, &block)
raise NotImplementedError
end

def append_column_queries(assets, columns)
if columns.present?
raise NotImplementedError
else
assets
end
end

protected
def timestamp_class?(klass)
TIMESTAMP_CLASSES.include?(klass)
Expand Down
24 changes: 17 additions & 7 deletions lib/datagrid/drivers/active_record.rb
Expand Up @@ -4,20 +4,20 @@ class ActiveRecord < AbstractDriver

def self.match?(scope)
return false unless defined?(::ActiveRecord)
if scope.is_a?(Class)
if scope.is_a?(Class)
scope.ancestors.include?(::ActiveRecord::Base)
else
scope.is_a?(::ActiveRecord::Relation)
scope.is_a?(::ActiveRecord::Relation)
end
end

def to_scope(scope)
return scope if scope.is_a?(::ActiveRecord::Relation)
# Model class or Active record association
# ActiveRecord association class hides itself under an Array
# ActiveRecord association class hides itself under an Array
# We can only reveal it by checking if it respond to some specific
# to ActiveRecord method like #scoped
if scope.is_a?(Class)
if scope.is_a?(Class)
Rails.version >= "4.0" ? scope.all : scope.scoped({})
elsif scope.respond_to?(:scoped)
scope.scoped
Expand All @@ -26,6 +26,16 @@ def to_scope(scope)
end
end

def append_column_queries(assets, columns)
if columns.present?
if assets.select_values.empty?
assets = assets.select(Arel.respond_to?(:star) ? assets.klass.arel_table[Arel.star] : "#{assets.quoted_table_name}.*")
end
assets = assets.select(*columns)
end
assets
end

def where(scope, attribute, value)
scope.where(attribute => value)
end
Expand Down Expand Up @@ -97,16 +107,16 @@ def batch_map(scope, batch_size, &block)
end
result
end

protected

def prefix_table_name(scope, field)
has_column?(scope, field) ? [scope.table_name, field].join(".") : field
end

def contains_predicate
defined?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) &&
::ActiveRecord::Base.connection.is_a?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) ?
defined?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) &&
::ActiveRecord::Base.connection.is_a?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) ?
'ilike' : 'like'
end

Expand Down
6 changes: 3 additions & 3 deletions lib/datagrid/drivers/array.rb
Expand Up @@ -34,14 +34,14 @@ def default_order(scope, column_name)

def greater_equal(scope, field, value)
scope.select do |object|
compare_value = object.send(field)
compare_value = object.send(field)
compare_value.respond_to?(:>=) && compare_value >= value
end
end

def less_equal(scope, field, value)
scope.select do |object|
compare_value = object.send(field)
compare_value = object.send(field)
compare_value.respond_to?(:<=) && compare_value <= value
end
end
Expand All @@ -51,7 +51,7 @@ def has_column?(scope, column_name)
end

def is_timestamp?(scope, column_name)
has_column?(scope, column_name) &&
has_column?(scope, column_name) &&
timestamp_class?(scope.first.send(column_name).class)
end

Expand Down
2 changes: 1 addition & 1 deletion lib/datagrid/drivers/mongo_mapper.rb
Expand Up @@ -4,7 +4,7 @@ class MongoMapper < AbstractDriver

def self.match?(scope)
return false unless defined?(::MongoMapper)
if scope.is_a?(Class)
if scope.is_a?(Class)
scope.ancestors.include?(::MongoMapper::Document)
else
scope.is_a?(::Plucky::Query)
Expand Down
2 changes: 1 addition & 1 deletion lib/datagrid/drivers/mongoid.rb
Expand Up @@ -4,7 +4,7 @@ class Mongoid < AbstractDriver

def self.match?(scope)
return false unless defined?(::Mongoid)
if scope.is_a?(Class)
if scope.is_a?(Class)
scope.ancestors.include?(::Mongoid::Document)
else
scope.is_a?(::Mongoid::Criteria)
Expand Down
2 changes: 1 addition & 1 deletion lib/datagrid/filters/base_filter.rb
Expand Up @@ -103,7 +103,7 @@ def default_filter(value, scope, grid)
def format(value)
value.nil? ? nil : value.to_s
end

def dummy?
options[:dummy]
end
Expand Down
14 changes: 12 additions & 2 deletions spec/datagrid/columns_spec.rb
Expand Up @@ -31,7 +31,7 @@
it "should generate header" do
subject.header.should == ["Shipping date", "Group", "Name", "Access level", "Pet"]
end

it "should return html_columns" do
report = test_report do
scope {Entry}
Expand Down Expand Up @@ -82,6 +82,16 @@
it "should support csv export options" do
subject.to_csv(:col_sep => ";").should == "Shipping date;Group;Name;Access level;Pet\n#{date};Pop;Star;admin;ROTTWEILER\n"
end

it "should support defining a query for a column" do
report = test_report do
scope {Entry}
filter(:name)
column(:id)
column(:sum_group_id, 'sum(group_id) sum_group_id')
end
report.assets.first.sum_group_id.should == group.id
end
end

it "should support columns with model and report arguments" do
Expand Down Expand Up @@ -215,7 +225,7 @@
column(:name) do
name.capitalize
end
column(:actions, html: true) do
column(:actions, html: true) do
"some link here"
end
end
Expand Down
7 changes: 6 additions & 1 deletion spec/datagrid/drivers/active_record_spec.rb
Expand Up @@ -15,5 +15,10 @@
subject.to_scope(Entry.limit(5)).should be_a(ActiveRecord::Relation)
subject.to_scope(Group.create!.entries).should be_a(ActiveRecord::Relation)
end


it "should support append_column_queries" do
scope = subject.append_column_queries(Entry.scoped, ['sum(entries.group_id) sum_group_id'])
scope.select_values.length.should == 2
scope.select_values.should == ["#{Entry.quoted_table_name}.*", 'sum(entries.group_id) sum_group_id']
end
end

0 comments on commit e647a20

Please sign in to comment.