vorpal 0.0.6.rc4 → 0.0.6

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.
@@ -2,49 +2,52 @@ require 'vorpal/aggregate_repository'
2
2
  require 'vorpal/config_builder'
3
3
 
4
4
  module Vorpal
5
+ module Configuration
5
6
 
6
- module Configuration
7
+ # Configures and creates a {Vorpal::AggregateRepository} instance.
8
+ #
9
+ # @param options [Hash] Global configuration options for the repository instance.
10
+ # @option options [Object] :db_driver (Object that will be used to interact with the DB.)
11
+ # Must be duck-type compatible with {Vorpal::DbDriver}.
12
+ #
13
+ # @return [Vorpal::AggregateRepository] Repository instance.
14
+ def define(options={}, &block)
15
+ master_config = build_config(&block)
16
+ db_driver = options.fetch(:db_driver, DbDriver.new)
17
+ AggregateRepository.new(db_driver, master_config)
18
+ end
7
19
 
8
- # Configures and creates a {Vorpal::AggregateRepository} instance.
9
- #
10
- # @param options [Hash] Global configuration options for the repository instance.
11
- # @option options [Object] :db_driver (Object that will be used to interact with the DB.)
12
- # Must be duck-type compatible with Vorpal::DbDriver.
13
- #
14
- # @return [Vorpal::AggregateRepository] Repository instance.
15
- def define(options={}, &block)
16
- master_config = build_config(&block)
17
- db_driver = options.fetch(:db_driver, DbDriver.new)
18
- AggregateRepository.new(db_driver, master_config)
19
- end
20
-
21
- # Maps a domain class to a relational table.
22
- #
23
- # @param domain_class [Class] Type of the domain model to be mapped
24
- # @param options [Hash] Configure how to map the domain model
25
- # @option options [String] :to (Class with the same name as the domain class with a 'DB' appended.)
26
- # Class of the ActiveRecord object that will map this domain class to the DB.
27
- # @option options [Object] :serializer (map the {ConfigBuilder#fields} directly)
28
- # Object that will convert the domain objects into a hash.
29
- #
30
- # Must have a `(Hash) serialize(Object)` method.
31
- # @option options [Object] :deserializer (map the {ConfigBuilder#fields} directly)
32
- # Object that will set a hash of attribute_names->values onto a new domain
33
- # object.
34
- #
35
- # Must have a `(Object) deserialize(Object, Hash)` method.
36
- def map(domain_class, options={}, &block)
37
- builder = ConfigBuilder.new(domain_class, options)
38
- builder.instance_exec(&block) if block_given?
20
+ # Maps a domain class to a relational table.
21
+ #
22
+ # @param domain_class [Class] Type of the domain model to be mapped
23
+ # @param options [Hash] Configure how to map the domain model
24
+ # @option options [String] :to (Class with the same name as the domain class with a 'DB' appended.)
25
+ # Class of the ActiveRecord object that will map this domain class to the DB.
26
+ # @option options [Object] :serializer (map the {ConfigBuilder#fields} directly)
27
+ # Object that will convert the domain objects into a hash.
28
+ #
29
+ # Must have a `(Hash) serialize(Object)` method.
30
+ # @option options [Object] :deserializer (map the {ConfigBuilder#fields} directly)
31
+ # Object that will set a hash of attribute_names->values onto a new domain
32
+ # object.
33
+ #
34
+ # Must have a `(Object) deserialize(Object, Hash)` method.
35
+ def map(domain_class, options={}, &block)
36
+ @class_configs << build_class_config(domain_class, options, &block)
37
+ end
39
38
 
40
- @class_configs << builder.build
41
- end
39
+ # @private
40
+ def build_class_config(domain_class, options={}, &block)
41
+ builder = ConfigBuilder.new(domain_class, options)
42
+ builder.instance_exec(&block) if block_given?
43
+ builder.build
44
+ end
42
45
 
43
- # @private
44
- def build_config(&block)
45
- @class_configs = []
46
- self.instance_exec(&block)
47
- MasterConfig.new(@class_configs)
46
+ # @private
47
+ def build_config(&block)
48
+ @class_configs = []
49
+ self.instance_exec(&block)
50
+ MasterConfig.new(@class_configs)
51
+ end
48
52
  end
49
53
  end
50
- end
@@ -1,4 +1,7 @@
1
1
  module Vorpal
2
+ # Interfaces between the database and Vorpal
3
+ #
4
+ # Currently only works for PostgreSQL.
2
5
  class DbDriver
3
6
  def initialize
4
7
  @sequence_names = {}
@@ -20,20 +23,29 @@ module Vorpal
20
23
  end
21
24
  end
22
25
 
23
- def destroy(class_config, db_objects)
24
- class_config.db_class.delete_all(id: db_objects.map(&:id))
26
+ def destroy(class_config, ids)
27
+ class_config.db_class.delete_all(id: ids)
25
28
  end
26
29
 
30
+ # Loads instances of the given class by primary key.
31
+ #
32
+ # @return [[Object]] An array of entities.
27
33
  def load_by_id(class_config, ids)
28
34
  class_config.db_class.where(id: ids)
29
35
  end
30
36
 
37
+ # Loads instances of the given class whose foreign key has the given value.
38
+ #
39
+ # @return [[Object]] An array of entities.
31
40
  def load_by_foreign_key(class_config, id, foreign_key_info)
32
41
  arel = class_config.db_class.where(foreign_key_info.fk_column => id)
33
42
  arel = arel.where(foreign_key_info.fk_type_column => foreign_key_info.fk_type) if foreign_key_info.polymorphic?
34
43
  arel.order(:id).all
35
44
  end
36
45
 
46
+ # Fetches primary key values to be used for new entities.
47
+ #
48
+ # @return [[Integer]] An array of unused primary keys.
37
49
  def get_primary_keys(class_config, count)
38
50
  result = execute("select nextval($1) from generate_series(1,$2);", [sequence_name(class_config), count])
39
51
  result.rows.map(&:first).map(&:to_i)
@@ -3,138 +3,136 @@ require 'vorpal/util/array_hash'
3
3
  require 'vorpal/db_driver'
4
4
 
5
5
  module Vorpal
6
+ # Handles loading of objects from the database.
7
+ #
8
+ # @private
9
+ class DbLoader
10
+ def initialize(only_owned, db_driver)
11
+ @only_owned = only_owned
12
+ @db_driver = db_driver
13
+ end
6
14
 
7
- # Handles loading of objects from the database.
8
- #
9
- # @private
10
- class DbLoader
11
- def initialize(only_owned, db_driver)
12
- @only_owned = only_owned
13
- @db_driver = db_driver
14
- end
15
+ def load_from_db(ids, config)
16
+ @loaded_objects = LoadedObjects.new
17
+ @lookup_instructions = LookupInstructions.new
18
+ @lookup_instructions.lookup_by_id(config, ids)
15
19
 
16
- def load_from_db(ids, config)
17
- @loaded_objects = LoadedObjects.new
18
- @lookup_instructions = LookupInstructions.new
19
- @lookup_instructions.lookup_by_id(config, ids)
20
+ until @lookup_instructions.empty?
21
+ lookup = @lookup_instructions.next_lookup
22
+ new_objects = lookup.load_all(@db_driver)
23
+ @loaded_objects.add(lookup.config, new_objects)
24
+ explore_objects(lookup.config, new_objects)
25
+ end
20
26
 
21
- until @lookup_instructions.empty?
22
- lookup = @lookup_instructions.next_lookup
23
- new_objects = lookup.load_all(@db_driver)
24
- @loaded_objects.add(lookup.config, new_objects)
25
- explore_objects(lookup.config, new_objects)
27
+ @loaded_objects
26
28
  end
27
29
 
28
- @loaded_objects
29
- end
30
-
31
- private
30
+ private
32
31
 
33
- def explore_objects(config, objects_to_explore)
34
- objects_to_explore.each do |db_object|
35
- config.has_manys.each do |has_many_config|
36
- lookup_by_fk(db_object, has_many_config) if explore_association?(has_many_config)
37
- end
32
+ def explore_objects(config, objects_to_explore)
33
+ objects_to_explore.each do |db_object|
34
+ config.has_manys.each do |has_many_config|
35
+ lookup_by_fk(db_object, has_many_config) if explore_association?(has_many_config)
36
+ end
38
37
 
39
- config.has_ones.each do |has_one_config|
40
- lookup_by_fk(db_object, has_one_config) if explore_association?(has_one_config)
41
- end
38
+ config.has_ones.each do |has_one_config|
39
+ lookup_by_fk(db_object, has_one_config) if explore_association?(has_one_config)
40
+ end
42
41
 
43
- config.belongs_tos.each do |belongs_to_config|
44
- lookup_by_id(db_object, belongs_to_config) if explore_association?(belongs_to_config)
42
+ config.belongs_tos.each do |belongs_to_config|
43
+ lookup_by_id(db_object, belongs_to_config) if explore_association?(belongs_to_config)
44
+ end
45
45
  end
46
46
  end
47
- end
48
47
 
49
- def explore_association?(association_config)
50
- !@only_owned || association_config.owned == true
51
- end
48
+ def explore_association?(association_config)
49
+ !@only_owned || association_config.owned == true
50
+ end
52
51
 
53
- def lookup_by_id(db_object, belongs_to_config)
54
- child_config = belongs_to_config.child_config(db_object)
55
- id = belongs_to_config.fk_value(db_object)
56
- return if @loaded_objects.id_lookup_done?(child_config, id)
57
- @lookup_instructions.lookup_by_id(child_config, id)
58
- end
52
+ def lookup_by_id(db_object, belongs_to_config)
53
+ child_config = belongs_to_config.child_config(db_object)
54
+ id = belongs_to_config.fk_value(db_object)
55
+ return if id.nil? || @loaded_objects.already_loaded?(child_config, id)
56
+ @lookup_instructions.lookup_by_id(child_config, id)
57
+ end
59
58
 
60
- def lookup_by_fk(db_object, has_some_config)
61
- child_config = has_some_config.child_config
62
- fk_info = has_some_config.foreign_key_info
63
- fk_value = db_object.id
64
- @lookup_instructions.lookup_by_fk(child_config, fk_info, fk_value)
59
+ def lookup_by_fk(db_object, has_some_config)
60
+ child_config = has_some_config.child_config
61
+ fk_info = has_some_config.foreign_key_info
62
+ fk_value = db_object.id
63
+ @lookup_instructions.lookup_by_fk(child_config, fk_info, fk_value)
64
+ end
65
65
  end
66
- end
67
66
 
68
- # @private
69
- class LookupInstructions
70
- include ArrayHash
71
- def initialize
72
- @lookup_by_id = {}
73
- @lookup_by_fk = {}
74
- end
67
+ # @private
68
+ class LookupInstructions
69
+ include ArrayHash
70
+ def initialize
71
+ @lookup_by_id = {}
72
+ @lookup_by_fk = {}
73
+ end
75
74
 
76
- def lookup_by_id(config, ids)
77
- add_to_hash(@lookup_by_id, config, Array(ids))
78
- end
75
+ def lookup_by_id(config, ids)
76
+ add_to_hash(@lookup_by_id, config, Array(ids))
77
+ end
79
78
 
80
- def lookup_by_fk(config, fk_info, fk_value)
81
- add_to_hash(@lookup_by_fk, [config, fk_info], fk_value)
82
- end
79
+ def lookup_by_fk(config, fk_info, fk_value)
80
+ add_to_hash(@lookup_by_fk, [config, fk_info], fk_value)
81
+ end
83
82
 
84
- def next_lookup
85
- if @lookup_by_id.empty?
86
- pop_fk_lookup
87
- else
88
- pop_id_lookup
83
+ def next_lookup
84
+ if @lookup_by_id.empty?
85
+ pop_fk_lookup
86
+ else
87
+ pop_id_lookup
88
+ end
89
89
  end
90
- end
91
90
 
92
- def empty?
93
- @lookup_by_id.empty? && @lookup_by_fk.empty?
94
- end
91
+ def empty?
92
+ @lookup_by_id.empty? && @lookup_by_fk.empty?
93
+ end
95
94
 
96
- private
95
+ private
97
96
 
98
- def pop_id_lookup
99
- config, ids = pop(@lookup_by_id)
100
- LookupById.new(config, ids)
101
- end
97
+ def pop_id_lookup
98
+ config, ids = pop(@lookup_by_id)
99
+ LookupById.new(config, ids)
100
+ end
102
101
 
103
- def pop_fk_lookup
104
- key, fk_values = pop(@lookup_by_fk)
105
- config = key.first
106
- fk_info = key.last
107
- LookupByFk.new(config, fk_info, fk_values)
102
+ def pop_fk_lookup
103
+ key, fk_values = pop(@lookup_by_fk)
104
+ config = key.first
105
+ fk_info = key.last
106
+ LookupByFk.new(config, fk_info, fk_values)
107
+ end
108
108
  end
109
- end
110
109
 
111
- # @private
112
- class LookupById
113
- attr_reader :config
114
- def initialize(config, ids)
115
- @config = config
116
- @ids = ids
117
- end
110
+ # @private
111
+ class LookupById
112
+ attr_reader :config
113
+ def initialize(config, ids)
114
+ @config = config
115
+ @ids = ids
116
+ end
118
117
 
119
- def load_all(db_driver)
120
- return [] if @ids.empty?
121
- db_driver.load_by_id(@config, @ids)
118
+ def load_all(db_driver)
119
+ return [] if @ids.empty?
120
+ db_driver.load_by_id(@config, @ids)
121
+ end
122
122
  end
123
- end
124
123
 
125
- # @private
126
- class LookupByFk
127
- attr_reader :config
128
- def initialize(config, fk_info, fk_values)
129
- @config = config
130
- @fk_info = fk_info
131
- @fk_values = fk_values
132
- end
124
+ # @private
125
+ class LookupByFk
126
+ attr_reader :config
127
+ def initialize(config, fk_info, fk_values)
128
+ @config = config
129
+ @fk_info = fk_info
130
+ @fk_values = fk_values
131
+ end
133
132
 
134
- def load_all(db_driver)
135
- return [] if @fk_values.empty?
136
- db_driver.load_by_foreign_key(@config, @fk_values, @fk_info)
133
+ def load_all(db_driver)
134
+ return [] if @fk_values.empty?
135
+ db_driver.load_by_foreign_key(@config, @fk_values, @fk_info)
136
+ end
137
137
  end
138
138
  end
139
-
140
- end
@@ -1,34 +1,34 @@
1
1
  module Vorpal
2
- class IdentityMap
3
- def initialize
4
- @entities = {}
5
- end
2
+ class IdentityMap
3
+ def initialize
4
+ @entities = {}
5
+ end
6
6
 
7
- def get(key_object)
8
- @entities[key(key_object)]
9
- end
7
+ def get(key_object)
8
+ @entities[key(key_object)]
9
+ end
10
10
 
11
- def set(key_object, object)
12
- @entities[key(key_object)] = object
13
- end
11
+ def set(key_object, object)
12
+ @entities[key(key_object)] = object
13
+ end
14
14
 
15
- def get_and_set(key_object)
16
- object = get(key_object)
17
- object = yield if object.nil?
18
- set(key_object, object)
19
- object
20
- end
15
+ def get_and_set(key_object)
16
+ object = get(key_object)
17
+ object = yield if object.nil?
18
+ set(key_object, object)
19
+ object
20
+ end
21
21
 
22
- def map(key_objects)
23
- key_objects.map { |k| @entities[key(k)] }
24
- end
22
+ def map(key_objects)
23
+ key_objects.map { |k| @entities[key(k)] }
24
+ end
25
25
 
26
- private
26
+ private
27
27
 
28
- def key(key_object)
29
- return nil unless key_object
30
- raise "Cannot put entity '#{key_object.inspect}' into IdentityMap without an id." if key_object.id.nil?
31
- [key_object.id, key_object.class.name]
28
+ def key(key_object)
29
+ return nil unless key_object
30
+ raise "Cannot put entity '#{key_object.inspect}' into IdentityMap without an id." if key_object.id.nil?
31
+ [key_object.id, key_object.class.name]
32
+ end
32
33
  end
33
34
  end
34
- end
@@ -3,38 +3,39 @@ require 'forwardable'
3
3
 
4
4
  module Vorpal
5
5
 
6
- # @private
7
- class LoadedObjects
8
- include ArrayHash
9
- extend Forwardable
10
- include Enumerable
11
-
12
- attr_reader :objects
13
- def_delegators :objects, :each
14
-
15
- def initialize
16
- @objects = Hash.new([])
17
- @objects_by_id = Hash.new
18
- end
19
-
20
- def add(config, objects)
21
- add_to_hash(@objects, config, objects)
6
+ # @private
7
+ class LoadedObjects
8
+ include ArrayHash
9
+ extend Forwardable
10
+ include Enumerable
11
+
12
+ attr_reader :objects
13
+ def_delegators :objects, :each
14
+
15
+ def initialize
16
+ @objects = Hash.new([])
17
+ @objects_by_id = Hash.new
18
+ end
22
19
 
23
- objects.each do |object|
24
- @objects_by_id[[config.domain_class.name, object.id]] = object
20
+ def add(config, objects)
21
+ objects_to_add = objects.map do |object|
22
+ if !already_loaded?(config, object.id)
23
+ @objects_by_id[[config.domain_class.name, object.id]] = object
24
+ end
25
+ end
26
+ add_to_hash(@objects, config, objects_to_add.compact)
25
27
  end
26
- end
27
28
 
28
- def find_by_id(config, id)
29
- @objects_by_id[[config.domain_class.name, id]]
30
- end
29
+ def find_by_id(config, id)
30
+ @objects_by_id[[config.domain_class.name, id]]
31
+ end
31
32
 
32
- def all_objects
33
- @objects_by_id.values
34
- end
33
+ def all_objects
34
+ @objects_by_id.values
35
+ end
35
36
 
36
- def id_lookup_done?(config, id)
37
- !find_by_id(config, id).nil?
37
+ def already_loaded?(config, id)
38
+ !find_by_id(config, id).nil?
39
+ end
38
40
  end
39
41
  end
40
- end