nobrainer 0.13.1 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/no_brainer/config.rb +3 -3
- data/lib/no_brainer/criteria/delete.rb +2 -2
- data/lib/no_brainer/criteria/order_by.rb +33 -10
- data/lib/no_brainer/criteria/update.rb +2 -2
- data/lib/no_brainer/criteria/where.rb +1 -1
- data/lib/no_brainer/document/association/belongs_to.rb +10 -5
- data/lib/no_brainer/document/association/core.rb +1 -1
- data/lib/no_brainer/document/association/has_many.rb +10 -4
- data/lib/no_brainer/document/attributes.rb +27 -3
- data/lib/no_brainer/document/callbacks.rb +2 -10
- data/lib/no_brainer/document/core.rb +5 -3
- data/lib/no_brainer/document/criteria.rb +9 -9
- data/lib/no_brainer/document/dirty.rb +11 -0
- data/lib/no_brainer/document/dynamic_attributes.rb +4 -0
- data/lib/no_brainer/document/id.rb +49 -4
- data/lib/no_brainer/document/index.rb +6 -2
- data/lib/no_brainer/document/persistance.rb +5 -4
- data/lib/no_brainer/document/readonly.rb +7 -0
- data/lib/no_brainer/document/serialization.rb +4 -0
- data/lib/no_brainer/document/types.rb +8 -0
- data/lib/no_brainer/document/uniqueness.rb +4 -2
- data/lib/no_brainer/document/validation.rb +1 -1
- data/lib/no_brainer/document.rb +2 -0
- data/lib/no_brainer/error.rb +8 -8
- data/lib/no_brainer/query_runner/logger.rb +18 -12
- data/lib/no_brainer/query_runner/missing_index.rb +15 -3
- data/lib/no_brainer/query_runner/reconnect.rb +15 -4
- data/lib/no_brainer/query_runner/table_on_demand.rb +14 -25
- data/lib/no_brainer/query_runner/write_error.rb +5 -8
- data/lib/no_brainer/rql.rb +25 -0
- data/lib/nobrainer.rb +5 -6
- metadata +11 -11
- data/lib/no_brainer/util.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 43783bcf0d625af679de685ab771782ce9b141f3
|
4
|
+
data.tar.gz: 4b5e4ff205f55adb754133b681b6e71c71772790
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c182d056390dd2c7b51bac68999d79c72cfaca49837239c4eead56ce8d0ff474630cc7b24e9fa12a4ff8d36641a5f6cc46fb7c588c45e975af42e8d4b5dbebc
|
7
|
+
data.tar.gz: 5226adb01498de4e82f8b3a4729c818ed1a40dc07e3596061d64956407b9a62129108046e0625bbe60bcac1804f0b8111cab4ec3a736151b6cfdc3d5799f2d88
|
data/lib/no_brainer/config.rb
CHANGED
@@ -38,7 +38,7 @@ module NoBrainer::Config
|
|
38
38
|
|
39
39
|
def default_rethinkdb_url
|
40
40
|
db = ENV['RETHINKDB_DB'] || ENV['RDB_DB']
|
41
|
-
db ||= "#{Rails.application.class.parent_name.underscore}_#{Rails.env}"
|
41
|
+
db ||= "#{Rails.application.class.parent_name.underscore}_#{Rails.env}" rescue nil
|
42
42
|
host = ENV['RETHINKDB_HOST'] || ENV['RDB_HOST'] || 'localhost'
|
43
43
|
port = ENV['RETHINKDB_PORT'] || ENV['RDB_PORT']
|
44
44
|
auth = ENV['RETHINKDB_AUTH'] || ENV['RDB_AUTH']
|
@@ -48,11 +48,11 @@ module NoBrainer::Config
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def default_logger
|
51
|
-
defined?(Rails) ? Rails.logger : Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
|
51
|
+
defined?(Rails.logger) ? Rails.logger : Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
|
52
52
|
end
|
53
53
|
|
54
54
|
def default_durability
|
55
|
-
(defined?(Rails) && (Rails.env.test? || Rails.env.development?)) ? :soft : :hard
|
55
|
+
(defined?(Rails.env) && (Rails.env.test? || Rails.env.development?)) ? :soft : :hard
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
@@ -2,10 +2,10 @@ module NoBrainer::Criteria::Delete
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
def delete_all
|
5
|
-
run(to_rql.delete)
|
5
|
+
run(without_ordering.to_rql.delete)
|
6
6
|
end
|
7
7
|
|
8
8
|
def destroy_all
|
9
|
-
to_a.each { |doc| doc.destroy }
|
9
|
+
without_ordering.to_a.each { |doc| doc.destroy }
|
10
10
|
end
|
11
11
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module NoBrainer::Criteria::OrderBy
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
-
included { attr_accessor :order, :
|
4
|
+
included { attr_accessor :order, :ordering_mode }
|
5
5
|
|
6
6
|
def initialize(options={})
|
7
7
|
super
|
@@ -24,36 +24,55 @@ module NoBrainer::Criteria::OrderBy
|
|
24
24
|
|
25
25
|
chain do |criteria|
|
26
26
|
criteria.order = rules
|
27
|
-
criteria.
|
27
|
+
criteria.ordering_mode = :normal
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
def without_ordering
|
32
|
+
chain { |criteria| criteria.ordering_mode = :disabled }
|
33
|
+
end
|
34
|
+
|
31
35
|
def merge!(criteria, options={})
|
32
36
|
super
|
33
37
|
# The latest order_by() wins
|
34
38
|
self.order = criteria.order if criteria.order.present?
|
35
|
-
self.
|
39
|
+
self.ordering_mode = criteria.ordering_mode unless criteria.ordering_mode.nil?
|
36
40
|
self
|
37
41
|
end
|
38
42
|
|
39
43
|
def reverse_order
|
40
|
-
chain
|
44
|
+
chain do |criteria|
|
45
|
+
criteria.ordering_mode =
|
46
|
+
case self.ordering_mode
|
47
|
+
when nil then :reversed
|
48
|
+
when :normal then :reversed
|
49
|
+
when :reversed then :normal
|
50
|
+
when :disabled then :disabled
|
51
|
+
end
|
52
|
+
end
|
41
53
|
end
|
42
54
|
|
43
55
|
private
|
44
56
|
|
45
57
|
def effective_order
|
46
|
-
self.order.presence || {
|
58
|
+
self.order.presence || (klass ? {klass.pk_name => :asc} : {})
|
47
59
|
end
|
48
60
|
|
49
61
|
def reverse_order?
|
50
|
-
|
62
|
+
self.ordering_mode == :reversed
|
63
|
+
end
|
64
|
+
|
65
|
+
def should_order?
|
66
|
+
self.ordering_mode != :disabled
|
51
67
|
end
|
52
68
|
|
53
69
|
def compile_rql_pass1
|
54
70
|
rql = super
|
71
|
+
return rql unless should_order?
|
72
|
+
_effective_order = effective_order
|
73
|
+
return rql if _effective_order.empty?
|
55
74
|
|
56
|
-
rql_rules =
|
75
|
+
rql_rules = _effective_order.map do |k,v|
|
57
76
|
case v
|
58
77
|
when :asc then reverse_order? ? RethinkDB::RQL.new.desc(k) : RethinkDB::RQL.new.asc(k)
|
59
78
|
when :desc then reverse_order? ? RethinkDB::RQL.new.asc(k) : RethinkDB::RQL.new.desc(k)
|
@@ -64,9 +83,10 @@ module NoBrainer::Criteria::OrderBy
|
|
64
83
|
# We are going to try to go so and if we cannot, we'll simply apply
|
65
84
|
# the ordering in pass2, which will happen after a potential filter().
|
66
85
|
|
67
|
-
|
86
|
+
NoBrainer::RQL.is_table?(rql)
|
87
|
+
if NoBrainer::RQL.is_table?(rql) && !without_index?
|
68
88
|
options = {}
|
69
|
-
first_key =
|
89
|
+
first_key = _effective_order.first[0]
|
70
90
|
if (first_key.is_a?(Symbol) || first_key.is_a?(String)) && klass.has_index?(first_key)
|
71
91
|
options[:index] = rql_rules.shift
|
72
92
|
end
|
@@ -83,7 +103,10 @@ module NoBrainer::Criteria::OrderBy
|
|
83
103
|
|
84
104
|
def compile_rql_pass2
|
85
105
|
rql = super
|
86
|
-
|
106
|
+
if @rql_rules_pass2
|
107
|
+
rql = rql.order_by(*@rql_rules_pass2)
|
108
|
+
@rql_rules_pass2 = nil
|
109
|
+
end
|
87
110
|
rql
|
88
111
|
end
|
89
112
|
|
@@ -2,10 +2,10 @@ module NoBrainer::Criteria::Update
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
def update_all(*args, &block)
|
5
|
-
run(to_rql.update(*args, &block))
|
5
|
+
run(without_ordering.to_rql.update(*args, &block))
|
6
6
|
end
|
7
7
|
|
8
8
|
def replace_all(*args, &block)
|
9
|
-
run(to_rql.replace(*args, &block))
|
9
|
+
run(without_ordering.to_rql.replace(*args, &block))
|
10
10
|
end
|
11
11
|
end
|
@@ -92,7 +92,7 @@ module NoBrainer::Criteria::Where
|
|
92
92
|
target_klass = association.target_klass
|
93
93
|
opts = { :attr_name => key, :value => value, :type => target_klass}
|
94
94
|
raise NoBrainer::Error::InvalidType.new(opts) unless value.is_a?(target_klass)
|
95
|
-
value.
|
95
|
+
value.pk_value
|
96
96
|
else
|
97
97
|
criteria.klass.cast_user_to_db_for(key, value)
|
98
98
|
end
|
@@ -2,13 +2,18 @@ class NoBrainer::Document::Association::BelongsTo
|
|
2
2
|
include NoBrainer::Document::Association::Core
|
3
3
|
|
4
4
|
class Metadata
|
5
|
-
VALID_OPTIONS = [:foreign_key, :class_name, :index, :validates, :required]
|
5
|
+
VALID_OPTIONS = [:primary_key, :foreign_key, :class_name, :index, :validates, :required]
|
6
6
|
include NoBrainer::Document::Association::Core::Metadata
|
7
7
|
extend NoBrainer::Document::Association::EagerLoader::Generic
|
8
8
|
|
9
9
|
def foreign_key
|
10
10
|
# TODO test :foreign_key
|
11
|
-
options[:foreign_key].try(:to_sym) || :"#{target_name}
|
11
|
+
options[:foreign_key].try(:to_sym) || :"#{target_name}_#{primary_key}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def primary_key
|
15
|
+
# TODO test :primary_key
|
16
|
+
options[:primary_key].try(:to_sym) || target_klass.pk_name
|
12
17
|
end
|
13
18
|
|
14
19
|
def target_klass
|
@@ -32,7 +37,7 @@ class NoBrainer::Document::Association::BelongsTo
|
|
32
37
|
add_callback_for(:after_validation)
|
33
38
|
end
|
34
39
|
|
35
|
-
eager_load_with :owner_key => ->{ foreign_key }, :target_key => ->{
|
40
|
+
eager_load_with :owner_key => ->{ foreign_key }, :target_key => ->{ primary_key },
|
36
41
|
:unscoped => true
|
37
42
|
end
|
38
43
|
|
@@ -49,13 +54,13 @@ class NoBrainer::Document::Association::BelongsTo
|
|
49
54
|
return target if loaded?
|
50
55
|
|
51
56
|
if fk = owner.read_attribute(foreign_key)
|
52
|
-
preload(target_klass.
|
57
|
+
preload(target_klass.unscoped.where(primary_key => fk).first)
|
53
58
|
end
|
54
59
|
end
|
55
60
|
|
56
61
|
def write(target)
|
57
62
|
assert_target_type(target)
|
58
|
-
owner.write_attribute(foreign_key, target.try(:
|
63
|
+
owner.write_attribute(foreign_key, target.try(:pk_value))
|
59
64
|
preload(target)
|
60
65
|
end
|
61
66
|
|
@@ -49,7 +49,7 @@ module NoBrainer::Document::Association::Core
|
|
49
49
|
|
50
50
|
included { attr_accessor :metadata, :owner }
|
51
51
|
|
52
|
-
delegate :foreign_key, :target_name, :target_klass, :to => :metadata
|
52
|
+
delegate :primary_key, :foreign_key, :target_name, :target_klass, :to => :metadata
|
53
53
|
|
54
54
|
def initialize(metadata, owner)
|
55
55
|
@metadata, @owner = metadata, owner
|
@@ -2,13 +2,18 @@ class NoBrainer::Document::Association::HasMany
|
|
2
2
|
include NoBrainer::Document::Association::Core
|
3
3
|
|
4
4
|
class Metadata
|
5
|
-
VALID_OPTIONS = [:foreign_key, :class_name, :dependent]
|
5
|
+
VALID_OPTIONS = [:primary_key, :foreign_key, :class_name, :dependent]
|
6
6
|
include NoBrainer::Document::Association::Core::Metadata
|
7
7
|
extend NoBrainer::Document::Association::EagerLoader::Generic
|
8
8
|
|
9
9
|
def foreign_key
|
10
10
|
# TODO test :foreign_key
|
11
|
-
options[:foreign_key].try(:to_sym) || owner_klass.name.
|
11
|
+
options[:foreign_key].try(:to_sym) || :"#{owner_klass.name.underscore}_#{owner_klass.pk_name}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def primary_key
|
15
|
+
# TODO test :primary_key
|
16
|
+
options[:primary_key].try(:to_sym) || target_klass.pk_name
|
12
17
|
end
|
13
18
|
|
14
19
|
def target_klass
|
@@ -25,6 +30,7 @@ class NoBrainer::Document::Association::HasMany
|
|
25
30
|
target_klass.association_metadata.values.select do |assoc|
|
26
31
|
assoc.is_a?(NoBrainer::Document::Association::BelongsTo::Metadata) and
|
27
32
|
assoc.foreign_key == self.foreign_key and
|
33
|
+
assoc.primary_key == self.primary_key and
|
28
34
|
assoc.target_klass.root_class == owner_klass.root_class
|
29
35
|
end
|
30
36
|
end
|
@@ -34,11 +40,11 @@ class NoBrainer::Document::Association::HasMany
|
|
34
40
|
add_callback_for(:before_destroy) if options[:dependent]
|
35
41
|
end
|
36
42
|
|
37
|
-
eager_load_with :owner_key => ->{
|
43
|
+
eager_load_with :owner_key => ->{ primary_key }, :target_key => ->{ foreign_key }
|
38
44
|
end
|
39
45
|
|
40
46
|
def target_criteria
|
41
|
-
@target_criteria ||= target_klass.where(foreign_key => owner.
|
47
|
+
@target_criteria ||= target_klass.where(foreign_key => owner.pk_value)
|
42
48
|
.after_find(set_inverse_proc)
|
43
49
|
end
|
44
50
|
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module NoBrainer::Document::Attributes
|
2
|
-
VALID_FIELD_OPTIONS = [:index, :default, :type, :cast_user_to_db,
|
3
|
-
|
2
|
+
VALID_FIELD_OPTIONS = [:index, :default, :type, :cast_user_to_db,
|
3
|
+
:cast_db_to_user, :validates, :required, :unique,
|
4
|
+
:readonly, :primary_key]
|
5
|
+
RESERVED_FIELD_NAMES = [:index, :default, :and, :or, :selector, :associations, :pk_value] \
|
6
|
+
+ NoBrainer::DecoratedSymbol::MODIFIERS.keys
|
4
7
|
extend ActiveSupport::Concern
|
5
8
|
|
6
9
|
included do
|
@@ -15,8 +18,12 @@ module NoBrainer::Document::Attributes
|
|
15
18
|
assign_attributes(attrs, options.reverse_merge(:pristine => true))
|
16
19
|
end
|
17
20
|
|
21
|
+
def readable_attributes
|
22
|
+
@_attributes.keys & self.class.fields.keys.map(&:to_s)
|
23
|
+
end
|
24
|
+
|
18
25
|
def attributes
|
19
|
-
Hash[
|
26
|
+
Hash[readable_attributes.map { |k| [k, read_attribute(k)] }].with_indifferent_access.freeze
|
20
27
|
end
|
21
28
|
|
22
29
|
def read_attribute(name)
|
@@ -97,6 +104,23 @@ module NoBrainer::Document::Attributes
|
|
97
104
|
_field(attr, self.fields[attr])
|
98
105
|
end
|
99
106
|
|
107
|
+
def _remove_field(attr, options={})
|
108
|
+
inject_in_layer :attributes do
|
109
|
+
remove_method("#{attr}=")
|
110
|
+
remove_method("#{attr}")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def remove_field(attr, options={})
|
115
|
+
attr = attr.to_sym
|
116
|
+
|
117
|
+
_remove_field(attr, options)
|
118
|
+
|
119
|
+
([self] + descendants).each do |klass|
|
120
|
+
klass.fields.delete(attr)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
100
124
|
def has_field?(attr)
|
101
125
|
!!fields[attr.to_sym]
|
102
126
|
end
|
@@ -1,19 +1,11 @@
|
|
1
1
|
module NoBrainer::Document::Callbacks
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
-
def self.terminator
|
5
|
-
if Gem.loaded_specs['activesupport'].version.release >= Gem::Version.new('4.1')
|
6
|
-
proc { false }
|
7
|
-
else
|
8
|
-
'false'
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
4
|
included do
|
13
5
|
extend ActiveModel::Callbacks
|
14
6
|
|
15
|
-
define_model_callbacks :initialize, :create, :update, :save, :destroy, :terminator =>
|
16
|
-
define_model_callbacks :find, :only => [:after], :terminator =>
|
7
|
+
define_model_callbacks :initialize, :create, :update, :save, :destroy, :terminator => proc { false }
|
8
|
+
define_model_callbacks :find, :only => [:after], :terminator => proc { false }
|
17
9
|
end
|
18
10
|
|
19
11
|
def initialize(*args, &block)
|
@@ -5,16 +5,18 @@ module NoBrainer::Document::Core
|
|
5
5
|
attr_accessor :_all
|
6
6
|
|
7
7
|
def all
|
8
|
-
Rails.application.eager_load! if defined?(Rails)
|
8
|
+
Rails.application.eager_load! if defined?(Rails.application.eager_load!)
|
9
9
|
@_all
|
10
10
|
end
|
11
11
|
end
|
12
12
|
self._all = []
|
13
13
|
|
14
|
-
# TODO This assume the primary key is id.
|
15
|
-
# RethinkDB can have a custom primary key. careful.
|
16
14
|
include ActiveModel::Conversion
|
17
15
|
|
16
|
+
def to_key
|
17
|
+
[pk_value]
|
18
|
+
end
|
19
|
+
|
18
20
|
included do
|
19
21
|
# TODO test these includes
|
20
22
|
extend ActiveModel::Naming
|
@@ -2,7 +2,7 @@ module NoBrainer::Document::Criteria
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
def selector
|
5
|
-
self.class.selector_for(
|
5
|
+
self.class.selector_for(pk_value)
|
6
6
|
end
|
7
7
|
|
8
8
|
included { cattr_accessor :default_scope_proc, :instance_accessor => false }
|
@@ -40,20 +40,20 @@ module NoBrainer::Document::Criteria
|
|
40
40
|
self.default_scope_proc = criteria.is_a?(Proc) ? criteria : proc { criteria }
|
41
41
|
end
|
42
42
|
|
43
|
-
def selector_for(
|
44
|
-
|
45
|
-
unscoped.where(:id => id)
|
43
|
+
def selector_for(pk)
|
44
|
+
rql_table.get(pk)
|
46
45
|
end
|
47
46
|
|
48
47
|
# XXX this doesn't have the same semantics as
|
49
48
|
# other ORMs. the equivalent is find!.
|
50
|
-
def find(
|
51
|
-
selector_for(
|
49
|
+
def find(pk)
|
50
|
+
attrs = NoBrainer.run { selector_for(pk) }
|
51
|
+
new_from_db(attrs).tap { |doc| doc.run_callbacks(:find) } if attrs
|
52
52
|
end
|
53
53
|
|
54
|
-
def find!(
|
55
|
-
find(
|
56
|
-
raise NoBrainer::Error::DocumentNotFound, "#{self
|
54
|
+
def find!(pk)
|
55
|
+
find(pk).tap do |doc|
|
56
|
+
raise NoBrainer::Error::DocumentNotFound, "#{self} #{pk_name}: #{pk} not found" unless doc
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
@@ -79,5 +79,16 @@ module NoBrainer::Document::Dirty
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
82
|
+
|
83
|
+
def _remove_field(attr, options={})
|
84
|
+
super
|
85
|
+
inject_in_layer :dirty_tracking do
|
86
|
+
remove_method("#{attr}_change")
|
87
|
+
remove_method("#{attr}_changed?")
|
88
|
+
remove_method("#{attr}_was")
|
89
|
+
remove_method("#{attr}=")
|
90
|
+
remove_method("#{attr}")
|
91
|
+
end
|
92
|
+
end
|
82
93
|
end
|
83
94
|
end
|
@@ -5,17 +5,23 @@ require 'digest/md5'
|
|
5
5
|
module NoBrainer::Document::Id
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
DEFAULT_PK_NAME = :id
|
9
|
+
|
10
|
+
def pk_value
|
11
|
+
__send__(self.class.pk_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def pk_value=(value)
|
15
|
+
__send__("#{self.class.pk_name}=", value)
|
10
16
|
end
|
11
17
|
|
12
18
|
def ==(other)
|
13
19
|
return super unless self.class == other.class
|
14
|
-
!
|
20
|
+
!pk_value.nil? && pk_value == other.pk_value
|
15
21
|
end
|
16
22
|
alias_method :eql?, :==
|
17
23
|
|
18
|
-
delegate :hash, :to => :
|
24
|
+
delegate :hash, :to => :pk_value
|
19
25
|
|
20
26
|
# The following code is inspired by the mongo-ruby-driver
|
21
27
|
|
@@ -46,4 +52,43 @@ module NoBrainer::Document::Id
|
|
46
52
|
|
47
53
|
oid.unpack("C12").map {|e| v=e.to_s(16); v.size == 1 ? "0#{v}" : v }.join
|
48
54
|
end
|
55
|
+
|
56
|
+
module ClassMethods
|
57
|
+
def define_default_pk
|
58
|
+
class_variable_set(:@@pk_name, nil)
|
59
|
+
field NoBrainer::Document::Id::DEFAULT_PK_NAME, :primary_key => :default,
|
60
|
+
:type => String, :default => ->{ NoBrainer::Document::Id.generate }
|
61
|
+
end
|
62
|
+
|
63
|
+
def define_pk(attr)
|
64
|
+
if fields[pk_name].try(:[], :primary_key) == :default
|
65
|
+
remove_field(pk_name, :set_default_pk => false)
|
66
|
+
end
|
67
|
+
class_variable_set(:@@pk_name, attr)
|
68
|
+
end
|
69
|
+
|
70
|
+
def pk_name
|
71
|
+
class_variable_get(:@@pk_name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def _field(attr, options={})
|
75
|
+
super
|
76
|
+
define_pk(attr) if options[:primary_key]
|
77
|
+
end
|
78
|
+
|
79
|
+
def field(attr, options={})
|
80
|
+
if options[:primary_key]
|
81
|
+
options = options.merge(:readonly => true) if options[:readonly].nil?
|
82
|
+
options = options.merge(:index => true)
|
83
|
+
end
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
def _remove_field(attr, options={})
|
88
|
+
super
|
89
|
+
if fields[attr][:primary_key] && options[:set_default_pk] != false
|
90
|
+
define_default_pk
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
49
94
|
end
|
@@ -5,7 +5,6 @@ module NoBrainer::Document::Index
|
|
5
5
|
included do
|
6
6
|
cattr_accessor :indexes, :instance_accessor => false
|
7
7
|
self.indexes = {}
|
8
|
-
self.index :id
|
9
8
|
end
|
10
9
|
|
11
10
|
module ClassMethods
|
@@ -57,6 +56,11 @@ module NoBrainer::Document::Index
|
|
57
56
|
end
|
58
57
|
end
|
59
58
|
|
59
|
+
def _remove_field(attr, options={})
|
60
|
+
super
|
61
|
+
remove_index(attr) if fields[attr][:index]
|
62
|
+
end
|
63
|
+
|
60
64
|
def perform_create_index(index_name, options={})
|
61
65
|
index_name = index_name.to_sym
|
62
66
|
index_args = self.indexes[index_name]
|
@@ -79,7 +83,7 @@ module NoBrainer::Document::Index
|
|
79
83
|
|
80
84
|
def perform_update_indexes(options={})
|
81
85
|
current_indexes = NoBrainer.run(self.rql_table.index_list).map(&:to_sym)
|
82
|
-
wanted_indexes = self.indexes.keys - [
|
86
|
+
wanted_indexes = self.indexes.keys - [self.pk_name]
|
83
87
|
|
84
88
|
(current_indexes - wanted_indexes).each do |index_name|
|
85
89
|
perform_drop_index(index_name, options)
|
@@ -19,7 +19,8 @@ module NoBrainer::Document::Persistance
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def reload(options={})
|
22
|
-
attrs =
|
22
|
+
attrs = NoBrainer.run { self.class.selector_for(pk_value) }
|
23
|
+
raise NoBrainer::Error::DocumentNotFound, "#{self.class} #{self.class.pk_name}: #{pk_value} not found" unless attrs
|
23
24
|
instance_variables.each { |ivar| remove_instance_variable(ivar) } unless options[:keep_ivars]
|
24
25
|
initialize(attrs, :pristine => true, :from_db => true)
|
25
26
|
self
|
@@ -28,13 +29,13 @@ module NoBrainer::Document::Persistance
|
|
28
29
|
def _create(options={})
|
29
30
|
return false if options[:validate] && !valid?
|
30
31
|
keys = self.class.insert_all(@_attributes)
|
31
|
-
self.
|
32
|
+
self.pk_value ||= keys.first
|
32
33
|
@new_record = false
|
33
34
|
true
|
34
35
|
end
|
35
36
|
|
36
37
|
def _update(attrs)
|
37
|
-
selector.
|
38
|
+
NoBrainer.run { selector.update(attrs) }
|
38
39
|
end
|
39
40
|
|
40
41
|
def _update_only_changed_attrs(options={})
|
@@ -73,7 +74,7 @@ module NoBrainer::Document::Persistance
|
|
73
74
|
|
74
75
|
def delete
|
75
76
|
unless @destroyed
|
76
|
-
selector.
|
77
|
+
NoBrainer.run { selector.delete }
|
77
78
|
@destroyed = true
|
78
79
|
end
|
79
80
|
@_attributes.freeze
|
@@ -171,5 +171,13 @@ module NoBrainer::Document::Types
|
|
171
171
|
end
|
172
172
|
super
|
173
173
|
end
|
174
|
+
|
175
|
+
def _remove_field(attr, options={})
|
176
|
+
super
|
177
|
+
inject_in_layer :types do
|
178
|
+
remove_method("#{attr}=")
|
179
|
+
remove_method("#{attr}?") if method_defined?("#{attr}?")
|
180
|
+
end
|
181
|
+
end
|
174
182
|
end
|
175
183
|
end
|
@@ -64,7 +64,9 @@ module NoBrainer::Document::Uniqueness
|
|
64
64
|
class UniquenessValidator < ActiveModel::EachValidator
|
65
65
|
attr_accessor :scope
|
66
66
|
|
67
|
-
def
|
67
|
+
def initialize(options={})
|
68
|
+
super
|
69
|
+
klass = options[:class]
|
68
70
|
self.scope = [*options[:scope]]
|
69
71
|
([klass] + klass.descendants).each do |_klass|
|
70
72
|
_klass.unique_validators << self
|
@@ -91,7 +93,7 @@ module NoBrainer::Document::Uniqueness
|
|
91
93
|
end
|
92
94
|
|
93
95
|
def exclude_doc(criteria, doc)
|
94
|
-
criteria.where(
|
96
|
+
criteria.where(doc.class.pk_name.ne => doc.pk_value)
|
95
97
|
end
|
96
98
|
end
|
97
99
|
end
|
@@ -6,7 +6,7 @@ module NoBrainer::Document::Validation
|
|
6
6
|
included do
|
7
7
|
# We don't want before_validation returning false to halt the chain.
|
8
8
|
define_callbacks :validation, :skip_after_callbacks_if_terminated => true, :scope => [:kind, :name],
|
9
|
-
:terminator =>
|
9
|
+
:terminator => proc { false }
|
10
10
|
end
|
11
11
|
|
12
12
|
def valid?(context=nil)
|
data/lib/no_brainer/document.rb
CHANGED
data/lib/no_brainer/error.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
module NoBrainer::Error
|
2
|
-
class Connection
|
3
|
-
class DocumentNotFound
|
4
|
-
class
|
5
|
-
class ChildrenExist
|
6
|
-
class CannotUseIndex
|
7
|
-
class MissingIndex
|
8
|
-
class InvalidType
|
2
|
+
class Connection < RuntimeError; end
|
3
|
+
class DocumentNotFound < RuntimeError; end
|
4
|
+
class DocumentNotPersisted < RuntimeError; end
|
5
|
+
class ChildrenExist < RuntimeError; end
|
6
|
+
class CannotUseIndex < RuntimeError; end
|
7
|
+
class MissingIndex < RuntimeError; end
|
8
|
+
class InvalidType < RuntimeError; end
|
9
9
|
class AssociationNotPersisted < RuntimeError; end
|
10
|
-
class ReadonlyField
|
10
|
+
class ReadonlyField < RuntimeError; end
|
11
11
|
|
12
12
|
class DocumentInvalid < RuntimeError
|
13
13
|
attr_accessor :instance
|
@@ -13,23 +13,29 @@ class NoBrainer::QueryRunner::Logger < NoBrainer::QueryRunner::Middleware
|
|
13
13
|
return unless NoBrainer.logger.debug?
|
14
14
|
|
15
15
|
duration = Time.now - start_time
|
16
|
-
msg = env[:query].inspect.gsub(/\n/, '').gsub(/ +/, ' ')
|
17
16
|
|
18
|
-
|
19
|
-
|
17
|
+
msg_duration = (duration * 1000.0).round(1).to_s
|
18
|
+
msg_duration = " " * [0, 5 - msg_duration.size].max + msg_duration
|
19
|
+
msg_duration = "[#{msg_duration}ms] "
|
20
|
+
|
21
|
+
msg_db = "[#{env[:db_name]}] " if env[:db_name]
|
22
|
+
msg_query = env[:query].inspect.gsub(/\n/, '').gsub(/ +/, ' ')
|
23
|
+
msg_exception = " #{exception.class} #{exception.message.split("\n").first}" if exception
|
24
|
+
msg_last = nil
|
20
25
|
|
21
26
|
if NoBrainer::Config.colorize_logger
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
query_color = case NoBrainer::RQL.type_of(env[:query])
|
28
|
+
when :write then "\e[1;31m" # red
|
29
|
+
when :read then "\e[1;32m" # green
|
30
|
+
when :management then "\e[1;33m" # yellow
|
31
|
+
end
|
32
|
+
msg_duration = [query_color, msg_duration].join
|
33
|
+
msg_db = ["\e[0;34m", msg_db, query_color].join if msg_db
|
34
|
+
msg_exception = ["\e[0;31m", msg_exception].join if msg_exception
|
35
|
+
msg_last = "\e[0m"
|
31
36
|
end
|
32
37
|
|
38
|
+
msg = [msg_duration, msg_db, msg_query, msg_exception, msg_last].join
|
33
39
|
NoBrainer.logger.debug(msg)
|
34
40
|
end
|
35
41
|
end
|
@@ -2,9 +2,21 @@ class NoBrainer::QueryRunner::MissingIndex < NoBrainer::QueryRunner::Middleware
|
|
2
2
|
def call(env)
|
3
3
|
@runner.call(env)
|
4
4
|
rescue RethinkDB::RqlRuntimeError => e
|
5
|
-
if e.message =~ /^Index `(.+)` was not found
|
6
|
-
|
7
|
-
|
5
|
+
if e.message =~ /^Index `(.+)` was not found on table `(.+)\.(.+)`\.$/
|
6
|
+
index_name = $1
|
7
|
+
database_name = $2
|
8
|
+
table_name = $3
|
9
|
+
|
10
|
+
klass = NoBrainer::Document.all.select { |m| m.table_name == table_name }.first
|
11
|
+
if klass && klass.pk_name.to_s == index_name
|
12
|
+
err_msg = "Please run update the primary key `#{index_name}` in the table `#{database_name}.#{table_name}`."
|
13
|
+
else
|
14
|
+
err_msg = "Please run \"rake db:update_indexes\" to create the index `#{index_name}`"
|
15
|
+
err_msg += " in the table `#{database_name}.#{table_name}`."
|
16
|
+
err_msg += "\n--> Read http://nobrainer.io/docs/indexes for more information."
|
17
|
+
end
|
18
|
+
|
19
|
+
raise NoBrainer::Error::MissingIndex.new(err_msg)
|
8
20
|
end
|
9
21
|
raise
|
10
22
|
end
|
@@ -4,20 +4,20 @@ class NoBrainer::QueryRunner::Reconnect < NoBrainer::QueryRunner::Middleware
|
|
4
4
|
rescue StandardError => e
|
5
5
|
# TODO test that thing
|
6
6
|
if is_connection_error_exception?(e)
|
7
|
-
retry if reconnect
|
7
|
+
retry if reconnect(e)
|
8
8
|
end
|
9
9
|
raise
|
10
10
|
end
|
11
11
|
|
12
12
|
private
|
13
13
|
|
14
|
-
def reconnect
|
14
|
+
def reconnect(e)
|
15
15
|
# FIXME thread safety? perhaps we need to use a connection pool
|
16
16
|
# XXX Possibly dangerous, as we could reexecute a non idempotent operation
|
17
17
|
# Check the semantics of the db
|
18
18
|
NoBrainer::Config.max_reconnection_tries.times do
|
19
19
|
begin
|
20
|
-
|
20
|
+
warn_reconnect(e)
|
21
21
|
sleep 1
|
22
22
|
NoBrainer.connection.reconnect(:noreply_wait => false)
|
23
23
|
return true
|
@@ -35,10 +35,21 @@ class NoBrainer::QueryRunner::Reconnect < NoBrainer::QueryRunner::Middleware
|
|
35
35
|
Errno::ECONNRESET, Errno::ETIMEDOUT, IOError
|
36
36
|
true
|
37
37
|
when RethinkDB::RqlRuntimeError
|
38
|
-
e.message =~ /
|
38
|
+
e.message =~ /No master available/ ||
|
39
|
+
e.message =~ /Master .* not available/ ||
|
39
40
|
e.message =~ /Error: Connection Closed/
|
40
41
|
else
|
41
42
|
false
|
42
43
|
end
|
43
44
|
end
|
45
|
+
|
46
|
+
def warn_reconnect(e)
|
47
|
+
if e.is_a?(RethinkDB::RqlRuntimeError)
|
48
|
+
e_msg = e.message.split("\n").first
|
49
|
+
msg = "Server #{NoBrainer::Config.rethinkdb_url} not ready - #{e_msg}, retrying..."
|
50
|
+
else
|
51
|
+
msg = "Connection issue with #{NoBrainer::Config.rethinkdb_url} - #{e}, retrying..."
|
52
|
+
end
|
53
|
+
NoBrainer.logger.try(:warn, msg)
|
54
|
+
end
|
44
55
|
end
|
@@ -3,8 +3,8 @@ class NoBrainer::QueryRunner::TableOnDemand < NoBrainer::QueryRunner::Middleware
|
|
3
3
|
@runner.call(env)
|
4
4
|
rescue RuntimeError => e
|
5
5
|
if NoBrainer::Config.auto_create_tables &&
|
6
|
-
e.message =~ /^Table `(.+)` does not exist\.$/
|
7
|
-
auto_create_table(env, $1)
|
6
|
+
e.message =~ /^Table `(.+)\.(.+)` does not exist\.$/
|
7
|
+
auto_create_table(env, $1, $2)
|
8
8
|
retry
|
9
9
|
end
|
10
10
|
raise
|
@@ -12,33 +12,22 @@ class NoBrainer::QueryRunner::TableOnDemand < NoBrainer::QueryRunner::Middleware
|
|
12
12
|
|
13
13
|
private
|
14
14
|
|
15
|
-
def auto_create_table(env, table_name)
|
16
|
-
|
17
|
-
|
15
|
+
def auto_create_table(env, database_name, table_name)
|
16
|
+
klass = NoBrainer::Document.all.select { |m| m.table_name == table_name }.first
|
17
|
+
if klass.nil?
|
18
|
+
raise "Auto table creation is not working for `#{database_name}.#{table_name}` -- Can't find the corresponding model."
|
18
19
|
end
|
19
|
-
env[:auto_create_table] = table_name
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
if env[:auto_create_table] == [database_name, table_name]
|
22
|
+
raise "Auto table creation is not working for `#{database_name}.#{table_name}`"
|
23
|
+
end
|
24
|
+
env[:auto_create_table] = [database_name, table_name]
|
25
|
+
|
26
|
+
NoBrainer.with_database(database_name) do
|
27
|
+
NoBrainer.table_create(table_name, :primary_key => klass.pk_name)
|
27
28
|
end
|
28
29
|
rescue RuntimeError => e
|
29
30
|
# We might have raced with another table create
|
30
|
-
raise unless e.message =~ /Table `#{table_name}` already exists/
|
31
|
-
end
|
32
|
-
|
33
|
-
def find_db_names(terms)
|
34
|
-
terms = terms.body.args if terms.is_a?(RethinkDB::RQL)
|
35
|
-
terms.map do |term|
|
36
|
-
next unless term.is_a?(Term)
|
37
|
-
if term.type == Term::TermType::DB
|
38
|
-
term.args.first.datum.r_str
|
39
|
-
else
|
40
|
-
find_db_names(term.args)
|
41
|
-
end
|
42
|
-
end.flatten.uniq
|
31
|
+
raise unless e.message =~ /Table `#{database_name}\.#{table_name}` already exists/
|
43
32
|
end
|
44
33
|
end
|
@@ -1,13 +1,10 @@
|
|
1
1
|
class NoBrainer::QueryRunner::WriteError < NoBrainer::QueryRunner::Middleware
|
2
2
|
def call(env)
|
3
|
-
write_query = NoBrainer::
|
3
|
+
write_query = NoBrainer::RQL.is_write_query?(env[:query])
|
4
4
|
@runner.call(env).tap do |result|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
if write_query && (result['errors'].to_i != 0 || result['skipped'].to_i != 0)
|
10
|
-
raise_write_error(env, result['first_error'])
|
5
|
+
if write_query && (result['errors'].to_i != 0)
|
6
|
+
error_msg = result['first_error']
|
7
|
+
raise_write_error(env, error_msg)
|
11
8
|
end
|
12
9
|
end
|
13
10
|
rescue RethinkDB::RqlRuntimeError => e
|
@@ -23,6 +20,6 @@ class NoBrainer::QueryRunner::WriteError < NoBrainer::QueryRunner::Middleware
|
|
23
20
|
def raise_write_error(env, error_msg)
|
24
21
|
error_msg ||= "Unknown error"
|
25
22
|
error_msg += "\nQuery was: #{env[:query].inspect[0..1000]}"
|
26
|
-
raise NoBrainer::Error::
|
23
|
+
raise NoBrainer::Error::DocumentNotPersisted, error_msg
|
27
24
|
end
|
28
25
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module NoBrainer::RQL
|
2
|
+
include RethinkDB::Term::TermType
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def is_write_query?(rql_query)
|
6
|
+
type_of(rql_query) == :write
|
7
|
+
end
|
8
|
+
|
9
|
+
def type_of(rql_query)
|
10
|
+
case rql_query.body.first
|
11
|
+
when UPDATE, DELETE, REPLACE, INSERT
|
12
|
+
:write
|
13
|
+
when DB_CREATE,DB_DROP, DB_LIST, TABLE_CREATE, TABLE_DROP, TABLE_LIST, SYNC,
|
14
|
+
INDEX_CREATE, INDEX_DROP, INDEX_LIST, INDEX_STATUS, INDEX_WAIT
|
15
|
+
:management
|
16
|
+
else
|
17
|
+
# XXX Not sure if that's correct, but we'll be happy for logging colors.
|
18
|
+
:read
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def is_table?(rql)
|
23
|
+
rql.body.first == TABLE
|
24
|
+
end
|
25
|
+
end
|
data/lib/nobrainer.rb
CHANGED
@@ -7,19 +7,18 @@ module NoBrainer
|
|
7
7
|
require 'no_brainer/autoload'
|
8
8
|
extend NoBrainer::Autoload
|
9
9
|
|
10
|
-
# We eager load things that could be loaded
|
10
|
+
# We eager load things that could be loaded when handling the first web request.
|
11
|
+
# Code that is loaded through the DSL of NoBrainer should not be eager loaded.
|
11
12
|
autoload :Document, :IndexManager, :Loader, :Fork, :DecoratedSymbol
|
12
|
-
eager_autoload :Config, :Connection, :Error, :QueryRunner, :Criteria, :
|
13
|
+
eager_autoload :Config, :Connection, :Error, :QueryRunner, :Criteria, :RQL
|
13
14
|
|
14
15
|
class << self
|
15
|
-
#
|
16
|
-
# we can refactor to return a connection depending on the context.
|
17
|
-
# Note that a connection is tied to a database in NoBrainer.
|
16
|
+
# A connection is tied to a database.
|
18
17
|
def connection
|
19
18
|
@connection ||= begin
|
20
19
|
url = NoBrainer::Config.rethinkdb_url
|
21
20
|
raise "Please specify a database connection to RethinkDB" unless url
|
22
|
-
Connection.new(url)
|
21
|
+
Connection.new(url)
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
metadata
CHANGED
@@ -1,57 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nobrainer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.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: 2014-
|
11
|
+
date: 2014-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rethinkdb
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
19
|
+
version: 1.13.0.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.13.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 4.
|
33
|
+
version: 4.1.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 4.
|
40
|
+
version: 4.1.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: activemodel
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 4.
|
47
|
+
version: 4.1.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 4.
|
54
|
+
version: 4.1.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: middleware
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,7 +137,7 @@ files:
|
|
137
137
|
- lib/no_brainer/query_runner/write_error.rb
|
138
138
|
- lib/no_brainer/railtie.rb
|
139
139
|
- lib/no_brainer/railtie/database.rake
|
140
|
-
- lib/no_brainer/
|
140
|
+
- lib/no_brainer/rql.rb
|
141
141
|
- lib/nobrainer.rb
|
142
142
|
- lib/rails/generators/nobrainer.rb
|
143
143
|
- lib/rails/generators/nobrainer/model/model_generator.rb
|
data/lib/no_brainer/util.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
module NoBrainer::Util
|
2
|
-
def self.is_write_query?(rql_query)
|
3
|
-
rql_type(rql_query) == :write
|
4
|
-
end
|
5
|
-
|
6
|
-
def self.rql_type(rql_query)
|
7
|
-
case rql_query.body.type
|
8
|
-
when Term::TermType::UPDATE, Term::TermType::DELETE,
|
9
|
-
Term::TermType::REPLACE, Term::TermType::INSERT
|
10
|
-
:write
|
11
|
-
when Term::TermType::DB_CREATE, Term::TermType::DB_DROP,
|
12
|
-
Term::TermType::DB_LIST, Term::TermType::TABLE_CREATE,
|
13
|
-
Term::TermType::TABLE_DROP, Term::TermType::TABLE_LIST,
|
14
|
-
Term::TermType::SYNC, Term::TermType::INDEX_CREATE,
|
15
|
-
Term::TermType::INDEX_DROP, Term::TermType::INDEX_LIST,
|
16
|
-
Term::TermType::INDEX_STATUS, Term::TermType::INDEX_WAIT
|
17
|
-
:management
|
18
|
-
else
|
19
|
-
# XXX Not sure if that's correct, but we'll be happy for logging colors.
|
20
|
-
:read
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|