nobrainer 0.9.1 → 0.10.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/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
|