praxis-mapper 3.1.1
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 +7 -0
- data/.gitignore +26 -0
- data/.rspec +3 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +83 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +102 -0
- data/Guardfile +11 -0
- data/LICENSE +22 -0
- data/README.md +19 -0
- data/Rakefile +14 -0
- data/lib/praxis-mapper/config_hash.rb +40 -0
- data/lib/praxis-mapper/connection_manager.rb +102 -0
- data/lib/praxis-mapper/finalizable.rb +38 -0
- data/lib/praxis-mapper/identity_map.rb +532 -0
- data/lib/praxis-mapper/logging.rb +22 -0
- data/lib/praxis-mapper/model.rb +430 -0
- data/lib/praxis-mapper/query/base.rb +213 -0
- data/lib/praxis-mapper/query/sql.rb +183 -0
- data/lib/praxis-mapper/query_statistics.rb +46 -0
- data/lib/praxis-mapper/resource.rb +226 -0
- data/lib/praxis-mapper/support/factory_girl.rb +104 -0
- data/lib/praxis-mapper/support/memory_query.rb +34 -0
- data/lib/praxis-mapper/support/memory_repository.rb +44 -0
- data/lib/praxis-mapper/support/schema_dumper.rb +66 -0
- data/lib/praxis-mapper/support/schema_loader.rb +56 -0
- data/lib/praxis-mapper/support.rb +2 -0
- data/lib/praxis-mapper/version.rb +5 -0
- data/lib/praxis-mapper.rb +60 -0
- data/praxis-mapper.gemspec +38 -0
- data/spec/praxis-mapper/connection_manager_spec.rb +117 -0
- data/spec/praxis-mapper/identity_map_spec.rb +905 -0
- data/spec/praxis-mapper/logging_spec.rb +9 -0
- data/spec/praxis-mapper/memory_repository_spec.rb +56 -0
- data/spec/praxis-mapper/model_spec.rb +389 -0
- data/spec/praxis-mapper/query/base_spec.rb +317 -0
- data/spec/praxis-mapper/query/sql_spec.rb +184 -0
- data/spec/praxis-mapper/resource_spec.rb +154 -0
- data/spec/praxis_mapper_spec.rb +21 -0
- data/spec/spec_fixtures.rb +12 -0
- data/spec/spec_helper.rb +63 -0
- data/spec/support/spec_models.rb +215 -0
- data/spec/support/spec_resources.rb +39 -0
- metadata +298 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7bb0361a7b29be6d0c9d393bf149369d22cfacd1
|
4
|
+
data.tar.gz: f86867db4ccfdd60992aab8cc66cf8959362c7ec
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 13f27e05d0c46b6ed3b906af2fbee4e05425530354ee873eba1097beeacb4df74955b9aa05e064f1ce1165fdda9f72b74beb4c5759454c4493d086c9ffea98d6
|
7
|
+
data.tar.gz: 697c2552d6c1b898df1d77e61e164f844d89361f66a5647ccac2005e745e7ccf7bc7d06acfaf066700d00e19af1f771ed8512f79672e786d0c90fdb9b023dbcb
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# praxis-mapper changelog
|
2
|
+
|
3
|
+
## next
|
4
|
+
|
5
|
+
* Next feature description here
|
6
|
+
|
7
|
+
## 3.1
|
8
|
+
|
9
|
+
* Begin migration to Sequel for `Query::Sql`.
|
10
|
+
* `#_execute` uses it for a database-agnostic adapter for running raw SQL.
|
11
|
+
* `#_multi_get` uses it generate a datbase-agnostic where clause
|
12
|
+
* Added accessor generation for `one_to_many` associations in resources.
|
13
|
+
* Imported `Model` extensions for FactoryGirl to `praxis-mapper/support/factory_girl`.
|
14
|
+
* This also adds `IdentityMap#persist!` to insert all rows in the identity map into the database.
|
15
|
+
* Added `SchemaDumper` and `SchemaLoader` for dumping and loading schema information in `praxis-mapper/support/schema_dumper` and `praxis-mapper/support/schema_loader` respectively.
|
16
|
+
* `IdentityMap#all` now accepts non-identity keys as conditions, and will create a secondary index for the key the first time it's queried.
|
17
|
+
* Added `Model#identities` to get a hash of identities and values for a record.
|
18
|
+
* Auto-generated attribute accessors on `Blueprint` do not load (coerce) the value using the attribute. Ensure values passed in already have the appropriate types. Blueprint attributes will still be wrapped properly, however.
|
19
|
+
* Performance and memory use optimizations.
|
20
|
+
* `IdentityMap#load` now supports eagerly-loading associated records in a query, and supports the full set of options on the inner query, including
|
21
|
+
* Tracked `:one_to_many` associations now support where clauses. Using `where` clauses when tracking other association types is not supported and will raise an exception.
|
22
|
+
|
23
|
+
|
24
|
+
## 3.0
|
25
|
+
|
26
|
+
* Moved Blueprint inner attribute creation to finalize!
|
27
|
+
* Added :dsl_compiler and :identity as valid options for Blueprint.
|
28
|
+
* `Praxis::Mapper::Model`
|
29
|
+
* `Model.context` allows named query parameters to be reused across queries. See documentation for details.
|
30
|
+
* `Model#_query` references original query that loaded the record.
|
31
|
+
* `Model.serialized_fields` returns hash of fields defined with either `json` or `yaml` directives as serialized fields.
|
32
|
+
* Fixed accessors will to raise `KeyError` if the record does not the field (i.e., if it was not loaded from the database), rather than silently returning `nil`.
|
33
|
+
* `Praxis::Mapper::Query`:
|
34
|
+
* Multiple `select` and `track` calls within one query are now additive.
|
35
|
+
* `track` option now takes a block that will be applied to the query used to load the tracked records.
|
36
|
+
* `context` directive to apply the named context from the model to the query. See documentation for more.
|
37
|
+
* `Praxis::Mapper::IdentityMap`
|
38
|
+
* Removed `:track` option from `#add_records` in favor of using `Model#_query` to determine tracked associations.
|
39
|
+
* Added `Praxis::Mapper::Support` with `MemoryQuery` and `MemoryRepository` for use in testing data loading without requiring a database.
|
40
|
+
|
41
|
+
|
42
|
+
### Notices
|
43
|
+
|
44
|
+
The `Model` accessor changes may break existing applications that (incorrectly) expect unloaded attributes to return `nil`.
|
45
|
+
|
46
|
+
|
47
|
+
## 2.1.0
|
48
|
+
|
49
|
+
* Blueprint.validate now only accepts objects of the its type.
|
50
|
+
* Improved handling of Blueprints with circular relations.
|
51
|
+
|
52
|
+
|
53
|
+
## 2.0.0
|
54
|
+
|
55
|
+
* First pass at reworking model associations.
|
56
|
+
* Split `belongs_to` into `many_to_one` and `array_to_many` associations
|
57
|
+
* Added `one_to_many` and `many_to_array` associations (the inverse of the above associations, aka: has_many)
|
58
|
+
* Added association configuration DSL to replace previous hash-based configuration.
|
59
|
+
|
60
|
+
|
61
|
+
## 0.3.1
|
62
|
+
|
63
|
+
* Added support for code coverage
|
64
|
+
* Added support for 'guard' gem (use 'bundle exec guard')
|
65
|
+
* Fixed bug in Praxis::Mapper::Model.undefine\_data\_accessors when method doesn't exist.
|
66
|
+
* Safer version checking with Gem::Version class
|
67
|
+
* Cleaned up Gemfile
|
68
|
+
* Updated 'slop' gem
|
69
|
+
|
70
|
+
|
71
|
+
## 0.3.0
|
72
|
+
|
73
|
+
* identity map hotfix
|
74
|
+
|
75
|
+
|
76
|
+
## 0.2.0
|
77
|
+
|
78
|
+
* don't know what happened here, check git log I guess
|
79
|
+
|
80
|
+
|
81
|
+
## 0.1.0
|
82
|
+
|
83
|
+
* initial release
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
praxis-mapper (3.1.0)
|
5
|
+
activesupport (~> 4)
|
6
|
+
randexp (~> 0)
|
7
|
+
sequel (~> 4)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activesupport (4.1.5)
|
13
|
+
i18n (~> 0.6, >= 0.6.9)
|
14
|
+
json (~> 1.7, >= 1.7.7)
|
15
|
+
minitest (~> 5.1)
|
16
|
+
thread_safe (~> 0.1)
|
17
|
+
tzinfo (~> 1.1)
|
18
|
+
binding_of_caller (0.7.2)
|
19
|
+
debug_inspector (>= 0.0.1)
|
20
|
+
byebug (2.7.0)
|
21
|
+
columnize (~> 0.3)
|
22
|
+
debugger-linecache (~> 1.2)
|
23
|
+
celluloid (0.15.2)
|
24
|
+
timers (~> 1.1.0)
|
25
|
+
coderay (1.1.0)
|
26
|
+
columnize (0.8.9)
|
27
|
+
debug_inspector (0.0.2)
|
28
|
+
debugger-linecache (1.2.0)
|
29
|
+
diff-lcs (1.2.5)
|
30
|
+
ffi (1.9.3)
|
31
|
+
formatador (0.2.5)
|
32
|
+
fuubar (1.3.3)
|
33
|
+
rspec (>= 2.14.0, < 3.1.0)
|
34
|
+
ruby-progressbar (~> 1.4)
|
35
|
+
guard (2.6.1)
|
36
|
+
formatador (>= 0.2.4)
|
37
|
+
listen (~> 2.7)
|
38
|
+
lumberjack (~> 1.0)
|
39
|
+
pry (>= 0.9.12)
|
40
|
+
thor (>= 0.18.1)
|
41
|
+
guard-rspec (4.3.1)
|
42
|
+
guard (~> 2.1)
|
43
|
+
rspec (>= 2.14, < 4.0)
|
44
|
+
i18n (0.6.11)
|
45
|
+
json (1.8.1)
|
46
|
+
listen (2.7.9)
|
47
|
+
celluloid (>= 0.15.2)
|
48
|
+
rb-fsevent (>= 0.9.3)
|
49
|
+
rb-inotify (>= 0.9)
|
50
|
+
lumberjack (1.0.9)
|
51
|
+
method_source (0.8.2)
|
52
|
+
minitest (5.4.0)
|
53
|
+
pry (0.10.1)
|
54
|
+
coderay (~> 1.1.0)
|
55
|
+
method_source (~> 0.8.1)
|
56
|
+
slop (~> 3.4)
|
57
|
+
pry-byebug (1.3.3)
|
58
|
+
byebug (~> 2.7)
|
59
|
+
pry (~> 0.10)
|
60
|
+
pry-stack_explorer (0.4.9.1)
|
61
|
+
binding_of_caller (>= 0.7)
|
62
|
+
pry (>= 0.9.11)
|
63
|
+
rake (0.9.6)
|
64
|
+
randexp (0.1.7)
|
65
|
+
rb-fsevent (0.9.4)
|
66
|
+
rb-inotify (0.9.5)
|
67
|
+
ffi (>= 0.5.0)
|
68
|
+
redcarpet (2.3.0)
|
69
|
+
rspec (2.14.1)
|
70
|
+
rspec-core (~> 2.14.0)
|
71
|
+
rspec-expectations (~> 2.14.0)
|
72
|
+
rspec-mocks (~> 2.14.0)
|
73
|
+
rspec-core (2.14.8)
|
74
|
+
rspec-expectations (2.14.5)
|
75
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
76
|
+
rspec-mocks (2.14.6)
|
77
|
+
ruby-progressbar (1.5.1)
|
78
|
+
sequel (4.13.0)
|
79
|
+
slop (3.6.0)
|
80
|
+
thor (0.19.1)
|
81
|
+
thread_safe (0.3.4)
|
82
|
+
timers (1.1.0)
|
83
|
+
tzinfo (1.2.2)
|
84
|
+
thread_safe (~> 0.1)
|
85
|
+
yard (0.8.7.4)
|
86
|
+
|
87
|
+
PLATFORMS
|
88
|
+
ruby
|
89
|
+
|
90
|
+
DEPENDENCIES
|
91
|
+
bundler (~> 1.6)
|
92
|
+
fuubar (~> 1)
|
93
|
+
guard (~> 2)
|
94
|
+
guard-rspec
|
95
|
+
praxis-mapper!
|
96
|
+
pry (~> 0)
|
97
|
+
pry-byebug (~> 1)
|
98
|
+
pry-stack_explorer (~> 0)
|
99
|
+
rake (~> 0)
|
100
|
+
redcarpet (< 3.0)
|
101
|
+
rspec (< 2.99)
|
102
|
+
yard (~> 0.8.7)
|
data/Guardfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Config file for Guard
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/praxis-mapper/(.+)\.rb$}) { |m| "spec/praxis-mapper/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/*.rb') { 'spec' }
|
8
|
+
watch('lib/praxis-mapper.rb') { 'spec' }
|
9
|
+
watch(%r{^spec/support/(.+)\.rb$}) { 'spec' }
|
10
|
+
end
|
11
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 RightScale
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Praxis::Mapper
|
2
|
+
|
3
|
+
Praxis::Mapper is a library that allows for large amounts of data to be loaded for a tree of associated models,
|
4
|
+
while minimizing requests to external services.
|
5
|
+
It does multi-stage fetch, with compaction between each stage.
|
6
|
+
|
7
|
+
Documentation and examples coming soon.
|
8
|
+
|
9
|
+
## Testing
|
10
|
+
|
11
|
+
To run unit tests:
|
12
|
+
|
13
|
+
bundle exec rspec
|
14
|
+
|
15
|
+
## License
|
16
|
+
|
17
|
+
This software is released under the [MIT License](http://www.opensource.org/licenses/MIT). Please see [LICENSE](LICENSE) for further details.
|
18
|
+
|
19
|
+
Copyright (c) 2014 RightScale
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
require 'rspec/core'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
desc "Run RSpec code examples with simplecov"
|
7
|
+
RSpec::Core::RakeTask.new do |spec|
|
8
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => :spec
|
12
|
+
|
13
|
+
require 'yard'
|
14
|
+
YARD::Rake::YardocTask.new
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Praxis::Mapper
|
2
|
+
class ConfigHash < BasicObject
|
3
|
+
|
4
|
+
attr_reader :hash
|
5
|
+
|
6
|
+
def self.from(hash={},&block)
|
7
|
+
self.new(hash,&block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(hash={},&block)
|
11
|
+
@hash = hash
|
12
|
+
@block = block
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_hash
|
16
|
+
self.instance_eval(&@block)
|
17
|
+
@hash
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(name, value, *rest, &block)
|
21
|
+
if (existing = @hash[name])
|
22
|
+
if block
|
23
|
+
existing << [value, block]
|
24
|
+
else
|
25
|
+
existing << value
|
26
|
+
rest.each do |v|
|
27
|
+
existing << v
|
28
|
+
end
|
29
|
+
end
|
30
|
+
else
|
31
|
+
if rest.any?
|
32
|
+
@hash[name] = [value] + rest
|
33
|
+
else
|
34
|
+
@hash[name] = value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Praxis::Mapper
|
2
|
+
class ConnectionManager
|
3
|
+
|
4
|
+
# Configures a data store.
|
5
|
+
def self.setup(config_data={}, &block)
|
6
|
+
config_data.each do |repository_name, data|
|
7
|
+
klass_name = data.delete(:connection_factory)
|
8
|
+
connection_factory_class = Object.const_get(klass_name)
|
9
|
+
repositories[repository_name][:connection_factory] = connection_factory_class.new(data[:connection_opts])
|
10
|
+
|
11
|
+
if (query_klass_name = data.delete(:query))
|
12
|
+
query_klass = Object.const_get(query_klass_name)
|
13
|
+
repositories[repository_name][:query] = query_klass
|
14
|
+
end
|
15
|
+
end
|
16
|
+
if block_given?
|
17
|
+
self.instance_eval(&block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.repository(repository_name, data=nil,&block)
|
22
|
+
return repositories[repository_name] if data.nil? && !block_given?
|
23
|
+
|
24
|
+
if data && data[:query]
|
25
|
+
query_klass = case data[:query]
|
26
|
+
when String
|
27
|
+
query_klass_name = data[:query]
|
28
|
+
Object.const_get(query_klass_name) #FIXME: won't really work consistently
|
29
|
+
when Class
|
30
|
+
data[:query]
|
31
|
+
when Symbol
|
32
|
+
raise "symbol support is not implemented yet"
|
33
|
+
else
|
34
|
+
raise "unknown type for query: #{data[:query].inspect} has type #{data[:query].class}"
|
35
|
+
end
|
36
|
+
repositories[repository_name][:query] = query_klass
|
37
|
+
end
|
38
|
+
|
39
|
+
if block_given?
|
40
|
+
# TODO: ? complain if data.has_key?(:connection_factory)
|
41
|
+
repositories[repository_name][:connection_factory] = block
|
42
|
+
elsif data
|
43
|
+
klass_name = data.delete(:connection_factory)
|
44
|
+
connection_factory_class = Object.const_get(klass_name) #FIXME: won't really work consistently
|
45
|
+
repositories[repository_name][:connection_factory] = connection_factory_class.new(data[:connection_opts])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def self.repositories
|
51
|
+
@repositories ||= Hash.new do |hash,key|
|
52
|
+
hash[key] = {
|
53
|
+
:connection_factory => nil,
|
54
|
+
:query => Praxis::Mapper::Query::Sql
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def repositories
|
60
|
+
self.class.repositories
|
61
|
+
end
|
62
|
+
|
63
|
+
def repository(repository_name)
|
64
|
+
self.repositories[repository_name]
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize
|
68
|
+
@connections = {}
|
69
|
+
end
|
70
|
+
|
71
|
+
def checkout(name)
|
72
|
+
connection = @connections[name]
|
73
|
+
return connection if connection
|
74
|
+
|
75
|
+
factory = repositories[name][:connection_factory]
|
76
|
+
connection = if factory.kind_of?(Proc)
|
77
|
+
factory.call
|
78
|
+
else
|
79
|
+
factory.checkout
|
80
|
+
end
|
81
|
+
|
82
|
+
@connections[name] = connection
|
83
|
+
end
|
84
|
+
|
85
|
+
def release_one(name)
|
86
|
+
if (connection = @connections.delete(name))
|
87
|
+
return true if repositories[name][:connection_factory].kind_of? Proc
|
88
|
+
repositories[name][:connection_factory].release(connection)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def release(name=nil)
|
93
|
+
if name
|
94
|
+
release_one(name)
|
95
|
+
else
|
96
|
+
names = @connections.keys
|
97
|
+
names.each { |name| release_one(name) }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Praxis::Mapper
|
2
|
+
module Finalizable
|
3
|
+
|
4
|
+
|
5
|
+
def self.extended(klass)
|
6
|
+
klass.module_eval do
|
7
|
+
@finalizable = Set.new
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def inherited(base)
|
12
|
+
@finalizable << base
|
13
|
+
base.instance_variable_set(:@finalizable, @finalizable)
|
14
|
+
base.instance_variable_set(:@finalized, false)
|
15
|
+
end
|
16
|
+
|
17
|
+
def finalizable
|
18
|
+
@finalizable
|
19
|
+
end
|
20
|
+
|
21
|
+
def finalized?
|
22
|
+
@finalized
|
23
|
+
end
|
24
|
+
|
25
|
+
def _finalize!
|
26
|
+
@finalized = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def finalize!
|
30
|
+
self.finalizable.reject(&:finalized?).each do |klass|
|
31
|
+
klass._finalize!
|
32
|
+
end
|
33
|
+
|
34
|
+
self.finalize! unless self.finalizable.all?(&:finalized?)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|