Permalink
Browse files

Merge branch 'sql_select_option' of https://github.com/iwiznia/datagrid

… into iwiznia-sql_select_option
  • Loading branch information...
2 parents 3f14535 + 15d9d66 commit e647a20d987f32d41c8f4163fbb85c53385a0977 @bogdan committed Mar 7, 2014
View
@@ -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"
View
@@ -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
@@ -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
@@ -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
@@ -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:
@@ -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:
@@ -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
@@ -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
@@ -326,7 +336,7 @@ def format(value, &block)
# Allows to access column values
#
# Example:
- #
+ #
# class MyGrid
# scope { User }
# column(:id)
@@ -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
@@ -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)
@@ -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
@@ -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
View
@@ -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 = {}
@@ -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)
@@ -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
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
@@ -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)
@@ -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
@@ -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}
@@ -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
@@ -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
@@ -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.