nobrainer 0.13.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/no_brainer/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
|