nobrainer 0.18.0 → 0.22.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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/lib/no_brainer/autoload.rb +0 -5
  3. data/lib/no_brainer/config.rb +73 -39
  4. data/lib/no_brainer/connection.rb +2 -4
  5. data/lib/no_brainer/criteria/after_find.rb +3 -11
  6. data/lib/no_brainer/criteria/aggregate.rb +2 -2
  7. data/lib/no_brainer/criteria/cache.rb +15 -10
  8. data/lib/no_brainer/criteria/core.rb +46 -11
  9. data/lib/no_brainer/criteria/delete.rb +2 -2
  10. data/lib/no_brainer/criteria/eager_load.rb +51 -0
  11. data/lib/no_brainer/criteria/extend.rb +4 -16
  12. data/lib/no_brainer/criteria/find.rb +27 -0
  13. data/lib/no_brainer/criteria/index.rb +7 -13
  14. data/lib/no_brainer/criteria/limit.rb +5 -12
  15. data/lib/no_brainer/criteria/order_by.rb +20 -36
  16. data/lib/no_brainer/criteria/pluck.rb +16 -22
  17. data/lib/no_brainer/criteria/raw.rb +4 -10
  18. data/lib/no_brainer/criteria/scope.rb +6 -19
  19. data/lib/no_brainer/criteria/update.rb +8 -6
  20. data/lib/no_brainer/criteria/where.rb +252 -138
  21. data/lib/no_brainer/criteria.rb +3 -2
  22. data/lib/no_brainer/document/aliases.rb +3 -3
  23. data/lib/no_brainer/document/association/belongs_to.rb +9 -5
  24. data/lib/no_brainer/document/association/core.rb +6 -5
  25. data/lib/no_brainer/document/association/eager_loader.rb +9 -9
  26. data/lib/no_brainer/document/association/has_many.rb +23 -9
  27. data/lib/no_brainer/document/association/has_many_through.rb +12 -3
  28. data/lib/no_brainer/document/atomic_ops.rb +79 -78
  29. data/lib/no_brainer/document/attributes.rb +24 -20
  30. data/lib/no_brainer/document/callbacks.rb +1 -1
  31. data/lib/no_brainer/document/core.rb +5 -2
  32. data/lib/no_brainer/document/criteria.rb +14 -19
  33. data/lib/no_brainer/document/dirty.rb +11 -16
  34. data/lib/no_brainer/document/index/index.rb +2 -1
  35. data/lib/no_brainer/document/index/meta_store.rb +1 -1
  36. data/lib/no_brainer/document/index.rb +14 -10
  37. data/lib/no_brainer/document/persistance.rb +24 -13
  38. data/lib/no_brainer/document/primary_key/generator.rb +83 -0
  39. data/lib/no_brainer/document/{id.rb → primary_key.rb} +9 -36
  40. data/lib/no_brainer/document/store_in.rb +2 -2
  41. data/lib/no_brainer/document/timestamps.rb +4 -2
  42. data/lib/no_brainer/document/types/binary.rb +2 -7
  43. data/lib/no_brainer/document/types/boolean.rb +2 -4
  44. data/lib/no_brainer/document/types/date.rb +2 -2
  45. data/lib/no_brainer/document/types/float.rb +2 -2
  46. data/lib/no_brainer/document/types/geo.rb +1 -0
  47. data/lib/no_brainer/document/types/integer.rb +2 -2
  48. data/lib/no_brainer/document/types/set.rb +2 -2
  49. data/lib/no_brainer/document/types/string.rb +5 -2
  50. data/lib/no_brainer/document/types/symbol.rb +2 -2
  51. data/lib/no_brainer/document/types/text.rb +18 -0
  52. data/lib/no_brainer/document/types/time.rb +2 -2
  53. data/lib/no_brainer/document/types.rb +17 -18
  54. data/lib/no_brainer/document/validation/not_null.rb +15 -0
  55. data/lib/no_brainer/document/{uniqueness.rb → validation/uniqueness.rb} +11 -11
  56. data/lib/no_brainer/document/validation.rb +35 -6
  57. data/lib/no_brainer/document.rb +1 -1
  58. data/lib/no_brainer/error.rb +21 -19
  59. data/lib/no_brainer/geo/base.rb +16 -0
  60. data/lib/no_brainer/geo/circle.rb +25 -0
  61. data/lib/no_brainer/geo/line_string.rb +11 -0
  62. data/lib/no_brainer/geo/point.rb +49 -0
  63. data/lib/no_brainer/geo/polygon.rb +11 -0
  64. data/lib/no_brainer/geo.rb +4 -0
  65. data/lib/no_brainer/locale/en.yml +1 -0
  66. data/lib/no_brainer/lock.rb +114 -0
  67. data/lib/no_brainer/query_runner/connection_lock.rb +1 -1
  68. data/lib/no_brainer/query_runner/database_on_demand.rb +0 -1
  69. data/lib/no_brainer/query_runner/missing_index.rb +1 -1
  70. data/lib/no_brainer/query_runner/reconnect.rb +9 -11
  71. data/lib/no_brainer/query_runner/run_options.rb +0 -3
  72. data/lib/no_brainer/query_runner/table_on_demand.rb +3 -4
  73. data/lib/no_brainer/railtie/database.rake +2 -2
  74. data/lib/no_brainer/rql.rb +1 -5
  75. data/lib/nobrainer.rb +2 -6
  76. data/lib/rails/generators/nobrainer.rb +1 -1
  77. metadata +34 -9
  78. data/lib/no_brainer/criteria/preload.rb +0 -50
  79. data/lib/no_brainer/decorated_symbol.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e146036464db0297a156c6987cb51011137f245
4
- data.tar.gz: 7f6815c09410489fbe26b1c0faebdc7aefe001e1
3
+ metadata.gz: c53755e5f9ec902612c417fcf053404cf114ca0b
4
+ data.tar.gz: 529fe4b627d366ba4813fd6ff750192caf0df316
5
5
  SHA512:
6
- metadata.gz: 00b32de4c7ccd965972369698d6619cfa873671d73c5805d03c37e026737eab42cfb5a3d2f94c00323ec05ce84c7baef0702c1572673ed90050f9f02dcf04458
7
- data.tar.gz: 661eca7f94fb210e481ecf064829a72b095cc59552de2197856a9226d2978b91c489c26a4045e919368f6d532433b6cbcd3aba8c459c9cd5d2e3bb7f115c6d92
6
+ metadata.gz: 920d6b1c7bec9acd28b4a0e7f54a9967d068a01d31174396ca961eaf8cbfec31e3b8397ab2242ac87c73b9ca3789e80b47c8ca4b809d7a71b5fa80aeedb81f4b
7
+ data.tar.gz: 92144b91eb03ac14d2a01ea5244480b215286228691e5406a9f7bcf218499853ecfb0d1313f1380f62d509d97dc2bb1dac65ae8e97c0c2c3cc2b7b4c3835e1c2
@@ -17,9 +17,4 @@ module NoBrainer::Autoload
17
17
  constants.each { |constant| autoload constant }
18
18
  constants.each { |constant| include const_get(constant) }
19
19
  end
20
-
21
- def eager_autoload_and_include(*constants)
22
- eager_autoload(*constants)
23
- constants.each { |constant| include const_get(constant) }
24
- end
25
20
  end
@@ -1,46 +1,61 @@
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
+ :colorize_logger => { :default => ->{ true }, :valid_values => [true, false] },
10
+ :warn_on_active_record => { :default => ->{ true }, :valid_values => [true, false] },
11
+ :max_retries_on_connection_failure => { :default => ->{ default_max_retries_on_connection_failure } },
12
+ :durability => { :default => ->{ default_durability }, :valid_values => [:hard, :soft] },
13
+ :max_string_length => { :default => -> { 255 } },
14
+ :user_timezone => { :default => ->{ :local }, :valid_values => [:unchanged, :utc, :local] },
15
+ :db_timezone => { :default => ->{ :utc }, :valid_values => [:unchanged, :utc, :local] },
16
+ :geo_options => { :default => ->{ {:geo_system => 'WGS84', :unit => 'm'} } },
17
+ :distributed_lock_class => { :default => ->{ NoBrainer::Lock } },
18
+ :lock_options => { :default => ->{ { :expire => 60, :timeout => 10 } } },
19
+ :per_thread_connection => { :default => ->{ false }, :valid_values => [true, false] },
20
+ :machine_id => { :default => ->{ default_machine_id } },
21
+ :criteria_cache_max_entries => { :default => -> { 10_000 } },
22
+ }
23
+
4
24
  class << self
5
- mattr_accessor :app_name, :environment, :rethinkdb_url,
6
- :logger, :warn_on_active_record,
7
- :auto_create_databases, :auto_create_tables,
8
- :max_retries_on_connection_failure, :durability,
9
- :user_timezone, :db_timezone, :colorize_logger,
10
- :distributed_lock_class, :per_thread_connection
25
+ attr_accessor(*SETTINGS.keys)
26
+
27
+ def auto_create_databases=(value)
28
+ STDERR.puts "[NoBrainer] config.auto_create_databases is no longer active."
29
+ STDERR.puts "[NoBrainer] The current behavior is now to always auto create databases"
30
+ end
31
+
32
+ def auto_create_tables=(value)
33
+ STDERR.puts "[NoBrainer] config.auto_create_tables is no longer active."
34
+ STDERR.puts "[NoBrainer] The current behavior is now to always auto create tables"
35
+ end
11
36
 
12
37
  def apply_defaults
13
- self.app_name = default_app_name
14
- self.environment = default_environment
15
- self.rethinkdb_url = default_rethinkdb_url
16
- self.logger = default_logger
17
- self.warn_on_active_record = true
18
- self.auto_create_databases = true
19
- self.auto_create_tables = true
20
- self.max_retries_on_connection_failure = default_max_retries_on_connection_failure
21
- self.durability = default_durability
22
- self.user_timezone = :local
23
- self.db_timezone = :utc
24
- self.colorize_logger = true
25
- self.distributed_lock_class = nil
26
- self.per_thread_connection = false
27
- end
28
-
29
- def max_reconnection_tries=(value)
30
- STDERR.puts "[NoBrainer] config.max_reconnection_tries is deprecated and will be removed"
31
- STDERR.puts "[NoBrainer] use config.max_retries_on_connection_failure instead."
32
- self.max_retries_on_connection_failure = value
38
+ @applied_defaults_for = SETTINGS.keys.reject { |k| instance_variable_defined?("@#{k}") }
39
+ @applied_defaults_for.each { |k| __send__("#{k}=", SETTINGS[k][:default].call) }
40
+ end
41
+
42
+ def geo_options=(value)
43
+ @geo_options = value.try(:symbolize_keys)
44
+ end
45
+
46
+ def assert_valid_options
47
+ SETTINGS.each { |k,v| assert_array_in(k, v[:valid_values]) if v[:valid_values] }
33
48
  end
34
49
 
35
50
  def reset!
36
- @configured = false
37
- apply_defaults
51
+ instance_variables.each { |ivar| remove_instance_variable(ivar) }
38
52
  end
39
53
 
40
54
  def configure(&block)
41
- apply_defaults unless configured?
55
+ @applied_defaults_for.to_a.each { |k| remove_instance_variable("@#{k}") }
42
56
  block.call(self) if block
43
- assert_valid_options!
57
+ apply_defaults
58
+ assert_valid_options
44
59
  @configured = true
45
60
 
46
61
  NoBrainer::ConnectionManager.disconnect_if_url_changed
@@ -50,20 +65,18 @@ module NoBrainer::Config
50
65
  !!@configured
51
66
  end
52
67
 
53
- def assert_valid_options!
54
- assert_array_in :durability, [:hard, :soft]
55
- assert_array_in :user_timezone, [:unchanged, :utc, :local]
56
- assert_array_in :db_timezone, [:unchanged, :utc, :local]
57
- end
58
-
59
68
  def assert_array_in(name, values)
60
69
  unless __send__(name).in?(values)
61
70
  raise ArgumentError.new("Unknown configuration for #{name}: #{__send__(name)}. Valid values are: #{values.inspect}")
62
71
  end
63
72
  end
64
73
 
74
+ def dev_mode?
75
+ self.environment.to_s.in? %w(development test)
76
+ end
77
+
65
78
  def default_app_name
66
- defined?(Rails) ? Rails.application.class.parent_name.underscore : nil
79
+ defined?(Rails) ? Rails.application.class.parent_name.underscore.presence : nil rescue nil
67
80
  end
68
81
 
69
82
  def default_environment
@@ -94,8 +107,29 @@ module NoBrainer::Config
94
107
  dev_mode? ? 1 : 15
95
108
  end
96
109
 
97
- def dev_mode?
98
- self.environment.to_sym.in?([:development, :test])
110
+ def default_machine_id
111
+ require 'socket'
112
+ require 'digest/md5'
113
+
114
+ return ENV['MACHINE_ID'] if ENV['MACHINE_ID']
115
+
116
+ host = Socket.gethostname
117
+ if host.in? %w(127.0.0.1 localhost)
118
+ raise "Please configure NoBrainer::Config.machine_id due to lack of appropriate hostname (Socket.gethostname = #{host})"
119
+ end
120
+
121
+ Digest::MD5.digest(host).unpack("N")[0] & NoBrainer::Document::PrimaryKey::Generator::MACHINE_ID_MASK
122
+ end
123
+
124
+ def machine_id=(machine_id)
125
+ machine_id = case machine_id
126
+ when Integer then machine_id
127
+ when /^[0-9]+$/ then machine_id.to_i
128
+ else raise "Invalid machine_id"
129
+ end
130
+ max_id = NoBrainer::Document::PrimaryKey::Generator::MACHINE_ID_MASK
131
+ raise "Invalid machine_id (must be between 0 and #{max_id})" unless machine_id.in?(0..max_id)
132
+ @machine_id = machine_id
99
133
  end
100
134
  end
101
135
  end
@@ -50,13 +50,11 @@ class NoBrainer::Connection
50
50
 
51
51
  # Note that truncating each table (purge) is much faster than dropping the
52
52
  # database (drop)
53
- def purge!(options={})
53
+ def purge!
54
54
  table_list.each do |table_name|
55
- next if table_name =~ /^nobrainer_/
55
+ next if table_name == 'nobrainer_index_meta' # keeping because indexes are not going away
56
56
  NoBrainer.run { |r| r.table(table_name).delete }
57
57
  end
58
58
  true
59
- rescue RuntimeError => e
60
- raise e unless e.message =~ /No entry with that name/
61
59
  end
62
60
  end
@@ -1,23 +1,15 @@
1
1
  module NoBrainer::Criteria::AfterFind
2
2
  extend ActiveSupport::Concern
3
3
 
4
- included { attr_accessor :_after_find }
4
+ included { criteria_option :after_find, :merge_with => :append_array }
5
5
 
6
6
  def after_find(b=nil, &block)
7
- chain { |criteria| criteria._after_find = [b || block] }
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
- self._after_find.to_a.each { |block| block.call(doc) }
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,11 +2,11 @@ module NoBrainer::Criteria::Aggregate
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  def min(*a, &b)
5
- instantiate_doc run { aggregate_rql(:min, *a, &b) }
5
+ order_by(a, &b).first
6
6
  end
7
7
 
8
8
  def max(*a, &b)
9
- instantiate_doc run { aggregate_rql(:max, *a, &b) }
9
+ order_by(a, &b).last
10
10
  end
11
11
 
12
12
  def sum(*a, &b)
@@ -1,14 +1,14 @@
1
1
  module NoBrainer::Criteria::Cache
2
2
  extend ActiveSupport::Concern
3
3
 
4
- included { attr_accessor :_with_cache }
4
+ included { criteria_option :with_cache, :merge_with => :set_scalar }
5
5
 
6
6
  def with_cache
7
- chain { |criteria| criteria._with_cache = true }
7
+ chain(:with_cache => true)
8
8
  end
9
9
 
10
10
  def without_cache
11
- chain { |criteria| criteria._with_cache = false }
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
- @_with_cache != false
28
+ finalized_criteria.options[:with_cache] != false
29
29
  end
30
30
 
31
31
  def reload
@@ -37,15 +37,20 @@ module NoBrainer::Criteria::Cache
37
37
  end
38
38
 
39
39
  def each(options={}, &block)
40
- return super unless with_cache? && !options[:no_cache] && block
40
+ return super unless with_cache? && !options[:no_cache] && block && !@cache_too_small
41
41
  return @cache.each(&block) if @cache
42
42
 
43
43
  cache = []
44
44
  super(options.merge(:no_cache => true)) do |instance|
45
45
  block.call(instance)
46
- cache << instance
46
+ cache << instance unless @cache_too_small
47
+
48
+ if cache.size > NoBrainer::Config.criteria_cache_max_entries
49
+ cache = []
50
+ @cache_too_small = true
51
+ end
47
52
  end
48
- @cache = cache
53
+ @cache = cache unless @cache_too_small
49
54
  self
50
55
  end
51
56
 
@@ -1,14 +1,26 @@
1
1
  module NoBrainer::Criteria::Core
2
2
  extend ActiveSupport::Concern
3
3
 
4
- included { attr_accessor :init_options }
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
- self.init_options = options
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
22
  def model
11
- init_options[:model]
23
+ @options[:model]
12
24
  end
13
25
 
14
26
  def to_rql
@@ -26,12 +38,16 @@ module NoBrainer::Criteria::Core
26
38
  end
27
39
 
28
40
  def merge!(criteria, options={})
29
- self.init_options = self.init_options.merge(criteria.init_options)
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
30
46
  self
31
47
  end
32
48
 
33
49
  def merge(criteria, options={})
34
- dup.tap { |new_criteria| new_criteria.merge!(criteria, options) }
50
+ dup.merge!(criteria, options)
35
51
  end
36
52
 
37
53
  def ==(other)
@@ -41,10 +57,8 @@ module NoBrainer::Criteria::Core
41
57
 
42
58
  private
43
59
 
44
- def chain(options={}, &block)
45
- tmp = self.class.new(self.init_options) # we might want to optimize that thing
46
- block.call(tmp)
47
- merge(tmp, options)
60
+ def chain(options={}, merge_options={})
61
+ merge(self.class.new(options), merge_options)
48
62
  end
49
63
 
50
64
  def compile_rql_pass1
@@ -59,7 +73,7 @@ module NoBrainer::Criteria::Core
59
73
  end
60
74
 
61
75
  def finalized?
62
- !!init_options[:finalized]
76
+ !!@options[:finalized]
63
77
  end
64
78
 
65
79
  def finalized_criteria
@@ -67,8 +81,29 @@ module NoBrainer::Criteria::Core
67
81
  end
68
82
 
69
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
+
70
94
  def _finalize_criteria(base)
71
- base.merge(base.class.new(:finalized => true))
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 append_array(a, b)
106
+ a ? a+b : b
72
107
  end
73
108
  end
74
109
  end
@@ -2,10 +2,10 @@ module NoBrainer::Criteria::Delete
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  def delete_all
5
- run { without_ordering.without_plucking.to_rql.delete }
5
+ run { without_distinct.without_ordering.without_plucking.to_rql.delete }
6
6
  end
7
7
 
8
8
  def destroy_all
9
- to_a.each { |doc| doc.destroy }
9
+ to_a.each(&:destroy)
10
10
  end
11
11
  end
@@ -0,0 +1,51 @@
1
+ module NoBrainer::Criteria::EagerLoad
2
+ extend ActiveSupport::Concern
3
+
4
+ included { criteria_option :eager_load, :merge_with => :append_array }
5
+
6
+ def eager_load(*values)
7
+ chain({:eager_load => values}, :copy_cache_from => self)
8
+ end
9
+
10
+ def preload(*values)
11
+ STDERR.puts "[NoBrainer] `preload' is deprecated and will be removed, please use `eager_load' instead"
12
+ eager_load(*values)
13
+ end
14
+
15
+ def merge!(criteria, options={})
16
+ super.tap do
17
+ # If we already have some cached documents, and we need to so some eager
18
+ # loading, then we it now. It's easier than doing it lazily.
19
+ if self.cached? && criteria.options[:eager_load].present?
20
+ perform_eager_load(@cache)
21
+ end
22
+ end
23
+ end
24
+
25
+ def each(options={}, &block)
26
+ return super unless should_eager_load? && !options[:no_eager_loading] && block
27
+
28
+ docs = []
29
+ super(options.merge(:no_eager_loading => true)) { |doc| docs << doc }
30
+ # TODO batch the eager loading with NoBrainer::Config.criteria_cache_max_entries
31
+ perform_eager_load(docs)
32
+ docs.each(&block)
33
+ self
34
+ end
35
+
36
+ private
37
+
38
+ def should_eager_load?
39
+ @options[:eager_load].present? && !raw?
40
+ end
41
+
42
+ def get_one(criteria)
43
+ super.tap { |doc| perform_eager_load([doc]) }
44
+ end
45
+
46
+ def perform_eager_load(docs)
47
+ if should_eager_load? && docs.present?
48
+ NoBrainer::Document::Association::EagerLoader.new.eager_load(docs, @options[:eager_load])
49
+ end
50
+ end
51
+ end
@@ -1,31 +1,19 @@
1
1
  module NoBrainer::Criteria::Extend
2
2
  extend ActiveSupport::Concern
3
3
 
4
- included { attr_accessor :extend_modules }
4
+ included { criteria_option :extend, :merge_with => :append_array }
5
5
 
6
6
  def extend(*modules, &block)
7
7
  options = modules.extract_options!
8
8
  modules << Module.new(&block) if block
9
9
 
10
10
  return super(*modules) if options[:original_behavior]
11
-
12
- chain do |criteria|
13
- criteria.extend_modules ||= []
14
- criteria.extend_modules += [modules]
15
- end
11
+ chain(:extend => [modules])
16
12
  end
17
13
 
18
14
  def merge!(criteria, options={})
19
- super
20
-
21
- if criteria.extend_modules.present?
22
- self.extend_modules = self.extend_modules.to_a + criteria.extend_modules
15
+ super.tap do
16
+ @options[:extend].to_a.each { |modules| extend(*modules, :original_behavior => true) }
23
17
  end
24
-
25
- if self.extend_modules.present?
26
- self.extend_modules.each { |modules| extend(*modules, :original_behavior => true) }
27
- end
28
-
29
- self
30
18
  end
31
19
  end
@@ -0,0 +1,27 @@
1
+ module NoBrainer::Criteria::Find
2
+ extend ActiveSupport::Concern
3
+
4
+ def find_by?(*args, &block)
5
+ where(*args, &block).first
6
+ end
7
+
8
+ def find_by(*args, &block)
9
+ find_by?(*args, &block).tap { |doc| raise_not_found(args) unless doc }
10
+ end
11
+ alias_method :find_by!, :find_by
12
+
13
+ def find?(pk)
14
+ without_ordering.find_by?(model.pk_name => pk)
15
+ end
16
+
17
+ def find(pk)
18
+ without_ordering.find_by(model.pk_name => pk)
19
+ end
20
+ alias_method :find!, :find
21
+
22
+ private
23
+
24
+ def raise_not_found(args)
25
+ raise NoBrainer::Error::DocumentNotFound, "#{model} #{args.inspect.gsub(/\[{(.*)}\]/, '\1')} not found"
26
+ end
27
+ end
@@ -1,10 +1,10 @@
1
1
  module NoBrainer::Criteria::Index
2
2
  extend ActiveSupport::Concern
3
3
 
4
- included { attr_accessor :with_index_name }
4
+ included { criteria_option :use_index, :merge_with => :set_scalar }
5
5
 
6
6
  def with_index(index_name=true)
7
- chain { |criteria| criteria.with_index_name = index_name }
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.with_index_name == false
15
+ finalized_criteria.options[:use_index] == false
16
16
  end
17
17
 
18
18
  def used_index
19
- # only one of them will be active.
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
- if with_index_name && (!used_index || order_by_index_name.to_s == model.pk_name.to_s)
32
- # The implicit ordering on the indexed pk does not count.
33
- raise NoBrainer::Error::CannotUseIndex.new(with_index_name)
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,30 +1,23 @@
1
1
  module NoBrainer::Criteria::Limit
2
2
  extend ActiveSupport::Concern
3
3
 
4
- included { attr_accessor :_skip, :_limit }
4
+ included { criteria_option :skip, :limit, :merge_with => :set_scalar }
5
5
 
6
6
  def limit(value)
7
- chain { |criteria| criteria._limit = value }
7
+ chain(:limit => value)
8
8
  end
9
9
 
10
10
  def skip(value)
11
- chain { |criteria| criteria._skip = value }
11
+ chain(:skip => value)
12
12
  end
13
13
  alias_method :offset, :skip
14
14
 
15
- def merge!(criteria, options={})
16
- super
17
- self._skip = criteria._skip if criteria._skip
18
- self._limit = criteria._limit if criteria._limit
19
- self
20
- end
21
-
22
15
  private
23
16
 
24
17
  def compile_rql_pass2
25
18
  rql = super
26
- rql = rql.skip(_skip) if _skip
27
- rql = rql.limit(_limit) if _limit
19
+ rql = rql.skip(@options[:skip]) if @options[:skip]
20
+ rql = rql.limit(@options[:limit]) if @options[:limit]
28
21
  rql
29
22
  end
30
23
  end