nobrainer 0.29.0 → 0.30.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/criteria.rb +5 -4
 - data/lib/no_brainer/criteria/first_or_create.rb +34 -7
 - data/lib/no_brainer/criteria/join.rb +4 -3
 - data/lib/no_brainer/criteria/virtual_attributes.rb +18 -0
 - data/lib/no_brainer/criteria/where.rb +3 -2
 - data/lib/no_brainer/document.rb +1 -1
 - data/lib/no_brainer/document/attributes.rb +1 -1
 - data/lib/no_brainer/document/index.rb +1 -0
 - data/lib/no_brainer/document/persistance.rb +7 -12
 - data/lib/no_brainer/document/virtual_attributes.rb +52 -0
 - data/lib/no_brainer/error.rb +1 -0
 - data/lib/no_brainer/lock.rb +56 -29
 - data/lib/no_brainer/profiler/controller_runtime.rb +2 -0
 - data/lib/no_brainer/reentrant_lock.rb +36 -0
 - data/lib/nobrainer.rb +1 -1
 - metadata +5 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: ebe188d50e06d6ef907cdf043560143e3bd7d703
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 15b1580f27a66b6670386cc117b4767f930e67c7
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: ff9b387f2045109c33efb88d36273325edf34573acb134bbb94d3d2e04c1efd6fdf65b761739eac61c185309cfa9deba5a07987fdf56becf3d78308b300838b9
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: acb0f33b44d27ac25599352b0d568dd563e13a81da91b0dfef6cf72596a1500026e739f2cc64c63e22edc72ddb57ee3b61a70acf72c139cd502f0b1d95e78c9b
         
     | 
    
        data/lib/no_brainer/criteria.rb
    CHANGED
    
    | 
         @@ -2,8 +2,9 @@ require 'rethinkdb' 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            class NoBrainer::Criteria
         
     | 
| 
       4 
4 
     | 
    
         
             
              extend NoBrainer::Autoload
         
     | 
| 
       5 
     | 
    
         
            -
              autoload_and_include :Core, :Run, :Raw, : 
     | 
| 
       6 
     | 
    
         
            -
                                   : 
     | 
| 
       7 
     | 
    
         
            -
                                   : 
     | 
| 
       8 
     | 
    
         
            -
                                   : 
     | 
| 
      
 5 
     | 
    
         
            +
              autoload_and_include :Core, :Run, :Raw, :VirtualAttributes, :Scope,
         
     | 
| 
      
 6 
     | 
    
         
            +
                                   :AfterFind, :Where, :OrderBy, :Limit, :Pluck, :Count,
         
     | 
| 
      
 7 
     | 
    
         
            +
                                   :Delete, :Enumerable, :Find, :First, :FirstOrCreate,
         
     | 
| 
      
 8 
     | 
    
         
            +
                                   :Changes, :Aggregate, :EagerLoad, :Update, :Cache,
         
     | 
| 
      
 9 
     | 
    
         
            +
                                   :Index, :Extend, :Join
         
     | 
| 
       9 
10 
     | 
    
         
             
            end
         
     | 
| 
         @@ -38,7 +38,11 @@ module NoBrainer::Criteria::FirstOrCreate 
     | 
|
| 
       38 
38 
     | 
    
         | 
| 
       39 
39 
     | 
    
         
             
              def _first_or_create(create_params, save_options, &block)
         
     | 
| 
       40 
40 
     | 
    
         
             
                raise "Cannot use .raw() with .first_or_create()" if raw?
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                if block && block.arity == 1
         
     | 
| 
      
 43 
     | 
    
         
            +
                  raise "When passing a block to first_or_create(), you must pass a block with no arguments.\n" +
         
     | 
| 
      
 44 
     | 
    
         
            +
                        "The passed block must return a hash of additional attributes for create()"
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
       42 
46 
     | 
    
         | 
| 
       43 
47 
     | 
    
         
             
                save_method = save_options.delete(:save_method)
         
     | 
| 
       44 
48 
     | 
    
         
             
                should_update = save_options.delete(:update)
         
     | 
| 
         @@ -62,6 +66,25 @@ module NoBrainer::Criteria::FirstOrCreate 
     | 
|
| 
       62 
66 
     | 
    
         
             
                    "\nend"
         
     | 
| 
       63 
67 
     | 
    
         
             
                end
         
     | 
| 
       64 
68 
     | 
    
         | 
| 
      
 69 
     | 
    
         
            +
                unless model.is_root_class? || (model.superclass.fields.keys & keys).empty?
         
     | 
| 
      
 70 
     | 
    
         
            +
                  # We can't allow the parent to share the keys we are matching on.
         
     | 
| 
      
 71 
     | 
    
         
            +
                  # Consider this case:
         
     | 
| 
      
 72 
     | 
    
         
            +
                  # - Base has the field :name, :uniq => true declared
         
     | 
| 
      
 73 
     | 
    
         
            +
                  # - A < Base
         
     | 
| 
      
 74 
     | 
    
         
            +
                  # - B < Base
         
     | 
| 
      
 75 
     | 
    
         
            +
                  # - A.create(:name => 'x'),
         
     | 
| 
      
 76 
     | 
    
         
            +
                  # - B.where(:name => 'x').first_or_create
         
     | 
| 
      
 77 
     | 
    
         
            +
                  # We are forced to return nil, or raise.
         
     | 
| 
      
 78 
     | 
    
         
            +
                  parent = model
         
     | 
| 
      
 79 
     | 
    
         
            +
                  parent = parent.superclass while parent.superclass < NoBrainer::Document &&
         
     | 
| 
      
 80 
     | 
    
         
            +
                                                  !(parent.superclass.fields.keys & keys).empty?
         
     | 
| 
      
 81 
     | 
    
         
            +
                  raise "A polymorphic problem has been detected: The fields `#{keys.inspect}' are defined on `#{parent}'.\n" +
         
     | 
| 
      
 82 
     | 
    
         
            +
                        "This is problematic as first_or_create() could return nil in some cases.\n" +
         
     | 
| 
      
 83 
     | 
    
         
            +
                        "Either 1) Only define `#{keys.inspect}' on `#{model}', \n" +
         
     | 
| 
      
 84 
     | 
    
         
            +
                        "or     2) Query the superclass, and pass :_type in first_or_create() as such:\n" +
         
     | 
| 
      
 85 
     | 
    
         
            +
                        "          `#{parent}.where(...).first_or_create(:_type => \"#{model}\")'."
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
       65 
88 
     | 
    
         
             
                # We don't want to access create_params yet, because invoking the block
         
     | 
| 
       66 
89 
     | 
    
         
             
                # might be costly (the user might be doing some API call or w/e), and
         
     | 
| 
       67 
90 
     | 
    
         
             
                # so we want to invoke the block only if necessary.
         
     | 
| 
         @@ -108,18 +131,22 @@ module NoBrainer::Criteria::FirstOrCreate 
     | 
|
| 
       108 
131 
     | 
    
         
             
                where_clauses = finalized_criteria.options[:where_ast]
         
     | 
| 
       109 
132 
     | 
    
         | 
| 
       110 
133 
     | 
    
         
             
                unless where_clauses.is_a?(NoBrainer::Criteria::Where::MultiOperator) &&
         
     | 
| 
       111 
     | 
    
         
            -
                       where_clauses.op == :and 
     | 
| 
       112 
     | 
    
         
            -
                       where_clauses.clauses.all? do |c|
         
     | 
| 
       113 
     | 
    
         
            -
                         c.is_a?(NoBrainer::Criteria::Where::BinaryOperator) &&
         
     | 
| 
       114 
     | 
    
         
            -
                         c.op == :eq && c.key_modifier == :scalar
         
     | 
| 
       115 
     | 
    
         
            -
                       end
         
     | 
| 
      
 134 
     | 
    
         
            +
                       where_clauses.op == :and
         
     | 
| 
       116 
135 
     | 
    
         
             
                  raise "Please use a query of the form `.where(...).first_or_create(...)'"
         
     | 
| 
       117 
136 
     | 
    
         
             
                end
         
     | 
| 
       118 
137 
     | 
    
         | 
| 
       119 
138 
     | 
    
         
             
                Hash[where_clauses.clauses.map do |c|
         
     | 
| 
      
 139 
     | 
    
         
            +
                  unless c.is_a?(NoBrainer::Criteria::Where::BinaryOperator) &&
         
     | 
| 
      
 140 
     | 
    
         
            +
                         c.op == :eq && c.key_modifier == :scalar
         
     | 
| 
      
 141 
     | 
    
         
            +
                    # Ignore params on the subclass type, we are handling this case directly
         
     | 
| 
      
 142 
     | 
    
         
            +
                    # in _first_or_create()
         
     | 
| 
      
 143 
     | 
    
         
            +
                    next if c.key_path == [:_type]
         
     | 
| 
      
 144 
     | 
    
         
            +
                    raise "Please only use equal constraints in your where() query when using first_or_create()"
         
     | 
| 
      
 145 
     | 
    
         
            +
                  end
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
       120 
147 
     | 
    
         
             
                  raise "You may not use nested hash queries with first_or.create()" if c.key_path.size > 1
         
     | 
| 
       121 
148 
     | 
    
         
             
                  [c.key_path.first.to_sym, c.value]
         
     | 
| 
       122 
     | 
    
         
            -
                end]
         
     | 
| 
      
 149 
     | 
    
         
            +
                end.compact].tap { |h| raise "Missing where() clauses for first_or_create()" if h.empty? }
         
     | 
| 
       123 
150 
     | 
    
         
             
              end
         
     | 
| 
       124 
151 
     | 
    
         | 
| 
       125 
152 
     | 
    
         
             
              def get_model_unique_fields
         
     | 
| 
         @@ -53,9 +53,10 @@ module NoBrainer::Criteria::Join 
     | 
|
| 
       53 
53 
     | 
    
         
             
                join_ast.reduce(super) do |rql, (association, criteria)|
         
     | 
| 
       54 
54 
     | 
    
         
             
                  rql.concat_map do |doc|
         
     | 
| 
       55 
55 
     | 
    
         
             
                    key = doc[association.eager_load_owner_key]
         
     | 
| 
       56 
     | 
    
         
            -
                     
     | 
| 
       57 
     | 
    
         
            -
                       
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
      
 56 
     | 
    
         
            +
                    RethinkDB::RQL.new.branch(key.eq(nil), [],
         
     | 
| 
      
 57 
     | 
    
         
            +
                      criteria.where(association.eager_load_target_key => key).to_rql.map do |assoc_doc|
         
     | 
| 
      
 58 
     | 
    
         
            +
                        doc.merge(association.target_name => assoc_doc)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end)
         
     | 
| 
       59 
60 
     | 
    
         
             
                  end
         
     | 
| 
       60 
61 
     | 
    
         
             
                end
         
     | 
| 
       61 
62 
     | 
    
         
             
              end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module NoBrainer::Criteria::VirtualAttributes
         
     | 
| 
      
 2 
     | 
    
         
            +
              extend ActiveSupport::Concern
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              def compile_rql_pass2
         
     | 
| 
      
 5 
     | 
    
         
            +
                rql = super
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                if model.virtual_fields
         
     | 
| 
      
 8 
     | 
    
         
            +
                  rql = rql.map do |_doc|
         
     | 
| 
      
 9 
     | 
    
         
            +
                    model.virtual_fields.reduce(_doc) do |doc, field|
         
     | 
| 
      
 10 
     | 
    
         
            +
                      field_rql = model.fields[field][:virtual].call(doc, RethinkDB::RQL.new)
         
     | 
| 
      
 11 
     | 
    
         
            +
                      field_rql.nil? ? doc : doc.merge(field => field_rql)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    end
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                rql
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -195,11 +195,12 @@ module NoBrainer::Criteria::Where 
     | 
|
| 
       195 
195 
     | 
    
         
             
                    else
         
     | 
| 
       196 
196 
     | 
    
         
             
                      # 1) Box value in array if we have an any/all modifier
         
     | 
| 
       197 
197 
     | 
    
         
             
                      # 2) Box value in hash if we have a nested query.
         
     | 
| 
       198 
     | 
    
         
            -
                       
     | 
| 
      
 198 
     | 
    
         
            +
                      box_value = key_modifier.in?([:any, :all]) || op == :include
         
     | 
| 
      
 199 
     | 
    
         
            +
                      value = [value] if box_value
         
     | 
| 
       199 
200 
     | 
    
         
             
                      value_hash = key_path.reverse.reduce(value) { |v,k| {k => v} }
         
     | 
| 
       200 
201 
     | 
    
         
             
                      value = model.cast_user_to_db_for(*value_hash.first)
         
     | 
| 
       201 
202 
     | 
    
         
             
                      value = key_path[1..-1].reduce(value) { |h,k| h[k] }
         
     | 
| 
       202 
     | 
    
         
            -
                      value = value.first if  
     | 
| 
      
 203 
     | 
    
         
            +
                      value = value.first if box_value
         
     | 
| 
       203 
204 
     | 
    
         
             
                      value
         
     | 
| 
       204 
205 
     | 
    
         
             
                    end
         
     | 
| 
       205 
206 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/no_brainer/document.rb
    CHANGED
    
    | 
         @@ -7,7 +7,7 @@ module NoBrainer::Document 
     | 
|
| 
       7 
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 
     | 
    
         
            -
                                   :MissingAttributes, :LazyFetch, :AtomicOps
         
     | 
| 
      
 10 
     | 
    
         
            +
                                   :MissingAttributes, :LazyFetch, :AtomicOps, :VirtualAttributes
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
              autoload :DynamicAttributes, :Timestamps
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
         @@ -2,7 +2,7 @@ module NoBrainer::Document::Attributes 
     | 
|
| 
       2 
2 
     | 
    
         
             
              VALID_FIELD_OPTIONS = [:index, :default, :type, :readonly, :primary_key,
         
     | 
| 
       3 
3 
     | 
    
         
             
                                     :lazy_fetch, :store_as, :validates, :required, :unique,
         
     | 
| 
       4 
4 
     | 
    
         
             
                                     :uniq, :format, :in, :length, :min_length, :max_length,
         
     | 
| 
       5 
     | 
    
         
            -
                                     :prefix, :suffix]
         
     | 
| 
      
 5 
     | 
    
         
            +
                                     :prefix, :suffix, :virtual]
         
     | 
| 
       6 
6 
     | 
    
         
             
              RESERVED_FIELD_NAMES = [:index, :default, :and, :or, :selector, :associations, :pk_value] +
         
     | 
| 
       7 
7 
     | 
    
         
             
                                      NoBrainer::SymbolDecoration::OPERATORS
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
         @@ -18,18 +18,13 @@ module NoBrainer::Document::Persistance 
     | 
|
| 
       18 
18 
     | 
    
         
             
                !new_record? && !destroyed?
         
     | 
| 
       19 
19 
     | 
    
         
             
              end
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
              def  
     | 
| 
       22 
     | 
    
         
            -
                 
     | 
| 
      
 21 
     | 
    
         
            +
              def _reload(options={})
         
     | 
| 
      
 22 
     | 
    
         
            +
                criteria = root_class.raw
         
     | 
| 
       23 
23 
     | 
    
         
             
                if opt = options[:missing_attributes]
         
     | 
| 
       24 
     | 
    
         
            -
                   
     | 
| 
       25 
     | 
    
         
            -
                   
     | 
| 
      
 24 
     | 
    
         
            +
                  criteria = criteria.pluck(opt[:pluck]) if opt[:pluck]
         
     | 
| 
      
 25 
     | 
    
         
            +
                  criteria = criteria.without(opt[:without]) if opt[:without]
         
     | 
| 
       26 
26 
     | 
    
         
             
                end
         
     | 
| 
       27 
     | 
    
         
            -
                 
     | 
| 
       28 
     | 
    
         
            -
              end
         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
              def _reload(options={})
         
     | 
| 
       31 
     | 
    
         
            -
                attrs = NoBrainer.run { _reload_selector(options) }
         
     | 
| 
       32 
     | 
    
         
            -
                raise NoBrainer::Error::DocumentNotFound, "#{self.class} :#{self.class.pk_name}=>\"#{pk_value}\" not found" unless attrs
         
     | 
| 
      
 27 
     | 
    
         
            +
                attrs = criteria.find(pk_value)
         
     | 
| 
       33 
28 
     | 
    
         | 
| 
       34 
29 
     | 
    
         
             
                options = options.merge(:pristine => true, :from_db => true)
         
     | 
| 
       35 
30 
     | 
    
         | 
| 
         @@ -60,7 +55,7 @@ module NoBrainer::Document::Persistance 
     | 
|
| 
       60 
55 
     | 
    
         
             
              end
         
     | 
| 
       61 
56 
     | 
    
         | 
| 
       62 
57 
     | 
    
         
             
              def _create(options={})
         
     | 
| 
       63 
     | 
    
         
            -
                attrs = self.class.persistable_attributes(@_attributes 
     | 
| 
      
 58 
     | 
    
         
            +
                attrs = self.class.persistable_attributes(@_attributes)
         
     | 
| 
       64 
59 
     | 
    
         
             
                result = NoBrainer.run(self.class.rql_table.insert(attrs))
         
     | 
| 
       65 
60 
     | 
    
         
             
                self.pk_value ||= result['generated_keys'].to_a.first
         
     | 
| 
       66 
61 
     | 
    
         
             
                @new_record = false
         
     | 
| 
         @@ -69,7 +64,7 @@ module NoBrainer::Document::Persistance 
     | 
|
| 
       69 
64 
     | 
    
         
             
              end
         
     | 
| 
       70 
65 
     | 
    
         | 
| 
       71 
66 
     | 
    
         
             
              def _update(attrs)
         
     | 
| 
       72 
     | 
    
         
            -
                rql = ->(doc){ self.class.persistable_attributes(attrs, : 
     | 
| 
      
 67 
     | 
    
         
            +
                rql = ->(doc){ self.class.persistable_attributes(attrs, :rql_doc => doc) }
         
     | 
| 
       73 
68 
     | 
    
         
             
                NoBrainer.run { selector.update(&rql) }
         
     | 
| 
       74 
69 
     | 
    
         
             
              end
         
     | 
| 
       75 
70 
     | 
    
         | 
| 
         @@ -0,0 +1,52 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module NoBrainer::Document::VirtualAttributes
         
     | 
| 
      
 2 
     | 
    
         
            +
              extend NoBrainer::Autoload
         
     | 
| 
      
 3 
     | 
    
         
            +
              extend ActiveSupport::Concern
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              VALID_VIRTUAL_FIELD_OPTIONS = [:type, :lazy_fetch, :virtual]
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              included do
         
     | 
| 
      
 8 
     | 
    
         
            +
                cattr_accessor :virtual_fields, :instance_accessor => false
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              module ClassMethods
         
     | 
| 
      
 12 
     | 
    
         
            +
                def virtual_field(attr, rql=nil, options={}, &block)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  rql, options = nil, rql if rql.is_a?(Hash)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  rql ||= block
         
     | 
| 
      
 15 
     | 
    
         
            +
                  rql_proc = rql.is_a?(Proc) ? rql : proc { rql }
         
     | 
| 
      
 16 
     | 
    
         
            +
                  field(attr, options.merge(:virtual => rql_proc))
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def field(attr, options={})
         
     | 
| 
      
 20 
     | 
    
         
            +
                  return super unless options.key?(:virtual)
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  raise "virtual attributes are limited to the root class `#{self.root_class}' for the moment.\n" +
         
     | 
| 
      
 23 
     | 
    
         
            +
                        "Ask on GitHub for polymorphic support." unless is_root_class?
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  raise "You cannot index a virtual attribute. Use an index with a lambda expression instead" if options[:index]
         
     | 
| 
      
 26 
     | 
    
         
            +
                  options.assert_valid_keys(*VALID_VIRTUAL_FIELD_OPTIONS)
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  self.virtual_fields ||= Set.new
         
     | 
| 
      
 29 
     | 
    
         
            +
                  virtual_fields << attr
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  inject_in_layer :virtual_attributes do
         
     | 
| 
      
 32 
     | 
    
         
            +
                    define_method("#{attr}=") do |value|
         
     | 
| 
      
 33 
     | 
    
         
            +
                      raise NoBrainer::Error::ReadonlyField.new("#{attr} is a virtual attribute and thus readonly.")
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  super
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def remove_field(attr, options={})
         
     | 
| 
      
 41 
     | 
    
         
            +
                  super
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  if fields[:virtual]
         
     | 
| 
      
 44 
     | 
    
         
            +
                    virtual_fields.try(:delete, attr)
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    inject_in_layer :virtual_attributes do
         
     | 
| 
      
 47 
     | 
    
         
            +
                      remove_method("#{attr}=")
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/no_brainer/error.rb
    CHANGED
    
    | 
         @@ -11,6 +11,7 @@ module NoBrainer::Error 
     | 
|
| 
       11 
11 
     | 
    
         
             
              class UnknownAttribute        < RuntimeError; end
         
     | 
| 
       12 
12 
     | 
    
         
             
              class AtomicBlock             < RuntimeError; end
         
     | 
| 
       13 
13 
     | 
    
         
             
              class LostLock                < RuntimeError; end
         
     | 
| 
      
 14 
     | 
    
         
            +
              class LockInvalidOp           < RuntimeError; end
         
     | 
| 
       14 
15 
     | 
    
         
             
              class LockUnavailable         < RuntimeError; end
         
     | 
| 
       15 
16 
     | 
    
         
             
              class InvalidPolymorphicType  < RuntimeError; end
         
     | 
| 
       16 
17 
     | 
    
         | 
    
        data/lib/no_brainer/lock.rb
    CHANGED
    
    | 
         @@ -7,27 +7,33 @@ class NoBrainer::Lock 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
              # Since PKs are limited to 127 characters, we can't use the user's key as a PK
         
     | 
| 
       9 
9 
     | 
    
         
             
              # as it could be arbitrarily long.
         
     | 
| 
       10 
     | 
    
         
            -
              field :key_hash, 
     | 
| 
       11 
     | 
    
         
            -
              field :key, 
     | 
| 
       12 
     | 
    
         
            -
              field : 
     | 
| 
       13 
     | 
    
         
            -
              field :expires_at, 
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
              # We always use a new token, even when reading from the DB, because that's
         
     | 
| 
       16 
     | 
    
         
            -
              # what represent our instance.
         
     | 
| 
       17 
     | 
    
         
            -
              after_initialize { self.token = NoBrainer::Document::PrimaryKey::Generator.generate }
         
     | 
| 
      
 10 
     | 
    
         
            +
              field :key_hash,       :type => String, :primary_key => true, :default => ->{ Digest::SHA1.base64digest(key.to_s) }
         
     | 
| 
      
 11 
     | 
    
         
            +
              field :key,            :type => String
         
     | 
| 
      
 12 
     | 
    
         
            +
              field :instance_token, :type => String, :default => ->{ get_new_instance_token }
         
     | 
| 
      
 13 
     | 
    
         
            +
              field :expires_at,     :type => Time
         
     | 
| 
       18 
14 
     | 
    
         | 
| 
       19 
15 
     | 
    
         
             
              scope :expired, where(:expires_at.lt(RethinkDB::RQL.new.now))
         
     | 
| 
       20 
16 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
              def  
     | 
| 
       22 
     | 
    
         
            -
                 
     | 
| 
      
 17 
     | 
    
         
            +
              def self.find(key)
         
     | 
| 
      
 18 
     | 
    
         
            +
                super(Digest::SHA1.base64digest(key.to_s))
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
       23 
20 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
                   
     | 
| 
       27 
     | 
    
         
            -
                   
     | 
| 
      
 21 
     | 
    
         
            +
              def initialize(key, options={})
         
     | 
| 
      
 22 
     | 
    
         
            +
                if options[:from_db]
         
     | 
| 
      
 23 
     | 
    
         
            +
                  super
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # We reset our instance_token to allow recoveries.
         
     | 
| 
      
 25 
     | 
    
         
            +
                  self.instance_token = get_new_instance_token
         
     | 
| 
      
 26 
     | 
    
         
            +
                else
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @default_options = options.slice(:expire, :timeout)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  options.delete(:expire); options.delete(:timeout);
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  super(options.merge(:key => key))
         
     | 
| 
      
 31 
     | 
    
         
            +
                  raise ArgumentError unless valid?
         
     | 
| 
       28 
32 
     | 
    
         
             
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
       29 
34 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
      
 35 
     | 
    
         
            +
              def get_new_instance_token
         
     | 
| 
      
 36 
     | 
    
         
            +
                NoBrainer::Document::PrimaryKey::Generator.generate
         
     | 
| 
       31 
37 
     | 
    
         
             
              end
         
     | 
| 
       32 
38 
     | 
    
         | 
| 
       33 
39 
     | 
    
         
             
              def synchronize(options={}, &block)
         
     | 
| 
         @@ -41,22 +47,21 @@ class NoBrainer::Lock 
     | 
|
| 
       41 
47 
     | 
    
         | 
| 
       42 
48 
     | 
    
         
             
              def lock(options={})
         
     | 
| 
       43 
49 
     | 
    
         
             
                options.assert_valid_keys(:expire, :timeout)
         
     | 
| 
       44 
     | 
    
         
            -
                timeout =  
     | 
| 
      
 50 
     | 
    
         
            +
                timeout = get_option_value(options, :timeout)
         
     | 
| 
       45 
51 
     | 
    
         
             
                sleep_amount = 0.1
         
     | 
| 
       46 
52 
     | 
    
         | 
| 
       47 
53 
     | 
    
         
             
                start_at = Time.now
         
     | 
| 
       48 
     | 
    
         
            -
                 
     | 
| 
       49 
     | 
    
         
            -
                  return if try_lock(options. 
     | 
| 
      
 54 
     | 
    
         
            +
                loop do
         
     | 
| 
      
 55 
     | 
    
         
            +
                  return if try_lock(options.slice(:expire))
         
     | 
| 
      
 56 
     | 
    
         
            +
                  raise_lock_unavailable! if Time.now - start_at + sleep_amount > timeout
         
     | 
| 
       50 
57 
     | 
    
         
             
                  sleep(sleep_amount)
         
     | 
| 
       51 
58 
     | 
    
         
             
                  sleep_amount = [1, sleep_amount * 2].min
         
     | 
| 
       52 
59 
     | 
    
         
             
                end
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
                raise NoBrainer::Error::LockUnavailable.new("Lock on `#{key}' unavailable")
         
     | 
| 
       55 
60 
     | 
    
         
             
              end
         
     | 
| 
       56 
61 
     | 
    
         | 
| 
       57 
62 
     | 
    
         
             
              def try_lock(options={})
         
     | 
| 
       58 
63 
     | 
    
         
             
                options.assert_valid_keys(:expire)
         
     | 
| 
       59 
     | 
    
         
            -
                 
     | 
| 
      
 64 
     | 
    
         
            +
                raise_if_locked!
         
     | 
| 
       60 
65 
     | 
    
         | 
| 
       61 
66 
     | 
    
         
             
                set_expiration(options)
         
     | 
| 
       62 
67 
     | 
    
         | 
| 
         @@ -71,28 +76,28 @@ class NoBrainer::Lock 
     | 
|
| 
       71 
76 
     | 
    
         
             
              end
         
     | 
| 
       72 
77 
     | 
    
         | 
| 
       73 
78 
     | 
    
         
             
              def unlock
         
     | 
| 
       74 
     | 
    
         
            -
                 
     | 
| 
      
 79 
     | 
    
         
            +
                raise_unless_locked!
         
     | 
| 
       75 
80 
     | 
    
         | 
| 
       76 
81 
     | 
    
         
             
                result = NoBrainer.run do |r|
         
     | 
| 
       77 
82 
     | 
    
         
             
                  selector.replace do |doc|
         
     | 
| 
       78 
     | 
    
         
            -
                    r.branch(doc[: 
     | 
| 
      
 83 
     | 
    
         
            +
                    r.branch(doc[:instance_token].default(nil).eq(self.instance_token),
         
     | 
| 
       79 
84 
     | 
    
         
             
                             nil, doc)
         
     | 
| 
       80 
85 
     | 
    
         
             
                  end
         
     | 
| 
       81 
86 
     | 
    
         
             
                end
         
     | 
| 
       82 
87 
     | 
    
         | 
| 
       83 
88 
     | 
    
         
             
                @locked = false
         
     | 
| 
       84 
     | 
    
         
            -
                 
     | 
| 
      
 89 
     | 
    
         
            +
                raise_lost_lock! unless result['deleted'] == 1
         
     | 
| 
       85 
90 
     | 
    
         
             
              end
         
     | 
| 
       86 
91 
     | 
    
         | 
| 
       87 
92 
     | 
    
         
             
              def refresh(options={})
         
     | 
| 
       88 
93 
     | 
    
         
             
                options.assert_valid_keys(:expire)
         
     | 
| 
       89 
     | 
    
         
            -
                 
     | 
| 
      
 94 
     | 
    
         
            +
                raise_unless_locked!
         
     | 
| 
       90 
95 
     | 
    
         | 
| 
       91 
     | 
    
         
            -
                set_expiration(options)
         
     | 
| 
      
 96 
     | 
    
         
            +
                set_expiration(options.merge(:use_previous_expire => true))
         
     | 
| 
       92 
97 
     | 
    
         | 
| 
       93 
98 
     | 
    
         
             
                result = NoBrainer.run do |r|
         
     | 
| 
       94 
99 
     | 
    
         
             
                  selector.update do |doc|
         
     | 
| 
       95 
     | 
    
         
            -
                    r.branch(doc[: 
     | 
| 
      
 100 
     | 
    
         
            +
                    r.branch(doc[:instance_token].eq(self.instance_token),
         
     | 
| 
       96 
101 
     | 
    
         
             
                             { :expires_at => self.expires_at }, nil)
         
     | 
| 
       97 
102 
     | 
    
         
             
                  end
         
     | 
| 
       98 
103 
     | 
    
         
             
                end
         
     | 
| 
         @@ -102,7 +107,7 @@ class NoBrainer::Lock 
     | 
|
| 
       102 
107 
     | 
    
         
             
                # unlikely to happen and should not harmful.
         
     | 
| 
       103 
108 
     | 
    
         
             
                unless result['replaced'] == 1
         
     | 
| 
       104 
109 
     | 
    
         
             
                  @locked = false
         
     | 
| 
       105 
     | 
    
         
            -
                   
     | 
| 
      
 110 
     | 
    
         
            +
                  raise_lost_lock!
         
     | 
| 
       106 
111 
     | 
    
         
             
                end
         
     | 
| 
       107 
112 
     | 
    
         
             
              end
         
     | 
| 
       108 
113 
     | 
    
         | 
| 
         @@ -112,7 +117,29 @@ class NoBrainer::Lock 
     | 
|
| 
       112 
117 
     | 
    
         
             
              private
         
     | 
| 
       113 
118 
     | 
    
         | 
| 
       114 
119 
     | 
    
         
             
              def set_expiration(options)
         
     | 
| 
       115 
     | 
    
         
            -
                expire =  
     | 
| 
      
 120 
     | 
    
         
            +
                expire = @previous_expire if options[:use_previous_expire] && !options[:expire]
         
     | 
| 
      
 121 
     | 
    
         
            +
                expire ||= get_option_value(options, :expire)
         
     | 
| 
      
 122 
     | 
    
         
            +
                @previous_expire = expire
         
     | 
| 
       116 
123 
     | 
    
         
             
                self.expires_at = RethinkDB::RQL.new.now + expire
         
     | 
| 
       117 
124 
     | 
    
         
             
              end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
              def get_option_value(options, key)
         
     | 
| 
      
 127 
     | 
    
         
            +
                NoBrainer::Config.lock_options.merge(@default_options || {}).merge(options)[key]
         
     | 
| 
      
 128 
     | 
    
         
            +
              end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
              def raise_if_locked!
         
     | 
| 
      
 131 
     | 
    
         
            +
                raise NoBrainer::Error::LockInvalidOp.new("Lock instance `#{key}' already locked") if @locked
         
     | 
| 
      
 132 
     | 
    
         
            +
              end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
              def raise_unless_locked!
         
     | 
| 
      
 135 
     | 
    
         
            +
                raise NoBrainer::Error::LockInvalidOp.new("Lock instance `#{key}' not locked") unless @locked
         
     | 
| 
      
 136 
     | 
    
         
            +
              end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
              def raise_lost_lock!
         
     | 
| 
      
 139 
     | 
    
         
            +
                raise NoBrainer::Error::LostLock.new("Lost lock on `#{key}'")
         
     | 
| 
      
 140 
     | 
    
         
            +
              end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
              def raise_lock_unavailable!
         
     | 
| 
      
 143 
     | 
    
         
            +
                raise NoBrainer::Error::LockUnavailable.new("Lock on `#{key}' unavailable")
         
     | 
| 
      
 144 
     | 
    
         
            +
              end
         
     | 
| 
       118 
145 
     | 
    
         
             
            end
         
     | 
| 
         @@ -47,6 +47,8 @@ module NoBrainer::Profiler::ControllerRuntime 
     | 
|
| 
       47 
47 
     | 
    
         
             
              end
         
     | 
| 
       48 
48 
     | 
    
         | 
| 
       49 
49 
     | 
    
         
             
              def cleanup_view_runtime
         
     | 
| 
      
 50 
     | 
    
         
            +
                return super unless Profiler.current
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
       50 
52 
     | 
    
         
             
                time_spent_in_db_before_views = Profiler.current.total_duration
         
     | 
| 
       51 
53 
     | 
    
         
             
                runtime = super
         
     | 
| 
       52 
54 
     | 
    
         
             
                time_spent_in_db_after_views = Profiler.current.total_duration
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class NoBrainer::ReentrantLock < NoBrainer::Lock
         
     | 
| 
      
 2 
     | 
    
         
            +
              field :lock_count, :type => Integer
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              def try_lock(options={})
         
     | 
| 
      
 5 
     | 
    
         
            +
                options.assert_valid_keys(:expire)
         
     | 
| 
      
 6 
     | 
    
         
            +
                set_expiration(options)
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                result = NoBrainer.run do |r|
         
     | 
| 
      
 9 
     | 
    
         
            +
                  selector.replace do |doc|
         
     | 
| 
      
 10 
     | 
    
         
            +
                    r.branch(doc[:instance_token].default(nil).eq(self.instance_token),
         
     | 
| 
      
 11 
     | 
    
         
            +
                             doc.merge(:expires_at => self.expires_at,
         
     | 
| 
      
 12 
     | 
    
         
            +
                                       :lock_count => doc[:lock_count] + 1),
         
     | 
| 
      
 13 
     | 
    
         
            +
                             r.branch(doc.eq(nil).or(doc[:expires_at] < r.now),
         
     | 
| 
      
 14 
     | 
    
         
            +
                                      self.attributes.merge(:lock_count => 1), doc))
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                @locked = true # to make refresh() and synchronize() happy, somewhat hacky
         
     | 
| 
      
 19 
     | 
    
         
            +
                return (result['inserted'] + result['replaced']) == 1
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              def unlock
         
     | 
| 
      
 23 
     | 
    
         
            +
                set_expiration(:use_previous_expire => true)
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                result = NoBrainer.run do |r|
         
     | 
| 
      
 26 
     | 
    
         
            +
                  selector.replace do |doc|
         
     | 
| 
      
 27 
     | 
    
         
            +
                    r.branch(doc[:instance_token].default(nil).eq(self.instance_token),
         
     | 
| 
      
 28 
     | 
    
         
            +
                             r.branch(doc[:lock_count] > 1,
         
     | 
| 
      
 29 
     | 
    
         
            +
                                      doc.merge(:expires_at => self.expires_at,
         
     | 
| 
      
 30 
     | 
    
         
            +
                                                :lock_count => doc[:lock_count] - 1), nil), doc)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                raise_lost_lock! unless (result['deleted'] + result['replaced']) == 1
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/nobrainer.rb
    CHANGED
    
    | 
         @@ -16,7 +16,7 @@ module NoBrainer 
     | 
|
| 
       16 
16 
     | 
    
         
             
              # Code that is loaded through the DSL of NoBrainer should not be eager loaded.
         
     | 
| 
       17 
17 
     | 
    
         
             
              autoload :Document, :IndexManager, :Loader, :Fork, :Geo, :SymbolDecoration
         
     | 
| 
       18 
18 
     | 
    
         
             
              eager_autoload :Config, :Connection, :ConnectionManager, :Error,
         
     | 
| 
       19 
     | 
    
         
            -
                             :QueryRunner, :Criteria, :RQL, :Lock, :Profiler, :System
         
     | 
| 
      
 19 
     | 
    
         
            +
                             :QueryRunner, :Criteria, :RQL, :Lock, :ReentrantLock, :Profiler, :System
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
              class << self
         
     | 
| 
       22 
22 
     | 
    
         
             
                delegate :connection, :disconnect, :to => 'NoBrainer::ConnectionManager'
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: nobrainer
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.30.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Nicolas Viennot
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2015- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2015-10-03 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: rethinkdb
         
     | 
| 
         @@ -116,6 +116,7 @@ files: 
     | 
|
| 
       116 
116 
     | 
    
         
             
            - lib/no_brainer/criteria/run.rb
         
     | 
| 
       117 
117 
     | 
    
         
             
            - lib/no_brainer/criteria/scope.rb
         
     | 
| 
       118 
118 
     | 
    
         
             
            - lib/no_brainer/criteria/update.rb
         
     | 
| 
      
 119 
     | 
    
         
            +
            - lib/no_brainer/criteria/virtual_attributes.rb
         
     | 
| 
       119 
120 
     | 
    
         
             
            - lib/no_brainer/criteria/where.rb
         
     | 
| 
       120 
121 
     | 
    
         
             
            - lib/no_brainer/document.rb
         
     | 
| 
       121 
122 
     | 
    
         
             
            - lib/no_brainer/document/aliases.rb
         
     | 
| 
         @@ -167,6 +168,7 @@ files: 
     | 
|
| 
       167 
168 
     | 
    
         
             
            - lib/no_brainer/document/validation/core.rb
         
     | 
| 
       168 
169 
     | 
    
         
             
            - lib/no_brainer/document/validation/not_null.rb
         
     | 
| 
       169 
170 
     | 
    
         
             
            - lib/no_brainer/document/validation/uniqueness.rb
         
     | 
| 
      
 171 
     | 
    
         
            +
            - lib/no_brainer/document/virtual_attributes.rb
         
     | 
| 
       170 
172 
     | 
    
         
             
            - lib/no_brainer/error.rb
         
     | 
| 
       171 
173 
     | 
    
         
             
            - lib/no_brainer/fork.rb
         
     | 
| 
       172 
174 
     | 
    
         
             
            - lib/no_brainer/geo.rb
         
     | 
| 
         @@ -194,6 +196,7 @@ files: 
     | 
|
| 
       194 
196 
     | 
    
         
             
            - lib/no_brainer/query_runner/write_error.rb
         
     | 
| 
       195 
197 
     | 
    
         
             
            - lib/no_brainer/railtie.rb
         
     | 
| 
       196 
198 
     | 
    
         
             
            - lib/no_brainer/railtie/database.rake
         
     | 
| 
      
 199 
     | 
    
         
            +
            - lib/no_brainer/reentrant_lock.rb
         
     | 
| 
       197 
200 
     | 
    
         
             
            - lib/no_brainer/rql.rb
         
     | 
| 
       198 
201 
     | 
    
         
             
            - lib/no_brainer/symbol_decoration.rb
         
     | 
| 
       199 
202 
     | 
    
         
             
            - lib/no_brainer/system.rb
         
     |