nobrainer 0.17.0 → 0.18.1
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.
- checksums.yaml +4 -4
- data/lib/no_brainer/config.rb +52 -28
- data/lib/no_brainer/connection.rb +1 -0
- data/lib/no_brainer/connection_manager.rb +1 -1
- data/lib/no_brainer/criteria/after_find.rb +3 -11
- data/lib/no_brainer/criteria/aggregate.rb +8 -5
- data/lib/no_brainer/criteria/cache.rb +7 -7
- data/lib/no_brainer/criteria/core.rb +56 -16
- data/lib/no_brainer/criteria/count.rb +1 -1
- data/lib/no_brainer/criteria/delete.rb +1 -1
- data/lib/no_brainer/criteria/extend.rb +19 -0
- data/lib/no_brainer/criteria/first.rb +1 -1
- data/lib/no_brainer/criteria/index.rb +7 -13
- data/lib/no_brainer/criteria/limit.rb +5 -13
- data/lib/no_brainer/criteria/order_by.rb +22 -41
- data/lib/no_brainer/criteria/pluck.rb +17 -23
- data/lib/no_brainer/criteria/preload.rb +9 -15
- data/lib/no_brainer/criteria/raw.rb +5 -11
- data/lib/no_brainer/criteria/scope.rb +9 -15
- data/lib/no_brainer/criteria/update.rb +3 -3
- data/lib/no_brainer/criteria/where.rb +33 -56
- data/lib/no_brainer/criteria.rb +1 -1
- data/lib/no_brainer/document/association/belongs_to.rb +6 -6
- data/lib/no_brainer/document/association/core.rb +11 -11
- data/lib/no_brainer/document/association/eager_loader.rb +4 -3
- data/lib/no_brainer/document/association/has_many.rb +7 -7
- data/lib/no_brainer/document/association/has_many_through.rb +1 -1
- data/lib/no_brainer/document/association/has_one.rb +1 -1
- data/lib/no_brainer/document/association.rb +5 -5
- data/lib/no_brainer/document/atomic_ops.rb +26 -18
- data/lib/no_brainer/document/attributes.rb +9 -8
- data/lib/no_brainer/document/callbacks.rb +1 -1
- data/lib/no_brainer/document/core.rb +1 -1
- data/lib/no_brainer/document/criteria.rb +9 -2
- data/lib/no_brainer/document/dirty.rb +1 -3
- data/lib/no_brainer/document/index/index.rb +83 -0
- data/lib/no_brainer/document/index/meta_store.rb +31 -0
- data/lib/no_brainer/document/index/synchronizer.rb +68 -0
- data/lib/no_brainer/document/index.rb +13 -79
- data/lib/no_brainer/document/lazy_fetch.rb +5 -5
- data/lib/no_brainer/document/persistance.rb +27 -7
- data/lib/no_brainer/document/polymorphic.rb +1 -1
- data/lib/no_brainer/document/types.rb +6 -4
- data/lib/no_brainer/document/uniqueness.rb +3 -3
- data/lib/no_brainer/document/validation.rb +13 -4
- data/lib/no_brainer/fork.rb +1 -0
- data/lib/no_brainer/query_runner/database_on_demand.rb +6 -5
- data/lib/no_brainer/query_runner/logger.rb +10 -6
- data/lib/no_brainer/query_runner/missing_index.rb +5 -4
- data/lib/no_brainer/query_runner/reconnect.rb +20 -17
- data/lib/no_brainer/query_runner/run_options.rb +3 -0
- data/lib/no_brainer/query_runner/table_on_demand.rb +11 -8
- data/lib/no_brainer/railtie/database.rake +12 -12
- data/lib/no_brainer/railtie.rb +5 -5
- data/lib/no_brainer/rql.rb +9 -0
- data/lib/nobrainer.rb +5 -3
- metadata +8 -8
- data/lib/no_brainer/index_manager.rb +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1d6a500e008eff372e800b011e917d121c07e00a
|
|
4
|
+
data.tar.gz: e43be89ac4963422c26559727c9a68011ee25f8a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 56015e819aba3537ab60ea5f039d4b391ec803cc61b5bf246e44da642787e62606e9c02339b17f05976d5428ec3e4fa0e335cb51694b840bd2ba7821b6d66b20
|
|
7
|
+
data.tar.gz: d4b78185fed14f5baa28165462a4088096da640d93c0dfb3f20c851e968deceb1f48f13602850075a7eea2408fa23e52c2694d140bea1c9a937d7d49d5084f53
|
data/lib/no_brainer/config.rb
CHANGED
|
@@ -1,36 +1,49 @@
|
|
|
1
1
|
require 'logger'
|
|
2
2
|
|
|
3
3
|
module NoBrainer::Config
|
|
4
|
+
SETTINGS = {
|
|
5
|
+
:app_name => { :default => ->{ default_app_name } },
|
|
6
|
+
:environment => { :default => ->{ default_environment } },
|
|
7
|
+
:rethinkdb_url => { :default => ->{ default_rethinkdb_url } },
|
|
8
|
+
:logger => { :default => ->{ default_logger } },
|
|
9
|
+
:warn_on_active_record => { :default => ->{ true }, :valid_values => [true, false] },
|
|
10
|
+
:auto_create_databases => { :default => ->{ true }, :valid_values => [true, false] },
|
|
11
|
+
:auto_create_tables => { :default => ->{ true }, :valid_values => [true, false] },
|
|
12
|
+
:max_retries_on_connection_failure => { :default => ->{ default_max_retries_on_connection_failure } },
|
|
13
|
+
:durability => { :default => ->{ default_durability }, :valid_values => [:hard, :soft] },
|
|
14
|
+
:user_timezone => { :default => ->{ :local }, :valid_values => [:unchanged, :utc, :local] },
|
|
15
|
+
:db_timezone => { :default => ->{ :utc }, :valid_values => [:unchanged, :utc, :local] },
|
|
16
|
+
:colorize_logger => { :default => ->{ true }, :valid_values => [true, false] },
|
|
17
|
+
:distributed_lock_class => { :default => ->{ nil } },
|
|
18
|
+
:per_thread_connection => { :default => ->{ false }, :valid_values => [true, false] },
|
|
19
|
+
}
|
|
20
|
+
|
|
4
21
|
class << self
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
22
|
+
attr_accessor(*SETTINGS.keys)
|
|
23
|
+
|
|
24
|
+
def max_reconnection_tries=(value)
|
|
25
|
+
STDERR.puts "[NoBrainer] config.max_reconnection_tries is deprecated and will be removed"
|
|
26
|
+
STDERR.puts "[NoBrainer] use config.max_retries_on_connection_failure instead."
|
|
27
|
+
self.max_retries_on_connection_failure = value
|
|
28
|
+
end
|
|
10
29
|
|
|
11
30
|
def apply_defaults
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
self.durability = default_durability
|
|
19
|
-
self.user_timezone = :local
|
|
20
|
-
self.db_timezone = :utc
|
|
21
|
-
self.colorize_logger = true
|
|
22
|
-
self.distributed_lock_class = nil
|
|
23
|
-
self.per_thread_connection = false
|
|
31
|
+
@applied_defaults_for = SETTINGS.keys.reject { |k| instance_variable_defined?("@#{k}") }
|
|
32
|
+
@applied_defaults_for.each { |k| __send__("#{k}=", SETTINGS[k][:default].call) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def assert_valid_options!
|
|
36
|
+
SETTINGS.each { |k,v| assert_array_in(k, v[:valid_values]) if v[:valid_values] }
|
|
24
37
|
end
|
|
25
38
|
|
|
26
39
|
def reset!
|
|
27
|
-
|
|
28
|
-
apply_defaults
|
|
40
|
+
instance_variables.each { |ivar| remove_instance_variable(ivar) }
|
|
29
41
|
end
|
|
30
42
|
|
|
31
43
|
def configure(&block)
|
|
32
|
-
|
|
44
|
+
@applied_defaults_for.to_a.each { |k| remove_instance_variable("@#{k}") }
|
|
33
45
|
block.call(self) if block
|
|
46
|
+
apply_defaults
|
|
34
47
|
assert_valid_options!
|
|
35
48
|
@configured = true
|
|
36
49
|
|
|
@@ -41,21 +54,28 @@ module NoBrainer::Config
|
|
|
41
54
|
!!@configured
|
|
42
55
|
end
|
|
43
56
|
|
|
44
|
-
def assert_valid_options!
|
|
45
|
-
assert_array_in :durability, [:hard, :soft]
|
|
46
|
-
assert_array_in :user_timezone, [:unchanged, :utc, :local]
|
|
47
|
-
assert_array_in :db_timezone, [:unchanged, :utc, :local]
|
|
48
|
-
end
|
|
49
|
-
|
|
50
57
|
def assert_array_in(name, values)
|
|
51
58
|
unless __send__(name).in?(values)
|
|
52
59
|
raise ArgumentError.new("Unknown configuration for #{name}: #{__send__(name)}. Valid values are: #{values.inspect}")
|
|
53
60
|
end
|
|
54
61
|
end
|
|
55
62
|
|
|
63
|
+
def dev_mode?
|
|
64
|
+
self.environment.to_s.in? %w(development test)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def default_app_name
|
|
68
|
+
defined?(Rails) ? Rails.application.class.parent_name.underscore.presence : nil rescue nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def default_environment
|
|
72
|
+
return Rails.env if defined?(Rails.env)
|
|
73
|
+
ENV['RUBY_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || :production
|
|
74
|
+
end
|
|
75
|
+
|
|
56
76
|
def default_rethinkdb_url
|
|
57
77
|
db = ENV['RETHINKDB_DB'] || ENV['RDB_DB']
|
|
58
|
-
db ||= "#{
|
|
78
|
+
db ||= "#{self.app_name}_#{self.environment}" if self.app_name && self.environment
|
|
59
79
|
host = ENV['RETHINKDB_HOST'] || ENV['RDB_HOST'] || 'localhost'
|
|
60
80
|
port = ENV['RETHINKDB_PORT'] || ENV['RDB_PORT']
|
|
61
81
|
auth = ENV['RETHINKDB_AUTH'] || ENV['RDB_AUTH']
|
|
@@ -69,7 +89,11 @@ module NoBrainer::Config
|
|
|
69
89
|
end
|
|
70
90
|
|
|
71
91
|
def default_durability
|
|
72
|
-
|
|
92
|
+
dev_mode? ? :soft : :hard
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def default_max_retries_on_connection_failure
|
|
96
|
+
dev_mode? ? 1 : 15
|
|
73
97
|
end
|
|
74
98
|
end
|
|
75
99
|
end
|
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
module NoBrainer::Criteria::AfterFind
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
included {
|
|
4
|
+
included { criteria_option :after_find, :merge_with => :append_array }
|
|
5
5
|
|
|
6
6
|
def after_find(b=nil, &block)
|
|
7
|
-
chain
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def merge!(criteria, options={})
|
|
11
|
-
super
|
|
12
|
-
if criteria._after_find.present?
|
|
13
|
-
self._after_find = (self._after_find || []) + criteria._after_find
|
|
14
|
-
end
|
|
15
|
-
self
|
|
7
|
+
chain(:after_find => [b, block].compact)
|
|
16
8
|
end
|
|
17
9
|
|
|
18
10
|
def _instantiate_doc(attrs)
|
|
19
11
|
super.tap do |doc|
|
|
20
|
-
|
|
12
|
+
@options[:after_find].to_a.each { |block| block.call(doc) }
|
|
21
13
|
doc.run_callbacks(:find) if doc.is_a?(NoBrainer::Document)
|
|
22
14
|
end
|
|
23
15
|
end
|
|
@@ -2,24 +2,27 @@ module NoBrainer::Criteria::Aggregate
|
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
4
|
def min(*a, &b)
|
|
5
|
-
instantiate_doc
|
|
5
|
+
instantiate_doc run { aggregate_rql(:min, *a, &b) }
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def max(*a, &b)
|
|
9
|
-
instantiate_doc
|
|
9
|
+
instantiate_doc run { aggregate_rql(:max, *a, &b) }
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def sum(*a, &b)
|
|
13
|
-
|
|
13
|
+
run { aggregate_rql(:sum, *a, &b) }
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def avg(*a, &b)
|
|
17
|
-
|
|
17
|
+
run { aggregate_rql(:avg, *a, &b) }
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
22
|
def aggregate_rql(type, *a, &b)
|
|
23
|
-
without_ordering.without_plucking.to_rql
|
|
23
|
+
rql = without_ordering.without_plucking.to_rql
|
|
24
|
+
rql = rql.__send__(type, *model.with_fields_aliased(a), &b)
|
|
25
|
+
rql = rql.default(nil) unless type == :sum
|
|
26
|
+
rql
|
|
24
27
|
end
|
|
25
28
|
end
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
module NoBrainer::Criteria::Cache
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
included {
|
|
4
|
+
included { criteria_option :with_cache, :merge_with => :set_scalar }
|
|
5
5
|
|
|
6
6
|
def with_cache
|
|
7
|
-
chain
|
|
7
|
+
chain(:with_cache => true)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def without_cache
|
|
11
|
-
chain
|
|
11
|
+
chain(:with_cache => false)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def inspect
|
|
@@ -18,14 +18,14 @@ module NoBrainer::Criteria::Cache
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def merge!(criteria, options={})
|
|
21
|
+
if options[:copy_cache_from] && options[:copy_cache_from].cached?
|
|
22
|
+
@cache = options[:copy_cache_from].instance_variable_get(:@cache)
|
|
23
|
+
end
|
|
21
24
|
super
|
|
22
|
-
self._with_cache = criteria._with_cache unless criteria._with_cache.nil?
|
|
23
|
-
self.reload unless options[:keep_cache]
|
|
24
|
-
self
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def with_cache?
|
|
28
|
-
|
|
28
|
+
finalized_criteria.options[:with_cache] != false
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def reload
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
module NoBrainer::Criteria::Core
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
included
|
|
4
|
+
included do
|
|
5
|
+
singleton_class.send(:attr_accessor, :options_definitions)
|
|
6
|
+
self.options_definitions = {}
|
|
7
|
+
attr_accessor :options
|
|
8
|
+
|
|
9
|
+
criteria_option :model, :merge_with => :set_scalar
|
|
10
|
+
criteria_option :finalized, :merge_with => :set_scalar
|
|
11
|
+
end
|
|
5
12
|
|
|
6
13
|
def initialize(options={})
|
|
7
|
-
|
|
14
|
+
@options = options
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def dup
|
|
18
|
+
# We don't keep any of the instance variables except options.
|
|
19
|
+
self.class.new(@options.dup)
|
|
8
20
|
end
|
|
9
21
|
|
|
10
|
-
def
|
|
11
|
-
|
|
22
|
+
def model
|
|
23
|
+
@options[:model]
|
|
12
24
|
end
|
|
13
25
|
|
|
14
26
|
def to_rql
|
|
@@ -16,16 +28,21 @@ module NoBrainer::Criteria::Core
|
|
|
16
28
|
end
|
|
17
29
|
|
|
18
30
|
def inspect
|
|
19
|
-
# rescue super because sometimes
|
|
31
|
+
# rescue super because sometimes model is not set.
|
|
20
32
|
to_rql.inspect rescue super
|
|
21
33
|
end
|
|
22
34
|
|
|
23
|
-
def run(
|
|
24
|
-
|
|
35
|
+
def run(&block)
|
|
36
|
+
block ||= proc { to_rql }
|
|
37
|
+
NoBrainer.run(:criteria => self, &block)
|
|
25
38
|
end
|
|
26
39
|
|
|
27
40
|
def merge!(criteria, options={})
|
|
28
|
-
|
|
41
|
+
criteria.options.each do |k,v|
|
|
42
|
+
merge_proc = self.class.options_definitions[k]
|
|
43
|
+
raise "Non declared option: #{k}" unless merge_proc
|
|
44
|
+
@options[k] = merge_proc.call(@options[k], v)
|
|
45
|
+
end
|
|
29
46
|
self
|
|
30
47
|
end
|
|
31
48
|
|
|
@@ -40,16 +57,14 @@ module NoBrainer::Criteria::Core
|
|
|
40
57
|
|
|
41
58
|
private
|
|
42
59
|
|
|
43
|
-
def chain(options={}, &block)
|
|
44
|
-
|
|
45
|
-
block.call(tmp)
|
|
46
|
-
merge(tmp, options)
|
|
60
|
+
def chain(options={}, merge_options={}, &block)
|
|
61
|
+
merge(self.class.new(options), merge_options)
|
|
47
62
|
end
|
|
48
63
|
|
|
49
64
|
def compile_rql_pass1
|
|
50
65
|
# This method is overriden by other modules.
|
|
51
|
-
raise "Criteria not bound to a
|
|
52
|
-
|
|
66
|
+
raise "Criteria not bound to a model" unless model
|
|
67
|
+
model.rql_table
|
|
53
68
|
end
|
|
54
69
|
|
|
55
70
|
def compile_rql_pass2
|
|
@@ -58,7 +73,7 @@ module NoBrainer::Criteria::Core
|
|
|
58
73
|
end
|
|
59
74
|
|
|
60
75
|
def finalized?
|
|
61
|
-
|
|
76
|
+
!!@options[:finalized]
|
|
62
77
|
end
|
|
63
78
|
|
|
64
79
|
def finalized_criteria
|
|
@@ -66,8 +81,33 @@ module NoBrainer::Criteria::Core
|
|
|
66
81
|
end
|
|
67
82
|
|
|
68
83
|
module ClassMethods
|
|
84
|
+
def criteria_option(*names)
|
|
85
|
+
options = names.extract_options!
|
|
86
|
+
|
|
87
|
+
names.map(&:to_sym).each do |name|
|
|
88
|
+
merge_proc = options[:merge_with]
|
|
89
|
+
merge_proc = MergeStrategies.method(merge_proc) if merge_proc.is_a?(Symbol)
|
|
90
|
+
self.options_definitions[name] = merge_proc
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
69
94
|
def _finalize_criteria(base)
|
|
70
|
-
base.
|
|
95
|
+
base.__send__(:chain, :finalized => true)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
module MergeStrategies
|
|
100
|
+
extend self
|
|
101
|
+
def set_scalar(a, b)
|
|
102
|
+
b
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def merge_hash(a, b)
|
|
106
|
+
a ? a.merge(b) : b
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def append_array(a, b)
|
|
110
|
+
a ? a+b : b
|
|
71
111
|
end
|
|
72
112
|
end
|
|
73
113
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module NoBrainer::Criteria::Extend
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included { criteria_option :extend, :merge_with => :append_array }
|
|
5
|
+
|
|
6
|
+
def extend(*modules, &block)
|
|
7
|
+
options = modules.extract_options!
|
|
8
|
+
modules << Module.new(&block) if block
|
|
9
|
+
|
|
10
|
+
return super(*modules) if options[:original_behavior]
|
|
11
|
+
chain(:extend => [modules])
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def merge!(criteria, options={})
|
|
15
|
+
super.tap do
|
|
16
|
+
@options[:extend].to_a.each { |modules| extend(*modules, :original_behavior => true) }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -18,7 +18,7 @@ module NoBrainer::Criteria::First
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def sample(n=nil)
|
|
21
|
-
result =
|
|
21
|
+
result = run { self.without_ordering.to_rql.sample(n.nil? ? 1 : n) }
|
|
22
22
|
result = result.map(&method(:instantiate_doc))
|
|
23
23
|
n.nil? ? result.first : result
|
|
24
24
|
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
module NoBrainer::Criteria::Index
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
included {
|
|
4
|
+
included { criteria_option :use_index, :merge_with => :set_scalar }
|
|
5
5
|
|
|
6
6
|
def with_index(index_name=true)
|
|
7
|
-
chain
|
|
7
|
+
chain(:use_index => index_name)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def without_index
|
|
@@ -12,25 +12,19 @@ module NoBrainer::Criteria::Index
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def without_index?
|
|
15
|
-
finalized_criteria.
|
|
15
|
+
finalized_criteria.options[:use_index] == false
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def used_index
|
|
19
|
-
#
|
|
19
|
+
# Only one of them will be active.
|
|
20
20
|
where_index_name || order_by_index_name
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def merge!(criteria, options={})
|
|
24
|
-
super
|
|
25
|
-
self.with_index_name = criteria.with_index_name unless criteria.with_index_name.nil?
|
|
26
|
-
self
|
|
27
|
-
end
|
|
28
|
-
|
|
29
23
|
def compile_rql_pass2
|
|
30
24
|
super.tap do
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
raise NoBrainer::Error::CannotUseIndex.new(
|
|
25
|
+
# The implicit ordering on the indexed pk does not count.
|
|
26
|
+
if @options[:use_index] && (!used_index || order_by_index_name.to_s == model.pk_name.to_s)
|
|
27
|
+
raise NoBrainer::Error::CannotUseIndex.new(@options[:use_index])
|
|
34
28
|
end
|
|
35
29
|
end
|
|
36
30
|
end
|
|
@@ -1,31 +1,23 @@
|
|
|
1
1
|
module NoBrainer::Criteria::Limit
|
|
2
|
-
# TODO Test these guys
|
|
3
2
|
extend ActiveSupport::Concern
|
|
4
3
|
|
|
5
|
-
included {
|
|
4
|
+
included { criteria_option :skip, :limit, :merge_with => :set_scalar }
|
|
6
5
|
|
|
7
6
|
def limit(value)
|
|
8
|
-
chain
|
|
7
|
+
chain(:limit => value)
|
|
9
8
|
end
|
|
10
9
|
|
|
11
10
|
def skip(value)
|
|
12
|
-
chain
|
|
11
|
+
chain(:skip => value)
|
|
13
12
|
end
|
|
14
13
|
alias_method :offset, :skip
|
|
15
14
|
|
|
16
|
-
def merge!(criteria, options={})
|
|
17
|
-
super
|
|
18
|
-
self._skip = criteria._skip if criteria._skip
|
|
19
|
-
self._limit = criteria._limit if criteria._limit
|
|
20
|
-
self
|
|
21
|
-
end
|
|
22
|
-
|
|
23
15
|
private
|
|
24
16
|
|
|
25
17
|
def compile_rql_pass2
|
|
26
18
|
rql = super
|
|
27
|
-
rql = rql.skip(
|
|
28
|
-
rql = rql.limit(
|
|
19
|
+
rql = rql.skip(@options[:skip]) if @options[:skip]
|
|
20
|
+
rql = rql.limit(@options[:limit]) if @options[:limit]
|
|
29
21
|
rql
|
|
30
22
|
end
|
|
31
23
|
end
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
module NoBrainer::Criteria::OrderBy
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def initialize(options={})
|
|
7
|
-
super
|
|
8
|
-
self.order = {}
|
|
9
|
-
end
|
|
4
|
+
# The latest order_by() wins
|
|
5
|
+
included { criteria_option :order_by, :ordering_mode, :merge_with => :set_scalar }
|
|
10
6
|
|
|
11
7
|
def order_by(*rules, &block)
|
|
12
8
|
# Note: We are relying on the fact that Hashes are ordered (since 1.9)
|
|
13
|
-
rules = [*rules, block].compact.map do |rule|
|
|
9
|
+
rules = [*rules, block].flatten.compact.map do |rule|
|
|
14
10
|
case rule
|
|
15
11
|
when Hash then
|
|
16
12
|
bad_rule = rule.values.reject { |v| v.in? [:asc, :desc] }.first
|
|
@@ -21,34 +17,20 @@ module NoBrainer::Criteria::OrderBy
|
|
|
21
17
|
end
|
|
22
18
|
end.reduce({}, :merge)
|
|
23
19
|
|
|
24
|
-
chain
|
|
25
|
-
criteria.order = rules
|
|
26
|
-
criteria.ordering_mode = :normal
|
|
27
|
-
end
|
|
20
|
+
chain(:order_by => rules, :ordering_mode => :normal)
|
|
28
21
|
end
|
|
29
22
|
|
|
30
23
|
def without_ordering
|
|
31
|
-
chain
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def merge!(criteria, options={})
|
|
35
|
-
super
|
|
36
|
-
# The latest order_by() wins
|
|
37
|
-
self.order = criteria.order if criteria.order.present?
|
|
38
|
-
self.ordering_mode = criteria.ordering_mode unless criteria.ordering_mode.nil?
|
|
39
|
-
self
|
|
24
|
+
chain(:ordering_mode => :disabled)
|
|
40
25
|
end
|
|
41
26
|
|
|
42
27
|
def reverse_order
|
|
43
|
-
chain
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
when :disabled then :disabled
|
|
50
|
-
end
|
|
51
|
-
end
|
|
28
|
+
chain(:ordering_mode => case @options[:ordering_mode]
|
|
29
|
+
when nil then :reversed
|
|
30
|
+
when :normal then :reversed
|
|
31
|
+
when :reversed then :normal
|
|
32
|
+
when :disabled then :disabled
|
|
33
|
+
end)
|
|
52
34
|
end
|
|
53
35
|
|
|
54
36
|
def order_by_indexed?
|
|
@@ -62,15 +44,15 @@ module NoBrainer::Criteria::OrderBy
|
|
|
62
44
|
private
|
|
63
45
|
|
|
64
46
|
def effective_order
|
|
65
|
-
|
|
47
|
+
@options[:order_by].presence || (model ? {model.pk_name => :asc} : {})
|
|
66
48
|
end
|
|
67
49
|
|
|
68
50
|
def reverse_order?
|
|
69
|
-
|
|
51
|
+
@options[:ordering_mode] == :reversed
|
|
70
52
|
end
|
|
71
53
|
|
|
72
54
|
def should_order?
|
|
73
|
-
|
|
55
|
+
@options[:ordering_mode] != :disabled
|
|
74
56
|
end
|
|
75
57
|
|
|
76
58
|
class IndexFinder < Struct.new(:criteria, :index_name, :rql_proc)
|
|
@@ -83,15 +65,15 @@ module NoBrainer::Criteria::OrderBy
|
|
|
83
65
|
end
|
|
84
66
|
|
|
85
67
|
def first_key_indexable?
|
|
86
|
-
(first_key.is_a?(Symbol) || first_key.is_a?(String)) && criteria.
|
|
68
|
+
(first_key.is_a?(Symbol) || first_key.is_a?(String)) && criteria.model.has_index?(first_key)
|
|
87
69
|
end
|
|
88
70
|
|
|
89
71
|
def find_index
|
|
90
72
|
return if criteria.without_index?
|
|
91
73
|
return unless first_key_indexable?
|
|
92
74
|
|
|
93
|
-
if criteria.
|
|
94
|
-
return unless first_key.to_s == criteria.
|
|
75
|
+
if criteria.options[:use_index] && criteria.options[:use_index] != true
|
|
76
|
+
return unless first_key.to_s == criteria.options[:use_index].to_s
|
|
95
77
|
end
|
|
96
78
|
|
|
97
79
|
# We need make sure that the where index finder has been invoked, it has priority.
|
|
@@ -116,9 +98,9 @@ module NoBrainer::Criteria::OrderBy
|
|
|
116
98
|
|
|
117
99
|
rql_rules = _effective_order.map do |k,v|
|
|
118
100
|
if order_by_index_finder.index_name == k
|
|
119
|
-
k =
|
|
101
|
+
k = model.lookup_index_alias(k)
|
|
120
102
|
else
|
|
121
|
-
k =
|
|
103
|
+
k = model.lookup_field_alias(k)
|
|
122
104
|
end
|
|
123
105
|
|
|
124
106
|
case v
|
|
@@ -127,14 +109,13 @@ module NoBrainer::Criteria::OrderBy
|
|
|
127
109
|
end
|
|
128
110
|
end
|
|
129
111
|
|
|
130
|
-
# We can only apply an
|
|
131
|
-
#
|
|
132
|
-
#
|
|
112
|
+
# We can only apply an indexed order_by on a table() RQL term.
|
|
113
|
+
# If we can, great. Otherwise, the ordering is applied in pass2, which will
|
|
114
|
+
# happen after a potential filter(), which is better for perfs.
|
|
133
115
|
if order_by_index_finder.could_find_index?
|
|
134
116
|
options = { :index => rql_rules.shift }
|
|
135
117
|
rql = rql.order_by(*rql_rules, options)
|
|
136
118
|
else
|
|
137
|
-
# Stashing @rql_rules for pass2
|
|
138
119
|
@rql_rules_pass2 = rql_rules
|
|
139
120
|
end
|
|
140
121
|
|