praxis-mapper 4.1.2 → 4.2
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/CHANGELOG.md +36 -0
- data/README.md +6 -2
- data/lib/praxis-mapper.rb +3 -2
- data/lib/praxis-mapper/identity_map.rb +17 -9
- data/lib/praxis-mapper/query/base.rb +35 -5
- data/lib/praxis-mapper/query/sequel.rb +1 -1
- data/lib/praxis-mapper/query/sql.rb +1 -1
- data/lib/praxis-mapper/resource.rb +20 -13
- data/lib/praxis-mapper/selector_generator.rb +98 -0
- data/lib/praxis-mapper/version.rb +1 -1
- data/praxis-mapper.gemspec +1 -0
- data/spec/factories/all.rb +8 -2
- data/spec/praxis-mapper/query/base_spec.rb +34 -6
- data/spec/praxis-mapper/query/sequel_spec.rb +11 -0
- data/spec/praxis-mapper/query/sql_spec.rb +15 -0
- data/spec/praxis-mapper/resource_spec.rb +17 -2
- data/spec/praxis-mapper/selector_generator_spec.rb +221 -0
- data/spec/spec_helper.rb +7 -2
- data/spec/support/spec_resources.rb +18 -2
- data/spec/support/spec_sequel_models.rb +30 -7
- data/spec/support/spec_sequel_resources.rb +57 -0
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ff56d63e3412602d1542aefa5a0ddeb2c6f053d
|
4
|
+
data.tar.gz: e55ac6835ca3be6495ccb699e72197502812664f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54bb23566ca0385a869114087e2fd91420f3e1b7e5ce890672dbbdb649d4d0fe7f63bf5b947e4d7c2af781996363d185e3ee0de160836e70d265bdd4b6a8673e
|
7
|
+
data.tar.gz: 088259294b44ba826033c1a39e279899be35f0c482827f0cc6deac8aa3282dd6323160ff0877b68d15b2a7ebe92e3df8c38ffc985ad050de775daee9b4388ba4
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,42 @@
|
|
2
2
|
|
3
3
|
## next
|
4
4
|
|
5
|
+
## 4.2
|
6
|
+
|
7
|
+
* Added `Resource.property` to specify the fields and associations from the
|
8
|
+
underlying model that are needed by methods added to the `Resource`.
|
9
|
+
* For example:
|
10
|
+
* `property :full_name, dependencies: [:first, :last]` specifies that the `full_name` method on the resource depends upon the values of the `first`
|
11
|
+
and `last` fields from the model.
|
12
|
+
* `property :owner_name, dependencies: ['owner.first', 'owner.last']`
|
13
|
+
defines a dependency on the `first` and `last` fields from the associated
|
14
|
+
`owner`.
|
15
|
+
* Dependencies must always be an array. Symbol values should be used for
|
16
|
+
fields or associations from the same resource, and strings can be used
|
17
|
+
for fields from associated resources.
|
18
|
+
* `:*` or `'association.*'` may be used to specify a
|
19
|
+
dependency on all of the fields of the resource (analagous to 'SELECT *').
|
20
|
+
* Additionally, property dependencies may specify other properties on the
|
21
|
+
same or related resources.
|
22
|
+
* Added `IdentityMap#add_selectors(resource, field_selector)` to define fields to
|
23
|
+
`select` and associations to `track` given `resource` and `field_selectors`.
|
24
|
+
* For example:
|
25
|
+
* `identity_map#add_selectors(PersonResource, full_name: true)` would
|
26
|
+
ensure any loads done for the model the `PersonResource` describes will
|
27
|
+
select any fields required by the `full_name` property (`first` and `last`
|
28
|
+
in the example above).
|
29
|
+
* `identity_map#add_selectors(PersonResource, id: true, address: true)`
|
30
|
+
could result in selecting the `id` and `address_id` fields of `PersonModel`,
|
31
|
+
as well as tracking the `address` association, for any queries involving
|
32
|
+
`PersonModel`.
|
33
|
+
* Added the ability to explicitly perform a "SELECT *" by specifying `select :*`
|
34
|
+
or `select '*'` in a query.
|
35
|
+
* Added `:through` option to many_to_many associations to define the series of
|
36
|
+
associations necessary to resolve it. This is unused by Sequel, but is required
|
37
|
+
by the SelectorGenerator to handle such associations. Specify it as an array
|
38
|
+
of symbols.
|
39
|
+
* For example: `many_to_many :commented_posts, through: [:comments, :post], ...`
|
40
|
+
|
5
41
|
## 4.1.2
|
6
42
|
|
7
43
|
* Remove finalizer from IdentityMap to fix memory leak that was preventing them from being GC'd properly.
|
data/README.md
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
# Praxis::Mapper [![TravisCI][travis-img-url]][travis-ci-url]
|
1
|
+
# Praxis::Mapper [![TravisCI][travis-img-url]][travis-ci-url] [![Coverage Status][coveralls-img-url]][coveralls-url] [![Dependency Status][gemnasium-img-url]][gemnasium-url]
|
2
2
|
|
3
3
|
[travis-img-url]:https://travis-ci.org/rightscale/praxis-mapper.svg?branch=master
|
4
4
|
[travis-ci-url]:https://travis-ci.org/rightscale/praxis-mapper
|
5
|
+
[coveralls-img-url]:https://coveralls.io/repos/rightscale/praxis-mapper/badge.svg?branch=master&service=github
|
6
|
+
[coveralls-url]:https://coveralls.io/github/rightscale/praxis-mapper?branch=master
|
7
|
+
[gemnasium-img-url]:https://gemnasium.com/rightscale/praxis-mapper.svg
|
8
|
+
[gemnasium-url]:https://gemnasium.com/rightscale/praxis-mapper
|
5
9
|
|
6
10
|
|
7
11
|
Praxis::Mapper is a library that allows for large amounts of data to be loaded for a tree of associated models,
|
@@ -20,4 +24,4 @@ To run unit tests:
|
|
20
24
|
|
21
25
|
This software is released under the [MIT License](http://www.opensource.org/licenses/MIT). Please see [LICENSE](LICENSE) for further details.
|
22
26
|
|
23
|
-
Copyright (c) 2014 RightScale
|
27
|
+
Copyright (c) 2014 RightScale
|
data/lib/praxis-mapper.rb
CHANGED
@@ -51,7 +51,7 @@ require 'praxis-mapper/identity_map'
|
|
51
51
|
require 'praxis-mapper/model'
|
52
52
|
require 'praxis-mapper/query_statistics'
|
53
53
|
|
54
|
-
require 'praxis-mapper/sequel_compat'
|
54
|
+
require 'praxis-mapper/sequel_compat'
|
55
55
|
|
56
56
|
require 'praxis-mapper/connection_manager'
|
57
57
|
|
@@ -64,5 +64,6 @@ require 'praxis-mapper/query/base'
|
|
64
64
|
require 'praxis-mapper/query/sql'
|
65
65
|
require 'praxis-mapper/query/sequel'
|
66
66
|
|
67
|
-
|
68
67
|
require 'praxis-mapper/config_hash'
|
68
|
+
|
69
|
+
require 'praxis-mapper/selector_generator'
|
@@ -10,7 +10,10 @@ module Praxis::Mapper
|
|
10
10
|
class UnsupportedModel < StandardError; end;
|
11
11
|
class UnknownIdentity < StandardError; end;
|
12
12
|
|
13
|
-
attr_reader :unloaded
|
13
|
+
attr_reader :unloaded
|
14
|
+
attr_reader :queries
|
15
|
+
attr_reader :blueprint_cache
|
16
|
+
|
14
17
|
attr_accessor :scope
|
15
18
|
|
16
19
|
class << self
|
@@ -69,9 +72,14 @@ module Praxis::Mapper
|
|
69
72
|
def initialize(scope={})
|
70
73
|
@connection_manager = ConnectionManager.new
|
71
74
|
@scope = scope
|
75
|
+
@selector_generator = Praxis::Mapper::SelectorGenerator.new
|
72
76
|
clear!
|
73
77
|
end
|
74
78
|
|
79
|
+
def selectors
|
80
|
+
@selector_generator.selectors
|
81
|
+
end
|
82
|
+
|
75
83
|
def clear!
|
76
84
|
@rows = Hash.new { |h,k| h[k] = Array.new }
|
77
85
|
|
@@ -315,7 +323,6 @@ module Praxis::Mapper
|
|
315
323
|
@row_keys[model][key].fetch(value) do
|
316
324
|
raise UnloadedRecordException, "Did not load #{model} with #{key} = #{value.inspect}."
|
317
325
|
end
|
318
|
-
|
319
326
|
end
|
320
327
|
|
321
328
|
|
@@ -368,12 +375,12 @@ module Praxis::Mapper
|
|
368
375
|
end
|
369
376
|
else
|
370
377
|
if @row_keys[model].has_key?(key)
|
371
|
-
values.collect do |
|
372
|
-
@row_keys[model][key][
|
378
|
+
values.collect do |v|
|
379
|
+
@row_keys[model][key][v]
|
373
380
|
end.compact
|
374
381
|
else
|
375
|
-
values.each_with_object(Array.new) do |
|
376
|
-
results.push
|
382
|
+
values.each_with_object(Array.new) do |v, results|
|
383
|
+
results.push(*index(model, key, v))
|
377
384
|
end
|
378
385
|
end
|
379
386
|
end
|
@@ -477,8 +484,6 @@ module Praxis::Mapper
|
|
477
484
|
end
|
478
485
|
end
|
479
486
|
|
480
|
-
model = records.first.class
|
481
|
-
|
482
487
|
tracked_associations = if (query = records.first._query)
|
483
488
|
query.tracked_associations.each do |tracked_association|
|
484
489
|
associated_model = tracked_association[:model]
|
@@ -490,7 +495,6 @@ module Praxis::Mapper
|
|
490
495
|
|
491
496
|
tracked_associations.each do |tracked_association|
|
492
497
|
associated_model = tracked_association[:model]
|
493
|
-
association_type = tracked_association[:type]
|
494
498
|
|
495
499
|
association_key, row_keys = stage_for!(tracked_association, records)
|
496
500
|
row_keys.each do |row_key|
|
@@ -549,5 +553,9 @@ module Praxis::Mapper
|
|
549
553
|
QueryStatistics.new(queries)
|
550
554
|
end
|
551
555
|
|
556
|
+
def add_selectors(resource, fields)
|
557
|
+
@selector_generator.add(resource, fields)
|
558
|
+
end
|
559
|
+
|
552
560
|
end
|
553
561
|
end
|
@@ -18,6 +18,7 @@ module Praxis::Mapper
|
|
18
18
|
# @param model [Praxis::Mapper::Model] handle to a Praxis::Mapper model
|
19
19
|
# @param &block [Block] will be instance_eval'ed here
|
20
20
|
def initialize(identity_map, model, &block)
|
21
|
+
|
21
22
|
@identity_map = identity_map
|
22
23
|
@model = model
|
23
24
|
|
@@ -32,9 +33,24 @@ module Praxis::Mapper
|
|
32
33
|
|
33
34
|
@statistics = Hash.new(0) # general-purpose hash
|
34
35
|
|
36
|
+
if (selector = identity_map.selectors[model])
|
37
|
+
self.apply_selector(selector)
|
38
|
+
end
|
39
|
+
|
35
40
|
if block_given?
|
36
41
|
self.instance_eval(&block)
|
37
42
|
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def apply_selector(selector)
|
47
|
+
if selector[:select]
|
48
|
+
self.select(*selector[:select])
|
49
|
+
end
|
50
|
+
|
51
|
+
if selector[:track]
|
52
|
+
self.track(*selector[:track])
|
53
|
+
end
|
38
54
|
end
|
39
55
|
|
40
56
|
# @return handle to configured data store
|
@@ -51,11 +67,20 @@ module Praxis::Mapper
|
|
51
67
|
# @example select(:account_id, "user_id", {"create_time" => :created_at})
|
52
68
|
def select(*fields)
|
53
69
|
if fields.any?
|
54
|
-
@select
|
70
|
+
return @select if @select == true
|
71
|
+
|
72
|
+
if @select.nil?
|
73
|
+
@select = default_select
|
74
|
+
end
|
55
75
|
fields.each do |field|
|
56
76
|
case field
|
57
77
|
when Symbol, String
|
58
|
-
|
78
|
+
if field == :* || field == "*"
|
79
|
+
@select = true
|
80
|
+
break
|
81
|
+
else
|
82
|
+
@select[field] = nil
|
83
|
+
end
|
59
84
|
when Hash
|
60
85
|
field.each do |alias_name, column_name|
|
61
86
|
@select[alias_name] = column_name
|
@@ -69,6 +94,11 @@ module Praxis::Mapper
|
|
69
94
|
end
|
70
95
|
end
|
71
96
|
|
97
|
+
def default_select
|
98
|
+
model.identities.each_with_object({}).each do |identity, hash|
|
99
|
+
hash[identity] = nil
|
100
|
+
end
|
101
|
+
end
|
72
102
|
|
73
103
|
# Gets or sets an SQL-like 'WHERE' clause to this query.
|
74
104
|
#
|
@@ -130,8 +160,8 @@ module Praxis::Mapper
|
|
130
160
|
raise "context #{name.inspect} not found for #{model}"
|
131
161
|
end
|
132
162
|
|
133
|
-
select
|
134
|
-
track
|
163
|
+
select(*spec[:select])
|
164
|
+
track(*spec[:track])
|
135
165
|
end
|
136
166
|
|
137
167
|
|
@@ -161,7 +191,7 @@ module Praxis::Mapper
|
|
161
191
|
rows = []
|
162
192
|
|
163
193
|
original_select = @select
|
164
|
-
self.select
|
194
|
+
self.select(*select.flatten.uniq) if select
|
165
195
|
|
166
196
|
values.each_slice(MULTI_GET_BATCH_SIZE) do |batch|
|
167
197
|
rows += _multi_get(identity, batch)
|
@@ -110,7 +110,7 @@ module Praxis::Mapper
|
|
110
110
|
# @return [String] SQL 'SELECT' clause
|
111
111
|
def select_clause
|
112
112
|
columns = []
|
113
|
-
if select
|
113
|
+
if select && select != true
|
114
114
|
select.each do |alias_name, column_name|
|
115
115
|
if column_name
|
116
116
|
# alias_name is always a String, not a Symbol
|
@@ -29,9 +29,12 @@ module Praxis::Mapper
|
|
29
29
|
|
30
30
|
attr_accessor :record
|
31
31
|
|
32
|
+
@properties = {}
|
33
|
+
|
32
34
|
class << self
|
33
35
|
attr_reader :model_map
|
34
36
|
attr_reader :decorations
|
37
|
+
attr_reader :properties
|
35
38
|
end
|
36
39
|
|
37
40
|
# TODO: also support an attribute of sorts on the versioned resource module. ie, V1::Resources.api_version.
|
@@ -46,10 +49,11 @@ module Praxis::Mapper
|
|
46
49
|
if self.superclass == Praxis::Mapper::Resource
|
47
50
|
@model_map = Hash.new
|
48
51
|
else
|
49
|
-
@model_map = self.superclass.model_map
|
52
|
+
@model_map = self.superclass.model_map
|
50
53
|
end
|
51
54
|
|
52
55
|
@decorations = {}
|
56
|
+
@properties = self.superclass.properties.clone
|
53
57
|
end
|
54
58
|
|
55
59
|
end
|
@@ -69,6 +73,10 @@ module Praxis::Mapper
|
|
69
73
|
self.decorations[name] = Class.new(ResourceDecorator, &block)
|
70
74
|
end
|
71
75
|
|
76
|
+
def self.property(name, **options)
|
77
|
+
self.properties[name] = options
|
78
|
+
end
|
79
|
+
|
72
80
|
def self._finalize!
|
73
81
|
finalize_resource_delegates
|
74
82
|
define_model_accessors
|
@@ -108,10 +116,10 @@ module Praxis::Mapper
|
|
108
116
|
|
109
117
|
def self.define_decorator(name, block)
|
110
118
|
unless self.instance_methods.include?(name)
|
111
|
-
# assume it'll be a regular accessor and create it
|
119
|
+
# assume it'll be a regular accessor and create it
|
112
120
|
self.define_accessor(name)
|
113
121
|
end
|
114
|
-
# alias original method and wrap it
|
122
|
+
# alias original method and wrap it
|
115
123
|
raw_name = "_raw_#{name}"
|
116
124
|
alias_method(raw_name.to_sym, name)
|
117
125
|
|
@@ -137,17 +145,16 @@ module Praxis::Mapper
|
|
137
145
|
end
|
138
146
|
|
139
147
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
+
def self.wrap(records)
|
149
|
+
case records
|
150
|
+
when nil
|
151
|
+
return []
|
152
|
+
when Enumerable
|
153
|
+
return records.compact.collect { |record| self.for_record(record) }
|
154
|
+
else
|
155
|
+
return self.for_record(records)
|
156
|
+
end
|
148
157
|
end
|
149
|
-
end
|
150
|
-
|
151
158
|
|
152
159
|
|
153
160
|
def self.get(condition)
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Praxis::Mapper
|
2
|
+
# Generates a set of selectors given a resource and
|
3
|
+
# list of resource attributes.
|
4
|
+
class SelectorGenerator
|
5
|
+
attr_reader :selectors
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@selectors = Hash.new do |hash, key|
|
9
|
+
hash[key] = {select: Set.new, track: Set.new}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(resource, fields)
|
14
|
+
fields.each do |name, field|
|
15
|
+
map_property(resource, name, field)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def select_all(resource)
|
20
|
+
selectors[resource.model][:select] = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def map_property(resource, name, field)
|
24
|
+
if resource.properties.key?(name)
|
25
|
+
add_property(resource, name)
|
26
|
+
elsif resource.model.associations.key?(name)
|
27
|
+
add_association(resource, name, field)
|
28
|
+
else
|
29
|
+
add_select(resource, name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_select(resource, name)
|
34
|
+
return select_all(resource) if name == :*
|
35
|
+
return if selectors[resource.model][:select] == true
|
36
|
+
|
37
|
+
selectors[resource.model][:select] << name
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_track(resource, name)
|
41
|
+
selectors[resource.model][:track] << name
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_association(resource, name, fields)
|
45
|
+
association = resource.model.associations.fetch(name) do
|
46
|
+
raise "missing association for #{resource} with name #{name}"
|
47
|
+
end
|
48
|
+
associated_resource = resource.model_map[association[:model]]
|
49
|
+
|
50
|
+
case association[:type]
|
51
|
+
when :many_to_one
|
52
|
+
add_track(resource, name)
|
53
|
+
add_select(resource, association[:key])
|
54
|
+
when :one_to_many
|
55
|
+
add_track(resource, name)
|
56
|
+
add_select(associated_resource, association[:key])
|
57
|
+
when :many_to_many
|
58
|
+
head, *tail = association.fetch(:through) do
|
59
|
+
raise "Association #{name} on #{resource.model} must specify the " +
|
60
|
+
"':through' option. "
|
61
|
+
end
|
62
|
+
new_fields = tail.reverse.inject(fields) do |thing, step|
|
63
|
+
{step => thing}
|
64
|
+
end
|
65
|
+
return add_association(resource, head, new_fields)
|
66
|
+
else
|
67
|
+
raise "no select applicable for #{association[:type].inspect}"
|
68
|
+
end
|
69
|
+
|
70
|
+
unless fields == true
|
71
|
+
# recurse into the field
|
72
|
+
add(associated_resource,fields)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_property(resource, name)
|
77
|
+
dependencies = resource.properties[name][:dependencies]
|
78
|
+
return if dependencies.nil?
|
79
|
+
|
80
|
+
dependencies.each do |dependency|
|
81
|
+
apply_dependency(resource, dependency)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def apply_dependency(resource, dependency)
|
86
|
+
case dependency
|
87
|
+
when Symbol
|
88
|
+
map_property(resource, dependency, {})
|
89
|
+
when String
|
90
|
+
head, tail = dependency.split('.').collect(&:to_sym)
|
91
|
+
raise "String dependencies can not be singular" if tail.nil?
|
92
|
+
|
93
|
+
add_association(resource, head, {tail => true})
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
data/praxis-mapper.gemspec
CHANGED
data/spec/factories/all.rb
CHANGED
@@ -2,8 +2,14 @@ FactoryGirl.define do
|
|
2
2
|
|
3
3
|
to_create { |i| i.save }
|
4
4
|
|
5
|
-
factory :
|
6
|
-
name {
|
5
|
+
factory :blog, class: BlogModel do
|
6
|
+
name { /\w+/.gen }
|
7
|
+
owner
|
8
|
+
end
|
9
|
+
|
10
|
+
factory :user, class: UserModel, aliases: [:author, :owner] do
|
11
|
+
first_name { /[:first_name:]/.gen }
|
12
|
+
last_name { /[:last_name]/.gen }
|
7
13
|
email { /[:email:]/.gen }
|
8
14
|
end
|
9
15
|
|
@@ -3,15 +3,16 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
|
3
3
|
describe Praxis::Mapper::Query::Base do
|
4
4
|
let(:scope) { {} }
|
5
5
|
let(:unloaded_ids) { [1, 2, 3] }
|
6
|
+
let(:selectors) { {} }
|
7
|
+
|
6
8
|
let(:connection) { double("connection") }
|
7
|
-
let(:identity_map) { double("identity_map", :scope
|
9
|
+
let(:identity_map) { double("identity_map", scope: scope, get_unloaded: unloaded_ids, selectors: selectors) }
|
8
10
|
|
9
11
|
let(:model) { SimpleModel }
|
10
12
|
|
11
13
|
let(:expected_ids_condition) { "id IN (#{unloaded_ids.join(", ")})" }
|
12
14
|
|
13
|
-
|
14
|
-
subject { query }
|
15
|
+
subject(:query) { Praxis::Mapper::Query::Base.new(identity_map, model) }
|
15
16
|
|
16
17
|
let(:rows) { [
|
17
18
|
{:id => 1, :name => "george jr", :parent_id => 1, :description => "one"},
|
@@ -110,6 +111,11 @@ describe Praxis::Mapper::Query::Base do
|
|
110
111
|
subject.select.should include(:id => nil, :name => nil)
|
111
112
|
end
|
112
113
|
|
114
|
+
it 'adds model identities if necessary' do
|
115
|
+
subject.select :id
|
116
|
+
subject.select.should eq(id: nil, name: nil)
|
117
|
+
end
|
118
|
+
|
113
119
|
it "accepts an array of strings" do
|
114
120
|
subject.select "id", "name"
|
115
121
|
subject.select.should include("id" => nil, "name" => nil)
|
@@ -135,7 +141,6 @@ describe Praxis::Mapper::Query::Base do
|
|
135
141
|
|
136
142
|
context "with symbols for the field definitions" do
|
137
143
|
it "and symbols to specify the field aliases" do
|
138
|
-
definition = {:my_id => :id, :name => :name}
|
139
144
|
subject.select :my_id => :id, :name => :name
|
140
145
|
subject.select.should include :my_id => :id, :name => :name
|
141
146
|
end
|
@@ -163,6 +168,27 @@ describe Praxis::Mapper::Query::Base do
|
|
163
168
|
subject.select.should include(definition)
|
164
169
|
end
|
165
170
|
|
171
|
+
context 'selecting all fields' do
|
172
|
+
it 'maps select :* to true' do
|
173
|
+
subject.select :*
|
174
|
+
subject.select.should be(true)
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'preserves * when selecting specific fields' do
|
178
|
+
subject.select :*
|
179
|
+
subject.select :id
|
180
|
+
subject.select.should be(true)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'overrides any selected fields when * is passed' do
|
184
|
+
subject.select :id
|
185
|
+
subject.select :*
|
186
|
+
subject.select.should be(true)
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
|
166
192
|
end
|
167
193
|
|
168
194
|
context "with no query body" do
|
@@ -310,9 +336,11 @@ describe Praxis::Mapper::Query::Base do
|
|
310
336
|
end
|
311
337
|
end
|
312
338
|
|
313
|
-
context
|
314
|
-
let(:
|
339
|
+
context 'with a selectors from the identity map' do
|
340
|
+
let(:selectors) { {model => {select: [:name, :state], track: [:address]}} }
|
315
341
|
|
342
|
+
its(:select) { should eq(id: nil, name: nil, state: nil) }
|
343
|
+
its(:track) { should eq Set[:address] }
|
316
344
|
end
|
317
345
|
|
318
346
|
end
|
@@ -41,6 +41,17 @@ describe Praxis::Mapper::Query::Sequel do
|
|
41
41
|
query.execute
|
42
42
|
connection.sqls.should eq(["SELECT id, name FROM items WHERE (name = 'something') LIMIT 10"])
|
43
43
|
end
|
44
|
+
|
45
|
+
context 'with select :* in query' do
|
46
|
+
it 'runs the correct sql with "SELECT *"' do
|
47
|
+
query.select :*
|
48
|
+
connection.sqls.should be_empty
|
49
|
+
query.execute
|
50
|
+
connection.sqls.should eq(["SELECT * FROM items WHERE (name = 'something') LIMIT 10"])
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
44
55
|
end
|
45
56
|
|
46
57
|
|
@@ -91,6 +91,21 @@ describe Praxis::Mapper::Query::Sql do
|
|
91
91
|
|
92
92
|
end
|
93
93
|
|
94
|
+
context 'selecting all fields' do
|
95
|
+
subject(:query) do
|
96
|
+
Praxis::Mapper::Query::Sql.new(identity_map, SimpleModel) do
|
97
|
+
select :id, :name
|
98
|
+
where "deployment_id=2"
|
99
|
+
track :parent
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'generates proper select clause if select is true' do
|
104
|
+
query.select :*
|
105
|
+
query.select_clause.should eq 'SELECT *'
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
94
109
|
context '#_multi_get' do
|
95
110
|
|
96
111
|
let(:ids) { [1, 2, 3] }
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
3
|
describe Praxis::Mapper::Resource do
|
4
|
-
|
5
4
|
let(:parent_record) { ParentModel.new(id: 100, name: 'george sr') }
|
6
5
|
let(:parent_records) { [ParentModel.new(id: 101, name: "georgia"),ParentModel.new(id: 102, name: 'georgina')] }
|
7
6
|
let(:record) { SimpleModel.new(id: 103, name: 'george xvi') }
|
@@ -15,8 +14,24 @@ describe Praxis::Mapper::Resource do
|
|
15
14
|
end
|
16
15
|
|
17
16
|
context 'configuration' do
|
18
|
-
subject { SimpleResource }
|
17
|
+
subject(:resource) { SimpleResource }
|
19
18
|
its(:model) { should == model }
|
19
|
+
|
20
|
+
context 'properties' do
|
21
|
+
subject(:properties) { resource.properties }
|
22
|
+
|
23
|
+
it 'includes directly-set properties' do
|
24
|
+
properties[:other_resource].should eq(dependencies: [:other_model])
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'inherits from a superclass' do
|
28
|
+
properties[:href].should eq(dependencies: [:id])
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'properly overrides a property from the parent' do
|
32
|
+
properties[:name].should eq(dependencies: [:simple_name])
|
33
|
+
end
|
34
|
+
end
|
20
35
|
end
|
21
36
|
|
22
37
|
context 'retrieving resources' do
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Praxis::Mapper::SelectorGenerator do
|
4
|
+
let(:properties) { {} }
|
5
|
+
let(:resource) { BlogResource }
|
6
|
+
subject(:generator) {Praxis::Mapper::SelectorGenerator.new }
|
7
|
+
|
8
|
+
before do
|
9
|
+
generator.add(resource,properties)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:expected_selectors) { {} }
|
13
|
+
|
14
|
+
context 'for a simple field' do
|
15
|
+
let(:properties) { {id: true} }
|
16
|
+
let(:expected_selectors) do
|
17
|
+
{
|
18
|
+
BlogModel => {
|
19
|
+
select: Set.new([:id]),
|
20
|
+
track: Set.new()
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'generates the correct set of selectors' do
|
26
|
+
generator.selectors.should eq expected_selectors
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'for a simple property' do
|
31
|
+
let(:properties) { {display_name: true} }
|
32
|
+
let(:expected_selectors) do
|
33
|
+
{
|
34
|
+
BlogModel => {
|
35
|
+
select: Set.new([:name]),
|
36
|
+
track: Set.new()
|
37
|
+
}
|
38
|
+
}
|
39
|
+
end
|
40
|
+
it 'generates the correct set of selectors' do
|
41
|
+
generator.selectors.should eq expected_selectors
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'for an association' do
|
46
|
+
let(:properties) { {owner: true} }
|
47
|
+
let(:expected_selectors) do
|
48
|
+
{
|
49
|
+
BlogModel => {
|
50
|
+
select: Set.new([:owner_id]),
|
51
|
+
track: Set.new([:owner])
|
52
|
+
}
|
53
|
+
}
|
54
|
+
end
|
55
|
+
it 'generates the correct set of selectors' do
|
56
|
+
generator.selectors.should eq expected_selectors
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'that is many_to_many' do
|
60
|
+
let(:properties) { {commented_posts: true} }
|
61
|
+
let(:resource) { UserResource }
|
62
|
+
let(:expected_selectors) do
|
63
|
+
{
|
64
|
+
CommentModel => {
|
65
|
+
select: Set.new([:author_id, :post_id]),
|
66
|
+
track: Set.new([:post])
|
67
|
+
},
|
68
|
+
UserModel => {
|
69
|
+
select: Set.new([]),
|
70
|
+
track: Set.new([:comments])
|
71
|
+
}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
it 'generates the correct set of selectors' do
|
75
|
+
generator.selectors.should eq expected_selectors
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'for a property that specifies a field from an association' do
|
81
|
+
let(:properties) { {owner_email: true} }
|
82
|
+
let(:expected_selectors) do
|
83
|
+
{
|
84
|
+
BlogModel => {
|
85
|
+
select: Set.new([:owner_id]),
|
86
|
+
track: Set.new([:owner])
|
87
|
+
},
|
88
|
+
UserModel => {
|
89
|
+
select: Set.new([:email]),
|
90
|
+
track: Set.new()
|
91
|
+
}
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'generates the correct set of selectors' do
|
96
|
+
generator.selectors.should eq expected_selectors
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'for a simple property that requires all fields' do
|
101
|
+
let(:properties) { {everything: true} }
|
102
|
+
let(:expected_selectors) do
|
103
|
+
{
|
104
|
+
BlogModel => {
|
105
|
+
select: true,
|
106
|
+
track: Set.new()
|
107
|
+
}
|
108
|
+
}
|
109
|
+
end
|
110
|
+
it 'generates the correct set of selectors' do
|
111
|
+
generator.selectors.should eq expected_selectors
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'for property that uses an associated property' do
|
116
|
+
let(:properties) { {owner_full_name: true} }
|
117
|
+
let(:expected_selectors) do
|
118
|
+
{
|
119
|
+
BlogModel => {
|
120
|
+
select: Set.new([:owner_id]),
|
121
|
+
track: Set.new([:owner])
|
122
|
+
},
|
123
|
+
UserModel => {
|
124
|
+
select: Set.new([:first_name, :last_name]),
|
125
|
+
track: Set.new()
|
126
|
+
}
|
127
|
+
}
|
128
|
+
end
|
129
|
+
it 'generates the correct set of selectors' do
|
130
|
+
generator.selectors.should eq expected_selectors
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
context 'for a property that requires all fields from an association' do
|
136
|
+
let(:properties) { {everything_from_owner: true} }
|
137
|
+
let(:expected_selectors) do
|
138
|
+
{
|
139
|
+
BlogModel => {
|
140
|
+
select: Set.new([:owner_id]),
|
141
|
+
track: Set.new([:owner])
|
142
|
+
},
|
143
|
+
UserModel => {
|
144
|
+
select: true,
|
145
|
+
track: Set.new()
|
146
|
+
}
|
147
|
+
}
|
148
|
+
end
|
149
|
+
it 'generates the correct set of selectors' do
|
150
|
+
generator.selectors.should eq expected_selectors
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'with a property that groups multiple fields' do
|
155
|
+
let(:properties) { {owner_full_name: {first: true}} }
|
156
|
+
let(:expected_selectors) do
|
157
|
+
{
|
158
|
+
BlogModel => {
|
159
|
+
select: Set.new([:owner_id]),
|
160
|
+
track: Set.new([:owner])
|
161
|
+
},
|
162
|
+
UserModel => {
|
163
|
+
select: Set.new([:first_name, :last_name]),
|
164
|
+
track: Set.new()
|
165
|
+
}
|
166
|
+
}
|
167
|
+
end
|
168
|
+
it 'generates selectors that ignore any unapplicable subrefinements' do
|
169
|
+
generator.selectors.should eq expected_selectors
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'for a property with no dependencies' do
|
174
|
+
let(:properties) { {id: true, kind: true} }
|
175
|
+
let(:expected_selectors) do
|
176
|
+
{
|
177
|
+
BlogModel => {
|
178
|
+
select: Set.new([:id]),
|
179
|
+
track: Set.new()
|
180
|
+
}
|
181
|
+
}
|
182
|
+
end
|
183
|
+
it 'generates the correct set of selectors' do
|
184
|
+
generator.selectors.should eq expected_selectors
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'with large set of properties' do
|
189
|
+
|
190
|
+
let(:properties) do
|
191
|
+
{
|
192
|
+
display_name: true,
|
193
|
+
owner: {
|
194
|
+
id: true,
|
195
|
+
full_name: true,
|
196
|
+
blogs_summary: {href: true, size: true},
|
197
|
+
main_blog: {id: true},
|
198
|
+
},
|
199
|
+
administrator: {id: true, full_name: true}
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
let(:expected_selectors) do
|
204
|
+
{
|
205
|
+
BlogModel=> {
|
206
|
+
select: Set.new([:id, :name, :owner_id, :administrator_id]),
|
207
|
+
track: Set.new([:owner, :administrator])
|
208
|
+
},
|
209
|
+
UserModel=> {
|
210
|
+
select: Set.new([:id, :first_name, :last_name, :main_blog_id]),
|
211
|
+
track: Set.new([:blogs, :main_blog])
|
212
|
+
}
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'generates the correct set of selectors' do
|
217
|
+
generator.selectors.should eq(expected_selectors)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear!
|
3
|
+
|
1
4
|
Encoding.default_external = Encoding::UTF_8
|
2
5
|
|
3
6
|
require 'rubygems'
|
@@ -19,9 +22,11 @@ require 'praxis-mapper'
|
|
19
22
|
require 'active_support/core_ext/kernel'
|
20
23
|
|
21
24
|
|
22
|
-
require_relative 'support/spec_sequel_models'
|
23
25
|
require_relative 'support/spec_models'
|
26
|
+
require_relative 'support/spec_sequel_models'
|
24
27
|
require_relative 'support/spec_resources'
|
28
|
+
require_relative 'support/spec_sequel_resources'
|
29
|
+
|
25
30
|
require_relative 'spec_fixtures'
|
26
31
|
|
27
32
|
require 'praxis-mapper/support'
|
@@ -43,7 +48,7 @@ RSpec.configure do |config|
|
|
43
48
|
]
|
44
49
|
|
45
50
|
config.include FactoryGirl::Syntax::Methods
|
46
|
-
|
51
|
+
|
47
52
|
config.before(:suite) do
|
48
53
|
FactoryGirl.find_definitions
|
49
54
|
Sequel::Model.db.transaction do
|
@@ -5,6 +5,7 @@ class BaseResource < Praxis::Mapper::Resource
|
|
5
5
|
base_href + "/#{self.class.collection_name}/#{self.id}"
|
6
6
|
end
|
7
7
|
|
8
|
+
property :href, dependencies: [:id]
|
8
9
|
end
|
9
10
|
|
10
11
|
class CompositeIdResource < BaseResource
|
@@ -28,6 +29,9 @@ class SimpleResource < BaseResource
|
|
28
29
|
self.other_model
|
29
30
|
end
|
30
31
|
|
32
|
+
property :other_resource, dependencies: [:other_model]
|
33
|
+
|
34
|
+
property :name, dependencies: [:simple_name]
|
31
35
|
end
|
32
36
|
|
33
37
|
class SimplerResource < BaseResource
|
@@ -63,8 +67,20 @@ end
|
|
63
67
|
class AddressResource < BaseResource
|
64
68
|
model AddressModel
|
65
69
|
|
66
|
-
|
70
|
+
|
71
|
+
def href
|
67
72
|
"/addresses/#{self.id}"
|
68
73
|
end
|
69
|
-
|
74
|
+
property :href, dependencies: [:id]
|
70
75
|
|
76
|
+
def owner_name
|
77
|
+
self.owner.name
|
78
|
+
end
|
79
|
+
property :owner_name, dependencies: ['owner.name']
|
80
|
+
|
81
|
+
def resident_count
|
82
|
+
self.residents.size
|
83
|
+
end
|
84
|
+
property :resident_count, dependencies: [:residents]
|
85
|
+
|
86
|
+
end
|
@@ -4,10 +4,20 @@
|
|
4
4
|
|
5
5
|
DB = Sequel.sqlite
|
6
6
|
|
7
|
-
DB.create_table! :
|
7
|
+
DB.create_table! :blogs do
|
8
8
|
primary_key :id
|
9
|
+
Integer :owner_id
|
10
|
+
Integer :administrator_id
|
9
11
|
|
10
12
|
String :name
|
13
|
+
end
|
14
|
+
|
15
|
+
DB.create_table! :users do
|
16
|
+
primary_key :id
|
17
|
+
Integer :main_blog_id
|
18
|
+
|
19
|
+
String :first_name
|
20
|
+
String :last_name
|
11
21
|
String :email
|
12
22
|
end
|
13
23
|
|
@@ -54,16 +64,29 @@ Praxis::Mapper::ConnectionManager.setup do
|
|
54
64
|
end
|
55
65
|
end
|
56
66
|
|
67
|
+
class BlogModel < Sequel::Model(:blogs)
|
68
|
+
include Praxis::Mapper::SequelCompat
|
69
|
+
many_to_one :owner, class: 'UserModel', key: :owner_id
|
70
|
+
many_to_one :administrator, class: 'UserModel', key: :administrator_id
|
71
|
+
|
72
|
+
end
|
73
|
+
|
57
74
|
class UserModel < Sequel::Model(:users)
|
58
75
|
include Praxis::Mapper::SequelCompat
|
59
76
|
|
60
77
|
repository_name :sequel
|
61
78
|
|
62
|
-
one_to_many :posts, class: 'PostModel'
|
63
|
-
one_to_many :comments, class: 'CommentModel'
|
79
|
+
one_to_many :posts, class: 'PostModel', key: :post_id
|
80
|
+
one_to_many :comments, class: 'CommentModel', key: :author_id
|
81
|
+
one_to_many :blogs, class: 'BlogModel', key: :owner_id
|
82
|
+
|
83
|
+
one_to_many :administered_blogs, class: 'BlogModel', key: :administrator_id
|
64
84
|
|
65
85
|
many_to_many :commented_posts, class: 'PostModel',
|
66
|
-
join_table: 'comments', join_model: 'CommentModel'
|
86
|
+
join_table: 'comments', join_model: 'CommentModel',
|
87
|
+
through: [:comments, :post]
|
88
|
+
|
89
|
+
many_to_one :main_blog, class: 'BlogModel', key: :main_blog_id
|
67
90
|
end
|
68
91
|
|
69
92
|
class PostModel < Sequel::Model(:posts)
|
@@ -89,7 +112,7 @@ end
|
|
89
112
|
|
90
113
|
class CompositeIdSequelModel < Sequel::Model(:composite_ids)
|
91
114
|
include Praxis::Mapper::SequelCompat
|
92
|
-
|
115
|
+
|
93
116
|
repository_name :sequel
|
94
117
|
|
95
118
|
set_primary_key [:id, :type]
|
@@ -101,9 +124,9 @@ end
|
|
101
124
|
|
102
125
|
class OtherSequelModel < Sequel::Model(:others)
|
103
126
|
include Praxis::Mapper::SequelCompat
|
104
|
-
|
127
|
+
|
105
128
|
repository_name :sequel
|
106
|
-
|
129
|
+
|
107
130
|
many_to_one :composite,
|
108
131
|
class: 'CompositeIdSequelModel',
|
109
132
|
key: [:composite_id, :composite_type]
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class BlogResource < BaseResource
|
2
|
+
property :display_name, dependencies: [:name]
|
3
|
+
property :owner_email, dependencies: ['owner.email']
|
4
|
+
property :owner_full_name, dependencies: ['owner.full_name']
|
5
|
+
property :everything, dependencies: [:*]
|
6
|
+
property :everything_from_owner, dependencies: ['owner.*']
|
7
|
+
property :kind, dependencies: nil
|
8
|
+
|
9
|
+
model BlogModel
|
10
|
+
|
11
|
+
def kind
|
12
|
+
self.class.name.demodulize
|
13
|
+
end
|
14
|
+
|
15
|
+
def display_name
|
16
|
+
self.name
|
17
|
+
end
|
18
|
+
|
19
|
+
def owner_email
|
20
|
+
self.owner.email
|
21
|
+
end
|
22
|
+
|
23
|
+
def owner_full_name
|
24
|
+
self.owner.full_name
|
25
|
+
end
|
26
|
+
|
27
|
+
def everything
|
28
|
+
'everything'
|
29
|
+
end
|
30
|
+
|
31
|
+
def everything_from_owner
|
32
|
+
'everything_from_owner'
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class UserResource < BaseResource
|
38
|
+
model UserModel
|
39
|
+
|
40
|
+
def full_name
|
41
|
+
"#{first_name} #{last_name}"
|
42
|
+
end
|
43
|
+
property :full_name, dependencies: [:first_name, :last_name]
|
44
|
+
|
45
|
+
def blogs_summary
|
46
|
+
{
|
47
|
+
href: "www.foo.com/#{self.id}",
|
48
|
+
size: blogs.size
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
property :blogs_summary, dependencies: [:id, :blogs]
|
53
|
+
end
|
54
|
+
|
55
|
+
class CommentResource < BaseResource
|
56
|
+
model CommentModel
|
57
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: praxis-mapper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: '4.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josep M. Blanquer
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-10-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: randexp
|
@@ -235,6 +235,20 @@ dependencies:
|
|
235
235
|
- - ">="
|
236
236
|
- !ruby/object:Gem::Version
|
237
237
|
version: '0'
|
238
|
+
- !ruby/object:Gem::Dependency
|
239
|
+
name: coveralls
|
240
|
+
requirement: !ruby/object:Gem::Requirement
|
241
|
+
requirements:
|
242
|
+
- - ">="
|
243
|
+
- !ruby/object:Gem::Version
|
244
|
+
version: '0'
|
245
|
+
type: :development
|
246
|
+
prerelease: false
|
247
|
+
version_requirements: !ruby/object:Gem::Requirement
|
248
|
+
requirements:
|
249
|
+
- - ">="
|
250
|
+
- !ruby/object:Gem::Version
|
251
|
+
version: '0'
|
238
252
|
description:
|
239
253
|
email:
|
240
254
|
- blanquer@gmail.com
|
@@ -267,6 +281,7 @@ files:
|
|
267
281
|
- lib/praxis-mapper/query/sql.rb
|
268
282
|
- lib/praxis-mapper/query_statistics.rb
|
269
283
|
- lib/praxis-mapper/resource.rb
|
284
|
+
- lib/praxis-mapper/selector_generator.rb
|
270
285
|
- lib/praxis-mapper/sequel_compat.rb
|
271
286
|
- lib/praxis-mapper/support.rb
|
272
287
|
- lib/praxis-mapper/support/factory_girl.rb
|
@@ -289,6 +304,7 @@ files:
|
|
289
304
|
- spec/praxis-mapper/query/sequel_spec.rb
|
290
305
|
- spec/praxis-mapper/query/sql_spec.rb
|
291
306
|
- spec/praxis-mapper/resource_spec.rb
|
307
|
+
- spec/praxis-mapper/selector_generator_spec.rb
|
292
308
|
- spec/praxis-mapper/sequel_compat_spec.rb
|
293
309
|
- spec/praxis_mapper_spec.rb
|
294
310
|
- spec/spec_fixtures.rb
|
@@ -296,6 +312,7 @@ files:
|
|
296
312
|
- spec/support/spec_models.rb
|
297
313
|
- spec/support/spec_resources.rb
|
298
314
|
- spec/support/spec_sequel_models.rb
|
315
|
+
- spec/support/spec_sequel_resources.rb
|
299
316
|
homepage: https://github.com/rightscale/praxis-mapper
|
300
317
|
licenses:
|
301
318
|
- MIT
|
@@ -334,6 +351,7 @@ test_files:
|
|
334
351
|
- spec/praxis-mapper/query/sequel_spec.rb
|
335
352
|
- spec/praxis-mapper/query/sql_spec.rb
|
336
353
|
- spec/praxis-mapper/resource_spec.rb
|
354
|
+
- spec/praxis-mapper/selector_generator_spec.rb
|
337
355
|
- spec/praxis-mapper/sequel_compat_spec.rb
|
338
356
|
- spec/praxis_mapper_spec.rb
|
339
357
|
- spec/spec_fixtures.rb
|
@@ -341,4 +359,5 @@ test_files:
|
|
341
359
|
- spec/support/spec_models.rb
|
342
360
|
- spec/support/spec_resources.rb
|
343
361
|
- spec/support/spec_sequel_models.rb
|
362
|
+
- spec/support/spec_sequel_resources.rb
|
344
363
|
has_rdoc:
|