nobrainer 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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