nobrainer 0.27.0 → 0.28.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.
- checksums.yaml +4 -4
- data/lib/no_brainer/config.rb +36 -8
- data/lib/no_brainer/connection.rb +16 -19
- data/lib/no_brainer/connection_manager.rb +10 -10
- data/lib/no_brainer/criteria.rb +1 -1
- data/lib/no_brainer/criteria/eager_load.rb +1 -1
- data/lib/no_brainer/criteria/find.rb +1 -1
- data/lib/no_brainer/criteria/first.rb +2 -2
- data/lib/no_brainer/criteria/first_or_create.rb +32 -19
- data/lib/no_brainer/criteria/join.rb +62 -0
- data/lib/no_brainer/criteria/where.rb +25 -14
- data/lib/no_brainer/document.rb +1 -1
- data/lib/no_brainer/document/association/belongs_to.rb +4 -3
- data/lib/no_brainer/document/association/eager_loader.rb +26 -25
- data/lib/no_brainer/document/association/has_many.rb +3 -2
- data/lib/no_brainer/document/association/has_many_through.rb +1 -2
- data/lib/no_brainer/document/association/has_one.rb +4 -0
- data/lib/no_brainer/document/atomic_ops.rb +31 -4
- data/lib/no_brainer/document/attributes.rb +12 -9
- data/lib/no_brainer/document/core.rb +18 -18
- data/lib/no_brainer/document/criteria.rb +3 -2
- data/lib/no_brainer/document/dirty.rb +3 -3
- data/lib/no_brainer/document/index.rb +3 -3
- data/lib/no_brainer/document/index/index.rb +5 -5
- data/lib/no_brainer/document/index/meta_store.rb +1 -1
- data/lib/no_brainer/document/index/synchronizer.rb +5 -17
- data/lib/no_brainer/document/missing_attributes.rb +7 -2
- data/lib/no_brainer/document/primary_key.rb +14 -8
- data/lib/no_brainer/document/table_config.rb +118 -0
- data/lib/no_brainer/document/table_config/synchronizer.rb +21 -0
- data/lib/no_brainer/document/timestamps.rb +4 -0
- data/lib/no_brainer/document/validation/core.rb +1 -1
- data/lib/no_brainer/document/validation/uniqueness.rb +1 -1
- data/lib/no_brainer/lock.rb +4 -4
- data/lib/no_brainer/profiler/logger.rb +1 -1
- data/lib/no_brainer/query_runner/database_on_demand.rb +1 -1
- data/lib/no_brainer/query_runner/reconnect.rb +37 -21
- data/lib/no_brainer/query_runner/table_on_demand.rb +12 -5
- data/lib/no_brainer/railtie/database.rake +14 -4
- data/lib/no_brainer/rql.rb +3 -2
- data/lib/no_brainer/symbol_decoration.rb +1 -1
- data/lib/no_brainer/system.rb +17 -0
- data/lib/no_brainer/system/cluster_config.rb +5 -0
- data/lib/no_brainer/system/db_config.rb +5 -0
- data/lib/no_brainer/system/document.rb +24 -0
- data/lib/no_brainer/system/issue.rb +10 -0
- data/lib/no_brainer/system/job.rb +10 -0
- data/lib/no_brainer/system/log.rb +11 -0
- data/lib/no_brainer/system/server_config.rb +7 -0
- data/lib/no_brainer/system/server_status.rb +9 -0
- data/lib/no_brainer/system/stat.rb +11 -0
- data/lib/no_brainer/system/table_config.rb +10 -0
- data/lib/no_brainer/system/table_status.rb +8 -0
- data/lib/nobrainer.rb +7 -6
- data/lib/rails/generators/templates/nobrainer.rb +11 -2
- metadata +17 -3
- data/lib/no_brainer/document/store_in.rb +0 -33
    
        data/lib/no_brainer/document.rb
    CHANGED
    
    | @@ -4,7 +4,7 @@ module NoBrainer::Document | |
| 4 4 | 
             
              extend ActiveSupport::Concern
         | 
| 5 5 | 
             
              extend NoBrainer::Autoload
         | 
| 6 6 |  | 
| 7 | 
            -
              autoload_and_include :Core, : | 
| 7 | 
            +
              autoload_and_include :Core, :TableConfig, :InjectionLayer, :Attributes, :Readonly,
         | 
| 8 8 | 
             
                                   :Persistance, :Callbacks, :Validation, :Types, :Dirty, :PrimaryKey,
         | 
| 9 9 | 
             
                                   :Association, :Serialization, :Criteria, :Polymorphic, :Index, :Aliases,
         | 
| 10 10 | 
             
                                   :MissingAttributes, :LazyFetch, :AtomicOps
         | 
| @@ -4,7 +4,7 @@ class NoBrainer::Document::Association::BelongsTo | |
| 4 4 | 
             
              class Metadata
         | 
| 5 5 | 
             
                VALID_OPTIONS = [:primary_key, :foreign_key, :class_name, :foreign_key_store_as, :index, :validates, :required]
         | 
| 6 6 | 
             
                include NoBrainer::Document::Association::Core::Metadata
         | 
| 7 | 
            -
                 | 
| 7 | 
            +
                include NoBrainer::Document::Association::EagerLoader::Generic
         | 
| 8 8 |  | 
| 9 9 | 
             
                def foreign_key
         | 
| 10 10 | 
             
                  options[:foreign_key].try(:to_sym) || :"#{target_name}_#{primary_key}"
         | 
| @@ -34,7 +34,7 @@ class NoBrainer::Document::Association::BelongsTo | |
| 34 34 | 
             
                end
         | 
| 35 35 |  | 
| 36 36 | 
             
                def base_criteria
         | 
| 37 | 
            -
                  target_model.unscoped
         | 
| 37 | 
            +
                  target_model.without_ordering.unscoped
         | 
| 38 38 | 
             
                end
         | 
| 39 39 |  | 
| 40 40 | 
             
                def hook
         | 
| @@ -68,7 +68,8 @@ class NoBrainer::Document::Association::BelongsTo | |
| 68 68 | 
             
                  add_callback_for(:after_validation)
         | 
| 69 69 | 
             
                end
         | 
| 70 70 |  | 
| 71 | 
            -
                 | 
| 71 | 
            +
                def eager_load_owner_key;  foreign_key; end
         | 
| 72 | 
            +
                def eager_load_target_key; primary_key; end
         | 
| 72 73 | 
             
              end
         | 
| 73 74 |  | 
| 74 75 | 
             
              # Note:
         | 
| @@ -1,31 +1,32 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            module NoBrainer::Document::Association::EagerLoader
         | 
| 2 | 
            +
              extend self
         | 
| 3 | 
            +
             | 
| 2 4 | 
             
              module Generic
         | 
| 3 5 | 
             
                # Used in associations to declare generic eager loading capabilities
         | 
| 4 | 
            -
                # The association should implement loaded | 
| 5 | 
            -
                 | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
                    owner_keys = unloaded_docs.map(&owner_key).compact.uniq
         | 
| 16 | 
            -
                    if owner_keys.present?
         | 
| 17 | 
            -
                      targets = criteria.where(target_key.in => owner_keys)
         | 
| 18 | 
            -
                                        .map { |target| [target.read_attribute(target_key), target] }
         | 
| 19 | 
            -
                                        .each_with_object(Hash.new { |k,v| k[v] = [] }) { |(k,v),h| h[k] << v }
         | 
| 20 | 
            -
             | 
| 21 | 
            -
                      unloaded_docs.each do |doc|
         | 
| 22 | 
            -
                        doc_targets = targets[doc.read_attribute(owner_key)]
         | 
| 23 | 
            -
                        doc.associations[self].preload(doc_targets)
         | 
| 24 | 
            -
                      end
         | 
| 25 | 
            -
                    end
         | 
| 6 | 
            +
                # The association should implement loaded?, preload,
         | 
| 7 | 
            +
                # eager_load_owner_key and eager_load_target_key.
         | 
| 8 | 
            +
                def eager_load(docs, additional_criteria=nil)
         | 
| 9 | 
            +
                  owner_key  = eager_load_owner_key
         | 
| 10 | 
            +
                  target_key = eager_load_target_key
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  criteria = base_criteria
         | 
| 13 | 
            +
                  criteria = criteria.merge(additional_criteria) if additional_criteria
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  unloaded_docs = docs.reject { |doc| doc.associations[self].loaded? }
         | 
| 26 16 |  | 
| 27 | 
            -
             | 
| 17 | 
            +
                  owner_keys = unloaded_docs.map(&owner_key).compact.uniq
         | 
| 18 | 
            +
                  if owner_keys.present?
         | 
| 19 | 
            +
                    targets = criteria.where(target_key.in => owner_keys)
         | 
| 20 | 
            +
                    .map { |target| [target.read_attribute(target_key), target] }
         | 
| 21 | 
            +
                    .each_with_object(Hash.new { |k,v| k[v] = [] }) { |(k,v),h| h[k] << v }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    unloaded_docs.each do |doc|
         | 
| 24 | 
            +
                      doc_targets = targets[doc.read_attribute(owner_key)]
         | 
| 25 | 
            +
                      doc.associations[self].preload(doc_targets)
         | 
| 26 | 
            +
                    end
         | 
| 28 27 | 
             
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  docs.map { |doc| doc.associations[self].read }.flatten.compact.uniq
         | 
| 29 30 | 
             
                end
         | 
| 30 31 | 
             
              end
         | 
| 31 32 |  | 
| @@ -40,7 +41,7 @@ class NoBrainer::Document::Association::EagerLoader | |
| 40 41 |  | 
| 41 42 | 
             
              def eager_load(docs, what)
         | 
| 42 43 | 
             
                case what
         | 
| 43 | 
            -
                when Hash | 
| 44 | 
            +
                when Hash then what.each do |k,v|
         | 
| 44 45 | 
             
                  if v.is_a?(NoBrainer::Criteria)
         | 
| 45 46 | 
             
                    v = v.dup
         | 
| 46 47 | 
             
                    nested_preloads, v.options[:eager_load] = v.options[:eager_load], []
         | 
| @@ -4,7 +4,7 @@ class NoBrainer::Document::Association::HasMany | |
| 4 4 | 
             
              class Metadata
         | 
| 5 5 | 
             
                VALID_OPTIONS = [:primary_key, :foreign_key, :class_name, :dependent, :scope]
         | 
| 6 6 | 
             
                include NoBrainer::Document::Association::Core::Metadata
         | 
| 7 | 
            -
                 | 
| 7 | 
            +
                include NoBrainer::Document::Association::EagerLoader::Generic
         | 
| 8 8 |  | 
| 9 9 | 
             
                def foreign_key
         | 
| 10 10 | 
             
                  options[:foreign_key].try(:to_sym) || :"#{owner_model.name.split('::').last.underscore}_#{primary_key}"
         | 
| @@ -53,7 +53,8 @@ class NoBrainer::Document::Association::HasMany | |
| 53 53 | 
             
                  end
         | 
| 54 54 | 
             
                end
         | 
| 55 55 |  | 
| 56 | 
            -
                 | 
| 56 | 
            +
                def eager_load_owner_key;  primary_key; end
         | 
| 57 | 
            +
                def eager_load_target_key; foreign_key; end
         | 
| 57 58 | 
             
              end
         | 
| 58 59 |  | 
| 59 60 | 
             
              def target_criteria
         | 
| @@ -17,7 +17,7 @@ class NoBrainer::Document::Association::HasManyThrough | |
| 17 17 | 
             
                def eager_load(docs, additional_criteria=nil)
         | 
| 18 18 | 
             
                  criteria = target_model.instance_exec(&options[:scope]) if options[:scope]
         | 
| 19 19 | 
             
                  criteria = criteria ? criteria.merge(additional_criteria) : additional_criteria if additional_criteria
         | 
| 20 | 
            -
                  NoBrainer::Document::Association::EagerLoader | 
| 20 | 
            +
                  NoBrainer::Document::Association::EagerLoader
         | 
| 21 21 | 
             
                    .eager_load_association(through_association.eager_load(docs), target_name, criteria)
         | 
| 22 22 | 
             
                end
         | 
| 23 23 |  | 
| @@ -29,7 +29,6 @@ class NoBrainer::Document::Association::HasManyThrough | |
| 29 29 | 
             
              end
         | 
| 30 30 |  | 
| 31 31 | 
             
              def read
         | 
| 32 | 
            -
                # TODO implement joins
         | 
| 33 32 | 
             
                @targets ||= metadata.eager_load([owner]).freeze
         | 
| 34 33 | 
             
              end
         | 
| 35 34 |  | 
| @@ -42,7 +42,7 @@ module NoBrainer::Document::AtomicOps | |
| 42 42 | 
             
                def to_s
         | 
| 43 43 | 
             
                  "<`#{@field}' with #{@ops.size} pending atomic operations>"
         | 
| 44 44 | 
             
                end
         | 
| 45 | 
            -
                 | 
| 45 | 
            +
                def inspect; to_s; end
         | 
| 46 46 |  | 
| 47 47 | 
             
                def method_missing(method, *a, &b)
         | 
| 48 48 | 
             
                  if method == :<<
         | 
| @@ -131,6 +131,18 @@ module NoBrainer::Document::AtomicOps | |
| 131 131 | 
             
                end
         | 
| 132 132 | 
             
              end
         | 
| 133 133 |  | 
| 134 | 
            +
              class PendingAtomicUnset < PendingAtomic
         | 
| 135 | 
            +
                def to_s
         | 
| 136 | 
            +
                  "<unset `#{@field}'>"
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                undef_method(:method_missing)
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                def compile_rql_value(rql_doc)
         | 
| 142 | 
            +
                  RethinkDB::RQL.new.literal()
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
              end
         | 
| 145 | 
            +
             | 
| 134 146 | 
             
              def clear_dirtiness(options={})
         | 
| 135 147 | 
             
                super
         | 
| 136 148 | 
             
                @_touched_attributes = Set.new
         | 
| @@ -172,13 +184,18 @@ module NoBrainer::Document::AtomicOps | |
| 172 184 | 
             
              end
         | 
| 173 185 |  | 
| 174 186 | 
             
              def _read_attribute(name)
         | 
| 187 | 
            +
                return super if name == self.class.pk_name
         | 
| 188 | 
            +
             | 
| 175 189 | 
             
                ensure_exclusive_atomic!
         | 
| 176 | 
            -
                 | 
| 177 | 
            -
             | 
| 190 | 
            +
                return super unless in_atomic?
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                # If we are missing fields, it's okay, we'll assume nil.
         | 
| 193 | 
            +
                value = missing_field?(name) ? nil : super
         | 
| 178 194 |  | 
| 179 195 | 
             
                case value
         | 
| 180 196 | 
             
                when PendingAtomicContainer then value
         | 
| 181 | 
            -
                when  | 
| 197 | 
            +
                when PendingAtomicUnset     then raise "Attribute `#{name}' is unset"
         | 
| 198 | 
            +
                when PendingAtomic          then value.dup
         | 
| 182 199 | 
             
                else PendingAtomic._new(self, name, value, _is_attribute_touched?(name))
         | 
| 183 200 | 
             
                end
         | 
| 184 201 | 
             
              end
         | 
| @@ -199,6 +216,11 @@ module NoBrainer::Document::AtomicOps | |
| 199 216 | 
             
                super
         | 
| 200 217 | 
             
              end
         | 
| 201 218 |  | 
| 219 | 
            +
              def unset(attr)
         | 
| 220 | 
            +
                return queue_atomic { unset(attr) } unless in_atomic?
         | 
| 221 | 
            +
                _write_attribute(attr, PendingAtomicUnset.new(self, attr, nil, true, nil))
         | 
| 222 | 
            +
              end
         | 
| 223 | 
            +
             | 
| 202 224 | 
             
              def save?(options={})
         | 
| 203 225 | 
             
                # TODO allow reload => true as an option to save+reload in a single op.
         | 
| 204 226 | 
             
                raise NoBrainer::Error::AtomicBlock.new('You may persist documents only outside of queue_atomic blocks') if in_atomic?
         | 
| @@ -212,6 +234,11 @@ module NoBrainer::Document::AtomicOps | |
| 212 234 | 
             
                end
         | 
| 213 235 | 
             
              end
         | 
| 214 236 |  | 
| 237 | 
            +
              def reload(*)
         | 
| 238 | 
            +
                raise NoBrainer::Error::AtomicBlock.new('You may not reload fields within an atomic block') if in_atomic?
         | 
| 239 | 
            +
                super
         | 
| 240 | 
            +
              end
         | 
| 241 | 
            +
             | 
| 215 242 | 
             
              module ClassMethods
         | 
| 216 243 | 
             
                def persistable_value(k, v, options={})
         | 
| 217 244 | 
             
                  v.is_a?(PendingAtomic) ? v.compile_rql_value(options[:rql_doc]) : super
         | 
| @@ -49,8 +49,10 @@ module NoBrainer::Document::Attributes | |
| 49 49 |  | 
| 50 50 | 
             
              def assign_defaults(options)
         | 
| 51 51 | 
             
                self.class.fields.each do |name, field_options|
         | 
| 52 | 
            -
                   | 
| 53 | 
            -
             | 
| 52 | 
            +
                  # :default => nil will not set the value to nil, but :default => ->{ nil } will.
         | 
| 53 | 
            +
                  # This is useful to unset a default value.
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  next if field_options[:default].nil? || @_attributes.key?(name)
         | 
| 54 56 |  | 
| 55 57 | 
             
                  if opt = options[:missing_attributes]
         | 
| 56 58 | 
             
                    if (opt[:pluck] && !opt[:pluck][name]) ||
         | 
| @@ -113,8 +115,14 @@ module NoBrainer::Document::Attributes | |
| 113 115 | 
             
                  super
         | 
| 114 116 | 
             
                end
         | 
| 115 117 |  | 
| 118 | 
            +
                # The different between _field and field is that field can set other options
         | 
| 119 | 
            +
                # (c.f. primary key module). _field always receive an immutable options list.
         | 
| 116 120 | 
             
                def _field(attr, options={})
         | 
| 117 | 
            -
                   | 
| 121 | 
            +
                  options.assert_valid_keys(*VALID_FIELD_OPTIONS)
         | 
| 122 | 
            +
                  if attr.in?(RESERVED_FIELD_NAMES)
         | 
| 123 | 
            +
                    raise "The field name `:#{attr}' is reserved. Please use another one."
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
             | 
| 118 126 | 
             
                  attr = attr.to_s
         | 
| 119 127 | 
             
                  inject_in_layer :attributes do
         | 
| 120 128 | 
             
                    define_method("#{attr}=") { |value| _write_attribute(attr, value) }
         | 
| @@ -125,11 +133,6 @@ module NoBrainer::Document::Attributes | |
| 125 133 | 
             
                def field(attr, options={})
         | 
| 126 134 | 
             
                  attr = attr.to_sym
         | 
| 127 135 |  | 
| 128 | 
            -
                  options.assert_valid_keys(*VALID_FIELD_OPTIONS)
         | 
| 129 | 
            -
                  if attr.in?(RESERVED_FIELD_NAMES)
         | 
| 130 | 
            -
                    raise "Cannot use a reserved field attr: #{attr}"
         | 
| 131 | 
            -
                  end
         | 
| 132 | 
            -
             | 
| 133 136 | 
             
                  subclass_tree.each do |subclass|
         | 
| 134 137 | 
             
                    subclass.fields[attr] ||= {}
         | 
| 135 138 | 
             
                    subclass.fields[attr].deep_merge!(options)
         | 
| @@ -161,7 +164,7 @@ module NoBrainer::Document::Attributes | |
| 161 164 |  | 
| 162 165 | 
             
                def ensure_valid_key!(key)
         | 
| 163 166 | 
             
                  return if has_field?(key) || has_index?(key)
         | 
| 164 | 
            -
                  raise NoBrainer::Error::UnknownAttribute, "`#{key}' is not a  | 
| 167 | 
            +
                  raise NoBrainer::Error::UnknownAttribute, "`#{key}' is not a valid attribute of #{self}"
         | 
| 165 168 | 
             
                end
         | 
| 166 169 | 
             
              end
         | 
| 167 170 | 
             
            end
         | 
| @@ -1,30 +1,30 @@ | |
| 1 1 | 
             
            module NoBrainer::Document::Core
         | 
| 2 2 | 
             
              extend ActiveSupport::Concern
         | 
| 3 3 |  | 
| 4 | 
            -
              singleton_class.class_eval  | 
| 5 | 
            -
                attr_accessor :_all, :_all_nobrainer
         | 
| 6 | 
            -
             | 
| 7 | 
            -
                def all
         | 
| 8 | 
            -
                  Rails.application.eager_load! if defined?(Rails.application.eager_load!)
         | 
| 9 | 
            -
                  @_all
         | 
| 10 | 
            -
                end
         | 
| 11 | 
            -
              end
         | 
| 4 | 
            +
              singleton_class.class_eval { attr_accessor :_all }
         | 
| 12 5 | 
             
              self._all = []
         | 
| 13 | 
            -
              self._all_nobrainer = []
         | 
| 14 | 
            -
             | 
| 15 | 
            -
              include ActiveModel::Conversion
         | 
| 16 | 
            -
             | 
| 17 | 
            -
              def to_key
         | 
| 18 | 
            -
                 # ActiveModel::Conversion stuff
         | 
| 19 | 
            -
                [pk_value]
         | 
| 20 | 
            -
              end
         | 
| 21 6 |  | 
| 22 7 | 
             
              included do
         | 
| 23 8 | 
             
                # TODO test these includes
         | 
| 24 9 | 
             
                extend ActiveModel::Naming
         | 
| 25 10 | 
             
                extend ActiveModel::Translation
         | 
| 26 11 |  | 
| 27 | 
            -
                 | 
| 28 | 
            -
             | 
| 12 | 
            +
                NoBrainer::Document::Core._all << self unless name =~ /^NoBrainer::/
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              def self.all(options={})
         | 
| 16 | 
            +
                (options[:types] || [:user]).map do |type|
         | 
| 17 | 
            +
                  case type
         | 
| 18 | 
            +
                  when :user
         | 
| 19 | 
            +
                    Rails.application.eager_load! if defined?(Rails.application.eager_load!)
         | 
| 20 | 
            +
                    _all
         | 
| 21 | 
            +
                  when :nobrainer
         | 
| 22 | 
            +
                    [NoBrainer::Document::Index::MetaStore, NoBrainer::Lock]
         | 
| 23 | 
            +
                  when :system
         | 
| 24 | 
            +
                    NoBrainer::System.constants
         | 
| 25 | 
            +
                      .map { |c| NoBrainer::System.const_get(c) }
         | 
| 26 | 
            +
                      .select { |m| m < NoBrainer::Document }
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end.reduce([], &:+)
         | 
| 29 29 | 
             
              end
         | 
| 30 30 | 
             
            end
         | 
| @@ -18,7 +18,7 @@ module NoBrainer::Document::Criteria | |
| 18 18 | 
             
                         :limit, :offset, :skip,         # Limit
         | 
| 19 19 | 
             
                         :order_by, :reverse_order, :without_ordering, :order_by_indexed?, :order_by_index_name, # OrderBy
         | 
| 20 20 | 
             
                         :unscoped,                      # Scope
         | 
| 21 | 
            -
                         :where, :where_indexed?, :where_index_name, :where_index_type, # Where
         | 
| 21 | 
            +
                         :_where, :where, :where_indexed?, :where_index_name, :where_index_type, # Where
         | 
| 22 22 | 
             
                         :with_index, :without_index, :used_index, # Index
         | 
| 23 23 | 
             
                         :with_cache, :without_cache,    # Cache
         | 
| 24 24 | 
             
                         :count, :empty?, :any?,         # Count
         | 
| @@ -26,12 +26,13 @@ module NoBrainer::Document::Criteria | |
| 26 26 | 
             
                         :preload, :eager_load,          # EagerLoad
         | 
| 27 27 | 
             
                         :each, :to_a,                   # Enumerable
         | 
| 28 28 | 
             
                         :first, :last, :first!, :last!, :sample, # First
         | 
| 29 | 
            -
                         :first_or_create, :first_or_create!, # FirstOrCreate
         | 
| 29 | 
            +
                         :upsert, :upsert!, :first_or_create, :first_or_create!, # FirstOrCreate
         | 
| 30 30 | 
             
                         :min, :max, :sum, :avg,         # Aggregate
         | 
| 31 31 | 
             
                         :update_all, :replace_all,      # Update
         | 
| 32 32 | 
             
                         :changes,                       # Changes
         | 
| 33 33 | 
             
                         :pluck, :without, :lazy_fetch, :without_plucking, # Pluck
         | 
| 34 34 | 
             
                         :find_by?, :find_by, :find_by!, :find?, :find, :find!, # Find
         | 
| 35 | 
            +
                         :join,                          #Join
         | 
| 35 36 | 
             
                         :to => :all
         | 
| 36 37 |  | 
| 37 38 | 
             
                def all
         | 
| @@ -56,7 +56,7 @@ module NoBrainer::Document::Dirty | |
| 56 56 | 
             
                  end
         | 
| 57 57 | 
             
                end
         | 
| 58 58 |  | 
| 59 | 
            -
                unless @_old_attributes. | 
| 59 | 
            +
                unless @_old_attributes.key?(attr)
         | 
| 60 60 | 
             
                  @_old_attributes[attr] = current_value.deep_dup
         | 
| 61 61 | 
             
                end
         | 
| 62 62 | 
             
              end
         | 
| @@ -81,7 +81,7 @@ module NoBrainer::Document::Dirty | |
| 81 81 |  | 
| 82 82 | 
             
                  inject_in_layer :dirty_tracking do
         | 
| 83 83 | 
             
                    define_method("#{attr}_change") do
         | 
| 84 | 
            -
                      if @_old_attributes. | 
| 84 | 
            +
                      if @_old_attributes.key?(attr)
         | 
| 85 85 | 
             
                        result = [@_old_attributes[attr], _read_attribute(attr)]
         | 
| 86 86 | 
             
                        result if result.first != result.last || !@_old_attributes_keys.include?(attr)
         | 
| 87 87 | 
             
                      end
         | 
| @@ -92,7 +92,7 @@ module NoBrainer::Document::Dirty | |
| 92 92 | 
             
                    end
         | 
| 93 93 |  | 
| 94 94 | 
             
                    define_method("#{attr}_was") do
         | 
| 95 | 
            -
                      @_old_attributes. | 
| 95 | 
            +
                      @_old_attributes.key?(attr) ? @_old_attributes[attr] : _read_attribute(attr)
         | 
| 96 96 | 
             
                    end
         | 
| 97 97 | 
             
                  end
         | 
| 98 98 | 
             
                end
         | 
| @@ -26,11 +26,11 @@ module NoBrainer::Document::Index | |
| 26 26 | 
             
                  end
         | 
| 27 27 |  | 
| 28 28 | 
             
                  if name.in?(NoBrainer::Document::Attributes::RESERVED_FIELD_NAMES)
         | 
| 29 | 
            -
                    raise " | 
| 29 | 
            +
                    raise "The index name `:#{name}' is reserved. Please use another one."
         | 
| 30 30 | 
             
                  end
         | 
| 31 31 |  | 
| 32 32 | 
             
                  if has_field?(name) && kind != :single
         | 
| 33 | 
            -
                    raise " | 
| 33 | 
            +
                    raise "The field `#{name}' is already declared. Please remove its definition first."
         | 
| 34 34 | 
             
                  end
         | 
| 35 35 |  | 
| 36 36 | 
             
                  if kind == :compound && what.size < 2
         | 
| @@ -56,7 +56,7 @@ module NoBrainer::Document::Index | |
| 56 56 |  | 
| 57 57 | 
             
                def _field(attr, options={})
         | 
| 58 58 | 
             
                  if has_index?(attr) && indexes[attr].kind != :single
         | 
| 59 | 
            -
                    raise " | 
| 59 | 
            +
                    raise "The index `#{attr}' is already declared. Please remove its definition first."
         | 
| 60 60 | 
             
                  end
         | 
| 61 61 |  | 
| 62 62 | 
             
                  super
         | 
| @@ -1,8 +1,6 @@ | |
| 1 1 | 
             
            class NoBrainer::Document::Index::Index < Struct.new(
         | 
| 2 2 | 
             
                :model, :name, :aliased_name, :kind, :what, :external, :geo, :multi, :meta)
         | 
| 3 3 |  | 
| 4 | 
            -
              MetaStore = NoBrainer::Document::Index::MetaStore
         | 
| 5 | 
            -
             | 
| 6 4 | 
             
              def initialize(*args)
         | 
| 7 5 | 
             
                super
         | 
| 8 6 |  | 
| @@ -59,8 +57,9 @@ class NoBrainer::Document::Index::Index < Struct.new( | |
| 59 57 | 
             
                NoBrainer::RQL.reset_lambda_var_counter
         | 
| 60 58 | 
             
                NoBrainer.run(model.rql_table.index_create(aliased_name, opt, &rql_proc))
         | 
| 61 59 |  | 
| 62 | 
            -
                MetaStore.create( | 
| 63 | 
            -
             | 
| 60 | 
            +
                NoBrainer::Document::Index::MetaStore.create(
         | 
| 61 | 
            +
                  :table_name => model.table_name, :index_name => aliased_name,
         | 
| 62 | 
            +
                  :rql_function => serialized_rql_proc)
         | 
| 64 63 | 
             
              end
         | 
| 65 64 |  | 
| 66 65 | 
             
              def delete(options={})
         | 
| @@ -68,7 +67,8 @@ class NoBrainer::Document::Index::Index < Struct.new( | |
| 68 67 |  | 
| 69 68 | 
             
                NoBrainer.run(model.rql_table.index_drop(aliased_name))
         | 
| 70 69 |  | 
| 71 | 
            -
                MetaStore.where( | 
| 70 | 
            +
                NoBrainer::Document::Index::MetaStore.where(
         | 
| 71 | 
            +
                  :table_name => model.table_name, :index_name => aliased_name).delete_all
         | 
| 72 72 | 
             
              end
         | 
| 73 73 |  | 
| 74 74 | 
             
              def update(wanted_index, options={})
         | 
| @@ -6,7 +6,7 @@ class NoBrainer::Document::Index::MetaStore | |
| 6 6 |  | 
| 7 7 | 
             
              default_scope ->{ order_by(:created_at) }
         | 
| 8 8 |  | 
| 9 | 
            -
               | 
| 9 | 
            +
              table_config :name => 'nobrainer_index_meta'
         | 
| 10 10 |  | 
| 11 11 | 
             
              field :table_name,   :type => String, :required => true
         | 
| 12 12 | 
             
              field :index_name,   :type => String, :required => true
         | 
| @@ -1,7 +1,4 @@ | |
| 1 1 | 
             
            class NoBrainer::Document::Index::Synchronizer
         | 
| 2 | 
            -
              Index = NoBrainer::Document::Index::Index
         | 
| 3 | 
            -
              MetaStore = NoBrainer::Document::Index::MetaStore
         | 
| 4 | 
            -
             | 
| 5 2 | 
             
              def initialize(models)
         | 
| 6 3 | 
             
                @models_indexes_map = Hash[models.map do |model|
         | 
| 7 4 | 
             
                  [model, model.indexes.values.reject { |index| index.name == model.pk_name }]
         | 
| @@ -9,7 +6,7 @@ class NoBrainer::Document::Index::Synchronizer | |
| 9 6 | 
             
              end
         | 
| 10 7 |  | 
| 11 8 | 
             
              def meta_store
         | 
| 12 | 
            -
                @meta_store ||= MetaStore.to_a
         | 
| 9 | 
            +
                @meta_store ||= NoBrainer::Document::Index::MetaStore.to_a
         | 
| 13 10 | 
             
              end
         | 
| 14 11 |  | 
| 15 12 | 
             
              class Op < Struct.new(:index, :op, :args)
         | 
| @@ -21,7 +18,8 @@ class NoBrainer::Document::Index::Synchronizer | |
| 21 18 | 
             
              def _generate_plan_for(model, wanted_indexes)
         | 
| 22 19 | 
             
                current_indexes = NoBrainer.run(model.rql_table.index_status).map do |s|
         | 
| 23 20 | 
             
                  meta = meta_store.select { |i| i.table_name == model.table_name && i.index_name == s['index'] }.last
         | 
| 24 | 
            -
                  Index.new( | 
| 21 | 
            +
                  NoBrainer::Document::Index::Index.new(
         | 
| 22 | 
            +
                    model, s['index'], s['index'], nil, nil, nil, s['geo'], s['multi'], meta)
         | 
| 25 23 | 
             
                end
         | 
| 26 24 |  | 
| 27 25 | 
             
                all_aliased_names = (wanted_indexes + current_indexes).map(&:aliased_name).uniq
         | 
| @@ -51,13 +49,11 @@ class NoBrainer::Document::Index::Synchronizer | |
| 51 49 | 
             
                lock = NoBrainer::Lock.new('nobrainer:sync_indexes')
         | 
| 52 50 |  | 
| 53 51 | 
             
                lock.synchronize do
         | 
| 54 | 
            -
                   | 
| 55 | 
            -
                  plan.each { |op| lock.refresh; op.run(options) }
         | 
| 52 | 
            +
                  generate_plan.each { |op| op.run(options) }
         | 
| 56 53 | 
             
                end
         | 
| 57 54 |  | 
| 58 55 | 
             
                unless options[:wait] == false
         | 
| 59 | 
            -
                  # Waiting on all models due to possible races | 
| 60 | 
            -
                  # sync_indexes()
         | 
| 56 | 
            +
                  # Waiting on all models due to possible races
         | 
| 61 57 | 
             
                  @models_indexes_map.each_key do |model|
         | 
| 62 58 | 
             
                    NoBrainer.run(model.rql_table.index_wait())
         | 
| 63 59 | 
             
                  end
         | 
| @@ -65,12 +61,4 @@ class NoBrainer::Document::Index::Synchronizer | |
| 65 61 |  | 
| 66 62 | 
             
                true
         | 
| 67 63 | 
             
              end
         | 
| 68 | 
            -
             | 
| 69 | 
            -
              class << self
         | 
| 70 | 
            -
                def instance
         | 
| 71 | 
            -
                  new(NoBrainer::Document.all)
         | 
| 72 | 
            -
                end
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                delegate :sync_indexes, :to => :instance
         | 
| 75 | 
            -
              end
         | 
| 76 64 | 
             
            end
         |