nobrainer 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/no_brainer/config.rb +9 -16
  3. data/lib/no_brainer/connection.rb +32 -26
  4. data/lib/no_brainer/criteria/chainable/core.rb +17 -20
  5. data/lib/no_brainer/criteria/chainable/limit.rb +2 -2
  6. data/lib/no_brainer/criteria/chainable/order_by.rb +24 -7
  7. data/lib/no_brainer/criteria/chainable/raw.rb +11 -3
  8. data/lib/no_brainer/criteria/chainable/scope.rb +11 -7
  9. data/lib/no_brainer/criteria/chainable/where.rb +32 -15
  10. data/lib/no_brainer/criteria/termination/cache.rb +13 -9
  11. data/lib/no_brainer/criteria/termination/eager_loading.rb +12 -26
  12. data/lib/no_brainer/document.rb +2 -6
  13. data/lib/no_brainer/document/association.rb +8 -7
  14. data/lib/no_brainer/document/association/belongs_to.rb +27 -22
  15. data/lib/no_brainer/document/association/core.rb +8 -8
  16. data/lib/no_brainer/document/association/eager_loader.rb +57 -0
  17. data/lib/no_brainer/document/association/has_many.rb +49 -26
  18. data/lib/no_brainer/document/association/has_many_through.rb +31 -0
  19. data/lib/no_brainer/document/association/has_one.rb +13 -0
  20. data/lib/no_brainer/document/association/has_one_through.rb +10 -0
  21. data/lib/no_brainer/document/attributes.rb +4 -3
  22. data/lib/no_brainer/document/core.rb +1 -1
  23. data/lib/no_brainer/document/criteria.rb +2 -3
  24. data/lib/no_brainer/document/index.rb +3 -3
  25. data/lib/no_brainer/document/polymorphic.rb +1 -1
  26. data/lib/no_brainer/document/serialization.rb +1 -1
  27. data/lib/no_brainer/document/store_in.rb +5 -3
  28. data/lib/no_brainer/error.rb +1 -0
  29. data/lib/no_brainer/query_runner.rb +2 -1
  30. data/lib/no_brainer/query_runner/driver.rb +1 -4
  31. data/lib/no_brainer/query_runner/missing_index.rb +11 -0
  32. data/lib/no_brainer/query_runner/run_options.rb +2 -3
  33. data/lib/no_brainer/railtie.rb +0 -1
  34. data/lib/no_brainer/version.rb +1 -1
  35. data/lib/nobrainer.rb +5 -5
  36. metadata +28 -24
  37. data/lib/no_brainer/database.rb +0 -41
@@ -0,0 +1,31 @@
1
+ class NoBrainer::Document::Association::HasManyThrough
2
+ include NoBrainer::Document::Association::Core
3
+
4
+ class Metadata
5
+ VALID_OPTIONS = [:through]
6
+ include NoBrainer::Document::Association::Core::Metadata
7
+
8
+ def through_association_name
9
+ options[:through].to_sym
10
+ end
11
+
12
+ def through_association
13
+ owner_klass.association_metadata[through_association_name] or
14
+ raise "#{through_association_name} association not found"
15
+ end
16
+
17
+ def eager_load(docs, criteria=nil)
18
+ NoBrainer::Document::Association::EagerLoader.new
19
+ .eager_load_association(through_association.eager_load(docs), target_name, criteria)
20
+ end
21
+ end
22
+
23
+ def read
24
+ # TODO implement joins
25
+ @targets ||= metadata.eager_load([owner]).freeze
26
+ end
27
+
28
+ def write(new_children)
29
+ raise "You can't assign #{target_name}"
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ class NoBrainer::Document::Association::HasOne < NoBrainer::Document::Association::HasMany
2
+ class Metadata < NoBrainer::Document::Association::HasMany::Metadata
3
+ def target_klass
4
+ (options[:class_name] || target_name.to_s.camelize).constantize
5
+ end
6
+ end
7
+
8
+ def read
9
+ targets = target_criteria.to_a # to load the cache
10
+ NoBrainer.logger.warn "#{owner} has more than one #{target_name}" if targets.size > 1
11
+ targets.first
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ class NoBrainer::Document::Association::HasOneThrough < NoBrainer::Document::Association::HasManyThrough
2
+ class Metadata < NoBrainer::Document::Association::HasManyThrough::Metadata
3
+ end
4
+
5
+ def read
6
+ targets = super
7
+ NoBrainer.logger.warn "#{owner} has more than one #{target_name}" if targets.size > 1
8
+ targets.first
9
+ end
10
+ end
@@ -1,11 +1,12 @@
1
1
  module NoBrainer::Document::Attributes
2
2
  VALID_FIELD_OPTIONS = [:index, :default]
3
+ RESERVED_FIELD_NAMES = [:index, :default, :and, :or, :selector, :associations] + NoBrainer::DecoratedSymbol::MODIFIERS.keys
3
4
  extend ActiveSupport::Concern
4
5
 
5
6
  included do
6
7
  # Not using class_attribute because we want to
7
8
  # use our custom logic
8
- class << self; attr_accessor :fields; end
9
+ singleton_class.send(:attr_accessor, :fields)
9
10
  self.fields = {}
10
11
  end
11
12
 
@@ -82,8 +83,8 @@ module NoBrainer::Document::Attributes
82
83
  def field(name, options={})
83
84
  name = name.to_sym
84
85
 
85
- options.assert_valid_keys(*NoBrainer::Document::Attributes::VALID_FIELD_OPTIONS)
86
- if name.in? NoBrainer::Criteria::Chainable::Where::RESERVED_FIELDS
86
+ options.assert_valid_keys(*VALID_FIELD_OPTIONS)
87
+ if name.in?(RESERVED_FIELD_NAMES)
87
88
  raise "Cannot use a reserved field name: #{name}"
88
89
  end
89
90
 
@@ -1,7 +1,7 @@
1
1
  module NoBrainer::Document::Core
2
2
  extend ActiveSupport::Concern
3
3
 
4
- class << self; attr_accessor :all; end
4
+ singleton_class.send(:attr_accessor, :all)
5
5
  self.all = []
6
6
 
7
7
  # TODO This assume the primary key is id.
@@ -5,9 +5,7 @@ module NoBrainer::Document::Criteria
5
5
  self.class.selector_for(id)
6
6
  end
7
7
 
8
- included do
9
- class_attribute :default_scope_proc
10
- end
8
+ included { cattr_accessor :default_scope_proc, :instance_accessor => false }
11
9
 
12
10
  module ClassMethods
13
11
  delegate :to_rql, # Core
@@ -39,6 +37,7 @@ module NoBrainer::Document::Criteria
39
37
 
40
38
  def default_scope(criteria=nil, &block)
41
39
  criteria ||= block
40
+ raise "store_in() must be called on the parent class" unless is_root_class?
42
41
  self.default_scope_proc = criteria.is_a?(Proc) ? criteria : proc { criteria }
43
42
  end
44
43
 
@@ -3,7 +3,7 @@ module NoBrainer::Document::Index
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- class_attribute :indexes
6
+ cattr_accessor :indexes, :instance_accessor => false
7
7
  self.indexes = {}
8
8
  self.index :id
9
9
  end
@@ -12,7 +12,7 @@ module NoBrainer::Document::Index
12
12
  def index(name, *args)
13
13
  name = name.to_sym
14
14
  options = args.extract_options!
15
- options.assert_valid_keys(*NoBrainer::Document::Index::VALID_INDEX_OPTIONS)
15
+ options.assert_valid_keys(*VALID_INDEX_OPTIONS)
16
16
 
17
17
  raise "Too many arguments: #{args}" if args.size > 1
18
18
 
@@ -24,7 +24,7 @@ module NoBrainer::Document::Index
24
24
  end
25
25
 
26
26
  # FIXME Primary key may not always be :id
27
- if name.in?(NoBrainer::Criteria::Chainable::Where::RESERVED_FIELDS)
27
+ if name.in?(NoBrainer::Document::Attributes::RESERVED_FIELD_NAMES)
28
28
  raise "Cannot use a reserved field name: #{name}"
29
29
  end
30
30
  if has_field?(name) && kind != :single
@@ -3,7 +3,7 @@ module NoBrainer::Document::Polymorphic
3
3
  include ActiveSupport::DescendantsTracker
4
4
 
5
5
  included do
6
- class_attribute :root_class
6
+ cattr_accessor :root_class
7
7
  self.root_class = self
8
8
  end
9
9
 
@@ -5,5 +5,5 @@ module NoBrainer::Document::Serialization
5
5
  include ActiveModel::Serializers::JSON
6
6
  include ActiveModel::Serializers::Xml
7
7
 
8
- included { self.include_root_in_json = NoBrainer::Config.include_root_in_json }
8
+ included { self.include_root_in_json = false }
9
9
  end
@@ -1,14 +1,16 @@
1
+ require 'rethinkdb'
2
+
1
3
  module NoBrainer::Document::StoreIn
2
4
  extend ActiveSupport::Concern
3
5
 
4
6
  included do
5
- class_attribute :store_in_options
7
+ cattr_accessor :store_in_options, :instance_accessor => false
6
8
  self.store_in_options = {}
7
9
  end
8
10
 
9
11
  module ClassMethods
10
12
  def store_in(options)
11
- raise "store_in() must be called on the parent class" unless root_class?
13
+ raise "store_in() must be called on the parent class" unless is_root_class?
12
14
  self.store_in_options.merge!(options)
13
15
  end
14
16
 
@@ -20,7 +22,7 @@ module NoBrainer::Document::StoreIn
20
22
  def table_name
21
23
  table = store_in_options[:table]
22
24
  table_name = table.is_a?(Proc) ? table.call : table
23
- table_name || root_class.name.underscore.gsub('/', '__').pluralize
25
+ table_name || root_class.name.tableize.gsub('/', '__')
24
26
  end
25
27
 
26
28
  def rql_table
@@ -5,6 +5,7 @@ module NoBrainer::Error
5
5
  class DocumentNotSaved < StandardError; end
6
6
  class ChildrenExist < StandardError; end
7
7
  class CannotUseIndex < StandardError; end
8
+ class MissingIndex < StandardError; end
8
9
  class InvalidType < StandardError; end
9
10
  class AssociationNotSaved < StandardError; end
10
11
  end
@@ -11,7 +11,7 @@ module NoBrainer::QueryRunner
11
11
  end
12
12
 
13
13
  autoload :Driver, :DatabaseOnDemand, :TableOnDemand, :WriteError,
14
- :Connection, :Selection, :RunOptions, :Logger
14
+ :Connection, :Selection, :RunOptions, :Logger, :MissingIndex
15
15
 
16
16
  class << self
17
17
  attr_accessor :stack
@@ -29,6 +29,7 @@ module NoBrainer::QueryRunner
29
29
  use RunOptions
30
30
  use Connection
31
31
  use WriteError
32
+ use MissingIndex
32
33
  use DatabaseOnDemand
33
34
  use TableOnDemand
34
35
  use Logger
@@ -1,8 +1,5 @@
1
1
  class NoBrainer::QueryRunner::Driver < NoBrainer::QueryRunner::Middleware
2
2
  def call(env)
3
- env[:query].run(NoBrainer.connection, env[:options])
4
- rescue NoMethodError => e
5
- raise "NoBrainer is not connected to a RethinkDB instance" unless NoBrainer.connection
6
- raise e
3
+ env[:query].run(NoBrainer.connection.raw, env[:options])
7
4
  end
8
5
  end
@@ -0,0 +1,11 @@
1
+ class NoBrainer::QueryRunner::MissingIndex < NoBrainer::QueryRunner::Middleware
2
+ def call(env)
3
+ @runner.call(env)
4
+ rescue RethinkDB::RqlRuntimeError => e
5
+ if e.message =~ /^Index `(.+)` was not found\.$/
6
+ raise NoBrainer::Error::MissingIndex.new("Please run \"rake db:update_indexes\" to create index `#{$1}`\n" +
7
+ "--> Read http://nobrainer.io/docs/indexes for more information.")
8
+ end
9
+ raise
10
+ end
11
+ end
@@ -1,12 +1,11 @@
1
1
  class NoBrainer::QueryRunner::RunOptions < NoBrainer::QueryRunner::Middleware
2
2
  # XXX NoBrainer::Database#drop() uses Thread.current[:nobrainer_options]
3
- # We should fix that hack.
4
3
 
5
4
  def self.with_database(db_name, &block)
6
- with_options(:db => db_name, &block)
5
+ with(:db => db_name, &block)
7
6
  end
8
7
 
9
- def self.with_options(options={}, &block)
8
+ def self.with(options={}, &block)
10
9
  old_options = Thread.current[:nobrainer_options]
11
10
  Thread.current[:nobrainer_options] = (old_options || {}).merge(options.symbolize_keys)
12
11
  block.call if block
@@ -5,7 +5,6 @@ class NoBrainer::Railtie < Rails::Railtie
5
5
  config.action_dispatch.rescue_responses.merge!(
6
6
  "NoBrainer::Errors::DocumentNotFound" => :not_found,
7
7
  "NoBrainer::Errors::DocumentInvalid" => :unprocessable_entity,
8
- "NoBrainer::Errors::DocumentNotSaved" => :unprocessable_entity,
9
8
  )
10
9
 
11
10
  rake_tasks do
@@ -1,3 +1,3 @@
1
1
  module NoBrainer
2
- VERSION = '0.8.0'
2
+ VERSION = '0.9.0'
3
3
  end
data/lib/nobrainer.rb CHANGED
@@ -34,13 +34,13 @@ module NoBrainer
34
34
  end
35
35
  end
36
36
 
37
- # No not use modules to extend, it's nice to see the NoBrainer module API here.
38
- delegate :db_create, :db_drop, :db_list, :database, :to => :connection
39
- delegate :table_create, :table_drop, :table_list,
40
- :drop!, :purge!, :to => :database
37
+ # Not using modules to extend, it's nicer to see the NoBrainer module API here.
38
+ delegate :db_create, :db_drop, :db_list,
39
+ :table_create, :table_drop, :table_list,
40
+ :drop!, :purge!, :to => :connection
41
41
  delegate :run, :to => 'NoBrainer::QueryRunner'
42
42
  delegate :update_indexes, :to => 'NoBrainer::IndexManager'
43
- delegate :with_options, :with_database, :to => 'NoBrainer::QueryRunner::RunOptions'
43
+ delegate :with, :with_database, :to => 'NoBrainer::QueryRunner::RunOptions'
44
44
  delegate :configure, :logger, :to => 'NoBrainer::Config'
45
45
 
46
46
  def jruby?
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.8.0
4
+ version: 0.9.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: 2013-12-31 00:00:00.000000000 Z
11
+ date: 2014-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rethinkdb
@@ -65,61 +65,65 @@ executables: []
65
65
  extensions: []
66
66
  extra_rdoc_files: []
67
67
  files:
68
- - lib/no_brainer/document/polymorphic.rb
69
68
  - lib/no_brainer/document/id.rb
70
69
  - lib/no_brainer/document/injection_layer.rb
71
- - lib/no_brainer/document/store_in.rb
72
70
  - lib/no_brainer/document/timestamps.rb
73
- - lib/no_brainer/document/association.rb
71
+ - lib/no_brainer/document/association/belongs_to.rb
74
72
  - lib/no_brainer/document/association/core.rb
73
+ - lib/no_brainer/document/association/eager_loader.rb
74
+ - lib/no_brainer/document/association/has_many_through.rb
75
75
  - lib/no_brainer/document/association/has_many.rb
76
- - lib/no_brainer/document/association/belongs_to.rb
77
- - lib/no_brainer/document/core.rb
78
- - lib/no_brainer/document/criteria.rb
76
+ - lib/no_brainer/document/association/has_one.rb
77
+ - lib/no_brainer/document/association/has_one_through.rb
79
78
  - lib/no_brainer/document/dirty.rb
80
79
  - lib/no_brainer/document/dynamic_attributes.rb
81
80
  - lib/no_brainer/document/persistance.rb
82
- - lib/no_brainer/document/serialization.rb
83
81
  - lib/no_brainer/document/validation.rb
84
- - lib/no_brainer/document/attributes.rb
82
+ - lib/no_brainer/document/serialization.rb
83
+ - lib/no_brainer/document/criteria.rb
85
84
  - lib/no_brainer/document/index.rb
85
+ - lib/no_brainer/document/polymorphic.rb
86
+ - lib/no_brainer/document/store_in.rb
87
+ - lib/no_brainer/document/attributes.rb
88
+ - lib/no_brainer/document/core.rb
89
+ - lib/no_brainer/document/association.rb
86
90
  - lib/no_brainer/query_runner/connection.rb
87
- - lib/no_brainer/query_runner/driver.rb
88
91
  - lib/no_brainer/query_runner/database_on_demand.rb
89
92
  - lib/no_brainer/query_runner/logger.rb
90
- - lib/no_brainer/query_runner/run_options.rb
91
93
  - lib/no_brainer/query_runner/table_on_demand.rb
92
94
  - lib/no_brainer/query_runner/write_error.rb
95
+ - lib/no_brainer/query_runner/driver.rb
96
+ - lib/no_brainer/query_runner/missing_index.rb
97
+ - lib/no_brainer/query_runner/run_options.rb
93
98
  - lib/no_brainer/railtie/database.rake
94
- - lib/no_brainer/criteria/chainable/raw.rb
95
- - lib/no_brainer/criteria/chainable/core.rb
96
99
  - lib/no_brainer/criteria/chainable/limit.rb
97
100
  - lib/no_brainer/criteria/chainable/order_by.rb
98
101
  - lib/no_brainer/criteria/chainable/scope.rb
102
+ - lib/no_brainer/criteria/chainable/raw.rb
103
+ - lib/no_brainer/criteria/chainable/core.rb
99
104
  - lib/no_brainer/criteria/chainable/where.rb
100
105
  - lib/no_brainer/criteria/termination/inc.rb
101
106
  - lib/no_brainer/criteria/termination/count.rb
102
107
  - lib/no_brainer/criteria/termination/first.rb
103
108
  - lib/no_brainer/criteria/termination/enumerable.rb
104
109
  - lib/no_brainer/criteria/termination/update.rb
105
- - lib/no_brainer/criteria/termination/cache.rb
106
110
  - lib/no_brainer/criteria/termination/delete.rb
107
111
  - lib/no_brainer/criteria/termination/eager_loading.rb
108
- - lib/no_brainer/autoload.rb
109
- - lib/no_brainer/railtie.rb
110
- - lib/no_brainer/criteria.rb
111
- - lib/no_brainer/database.rb
112
+ - lib/no_brainer/criteria/termination/cache.rb
112
113
  - lib/no_brainer/decorated_symbol.rb
113
- - lib/no_brainer/document.rb
114
- - lib/no_brainer/error.rb
115
114
  - lib/no_brainer/index_manager.rb
116
115
  - lib/no_brainer/loader.rb
117
116
  - lib/no_brainer/locale/en.yml
118
- - lib/no_brainer/query_runner.rb
119
117
  - lib/no_brainer/util.rb
120
- - lib/no_brainer/config.rb
121
- - lib/no_brainer/connection.rb
122
118
  - lib/no_brainer/fork.rb
119
+ - lib/no_brainer/criteria.rb
120
+ - lib/no_brainer/connection.rb
121
+ - lib/no_brainer/query_runner.rb
122
+ - lib/no_brainer/error.rb
123
+ - lib/no_brainer/document.rb
124
+ - lib/no_brainer/config.rb
125
+ - lib/no_brainer/railtie.rb
126
+ - lib/no_brainer/autoload.rb
123
127
  - lib/no_brainer/version.rb
124
128
  - lib/nobrainer.rb
125
129
  - README.md
@@ -1,41 +0,0 @@
1
- class NoBrainer::Database
2
- attr_accessor :connection
3
-
4
- # FIXME This class is a bit weird as we don't use it to represent the current
5
- # database.
6
-
7
- delegate :database_name, :to => :connection
8
-
9
- def initialize(connection)
10
- self.connection = connection
11
- end
12
-
13
- def raw
14
- @raw ||= RethinkDB::RQL.new.db(database_name)
15
- end
16
-
17
- def drop!
18
- # FIXME Sad hack.
19
- db = (Thread.current[:nobrainer_options] || {})[:db] || database_name
20
- connection.db_drop(db)['dropped'] == 1
21
- end
22
-
23
- # Note that truncating each table (purge) is much faster than dropping the
24
- # database (drop)
25
- def purge!(options={})
26
- table_list.each do |table_name|
27
- NoBrainer.run { |r| r.table(table_name).delete }
28
- end
29
- true
30
- rescue RuntimeError => e
31
- raise e unless e.message =~ /No entry with that name/
32
- end
33
-
34
- [:table_create, :table_drop, :table_list].each do |cmd|
35
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
36
- def #{cmd}(*args)
37
- NoBrainer.run { |r| r.#{cmd}(*args) }
38
- end
39
- RUBY
40
- end
41
- end