irie 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'bundler'
2
+ require 'rake/testtask'
3
+ require 'appraisal'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs = ['lib','test']
9
+ t.test_files = Dir.glob(["test/**/*_test.rb"])
10
+ t.verbose = true
11
+ end
12
+
13
+ task :default => [:test]
14
+ task :spec => [:test]
15
+
16
+ desc "Setup Appraisal."
17
+ task 'appraisal:setup' do
18
+ Rake::Task['appraisal:cleanup'].invoke
19
+ Rake::Task['appraisal:gemfiles'].invoke
20
+ Rake::Task['appraisal:install'].invoke
21
+ end
data/lib/irie.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'irie/version'
2
+ require 'irie/configuration_error'
3
+ require 'irie/config'
4
+ require 'irie/class_methods'
5
+ require 'irie/param_aliases'
6
+ require 'irie/extensions/autorender_count'
7
+ require 'irie/extensions/params_to_joins'
8
+ require 'irie/extensions/conversion/nil_params'
9
+ require 'irie/extensions/count'
10
+ require 'irie/extensions/index_query'
11
+ require 'irie/extensions/limit'
12
+ require 'irie/extensions/offset'
13
+ require 'irie/extensions/order'
14
+ require 'irie/extensions/paging/autorender_page_count'
15
+ require 'irie/extensions/paging'
16
+ require 'irie/extensions/param_filters'
17
+ require 'irie/extensions/query_filter'
18
+ require 'irie/extensions/query_includes'
19
+ require 'irie/extensions/smart_layout'
20
+
21
+ class ::ActionController::Base
22
+ extend ::Irie::ClassMethods
23
+ end
@@ -0,0 +1,61 @@
1
+ module Irie
2
+ module ClassMethods
3
+
4
+ protected
5
+
6
+ def extensions(*extension_syms)
7
+ ::Irie::CONTROLLER_OPTIONS.each do |key|
8
+ class_attribute key, instance_writer: true unless respond_to?(key)
9
+ # doing unless self.send(key.to_sym) to ensure parent override of defaults is kept
10
+ self.send("#{key}=".to_sym, ::Irie.send(key)) unless self.send(key.to_sym)
11
+ end
12
+
13
+ #raise "self.inherited_resources_defined_actions = #{inherited_resources_defined_actions.inspect}\n\ninstance_methods = #{self.instance_methods.collect(&:to_s).sort.join(', ')}\n\nself.autoincludes = #{self.autoincludes.inspect}" if $gary
14
+
15
+ self.autoincludes.keys.each do |action_sym|
16
+ if self.instance_methods.include?(action_sym.to_sym)
17
+ autoloading_extensions = self.autoincludes[action_sym]
18
+ if autoloading_extensions && autoloading_extensions.size > 0
19
+ extension_syms += autoloading_extensions
20
+ end
21
+ end
22
+ end
23
+
24
+ extensions! extension_syms
25
+ end
26
+
27
+ # Load specified extensions in the order defined by self.extension_include_order.
28
+ def extensions!(*extension_syms)
29
+ return extension_syms if extension_syms.length == 0
30
+
31
+ extension_syms = extension_syms.flatten.collect {|es| es.to_sym}.compact
32
+
33
+ if extension_syms.include?(:all)
34
+ ordered_extension_syms = self.extension_include_order.dup
35
+ else
36
+ extensions_without_defined_order = extension_syms.uniq - self.extension_include_order.uniq
37
+ if extensions_without_defined_order.length > 0
38
+ raise ::Irie::ConfigurationError.new "The following must be added to the self.extension_include_order array in Irie configuration: #{extensions_without_defined_order.collect(&:inspect).join(', ')}"
39
+ else
40
+ ordered_extension_syms = self.extension_include_order & extension_syms
41
+ end
42
+ end
43
+
44
+ # load requested extensions
45
+ ordered_extension_syms.each do |arg_sym|
46
+ if module_class_name = self.available_extensions[arg_sym]
47
+ begin
48
+ logger.debug("Irie::ClassMethods.extensions! #{self} including #{module_class_name}") if ::Irie.debug?
49
+ include module_class_name.constantize
50
+ rescue NameError => e
51
+ raise ::Irie::ConfigurationError.new "Failed to constantize '#{module_class_name}' with extension key #{arg_sym.inspect} in self.available_extensions. Error: \n#{e.message}\n#{e.backtrace.join("\n")}"
52
+ end
53
+ else
54
+ raise ::Irie::ConfigurationError.new "#{arg_sym.inspect} isn't defined in self.available_extensions"
55
+ end
56
+ end
57
+
58
+ extension_syms
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,131 @@
1
+ module Irie
2
+
3
+ CONTROLLER_OPTIONS = [
4
+ :autoincludes,
5
+ :available_actions,
6
+ :available_extensions,
7
+ :can_filter_by_default_using,
8
+ :debug,
9
+ :function_param_names,
10
+ :id_is_primary_key_param,
11
+ :number_of_records_in_a_page,
12
+ :predicate_prefix,
13
+ :update_should_return_entity,
14
+ :extension_include_order
15
+ ]
16
+
17
+ class << self
18
+ CONTROLLER_OPTIONS.each{|name|attr_accessor name; define_method("#{name}?") { !!public_send(name) } }
19
+ def configure(&blk); class_eval(&blk); end
20
+
21
+ # Adds to extension_include_order and extension_include_order, e.g.
22
+ # ::Irie.register_extension :boolean_params, '::Focal::Irie::BooleanParams'
23
+ # Is equivalent to:
24
+ # ::Irie.available_extensions[:boolean_params] = '::Focal::Irie::BooleanParams'
25
+ # ::Irie.extension_include_order << extension_sym
26
+ # Allowed options are `:include`, `:after`, and `:before`. Some examples:
27
+ # ::Irie.register_extension :boolean_params, '::Example::BooleanParams', include: :last # the default, so unnecessary
28
+ # ::Irie.register_extension :boolean_params, '::Example::BooleanParams', include: :first # includes module after all others registered at this point
29
+ # ::Irie.register_extension :boolean_params, '::Example::BooleanParams', after: :nil_params # includes after :nil_params
30
+ # ::Irie.register_extension :boolean_params, '::Example::BooleanParams', before: :nil_params # includes after :nil_params
31
+ def register_extension(extension_sym, extension_class_name, options = {})
32
+ raise ::Irie::ConfigurationError.new "Irie.register_extension must provide an extension symbol as the first argument" unless extension_sym
33
+ raise ::Irie::ConfigurationError.new "Irie.register_extension must provide an extension class name (string) as the second argument" unless extension_sym
34
+ raise ::Irie::ConfigurationError.new "Irie.register_extension can only provide a single option: :include, :after, or :before" if options.size > 1
35
+ initial_opts = options.dup
36
+ include_opt, after_opt, before_opt = *[:include, :after, :before].collect{|opt_name| options.delete(opt_name)}
37
+ include_opt = :last unless include_opt || after_opt || before_opt
38
+ raise ::Irie::ConfigurationError.new "Irie.register_extension unrecognized options: #{options.inspect}" if options.size > 0
39
+
40
+ ::Irie.extension_include_order.delete(extension_sym)
41
+
42
+ before_or_after_opt_value = before_opt || after_opt
43
+ if include_opt == :first
44
+ ::Irie.extension_include_order.unshift extension_sym
45
+ elsif include_opt == :last
46
+ ::Irie.extension_include_order << extension_sym
47
+ elsif before_or_after_opt_value
48
+ ind = ::Irie.extension_include_order.index(before_or_after_opt_value)
49
+ raise ::Irie::ConfigurationError.new "Irie.register_extension cannot insert #{before_opt ? 'before' : 'after'} #{before_or_after_opt_value.inspect}, because #{before_or_after_opt_value.inspect} was not yet registered. A possible workaround for deferred registration may be to require the code that does the prerequisite registration."
50
+ ::Irie.extension_include_order.insert(ind + (after_opt ? 1 : 0), extension_sym)
51
+ else
52
+ raise ::Irie::ConfigurationError.new "Irie.register_extension unsupported options: #{initial_opts.inspect}"
53
+ end
54
+
55
+ ::Irie.available_extensions[extension_sym] = extension_class_name
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ ::Irie.configure do
62
+
63
+ # Default for :using in can_filter_by.
64
+ self.can_filter_by_default_using = [:eq]
65
+
66
+ # Use one or more alternate request parameter names for functions, e.g.
67
+ # `self.function_param_names = {distinct: :very_distinct, limit: [:limit, :limita]}`
68
+ self.function_param_names = {}
69
+
70
+ # Delimiter for ARel predicate in the request parameter name.
71
+ self.predicate_prefix = '.'
72
+
73
+ # You'd set this to false if id is used for something else other than primary key.
74
+ self.id_is_primary_key_param = true
75
+
76
+ # Used when paging is enabled.
77
+ self.number_of_records_in_a_page = 15
78
+
79
+ # When you include the defined action module, it includes the associated modules.
80
+ # If value or value array contains symbol it will look up symbol in
81
+ # ::Irie.available_extensions in the controller (which is defaulted to
82
+ # `::Irie.available_extensions`). If value is String will assume String is the
83
+ # fully-qualified module name to include, e.g. `index: '::My::Module'`, If constant,
84
+ # it will just include constant (module), e.g. `index: ::My::Module`.
85
+ self.autoincludes = {
86
+ create: [:smart_layout, :query_includes],
87
+ destroy: [:smart_layout, :query_includes],
88
+ edit: [:smart_layout, :query_includes],
89
+ index: [:smart_layout, :index_query, :order, :param_filters, :params_to_joins, :query_filter, :query_includes],
90
+ new: [:smart_layout],
91
+ show: [:smart_layout, :query_includes],
92
+ update: [:smart_layout, :query_includes]
93
+ }
94
+
95
+ # This ensures the correct order of includes via the extensions method. It bears no
96
+ # relevance on whether the include is included or not. Since many includes call
97
+ # super in their methods, the order may seem partially reversed, but this is the actual
98
+ # include order. If extensions are not listed here, they will not be included by
99
+ # the extensions method.
100
+ self.extension_include_order = [
101
+ :smart_layout,
102
+ :autorender_page_count,
103
+ :autorender_count,
104
+ :count,
105
+ :paging,
106
+ :order,
107
+ :offset,
108
+ :limit,
109
+ :param_filters,
110
+ :query_filter,
111
+ :params_to_joins,
112
+ :query_includes,
113
+ :index_query,
114
+ :nil_params
115
+ ]
116
+
117
+ # By default, it sets the instance variable, but does not return entity if request
118
+ # update, e.g. in JSON format.
119
+ self.update_should_return_entity = false
120
+
121
+ # Extensions to actions that you can implement in the controller via
122
+ # `include_extensions`, e.g. `include_extensions :count, :paging`
123
+ # Each is added as each file is required when the gem is loaded, so for a full list,
124
+ # check `::Irie.available_extensions` in rails console.
125
+ # You shouldn't have to worry about configuring this typically.
126
+ self.available_extensions = {}
127
+
128
+ # If true, will logger.debug in instance methods to help with execution tracing at
129
+ # runtime.
130
+ self.debug = false
131
+ end
@@ -0,0 +1,4 @@
1
+ module Irie
2
+ class ConfigurationError < ::StandardError
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ module Irie
2
+ module Extensions
3
+ # Standard rendering of index page count in all formats except html so you don't need views for them.
4
+ module AutorenderCount
5
+ extend ::ActiveSupport::Concern
6
+ ::Irie.available_extensions[:autorender_count] = '::' + AutorenderCount.name
7
+
8
+ protected
9
+
10
+ def autorender_count(options={}, &block)
11
+ logger.debug("Irie::Extensions::AutorenderCount.autorender_count") if ::Irie.debug?
12
+ render request.format.symbol => { count: @count }, status: 200, layout: false
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ module Irie
2
+ module Extensions
3
+ module Conversion
4
+ # Converts the following filter request param values to nil: 'NULL', 'null', 'nil'
5
+ module NilParams
6
+ extend ::ActiveSupport::Concern
7
+ ::Irie.available_extensions[:nil_params] = '::' + NilParams.name
8
+
9
+ NILS = ['NULL'.freeze, 'null'.freeze, 'nil'.freeze].to_set
10
+
11
+ protected
12
+
13
+ # Converts request param value(s) 'NULL', 'null', and 'nil' to nil.
14
+ def convert_param(param_name, param_value_or_values)
15
+ logger.debug("Irie::Extensions::Conversion::NilParams.convert_param(#{param_name.inspect}, #{param_value_or_values.inspect})") if ::Irie.debug?
16
+ param_value_or_values = super if defined?(super)
17
+ if param_value_or_values.is_a? Array
18
+ param_value_or_values.map{|v| v && NILS.include?(v) ? nil : v }
19
+ else
20
+ param_value_or_values && NILS.include?(param_value_or_values) ? nil : param_value_or_values
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ module Irie
2
+ module Extensions
3
+ # Allowing setting `@count` with the count of the records in the index query.
4
+ module Count
5
+ extend ::ActiveSupport::Concern
6
+ ::Irie.available_extensions[:count] = '::' + Count.name
7
+
8
+ included do
9
+ include ::Irie::ParamAliases
10
+ end
11
+
12
+ def index(options={}, &block)
13
+ logger.debug("Irie::Extensions::Count.index") if ::Irie.debug?
14
+ return super(options, &block) unless aliased_param_present?(:count)
15
+ @count = collection.count
16
+ return respond_to?(:autorender_count, true) ? autorender_count(options, &block) : super(options, &block)
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,48 @@
1
+ module Irie
2
+ module Extensions
3
+ # Allows use of a lambda for the index query.
4
+ module IndexQuery
5
+ extend ::ActiveSupport::Concern
6
+ ::Irie.available_extensions[:index_query] = '::' + IndexQuery.name
7
+
8
+ included do
9
+ include ::Irie::ParamAliases
10
+
11
+ class_attribute(:custom_index_query, instance_writer: true) unless self.respond_to? :custom_index_query
12
+
13
+ self.custom_index_query ||= nil
14
+ end
15
+
16
+ module ClassMethods
17
+
18
+ protected
19
+
20
+ # Specify a custom query/additional filtering of the collection, e.g.
21
+ # index_query ->(q) { q.where(:status_code => 'green') }
22
+ # You could also completely overwrite the collection which would lead
23
+ # to certain peril, as you would need to then ensure all filters
24
+ # are included in the correct order to be executed after the query.
25
+ def index_query(query)
26
+ self.custom_index_query = query
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ def collection
33
+ logger.debug("Irie::Extensions::IndexQuery.collection") if ::Irie.debug?
34
+ object = super
35
+ if self.custom_index_query
36
+ # convert to relation if model class because proc expects a relation
37
+ object = object.all unless object.is_a?(ActiveRecord::Relation)
38
+ a = object.to_s
39
+ object = self.custom_index_query.call(object)
40
+ end
41
+
42
+ logger.debug("Irie::Extensions::IndexQuery.collection: relation.to_sql so far: #{object.to_sql}") if ::Irie.debug? && object.respond_to?(:to_sql)
43
+
44
+ set_collection_ivar object
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,25 @@
1
+ module Irie
2
+ module Extensions
3
+ # Allows limiting of the number of records returned by the index query.
4
+ module Limit
5
+ extend ::ActiveSupport::Concern
6
+ ::Irie.available_extensions[:limit] = '::' + Limit.name
7
+
8
+ included do
9
+ include ::Irie::ParamAliases
10
+ end
11
+
12
+ protected
13
+
14
+ def collection
15
+ logger.debug("Irie::Extensions::Limit.collection") if ::Irie.debug?
16
+ object = super
17
+ aliased_params(:limit).each {|param_value| object = object.limit(param_value)}
18
+
19
+ logger.debug("Irie::Extensions::Limit.collection: relation.to_sql so far: #{object.to_sql}") if ::Irie.debug? && object.respond_to?(:to_sql)
20
+
21
+ set_collection_ivar object
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Irie
2
+ module Extensions
3
+ # Allowing offsetting (skipping) records that would be returned by the index query.
4
+ module Offset
5
+ extend ::ActiveSupport::Concern
6
+ ::Irie.available_extensions[:offset] = '::' + Offset.name
7
+
8
+ included do
9
+ include ::Irie::ParamAliases
10
+ end
11
+
12
+ protected
13
+
14
+ def collection
15
+ logger.debug("Irie::Extensions::Offset.collection") if ::Irie.debug?
16
+ object = super
17
+ aliased_params(:offset).each {|param_value| object = object.offset(param_value)}
18
+
19
+ logger.debug("Irie::Extensions::Offset.collection: relation.to_sql so far: #{object.to_sql}") if ::Irie.debug? && object.respond_to?(:to_sql)
20
+
21
+ set_collection_ivar object
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,153 @@
1
+ module Irie
2
+ module Extensions
3
+ # Allows setting of attributes that can be used for ordering, and allows default ordering to be set.
4
+ module Order
5
+ extend ::ActiveSupport::Concern
6
+ ::Irie.available_extensions[:order] = '::' + Order.name
7
+
8
+ included do
9
+ include ::Irie::ParamAliases
10
+
11
+ class_attribute(:can_be_ordered_by, instance_writer: true) unless self.respond_to? :can_be_ordered_by
12
+ class_attribute(:default_ordered_by, instance_writer: true) unless self.respond_to? :default_ordered_by
13
+
14
+ self.can_be_ordered_by ||= []
15
+ self.default_ordered_by ||= {}
16
+ end
17
+
18
+ module ClassMethods
19
+
20
+ protected
21
+
22
+ # A whitelist of orderable attributes.
23
+ #
24
+ # If no options are provided or the :using option is provided, defines attributes that are orderable through the operation(s) already defined in can_filter_by_default_using, or can specify attributes:
25
+ # can_order_by :attr_name_1, :attr_name_2
26
+ # So that you could call: http://.../foobars?order=attr_name_1,-attr_name_2
27
+ # to order by attr_name_1 asc then attr_name_2 desc.
28
+ #
29
+ # When :through is specified, it will take the array supplied to through as 0 to many model names following by an attribute name. It will follow through
30
+ # each association until it gets to the attribute to filter by that via ARel joins, e.g. if the model Foobar has an association to :foo, and on the Foo model there is an assocation
31
+ # to :bar, and you want to order by bar.name (foobar.foo.bar.name):
32
+ # can_order_by :my_param_name, through: {foo: {bar: :name}}
33
+ def can_order_by(*args)
34
+ options = args.extract_options!
35
+
36
+ opt_through = options.delete(:through)
37
+ raise ::Irie::ConfigurationError.new "options #{options.inspect} not supported by can_order_by" if options.present?
38
+
39
+ self.can_be_ordered_by = self.can_be_ordered_by.deep_dup
40
+
41
+ args.each do |arg|
42
+ # store as strings because we have to do a string comparison later to avoid req param symbol attack
43
+ self.can_be_ordered_by << arg.to_s unless self.can_be_ordered_by.include?(arg.to_s)
44
+ end
45
+
46
+ if opt_through
47
+ raise ::Irie::ConfigurationError.new "Must use extension :params_to_joins to use can_order_by :through" unless ancestors.include?(::Irie::Extensions::ParamsToJoins)
48
+ args.each do |through_key|
49
+ # note: handles cloning, etc.
50
+ self.define_params(through_key => opt_through)
51
+ end
52
+ end
53
+ end
54
+
55
+ # Takes an string, symbol, array, hash to indicate order. If not a hash, assumes is ascending. Is cumulative and order defines order of sorting, e.g:
56
+ # #would order by foo_color attribute ascending
57
+ # default_order_by :foo_color
58
+ # or
59
+ # default_order_by foo_date: :asc, bar_date: :desc
60
+ # or you could be completely insane and do:
61
+ # default_order_by {foo_date: :asc}, :foo_color, 'foo_name', another_date: :asc, bar_date: :desc
62
+ def default_order_by(*args)
63
+ options = args.extract_options!
64
+
65
+ self.default_ordered_by = self.default_ordered_by.deep_dup
66
+
67
+ # hash is ordered in recent versions of Ruby we support
68
+ args.flatten.each do |item|
69
+ case item
70
+ when Hash
71
+ # merge hash converting keys to string
72
+ item.each {|param_name, direction| self.default_ordered_by[param_name.to_s] = direction}
73
+ when String, Symbol
74
+ self.default_ordered_by[item.to_s] = :asc
75
+ else
76
+ raise ::Irie::ConfigurationError.new "Can't default_order_by #{item}"
77
+ end
78
+ end
79
+
80
+ # merge hash converting keys to string
81
+ options.each {|param_name, direction| self.default_ordered_by[param_name.to_s] = direction}
82
+ end
83
+ end
84
+
85
+ protected
86
+
87
+ def collection
88
+ logger.debug("Irie::Extensions::Order.collection") if ::Irie.debug?
89
+ object = super
90
+
91
+ already_ordered_by = []
92
+ aliased_params(:order).collect{|p| p.split(',')}.flatten.collect(&:strip).each do |split_param_name|
93
+
94
+ # not using convert_param here.
95
+ # (these should be attribute names, not attribute values.)
96
+
97
+ # remove optional preceding - or + to act as directional
98
+ direction = :asc
99
+ if split_param_name[0] == '-'
100
+ direction = :desc
101
+ split_param_name = split_param_name.reverse.chomp('-').reverse
102
+ elsif split_param_name[0] == '+'
103
+ split_param_name = split_param_name.reverse.chomp('+').reverse
104
+ end
105
+
106
+ # support for named_params/:through renaming of param name
107
+ attr_sym = attr_sym_for_param(split_param_name)
108
+
109
+ # order of logic here is important:
110
+ # do not to_sym the partial param value until passes whitelist to avoid symbol attack.
111
+ # be sure to pass in the same param name as the default param it is trying to override,
112
+ # if there is one.
113
+ if self.can_be_ordered_by.include?(split_param_name) && !already_ordered_by.include?(attr_sym)
114
+ join_to_apply = join_for_param(split_param_name)
115
+ object = object.joins(join_to_apply) if join_to_apply
116
+ arel_table_column = get_arel_table(split_param_name)[attr_sym]
117
+ raise ::Irie::ConfigurationError.new "can_order_by/define_params config problem: could not find arel table/column for param name #{split_param_name.inspect} and/or attr_sym #{attr_sym.inspect}" unless arel_table_column
118
+ #TODO: is there a better way? not sure how else to order on joined table columns- no example
119
+ sql_fragment = "#{arel_table_column.relation.name}.#{arel_table_column.name}#{direction == :desc ? ' DESC' : ''}"
120
+ # Important note! the behavior of multiple `order`'s' got reversed between Rails 4.0.0 and 4.0.1:
121
+ # http://weblog.rubyonrails.org/2013/11/1/Rails-4-0-1-has-been-released/
122
+ object = object.order(sql_fragment)
123
+ already_ordered_by << attr_sym
124
+ end
125
+
126
+ set_collection_ivar object
127
+
128
+ object
129
+ end
130
+
131
+ self.default_ordered_by.each do |split_param_name, direction|
132
+ unless already_ordered_by.include?(split_param_name)
133
+ attr_sym = attr_sym_for_param(split_param_name)
134
+ join_to_apply = join_for_param(split_param_name)
135
+ object = object.joins(join_to_apply) if join_to_apply
136
+ arel_table_column = get_arel_table(split_param_name)[attr_sym]
137
+ raise ::Irie::ConfigurationError.new "default_order_by/define_params config problem: could not find arel table/column for param name #{split_param_name.inspect} and/or attr_sym #{attr_sym.inspect}" unless arel_table_column
138
+ #TODO: is there a better way? not sure how else to order on joined table columns- no example
139
+ sql_fragment = "#{arel_table_column.relation.name}.#{arel_table_column.name}#{direction == :desc ? ' DESC' : ''}"
140
+ object = object.order(sql_fragment)
141
+ already_ordered_by << attr_sym
142
+ end
143
+ end
144
+
145
+ logger.debug("Irie::Extensions::Order.collection: relation.to_sql so far: #{object.to_sql}") if ::Irie.debug? && object.respond_to?(:to_sql)
146
+
147
+ set_collection_ivar object
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+