diff --git a/app/controllers/concerns/data_table.rb b/app/controllers/concerns/data_table.rb index d8bac14..abdfb4d 100644 --- a/app/controllers/concerns/data_table.rb +++ b/app/controllers/concerns/data_table.rb @@ -1,52 +1,52 @@ +class DataTable -module DataTable - extend ActiveSupport::Concern + def initialize user, params + @user = user + @params = sanitize_params(params) + @transactions = TransactionsQuery.new(@user) + @table = @transactions.arel_table + end - def apply_filter(user, params) - params = sanitize_params(params) - selection = user.transactions + def json + response_json(@params[:draw], data) + end - # filter time - lower = params[:columns][:time][:lower] - upper = params[:columns][:time][:upper] - if lower and upper - selection = selection.where(created_at: lower..upper) - elsif lower - selection = selection.where('created_at > :lower', lower: lower) - elsif upper - selection = selection.where('created_at < :upper', upper: upper) - end + def data + ActiveRecord::Base.connection.execute(self.query.to_sql) + end - # filter amount TODO this filters on absolute value - lower = params[:columns][:amount][:lower] - upper = params[:columns][:amount][:upper] - if lower and upper - selection = selection.where(amount: lower..upper) - elsif lower - selection = selection.where('amount > :lower', lower: lower) - elsif upper - selection = selection.where('amount < :upper', upper: upper) - end + def query + pred = predicates + q = @transactions.query + q = q.where(pred) if pred + q + end - # filter peer # TODO peer.name - peer = params[:columns][:peer][:value] - if peer - selection = selection.where("(debtor_id = :id AND creditor_id LIKE :peer) OR (creditor_id = :id AND debtor_id LIKE :peer)", id: user.id, peer: "%#{peer}%") - end + def predicates + [ *range_predicates(:amount), + *range_predicates(:time), + eq_predicate(:peer), + eq_predicate(:issuer), + like_predicate(:message) + ].compact.inject { |l, r| l.and(r) } + end - # filter issuer # TODO issuer.name - issuer = params[:columns][:issuer][:value] - if issuer - selection = selection.where("issuer_id LIKE :re", re: "%#{issuer}%") - end + def range_predicates name + col = @params[:columns][name] + [ + (@table[:name].gteq(col[:lower]) if col[:lower]), + (@table[:name].lteq(col[:upper]) if col[:upper]) + ] + end - # filter message - message = params[:columns][:message][:value] - if message - selection = selection.where("message LIKE :re", re: "%#{message}%") - end + def eq_predicate name + value = @params[:columns][:name][:value] + @table[:name].eq(value) if value + end - selection_to_json(user, params[:draw], selection) + def like_predicate name + value = @params[:columns][:name][:value] + @table[:name].matches("%#{value}%") if value end private @@ -78,18 +78,19 @@ module DataTable return clean end - def selection_to_json(user, draw, selection) + def response_json(draw, selection) { draw: draw, - recordsTotal: user.transactions.count, + recordsTotal: @user.transactions.count, recordsFiltered: selection.count, - data: selection.offset(params[:start]).take(params[:length]).map { |transaction| { - time: transaction.created_at, - amount: transaction.signed_amount_for(user), - peer: transaction.peer_of(user).try(:name), - issuer: transaction.issuer.name, - message: transaction.message, - }} + #data: selection.offset(params[:start]).take(params[:length]).map { |transaction| { + #time: transaction.created_at, + #amount: transaction.signed_amount_for(user), + #peer: transaction.peer_of(user).try(:name), + #issuer: transaction.issuer.name, + #message: transaction.message, + #}} + data: selection } end end diff --git a/app/controllers/concerns/transactions_query.rb b/app/controllers/concerns/transactions_query.rb new file mode 100644 index 0000000..8bfd543 --- /dev/null +++ b/app/controllers/concerns/transactions_query.rb @@ -0,0 +1,74 @@ +class TransactionsQuery + attr_reader :arel_table + + def initialize user + @user = user + @transactions = Arel::Table.new(:transactions) + @perspectived = Arel::Table.new(:perspectived_transactions) + @peers = Arel::Table.new(:users).alias('peers') + @arel_table = Arel::Table.new(@user.name.concat('_transactions')) + end + + def query + Arel::SelectManager.new(ActiveRecord::Base) + .from(arel) + .project(Arel.star) + end + + def arel + Arel::Nodes::TableAlias.new( + issued_by(User).union(:all, issued_by(Client)), + arel_table.name + ) + end + + def issued_by klass + issuers = klass.arel_table.alias('issuer') + Arel::SelectManager.new(ActiveRecord::Base) + .from(transactions) + .join(@peers).on(@peers[:id].eq(@perspectived[:peer_id])) + .join(issuers).on(issuers[:id].eq(@perspectived[:issuer_id])) + .where(@perspectived[:issuer_type].eq(klass.name)) + .project( + @perspectived[:amount], + @perspectived[:time], + @perspectived[:message], + @peers[:name].as('peer'), + issuers[:name].as('issuer') + ) + end + + def transactions + Arel::Nodes::TableAlias.new( + incoming.union(:all, outgoing), + @perspectived.name + ) + end + + def outgoing + @transactions + .where(@transactions[:debtor_id].eq(@user.id)) + .project( + (@transactions[:amount]*Arel::Nodes::SqlLiteral.new("-1")).as('amount'), + @transactions[:creditor_id].as('peer_id'), + @transactions[:created_at].as('time'), + @transactions[:issuer_id], + @transactions[:issuer_type], + @transactions[:message] + ) + end + + def incoming + @user.incoming_transactions.arel + @transactions + .where(@transactions[:debtor_id].eq(@user.id)) + .project( + @transactions[:amount], + @transactions[:debtor_id].as('peer_id'), + @transactions[:created_at].as('time'), + @transactions[:issuer_id], + @transactions[:issuer_type], + @transactions[:message] + ) + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 6dcaeb3..d1e5a0a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,13 +1,14 @@ class UsersController < ApplicationController load_and_authorize_resource - include DataTable - def show @user = User.find(params[:id]) respond_to do |format| format.html - format.json { render json: apply_filter(@user, params) } + format.json do + datatable = DataTable.new(@user, params) + render json: datatable.json + end end end