nobrainer 0.9.1 → 0.10.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/autoload.rb +0 -2
- data/lib/no_brainer/criteria/termination/cache.rb +1 -1
- data/lib/no_brainer/document.rb +2 -2
- data/lib/no_brainer/document/association.rb +2 -1
- data/lib/no_brainer/document/association/eager_loader.rb +3 -1
- data/lib/no_brainer/document/attributes.rb +14 -22
- data/lib/no_brainer/document/dirty.rb +2 -2
- data/lib/no_brainer/document/index.rb +0 -1
- data/lib/no_brainer/document/injection_layer.rb +1 -1
- data/lib/no_brainer/document/timestamps.rb +2 -2
- data/lib/no_brainer/document/types.rb +129 -0
- data/lib/no_brainer/document/validation.rb +0 -1
- data/lib/no_brainer/locale/en.yml +1 -0
- data/lib/nobrainer.rb +8 -10
- metadata +16 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0fa1301301888d07f8c607cfd1102555aad40a92
|
4
|
+
data.tar.gz: f05a189ae40c4e6b1f2d420dd84a4928a6f65203
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bfa4718cb043f4847f972d46b2728c772475b8d07683a591d38d6ec039d06c09508d2df855791e935bbaa85f5d11780a367a383e9ebae0c19dce278dab7c83d
|
7
|
+
data.tar.gz: 4584d38e6646c426672cea00c0f9aa58f05a6df4778586ae2d699662d927984b1f32ba3b87ebc3b787d4ad9181b19e9e4ecf3f4cfafdbc116f85920566534ed0
|
data/lib/no_brainer/autoload.rb
CHANGED
data/lib/no_brainer/document.rb
CHANGED
@@ -4,8 +4,8 @@ module NoBrainer::Document
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
extend NoBrainer::Autoload
|
6
6
|
|
7
|
-
autoload_and_include :Core, :StoreIn, :InjectionLayer, :Attributes, :
|
8
|
-
:Id, :Association, :Serialization, :Criteria,
|
7
|
+
autoload_and_include :Core, :StoreIn, :InjectionLayer, :Attributes, :Validation, :Types,
|
8
|
+
:Persistance, :Dirty, :Id, :Association, :Serialization, :Criteria,
|
9
9
|
:Polymorphic, :Index, :Timestamps
|
10
10
|
|
11
11
|
autoload :DynamicAttributes
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module NoBrainer::Document::Association
|
2
2
|
extend NoBrainer::Autoload
|
3
3
|
autoload :Core, :BelongsTo, :HasMany, :HasManyThrough, :HasOne, :HasOneThrough, :EagerLoader
|
4
|
+
METHODS = [:belongs_to, :has_many, :has_one]
|
4
5
|
|
5
6
|
extend ActiveSupport::Concern
|
6
7
|
|
@@ -19,7 +20,7 @@ module NoBrainer::Document::Association
|
|
19
20
|
subclass.association_metadata = self.association_metadata.dup
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
|
+
METHODS.each do |association|
|
23
24
|
define_method(association) do |target, options={}|
|
24
25
|
target = target.to_sym
|
25
26
|
|
@@ -33,7 +33,9 @@ class NoBrainer::Document::Association::EagerLoader
|
|
33
33
|
def eager_load_association(docs, association_name, criteria=nil)
|
34
34
|
docs = docs.compact
|
35
35
|
return [] if docs.empty?
|
36
|
-
|
36
|
+
meta = docs.first.root_class.association_metadata
|
37
|
+
# TODO test the singularize thingy.
|
38
|
+
association = meta[association_name.to_sym] || meta[association_name.to_s.singularize.to_sym]
|
37
39
|
raise "Unknown association #{association_name}" unless association
|
38
40
|
association.eager_load(docs, criteria)
|
39
41
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module NoBrainer::Document::Attributes
|
2
|
-
VALID_FIELD_OPTIONS = [:index, :default]
|
2
|
+
VALID_FIELD_OPTIONS = [:index, :default, :type]
|
3
3
|
RESERVED_FIELD_NAMES = [:index, :default, :and, :or, :selector, :associations] + NoBrainer::DecoratedSymbol::MODIFIERS.keys
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
@@ -40,39 +40,31 @@ module NoBrainer::Document::Attributes
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
def _assign_attributes(attrs, options={})
|
44
|
+
attrs.each { |k,v| self.write_attribute(k,v) }
|
45
|
+
end
|
46
|
+
|
43
47
|
def assign_attributes(attrs, options={})
|
44
|
-
# XXX We don't save field that are not explicitly set. The row will
|
45
|
-
# therefore not contain nil for unset attributes.
|
46
48
|
@attributes.clear if options[:pristine]
|
47
|
-
|
48
|
-
if options[:
|
49
|
-
# TODO Should we reject undeclared fields ?
|
50
|
-
#
|
51
|
-
# TODO Not using the getter/setters, the dirty tracking won't notice it,
|
52
|
-
# also we should start thinking about custom serializer/deserializer.
|
53
|
-
@attributes.merge! attrs
|
54
|
-
else
|
55
|
-
attrs.each { |k,v| self.write_attribute(k, v) }
|
56
|
-
end
|
57
|
-
|
58
|
-
assign_defaults if options[:pristine] || options[:from_db]
|
49
|
+
_assign_attributes(attrs, options)
|
50
|
+
assign_defaults if options[:pristine]
|
59
51
|
self
|
60
52
|
end
|
61
53
|
def attributes=(*args); assign_attributes(*args); end
|
62
54
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
"#<#{self.class} #{attrs.join(', ')}>"
|
55
|
+
def inspectable_attributes
|
56
|
+
# TODO test that thing
|
57
|
+
Hash[@attributes.sort_by { |k,v| self.class.fields.keys.index(k.to_sym) || 2**10 }]
|
67
58
|
end
|
68
59
|
|
69
|
-
def
|
70
|
-
inspect
|
60
|
+
def inspect
|
61
|
+
"#<#{self.class} #{inspectable_attributes.map { |k,v| "#{k}: #{v.inspect}" }.join(', ')}>"
|
71
62
|
end
|
72
63
|
|
73
64
|
module ClassMethods
|
74
65
|
def new_from_db(attrs, options={})
|
75
|
-
|
66
|
+
options = options.reverse_merge(:pristine => true, :from_db => true)
|
67
|
+
klass_from_attrs(attrs).new(attrs, options) if attrs
|
76
68
|
end
|
77
69
|
|
78
70
|
def inherited(subclass)
|
@@ -15,9 +15,9 @@ module NoBrainer::Document::Dirty
|
|
15
15
|
@changed_attributes ||= {}
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
clear_dirtiness if options[:pristine] || options[:from_db]
|
18
|
+
def _assign_attributes(attrs, options={})
|
20
19
|
super
|
20
|
+
clear_dirtiness if options[:pristine]
|
21
21
|
end
|
22
22
|
|
23
23
|
def clear_dirtiness
|
@@ -23,7 +23,6 @@ module NoBrainer::Document::Index
|
|
23
23
|
else raise "Index argument must be a lambda or a list of fields"
|
24
24
|
end
|
25
25
|
|
26
|
-
# FIXME Primary key may not always be :id
|
27
26
|
if name.in?(NoBrainer::Document::Attributes::RESERVED_FIELD_NAMES)
|
28
27
|
raise "Cannot use a reserved field name: #{name}"
|
29
28
|
end
|
@@ -3,7 +3,7 @@ module NoBrainer::Document::InjectionLayer
|
|
3
3
|
|
4
4
|
module ClassMethods
|
5
5
|
def inject_in_layer(name, code=nil, file=nil, line=nil, &block)
|
6
|
-
mod = class_eval "module
|
6
|
+
mod = class_eval "module NoBrainerLayer; module #{name.to_s.camelize}; self; end; end"
|
7
7
|
mod.module_eval(code, file, line) if code
|
8
8
|
mod.module_exec(&block) if block
|
9
9
|
include mod
|
@@ -2,8 +2,8 @@ module NoBrainer::Document::Timestamps
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
included do
|
5
|
-
self.field :created_at
|
6
|
-
self.field :updated_at
|
5
|
+
self.field :created_at, :type => Time
|
6
|
+
self.field :updated_at, :type => Time
|
7
7
|
|
8
8
|
before_create { self.created_at = Time.now if self.respond_to?(:created_at=) }
|
9
9
|
before_save { self.updated_at = Time.now if self.respond_to?(:updated_at=) }
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module NoBrainer::Document::Types
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
# We namespace our fake Boolean class to avoid polluting the global namespace
|
6
|
+
class_exec { class Boolean; def initialize; raise; end; end }
|
7
|
+
before_validation :add_type_errors
|
8
|
+
end
|
9
|
+
|
10
|
+
module CastingRules
|
11
|
+
extend self
|
12
|
+
|
13
|
+
def String(value)
|
14
|
+
case value
|
15
|
+
when Symbol then value.to_s
|
16
|
+
else raise InvalidType
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def Integer(value)
|
21
|
+
case value
|
22
|
+
when String
|
23
|
+
value = value.strip.gsub(/^\+/, '')
|
24
|
+
value.to_i.tap { |new_value| new_value.to_s == value or raise InvalidType }
|
25
|
+
when Float
|
26
|
+
value.to_i.tap { |new_value| new_value.to_f == value or raise InvalidType }
|
27
|
+
else raise InvalidType
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def Float(value)
|
32
|
+
case value
|
33
|
+
when Integer then value.to_f
|
34
|
+
when String
|
35
|
+
value = value.strip.gsub(/^\+/, '')
|
36
|
+
value = value.gsub(/0+$/, '') if value['.']
|
37
|
+
value = value.gsub(/\.$/, '')
|
38
|
+
value = "#{value}.0" unless value['.']
|
39
|
+
value.to_f.tap { |new_value| new_value.to_s == value or raise InvalidType }
|
40
|
+
else raise InvalidType
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def Boolean(value)
|
45
|
+
case value
|
46
|
+
when TrueClass then true
|
47
|
+
when FalseClass then false
|
48
|
+
when String, Integer
|
49
|
+
value = value.to_s.strip.downcase
|
50
|
+
return true if value.in? %w(true yes t 1)
|
51
|
+
return false if value.in? %w(false no f 0)
|
52
|
+
raise InvalidType
|
53
|
+
else raise InvalidType
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def Symbol(value)
|
58
|
+
case value
|
59
|
+
when String
|
60
|
+
value = value.strip
|
61
|
+
raise InvalidType if value.empty?
|
62
|
+
value.to_sym
|
63
|
+
else raise InvalidType
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.cast(value, type, cast_method)
|
69
|
+
return value if value.nil? || type.nil? || value.is_a?(type)
|
70
|
+
cast_method.call(value)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.lookup_cast_method(type)
|
74
|
+
type = type.to_s
|
75
|
+
type = 'Boolean' if type == 'NoBrainer::Document::Types::Boolean'
|
76
|
+
CastingRules.method(type)
|
77
|
+
rescue NameError
|
78
|
+
proc { raise InvalidType }
|
79
|
+
end
|
80
|
+
|
81
|
+
class InvalidType < RuntimeError
|
82
|
+
attr_accessor :type
|
83
|
+
def initialize(type=nil)
|
84
|
+
@type = type
|
85
|
+
end
|
86
|
+
|
87
|
+
def validation_error_args
|
88
|
+
[:invalid_type, :type => type.to_s.underscore.humanize.downcase]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_type_errors
|
93
|
+
return unless @pending_type_errors
|
94
|
+
@pending_type_errors.each do |name, error|
|
95
|
+
errors.add(name, *error.validation_error_args)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
module ClassMethods
|
100
|
+
def field(name, options={})
|
101
|
+
super
|
102
|
+
return unless options.has_key?(:type)
|
103
|
+
name = name.to_sym
|
104
|
+
type = options[:type]
|
105
|
+
cast_method = NoBrainer::Document::Types.lookup_cast_method(type)
|
106
|
+
|
107
|
+
inject_in_layer :types do
|
108
|
+
define_method("#{name}=") do |value|
|
109
|
+
begin
|
110
|
+
value = NoBrainer::Document::Types.cast(value, type, cast_method)
|
111
|
+
@pending_type_errors.try(:delete, name)
|
112
|
+
rescue NoBrainer::Document::Types::InvalidType => error
|
113
|
+
error.type ||= type
|
114
|
+
@pending_type_errors ||= {}
|
115
|
+
@pending_type_errors[name] = error
|
116
|
+
end
|
117
|
+
super(value)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def remove_field(name)
|
123
|
+
super
|
124
|
+
inject_in_layer :types, <<-RUBY, __FILE__, __LINE__ + 1
|
125
|
+
undef #{name}=
|
126
|
+
RUBY
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/nobrainer.rb
CHANGED
@@ -2,12 +2,10 @@ if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('1.9')
|
|
2
2
|
raise 'Please use Ruby 1.9 or later'
|
3
3
|
end
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
object/inclusion object/duplicable hash/keys hash/reverse_merge array/extract_options)
|
10
|
-
.each { |dep| require "active_support/core_ext/#{dep}" }
|
5
|
+
require 'active_support'
|
6
|
+
%w(module/delegation module/attribute_accessors class/attribute object/blank object/inclusion
|
7
|
+
object/duplicable object/try hash/keys hash/reverse_merge array/extract_options)
|
8
|
+
.each { |dep| require "active_support/core_ext/#{dep}" }
|
11
9
|
|
12
10
|
module NoBrainer
|
13
11
|
require 'no_brainer/autoload'
|
@@ -40,14 +38,14 @@ module NoBrainer
|
|
40
38
|
end
|
41
39
|
end
|
42
40
|
|
43
|
-
# Not using modules to extend, it's nicer to see the NoBrainer module API here.
|
44
41
|
delegate :db_create, :db_drop, :db_list,
|
45
42
|
:table_create, :table_drop, :table_list,
|
46
43
|
:drop!, :purge!, :to => :connection
|
47
|
-
|
48
|
-
delegate :
|
44
|
+
|
45
|
+
delegate :configure, :logger, :to => 'NoBrainer::Config'
|
46
|
+
delegate :run, :to => 'NoBrainer::QueryRunner'
|
47
|
+
delegate :update_indexes, :to => 'NoBrainer::IndexManager'
|
49
48
|
delegate :with, :with_database, :to => 'NoBrainer::QueryRunner::RunOptions'
|
50
|
-
delegate :configure, :logger, :to => 'NoBrainer::Config'
|
51
49
|
|
52
50
|
def jruby?
|
53
51
|
RUBY_PLATFORM == 'java'
|
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.
|
4
|
+
version: 0.10.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-01-
|
11
|
+
date: 2014-01-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rethinkdb
|
@@ -66,27 +66,28 @@ extensions: []
|
|
66
66
|
extra_rdoc_files: []
|
67
67
|
files:
|
68
68
|
- lib/no_brainer/document/id.rb
|
69
|
-
- lib/no_brainer/document/injection_layer.rb
|
70
|
-
- lib/no_brainer/document/timestamps.rb
|
71
69
|
- lib/no_brainer/document/association/belongs_to.rb
|
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
70
|
- lib/no_brainer/document/association/has_many.rb
|
76
71
|
- lib/no_brainer/document/association/has_one.rb
|
77
72
|
- lib/no_brainer/document/association/has_one_through.rb
|
78
|
-
- lib/no_brainer/document/
|
73
|
+
- lib/no_brainer/document/association/has_many_through.rb
|
74
|
+
- lib/no_brainer/document/association/eager_loader.rb
|
75
|
+
- lib/no_brainer/document/association/core.rb
|
79
76
|
- lib/no_brainer/document/dynamic_attributes.rb
|
80
|
-
- lib/no_brainer/document/persistance.rb
|
81
|
-
- lib/no_brainer/document/validation.rb
|
82
|
-
- lib/no_brainer/document/serialization.rb
|
83
77
|
- lib/no_brainer/document/criteria.rb
|
84
|
-
- lib/no_brainer/document/index.rb
|
85
78
|
- lib/no_brainer/document/polymorphic.rb
|
86
79
|
- lib/no_brainer/document/store_in.rb
|
87
|
-
- lib/no_brainer/document/attributes.rb
|
88
80
|
- lib/no_brainer/document/core.rb
|
81
|
+
- lib/no_brainer/document/injection_layer.rb
|
82
|
+
- lib/no_brainer/document/index.rb
|
83
|
+
- lib/no_brainer/document/serialization.rb
|
89
84
|
- lib/no_brainer/document/association.rb
|
85
|
+
- lib/no_brainer/document/validation.rb
|
86
|
+
- lib/no_brainer/document/timestamps.rb
|
87
|
+
- lib/no_brainer/document/dirty.rb
|
88
|
+
- lib/no_brainer/document/persistance.rb
|
89
|
+
- lib/no_brainer/document/attributes.rb
|
90
|
+
- lib/no_brainer/document/types.rb
|
90
91
|
- lib/no_brainer/query_runner/connection.rb
|
91
92
|
- lib/no_brainer/query_runner/database_on_demand.rb
|
92
93
|
- lib/no_brainer/query_runner/logger.rb
|
@@ -120,10 +121,10 @@ files:
|
|
120
121
|
- lib/no_brainer/connection.rb
|
121
122
|
- lib/no_brainer/query_runner.rb
|
122
123
|
- lib/no_brainer/error.rb
|
123
|
-
- lib/no_brainer/document.rb
|
124
124
|
- lib/no_brainer/railtie.rb
|
125
|
-
- lib/no_brainer/autoload.rb
|
126
125
|
- lib/no_brainer/config.rb
|
126
|
+
- lib/no_brainer/autoload.rb
|
127
|
+
- lib/no_brainer/document.rb
|
127
128
|
- lib/rails/generators/nobrainer.rb
|
128
129
|
- lib/rails/generators/nobrainer/model/model_generator.rb
|
129
130
|
- lib/rails/generators/nobrainer/model/templates/model.rb.tt
|