praxis-mapper 4.1.2 → 4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|