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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a973d430bb1e593bac8d5d072cea1a815335b9c0
4
- data.tar.gz: 002f64fd3e97cb4b987c906a53bb0048b60fe120
3
+ metadata.gz: 0fa1301301888d07f8c607cfd1102555aad40a92
4
+ data.tar.gz: f05a189ae40c4e6b1f2d420dd84a4928a6f65203
5
5
  SHA512:
6
- metadata.gz: e5098627bcc261dd22e3c12f2984b98f59a38948ce671881b7656e1ed0dd268b321bc18d89304f1395e38e00976fbd9407b4cb639a79a56ad470011130ce4079
7
- data.tar.gz: 78ba3dcc4ae4efd55c0291dee1b88ae8216bcb7e9b0694fb875296392c5b82abebb1da411a6dea0677865930c7a5abd6f1a4b3351c59d0851fc3f1fad370292a
6
+ metadata.gz: 1bfa4718cb043f4847f972d46b2728c772475b8d07683a591d38d6ec039d06c09508d2df855791e935bbaa85f5d11780a367a383e9ebae0c19dce278dab7c83d
7
+ data.tar.gz: 4584d38e6646c426672cea00c0f9aa58f05a6df4778586ae2d699662d927984b1f32ba3b87ebc3b787d4ad9181b19e9e4ecf3f4cfafdbc116f85920566534ed0
@@ -1,5 +1,3 @@
1
- require 'active_support/dependencies/autoload'
2
-
3
1
  module NoBrainer::Autoload
4
2
  include ActiveSupport::Autoload
5
3
 
@@ -45,7 +45,7 @@ module NoBrainer::Criteria::Termination::Cache
45
45
  block.call(instance)
46
46
  cache << instance
47
47
  end
48
- @cache = cache.freeze
48
+ @cache = cache
49
49
  self
50
50
  end
51
51
 
@@ -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, :Persistance, :Dirty,
8
- :Id, :Association, :Serialization, :Criteria, :Validation,
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
- [:belongs_to, :has_many, :has_one].each do |association|
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
- association = docs.first.root_class.association_metadata[association_name.to_sym]
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[:from_db]
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
- # TODO test that thing
64
- def inspect
65
- attrs = self.class.fields.keys.map { |f| "#{f}: #{@attributes[f.to_s].inspect}" }
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 to_s
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
- klass_from_attrs(attrs).new(attrs, options.reverse_merge(:from_db => true)) if attrs
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 assign_attributes(attrs, options={})
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 NoBrainer; module #{name.to_s.camelize}; self; end; end"
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
@@ -3,7 +3,6 @@ module NoBrainer::Document::Validation
3
3
  include ActiveModel::Validations
4
4
  include ActiveModel::Validations::Callbacks
5
5
 
6
- # TODO Test that thing
7
6
  def valid?(context=nil)
8
7
  super(context || (new_record? ? :create : :update))
9
8
  end
@@ -2,3 +2,4 @@ en:
2
2
  errors:
3
3
  messages:
4
4
  taken: "is already taken"
5
+ invalid_type: "should be a %{type}"
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
- # Load only what we need from ActiveSupport
6
- require 'active_support/concern'
7
- require 'active_support/lazy_load_hooks'
8
- %w(module/delegation module/attribute_accessors class/attribute object/blank
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
- delegate :run, :to => 'NoBrainer::QueryRunner'
48
- delegate :update_indexes, :to => 'NoBrainer::IndexManager'
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.9.1
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-06 00:00:00.000000000 Z
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/dirty.rb
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