massive_record 0.2.0 → 0.2.1.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +43 -4
- data/Gemfile.lock +3 -1
- data/README.md +5 -0
- data/lib/massive_record/adapters/thrift/connection.rb +23 -16
- data/lib/massive_record/adapters/thrift/row.rb +13 -33
- data/lib/massive_record/adapters/thrift/table.rb +24 -10
- data/lib/massive_record/orm/attribute_methods.rb +27 -1
- data/lib/massive_record/orm/attribute_methods/dirty.rb +2 -2
- data/lib/massive_record/orm/attribute_methods/read.rb +36 -1
- data/lib/massive_record/orm/attribute_methods/time_zone_conversion.rb +81 -0
- data/lib/massive_record/orm/attribute_methods/write.rb +18 -0
- data/lib/massive_record/orm/base.rb +52 -10
- data/lib/massive_record/orm/callbacks.rb +1 -1
- data/lib/massive_record/orm/default_id.rb +20 -0
- data/lib/massive_record/orm/errors.rb +4 -0
- data/lib/massive_record/orm/finders.rb +102 -57
- data/lib/massive_record/orm/finders/rescue_missing_table_on_find.rb +45 -0
- data/lib/massive_record/orm/id_factory.rb +1 -1
- data/lib/massive_record/orm/log_subscriber.rb +85 -0
- data/lib/massive_record/orm/persistence.rb +82 -37
- data/lib/massive_record/orm/query_instrumentation.rb +64 -0
- data/lib/massive_record/orm/relations/interface.rb +10 -0
- data/lib/massive_record/orm/relations/metadata.rb +2 -0
- data/lib/massive_record/orm/relations/proxy/references_one_polymorphic.rb +1 -1
- data/lib/massive_record/orm/schema/field.rb +33 -6
- data/lib/massive_record/orm/timestamps.rb +1 -1
- data/lib/massive_record/orm/validations.rb +2 -2
- data/lib/massive_record/rails/controller_runtime.rb +55 -0
- data/lib/massive_record/rails/railtie.rb +16 -0
- data/lib/massive_record/version.rb +1 -1
- data/lib/massive_record/wrapper/cell.rb +32 -3
- data/massive_record.gemspec +1 -0
- data/spec/{wrapper/cases → adapter/thrift}/adapter_spec.rb +0 -0
- data/spec/adapter/thrift/atomic_increment_spec.rb +55 -0
- data/spec/{wrapper/cases → adapter/thrift}/connection_spec.rb +0 -10
- data/spec/adapter/thrift/table_find_spec.rb +40 -0
- data/spec/{wrapper/cases → adapter/thrift}/table_spec.rb +55 -13
- data/spec/orm/cases/attribute_methods_spec.rb +6 -1
- data/spec/orm/cases/base_spec.rb +18 -4
- data/spec/orm/cases/callbacks_spec.rb +1 -1
- data/spec/orm/cases/default_id_spec.rb +38 -0
- data/spec/orm/cases/default_values_spec.rb +37 -0
- data/spec/orm/cases/dirty_spec.rb +25 -1
- data/spec/orm/cases/encoding_spec.rb +3 -3
- data/spec/orm/cases/finder_default_scope.rb +8 -1
- data/spec/orm/cases/finder_scope_spec.rb +2 -2
- data/spec/orm/cases/finders_spec.rb +8 -18
- data/spec/orm/cases/id_factory_spec.rb +38 -21
- data/spec/orm/cases/log_subscriber_spec.rb +133 -0
- data/spec/orm/cases/mass_assignment_security_spec.rb +97 -0
- data/spec/orm/cases/persistence_spec.rb +132 -27
- data/spec/orm/cases/single_table_inheritance_spec.rb +2 -2
- data/spec/orm/cases/time_zone_awareness_spec.rb +157 -0
- data/spec/orm/cases/timestamps_spec.rb +15 -0
- data/spec/orm/cases/validation_spec.rb +2 -2
- data/spec/orm/models/model_without_default_id.rb +5 -0
- data/spec/orm/models/person.rb +1 -0
- data/spec/orm/models/test_class.rb +1 -0
- data/spec/orm/relations/interface_spec.rb +2 -2
- data/spec/orm/relations/metadata_spec.rb +1 -1
- data/spec/orm/relations/proxy/references_many_spec.rb +21 -15
- data/spec/orm/relations/proxy/references_one_polymorphic_spec.rb +7 -1
- data/spec/orm/relations/proxy/references_one_spec.rb +7 -0
- data/spec/orm/schema/field_spec.rb +61 -5
- data/spec/support/connection_helpers.rb +2 -1
- data/spec/support/mock_massive_record_connection.rb +7 -0
- data/spec/support/time_zone_helper.rb +25 -0
- metadata +51 -14
| @@ -9,6 +9,24 @@ module MassiveRecord | |
| 9 9 | 
             
                    end
         | 
| 10 10 |  | 
| 11 11 |  | 
| 12 | 
            +
                    module ClassMethods
         | 
| 13 | 
            +
                      protected
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                      def define_method_attribute=(attr_name)
         | 
| 16 | 
            +
                        if attr_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
         | 
| 17 | 
            +
                          generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__
         | 
| 18 | 
            +
                            def #{attr_name}=(value)
         | 
| 19 | 
            +
                              write_attribute('#{attr_name}', value)
         | 
| 20 | 
            +
                            end
         | 
| 21 | 
            +
                          RUBY
         | 
| 22 | 
            +
                        else
         | 
| 23 | 
            +
                          generated_attribute_methods.send(:define_method, "#{attr_name}=") do |value|
         | 
| 24 | 
            +
                            write_attribute(attr_name, value)
         | 
| 25 | 
            +
                          end
         | 
| 26 | 
            +
                        end
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
             | 
| 12 30 | 
             
                    def write_attribute(attr_name, value)
         | 
| 13 31 | 
             
                      @attributes[attr_name.to_s] = value
         | 
| 14 32 | 
             
                    end
         | 
| @@ -4,6 +4,7 @@ require 'active_support/core_ext/class/attribute' | |
| 4 4 | 
             
            require 'active_support/core_ext/class/subclasses'
         | 
| 5 5 | 
             
            require 'active_support/core_ext/module'
         | 
| 6 6 | 
             
            require 'active_support/core_ext/string'
         | 
| 7 | 
            +
            require 'active_support/core_ext/array'
         | 
| 7 8 | 
             
            require 'active_support/memoizable'
         | 
| 8 9 |  | 
| 9 10 | 
             
            require 'massive_record/orm/schema'
         | 
| @@ -13,7 +14,9 @@ require 'massive_record/orm/config' | |
| 13 14 | 
             
            require 'massive_record/orm/relations'
         | 
| 14 15 | 
             
            require 'massive_record/orm/finders'
         | 
| 15 16 | 
             
            require 'massive_record/orm/finders/scope'
         | 
| 17 | 
            +
            require 'massive_record/orm/finders/rescue_missing_table_on_find'
         | 
| 16 18 | 
             
            require 'massive_record/orm/attribute_methods'
         | 
| 19 | 
            +
            require 'massive_record/orm/attribute_methods/time_zone_conversion'
         | 
| 17 20 | 
             
            require 'massive_record/orm/attribute_methods/write'
         | 
| 18 21 | 
             
            require 'massive_record/orm/attribute_methods/read'
         | 
| 19 22 | 
             
            require 'massive_record/orm/attribute_methods/dirty'
         | 
| @@ -22,6 +25,8 @@ require 'massive_record/orm/validations' | |
| 22 25 | 
             
            require 'massive_record/orm/callbacks'
         | 
| 23 26 | 
             
            require 'massive_record/orm/timestamps'
         | 
| 24 27 | 
             
            require 'massive_record/orm/persistence'
         | 
| 28 | 
            +
            require 'massive_record/orm/default_id'
         | 
| 29 | 
            +
            require 'massive_record/orm/query_instrumentation'
         | 
| 25 30 |  | 
| 26 31 |  | 
| 27 32 | 
             
            module MassiveRecord
         | 
| @@ -35,6 +40,19 @@ module MassiveRecord | |
| 35 40 | 
             
                  # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class,
         | 
| 36 41 | 
             
                  cattr_accessor :logger, :instance_writer => false
         | 
| 37 42 |  | 
| 43 | 
            +
             | 
| 44 | 
            +
                  #
         | 
| 45 | 
            +
                  # Integers are now persisted as a hax representation in hbase, not as
         | 
| 46 | 
            +
                  # a string any more. This makes for instance atomic_increment!(:int_attr) work
         | 
| 47 | 
            +
                  # as expected.
         | 
| 48 | 
            +
                  #
         | 
| 49 | 
            +
                  # The problem is that if you have old data in your database, you need to handle
         | 
| 50 | 
            +
                  # this. Every new integer will still be written as hex though.
         | 
| 51 | 
            +
                  #
         | 
| 52 | 
            +
                  cattr_accessor :backward_compatibility_integers_might_be_persisted_as_strings, :instance_writer => false
         | 
| 53 | 
            +
                  self.backward_compatibility_integers_might_be_persisted_as_strings = false
         | 
| 54 | 
            +
             | 
| 55 | 
            +
             | 
| 38 56 | 
             
                  # Add a prefix or a suffix to the table name
         | 
| 39 57 | 
             
                  # example:
         | 
| 40 58 | 
             
                  #
         | 
| @@ -47,6 +65,18 @@ module MassiveRecord | |
| 47 65 |  | 
| 48 66 | 
             
                  class_attribute :table_name_suffix, :instance_writer => false
         | 
| 49 67 | 
             
                  self.table_name_suffix = ""
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  #
         | 
| 70 | 
            +
                  # Will do a simple exists?(id) check before create as a simple (and
         | 
| 71 | 
            +
                  # kinda insecure) sanity check on if that ID exists or not. If it do
         | 
| 72 | 
            +
                  # exists a RecordNotUnique will be raised. This is done from the ORM
         | 
| 73 | 
            +
                  # layer, so obviously there is a speed cost on create.
         | 
| 74 | 
            +
                  #
         | 
| 75 | 
            +
                  class_attribute :check_record_uniqueness_on_create, :instance_writer => false
         | 
| 76 | 
            +
                  self.check_record_uniqueness_on_create = false
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  class_attribute :auto_increment_id, :instance_writer => false
         | 
| 79 | 
            +
                  self.auto_increment_id = true
         | 
| 50 80 |  | 
| 51 81 | 
             
                  class << self
         | 
| 52 82 | 
             
                    def table_name
         | 
| @@ -104,15 +134,20 @@ module MassiveRecord | |
| 104 134 | 
             
                  # and assign to instance variables. How read- and write 
         | 
| 105 135 | 
             
                  # methods are defined might change over time when the DSL
         | 
| 106 136 | 
             
                  # for describing column families and fields are in place
         | 
| 137 | 
            +
                  # You can call initialize in multiple ways:
         | 
| 138 | 
            +
                  #   ORMClass.new(attr_one: value, attr_two: value)
         | 
| 139 | 
            +
                  #   ORMClass.new("the-id-of-the-new-record")
         | 
| 140 | 
            +
                  #   ORMClass.new("the-id-of-the-new-record", attr_one: value, attr_two: value)
         | 
| 107 141 | 
             
                  #
         | 
| 108 | 
            -
                  def initialize( | 
| 142 | 
            +
                  def initialize(*args)
         | 
| 143 | 
            +
                    attributes = args.extract_options!
         | 
| 144 | 
            +
                    id = args.first
         | 
| 145 | 
            +
             | 
| 109 146 | 
             
                    @new_record = true
         | 
| 110 147 | 
             
                    @destroyed = @readonly = false
         | 
| 111 148 | 
             
                    @relation_proxy_cache = {}
         | 
| 112 149 |  | 
| 113 | 
            -
                     | 
| 114 | 
            -
             | 
| 115 | 
            -
                    self.attributes_raw = attributes_from_field_definition
         | 
| 150 | 
            +
                    self.attributes_raw = attributes_from_field_definition.merge('id' => id)
         | 
| 116 151 | 
             
                    self.attributes = attributes
         | 
| 117 152 |  | 
| 118 153 | 
             
                    clear_dirty_states!
         | 
| @@ -186,6 +221,11 @@ module MassiveRecord | |
| 186 221 | 
             
                    read_attribute(:id)
         | 
| 187 222 | 
             
                  end
         | 
| 188 223 |  | 
| 224 | 
            +
                  def id=(id)
         | 
| 225 | 
            +
                    id = id.to_s unless id.blank?
         | 
| 226 | 
            +
                    write_attribute(:id, id)
         | 
| 227 | 
            +
                  end
         | 
| 228 | 
            +
             | 
| 189 229 |  | 
| 190 230 |  | 
| 191 231 | 
             
                  def readonly?
         | 
| @@ -238,17 +278,20 @@ module MassiveRecord | |
| 238 278 |  | 
| 239 279 | 
             
                Base.class_eval do
         | 
| 240 280 | 
             
                  include Config
         | 
| 241 | 
            -
                  include Relations::Interface
         | 
| 242 281 | 
             
                  include Persistence
         | 
| 282 | 
            +
                  include Relations::Interface
         | 
| 243 283 | 
             
                  include Finders
         | 
| 244 | 
            -
                   | 
| 284 | 
            +
                  extend  RescueMissingTableOnFind
         | 
| 245 285 | 
             
                  include AttributeMethods
         | 
| 246 286 | 
             
                  include AttributeMethods::Write, AttributeMethods::Read
         | 
| 287 | 
            +
                  include AttributeMethods::TimeZoneConversion
         | 
| 247 288 | 
             
                  include AttributeMethods::Dirty
         | 
| 248 289 | 
             
                  include Validations
         | 
| 249 290 | 
             
                  include Callbacks
         | 
| 250 291 | 
             
                  include Timestamps
         | 
| 251 292 | 
             
                  include SingleTableInheritance
         | 
| 293 | 
            +
                  include DefaultId
         | 
| 294 | 
            +
                  include QueryInstrumentation
         | 
| 252 295 |  | 
| 253 296 |  | 
| 254 297 | 
             
                  alias [] read_attribute
         | 
| @@ -257,10 +300,9 @@ module MassiveRecord | |
| 257 300 | 
             
              end
         | 
| 258 301 | 
             
            end
         | 
| 259 302 |  | 
| 260 | 
            -
             | 
| 261 | 
            -
             | 
| 262 | 
            -
             | 
| 263 | 
            -
             | 
| 264 303 | 
             
            require 'massive_record/orm/table'
         | 
| 265 304 | 
             
            require 'massive_record/orm/column'
         | 
| 266 305 | 
             
            require 'massive_record/orm/id_factory'
         | 
| 306 | 
            +
            require 'massive_record/orm/log_subscriber'
         | 
| 307 | 
            +
             | 
| 308 | 
            +
            ActiveSupport.run_load_hooks(:massive_record, MassiveRecord::ORM::Base)
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            module MassiveRecord
         | 
| 2 | 
            +
              module ORM
         | 
| 3 | 
            +
                module DefaultId
         | 
| 4 | 
            +
                  extend ActiveSupport::Concern
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  included do
         | 
| 7 | 
            +
                    before_create :ensure_record_has_id, :if => :auto_increment_id
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
             | 
| 11 | 
            +
                  module InstanceMethods
         | 
| 12 | 
            +
                    private
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    def ensure_record_has_id
         | 
| 15 | 
            +
                      self.id = next_id if id.blank?
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         | 
| @@ -20,6 +20,10 @@ module MassiveRecord | |
| 20 20 | 
             
                class RecordNotFound < MassiveRecordError
         | 
| 21 21 | 
             
                end
         | 
| 22 22 |  | 
| 23 | 
            +
                # Raised when we try to create a new record with an id which exists.
         | 
| 24 | 
            +
                class RecordNotUnique < MassiveRecordError
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 23 27 | 
             
                # Raised if an attribute is unkown
         | 
| 24 28 | 
             
                class UnknownAttributeError < MassiveRecordError
         | 
| 25 29 | 
             
                end
         | 
| @@ -12,6 +12,79 @@ module MassiveRecord | |
| 12 12 | 
             
                  end
         | 
| 13 13 |  | 
| 14 14 | 
             
                  module ClassMethods
         | 
| 15 | 
            +
                    #
         | 
| 16 | 
            +
                    # Find records in batches. Makes it easier to work with
         | 
| 17 | 
            +
                    # big data sets where you don't want to load every record up front.
         | 
| 18 | 
            +
                    #
         | 
| 19 | 
            +
                    def find_in_batches(*args)
         | 
| 20 | 
            +
                      table.find_in_batches(*args) do |rows|
         | 
| 21 | 
            +
                        records = rows.collect do |row|
         | 
| 22 | 
            +
                          instantiate(transpose_hbase_columns_to_record_attributes(row))
         | 
| 23 | 
            +
                        end    
         | 
| 24 | 
            +
                        yield records
         | 
| 25 | 
            +
                      end
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
                    
         | 
| 28 | 
            +
                    #
         | 
| 29 | 
            +
                    # Similar to all, except that this will use find_in_batches
         | 
| 30 | 
            +
                    # behind the scene.
         | 
| 31 | 
            +
                    #
         | 
| 32 | 
            +
                    def find_each(*args)
         | 
| 33 | 
            +
                      find_in_batches(*args) do |rows|
         | 
| 34 | 
            +
                        rows.each do |row|
         | 
| 35 | 
            +
                          yield row
         | 
| 36 | 
            +
                        end
         | 
| 37 | 
            +
                      end
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
             | 
| 41 | 
            +
                    #
         | 
| 42 | 
            +
                    # Returns true if a record do exist
         | 
| 43 | 
            +
                    #
         | 
| 44 | 
            +
                    def exists?(id)
         | 
| 45 | 
            +
                      !!find(id) rescue false
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
             | 
| 49 | 
            +
             | 
| 50 | 
            +
                    #
         | 
| 51 | 
            +
                    # Entry point for method delegation like find, first, all etc.
         | 
| 52 | 
            +
                    #
         | 
| 53 | 
            +
                    def finder_scope
         | 
| 54 | 
            +
                      default_scoping || unscoped
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
             | 
| 58 | 
            +
                    #
         | 
| 59 | 
            +
                    # Sets a default scope which will be used for calls like find, first, all etc.
         | 
| 60 | 
            +
                    # Makes it possible to for instance set default column families to load on all
         | 
| 61 | 
            +
                    # calls to the database.
         | 
| 62 | 
            +
                    #
         | 
| 63 | 
            +
                    def default_scope(scope)
         | 
| 64 | 
            +
                      self.default_scoping =  case scope
         | 
| 65 | 
            +
                                                when Scope, nil
         | 
| 66 | 
            +
                                                  scope
         | 
| 67 | 
            +
                                                when Hash
         | 
| 68 | 
            +
                                                  Scope.new(self, :find_options => scope)
         | 
| 69 | 
            +
                                                else
         | 
| 70 | 
            +
                                                  raise "Don't know how to set scope with #{scope.class}."
         | 
| 71 | 
            +
                                                end
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    #
         | 
| 75 | 
            +
                    # Returns an fresh scope object with no limitations set by
         | 
| 76 | 
            +
                    # for instance the default scope
         | 
| 77 | 
            +
                    #
         | 
| 78 | 
            +
                    def unscoped
         | 
| 79 | 
            +
                      Scope.new(self)
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
             | 
| 83 | 
            +
             | 
| 84 | 
            +
             | 
| 85 | 
            +
                    #
         | 
| 86 | 
            +
                    # This do_find method is not very nice it's logic should be re-factored at some point.
         | 
| 87 | 
            +
                    #
         | 
| 15 88 | 
             
                    def do_find(*args) # :nodoc:
         | 
| 16 89 | 
             
                      options = args.extract_options!.to_options
         | 
| 17 90 | 
             
                      raise ArgumentError.new("At least one argument required!") if args.empty?
         | 
| @@ -25,26 +98,10 @@ module MassiveRecord | |
| 25 98 | 
             
                      type = args.shift if args.first.is_a? Symbol
         | 
| 26 99 | 
             
                      find_many = type == :all
         | 
| 27 100 | 
             
                      expected_result_size = nil
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                       | 
| 101 | 
            +
                      what_to_find = []
         | 
| 102 | 
            +
                      result_from_table = []
         | 
| 30 103 |  | 
| 31 | 
            -
                      result_from_table =  | 
| 32 | 
            -
                                            table.send(type, *args) # first() / all()
         | 
| 33 | 
            -
                                          else
         | 
| 34 | 
            -
                                            options = args.extract_options!
         | 
| 35 | 
            -
                                            what_to_find = args.first
         | 
| 36 | 
            -
                                            expected_result_size = 1
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                                            if args.first.kind_of?(Array)
         | 
| 39 | 
            -
                                              find_many = true
         | 
| 40 | 
            -
                                            elsif args.length > 1
         | 
| 41 | 
            -
                                              find_many = true
         | 
| 42 | 
            -
                                              what_to_find = args
         | 
| 43 | 
            -
                                            end
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                                            expected_result_size = what_to_find.length if what_to_find.is_a? Array
         | 
| 46 | 
            -
                                            table.find(what_to_find, options)
         | 
| 47 | 
            -
                                          end
         | 
| 104 | 
            +
                      find_many, expected_result_size, what_to_find, result_from_table = query_hbase(type, args, find_many)
         | 
| 48 105 |  | 
| 49 106 | 
             
                      # Filter out unexpected IDs (unless type is set (all/first), in that case
         | 
| 50 107 | 
             
                      # we have no expectations on the returned rows' ids)
         | 
| @@ -71,54 +128,42 @@ module MassiveRecord | |
| 71 128 | 
             
                      find_many ? records : records.first
         | 
| 72 129 | 
             
                    end
         | 
| 73 130 |  | 
| 74 | 
            -
                    def find_in_batches(*args)
         | 
| 75 | 
            -
                      return unless table.exists?
         | 
| 76 131 |  | 
| 77 | 
            -
                      table.find_in_batches(*args) do |rows|
         | 
| 78 | 
            -
                        records = rows.collect do |row|
         | 
| 79 | 
            -
                          instantiate(transpose_hbase_columns_to_record_attributes(row))
         | 
| 80 | 
            -
                        end    
         | 
| 81 | 
            -
                        yield records
         | 
| 82 | 
            -
                      end
         | 
| 83 | 
            -
                    end
         | 
| 84 | 
            -
                    
         | 
| 85 | 
            -
                    def find_each(*args)
         | 
| 86 | 
            -
                      find_in_batches(*args) do |rows|
         | 
| 87 | 
            -
                        rows.each do |row|
         | 
| 88 | 
            -
                          yield row
         | 
| 89 | 
            -
                        end
         | 
| 90 | 
            -
                      end
         | 
| 91 | 
            -
                    end
         | 
| 92 132 |  | 
| 93 133 |  | 
| 94 | 
            -
                     | 
| 95 | 
            -
                      !!find(id) rescue false
         | 
| 96 | 
            -
                    end
         | 
| 134 | 
            +
                    private
         | 
| 97 135 |  | 
| 136 | 
            +
                    def query_hbase(type, args, find_many) # :nodoc:
         | 
| 137 | 
            +
                      result_from_table = if type
         | 
| 138 | 
            +
                                            hbase_query_all_first(type, args)
         | 
| 139 | 
            +
                                          else
         | 
| 140 | 
            +
                                            options = args.extract_options!
         | 
| 141 | 
            +
                                            what_to_find = args.first
         | 
| 142 | 
            +
                                            expected_result_size = 1
         | 
| 98 143 |  | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 144 | 
            +
                                            if args.first.kind_of?(Array)
         | 
| 145 | 
            +
                                              find_many = true
         | 
| 146 | 
            +
                                            elsif args.length > 1
         | 
| 147 | 
            +
                                              find_many = true
         | 
| 148 | 
            +
                                              what_to_find = args
         | 
| 149 | 
            +
                                            end
         | 
| 102 150 |  | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
                                                  scope
         | 
| 107 | 
            -
                                                when Hash
         | 
| 108 | 
            -
                                                  Scope.new(self, :find_options => scope)
         | 
| 109 | 
            -
                                                else
         | 
| 110 | 
            -
                                                  raise "Don't know how to set scope with #{scope.class}."
         | 
| 111 | 
            -
                                                end
         | 
| 112 | 
            -
                    end
         | 
| 151 | 
            +
                                            expected_result_size = what_to_find.length if what_to_find.is_a? Array
         | 
| 152 | 
            +
                                            hbase_query_find(what_to_find, options)
         | 
| 153 | 
            +
                                          end
         | 
| 113 154 |  | 
| 114 | 
            -
             | 
| 115 | 
            -
                      Scope.new(self)
         | 
| 155 | 
            +
                      [find_many, expected_result_size, what_to_find, result_from_table]
         | 
| 116 156 | 
             
                    end
         | 
| 117 157 |  | 
| 158 | 
            +
                    def hbase_query_all_first(type, args)
         | 
| 159 | 
            +
                      table.send(type, *args) # first() / all()
         | 
| 160 | 
            +
                    end
         | 
| 118 161 |  | 
| 119 | 
            -
                     | 
| 162 | 
            +
                    def hbase_query_find(what_to_find, options)
         | 
| 163 | 
            +
                      table.find(what_to_find, options)
         | 
| 164 | 
            +
                    end
         | 
| 120 165 |  | 
| 121 | 
            -
                    def transpose_hbase_columns_to_record_attributes(row)
         | 
| 166 | 
            +
                    def transpose_hbase_columns_to_record_attributes(row) #: nodoc:
         | 
| 122 167 | 
             
                      attributes = {:id => row.id}
         | 
| 123 168 |  | 
| 124 169 | 
             
                      autoload_column_families_and_fields_with(row.columns.keys)
         | 
| @@ -131,7 +176,7 @@ module MassiveRecord | |
| 131 176 | 
             
                      attributes
         | 
| 132 177 | 
             
                    end
         | 
| 133 178 |  | 
| 134 | 
            -
                    def instantiate(record)
         | 
| 179 | 
            +
                    def instantiate(record) # :nodoc:
         | 
| 135 180 | 
             
                      model = if klass = record[inheritance_attribute] and klass.present?
         | 
| 136 181 | 
             
                                klass.constantize.allocate
         | 
| 137 182 | 
             
                              else
         | 
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            module MassiveRecord
         | 
| 2 | 
            +
              module ORM
         | 
| 3 | 
            +
                #
         | 
| 4 | 
            +
                # Module which adds functionality so we rescue errors which might occur on
         | 
| 5 | 
            +
                # find calls when we are querying tables which does not exist.
         | 
| 6 | 
            +
                # Small problem with this, which will need to look into.
         | 
| 7 | 
            +
                #
         | 
| 8 | 
            +
                module RescueMissingTableOnFind
         | 
| 9 | 
            +
                  def do_find(*args)
         | 
| 10 | 
            +
                    create_table_and_retry_if_table_missing { super }
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def find_in_batches(*args) 
         | 
| 14 | 
            +
                    create_table_and_retry_if_table_missing { super }
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
             | 
| 18 | 
            +
             | 
| 19 | 
            +
                  private
         | 
| 20 | 
            +
             | 
| 21 | 
            +
             | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  # Yields the block and if any errors occur we will check if table does exist or not.
         | 
| 24 | 
            +
                  # Create it if it's missing and try again.
         | 
| 25 | 
            +
                  #
         | 
| 26 | 
            +
                  # Errors which we'll retry on are:
         | 
| 27 | 
            +
                  #   Apache::Hadoop::Hbase::Thrift::IOError          -> Raised on simple find(id) calls
         | 
| 28 | 
            +
                  #   Apache::Hadoop::Hbase::Thrift::IllegalArgument  -> Raised when a scanner is used
         | 
| 29 | 
            +
                  #
         | 
| 30 | 
            +
                  def create_table_and_retry_if_table_missing # :nodoc:
         | 
| 31 | 
            +
                    begin
         | 
| 32 | 
            +
                      yield
         | 
| 33 | 
            +
                    rescue Apache::Hadoop::Hbase::Thrift::IOError, Apache::Hadoop::Hbase::Thrift::IllegalArgument => error
         | 
| 34 | 
            +
                      if table.exists?
         | 
| 35 | 
            +
                        raise error
         | 
| 36 | 
            +
                      else
         | 
| 37 | 
            +
                        logger.try :info, "*** TABLE MISSING: Table '#{table_name}' seems to be missing. Will create it, then retry call to find()."
         | 
| 38 | 
            +
                        hbase_create_table!
         | 
| 39 | 
            +
                        yield
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
            end
         |