praxis-mapper 4.2 → 4.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ff56d63e3412602d1542aefa5a0ddeb2c6f053d
4
- data.tar.gz: e55ac6835ca3be6495ccb699e72197502812664f
3
+ metadata.gz: cc62b721f14cfb74cf3915d1123dc9970b9033ff
4
+ data.tar.gz: bc4dda08a87b9d20ffd0be68d963406ea55b3972
5
5
  SHA512:
6
- metadata.gz: 54bb23566ca0385a869114087e2fd91420f3e1b7e5ce890672dbbdb649d4d0fe7f63bf5b947e4d7c2af781996363d185e3ee0de160836e70d265bdd4b6a8673e
7
- data.tar.gz: 088259294b44ba826033c1a39e279899be35f0c482827f0cc6deac8aa3282dd6323160ff0877b68d15b2a7ebe92e3df8c38ffc985ad050de775daee9b4388ba4
6
+ metadata.gz: 090b9a57b17a4abaea12a5c58c8540d8c940c323b3ed32a183922353c155279d58561e2454a0c84591d6b18522fd28e4ec15b964c24b6d503c77c3edb93e46cb
7
+ data.tar.gz: a8425a849b5456eeff9cfa36e954a4f89dcdf0ae0d361b80b8411b29328d4d68fbd19c83b42da95c25ffe56de6b8950670c6258afe50a111cd447ac2acafa7f9
data/.travis.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  rvm:
4
- - 2.1.2
5
- - 2.2.2
4
+ - 2.2.5
5
+ - 2.3.1
6
6
  script: bundle exec rspec spec
data/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## next
4
4
 
5
+ ## 4.3
6
+
7
+ * Added a `:through` option to `Resource.property` to specify that the property
8
+ effectively traverses the association(s) given so that it may continue to
9
+ process additional subfield selections.
10
+ * Fixed `SystemStackError` from `IdentityMap#add_selectors` if the provided
11
+ field hashes are self-referencing. I.e. `{author: {posts: {author: {...}}}}`
12
+ * Fixed `SystemStackError` from `SelectorGenerator` for circular `Resource.property` definitions like `property :foo, dependencies: [:foo, :bar]`.
13
+ * Fixed `Query::Sequel` not working with raw SQL queries.
14
+ * Fixed SQL logging in `Query::Sequel` for query execution passing a dataset.
15
+
5
16
  ## 4.2
6
17
 
7
18
  * Added `Resource.property` to specify the fields and associations from the
@@ -52,7 +52,7 @@ module Praxis::Mapper
52
52
  #
53
53
  # @return [Array] result-set
54
54
  def _execute(ds=nil)
55
- Praxis::Mapper.logger.debug "SQL:\n#{self.describe}\n"
55
+ Praxis::Mapper.logger.debug "SQL:\n#{self.describe(ds)}\n"
56
56
  self.statistics[:datastore_interactions] += 1
57
57
  start_time = Time.now
58
58
 
@@ -60,7 +60,7 @@ module Praxis::Mapper
60
60
  unless ds.nil?
61
61
  warn 'WARNING: Query::Sequel#_execute ignoring passed dataset due to previously-specified raw SQL'
62
62
  end
63
- connection.run(@raw_query).to_a
63
+ connection[@raw_query].to_a
64
64
  else
65
65
  (ds || self.dataset).to_a
66
66
  end
@@ -70,8 +70,8 @@ module Praxis::Mapper
70
70
  end
71
71
 
72
72
  # @see #sql
73
- def describe
74
- self.sql
73
+ def describe(ds=nil)
74
+ (ds || self).sql
75
75
  end
76
76
 
77
77
  # Constructs a raw SQL statement.
@@ -8,9 +8,15 @@ module Praxis::Mapper
8
8
  @selectors = Hash.new do |hash, key|
9
9
  hash[key] = {select: Set.new, track: Set.new}
10
10
  end
11
+ @seen = Hash.new do |hash, resource|
12
+ hash[resource] = Set.new
13
+ end
11
14
  end
12
15
 
13
16
  def add(resource, fields)
17
+ return if @seen[resource].include? fields
18
+ @seen[resource] << fields
19
+
14
20
  fields.each do |name, field|
15
21
  map_property(resource, name, field)
16
22
  end
@@ -20,11 +26,11 @@ module Praxis::Mapper
20
26
  selectors[resource.model][:select] = true
21
27
  end
22
28
 
23
- def map_property(resource, name, field)
29
+ def map_property(resource, name, fields)
24
30
  if resource.properties.key?(name)
25
- add_property(resource, name)
31
+ add_property(resource, name, fields)
26
32
  elsif resource.model.associations.key?(name)
27
- add_association(resource, name, field)
33
+ add_association(resource, name, fields)
28
34
  else
29
35
  add_select(resource, name)
30
36
  end
@@ -73,13 +79,27 @@ module Praxis::Mapper
73
79
  end
74
80
  end
75
81
 
76
- def add_property(resource, name)
82
+ def add_property(resource, name, fields)
77
83
  dependencies = resource.properties[name][:dependencies]
78
- return if dependencies.nil?
84
+ if dependencies
85
+ dependencies.each do |dependency|
86
+ # if dependency includes the name, then map it directly as the field
87
+ if dependency == name
88
+ add_select(resource, name)
89
+ else
90
+ apply_dependency(resource, dependency)
91
+ end
92
+ end
93
+ end
94
+
95
+ head, *tail = resource.properties[name][:through]
96
+ return if head.nil?
79
97
 
80
- dependencies.each do |dependency|
81
- apply_dependency(resource, dependency)
98
+ new_fields = tail.reverse.inject(fields) do |thing, step|
99
+ {step => thing}
82
100
  end
101
+
102
+ add_association(resource, head, new_fields)
83
103
  end
84
104
 
85
105
  def apply_dependency(resource, dependency)
@@ -1,5 +1,5 @@
1
1
  module Praxis
2
2
  module Mapper
3
- VERSION = "4.2"
3
+ VERSION = "4.3"
4
4
  end
5
5
  end
@@ -16,6 +16,7 @@ FactoryGirl.define do
16
16
  factory :post, class: PostModel do
17
17
  title { /\w+/.gen }
18
18
  body { /\w+/.gen }
19
+ created_at { DateTime.now - rand(100) }
19
20
  author
20
21
  end
21
22
 
@@ -151,21 +151,59 @@ describe Praxis::Mapper::SelectorGenerator do
151
151
  end
152
152
  end
153
153
 
154
- context 'with a property that groups multiple fields' do
155
- let(:properties) { {owner_full_name: {first: true}} }
154
+ context 'using a property that specifies a :through option' do
155
+ let(:properties) { {recent_posts: {author: {full_name: true}}} }
156
+ let(:resource) { UserResource }
156
157
  let(:expected_selectors) do
157
158
  {
158
- BlogModel => {
159
- select: Set.new([:owner_id]),
160
- track: Set.new([:owner])
159
+ PostModel => {
160
+ select: Set.new([:author_id, :created_at]),
161
+ track: Set.new([:author])
161
162
  },
162
163
  UserModel => {
163
164
  select: Set.new([:first_name, :last_name]),
164
- track: Set.new()
165
+ track: Set.new([:posts])
165
166
  }
166
167
  }
167
168
  end
168
- it 'generates selectors that ignore any unapplicable subrefinements' do
169
+ it 'generates the correct set of selectors' do
170
+ generator.selectors.should eq expected_selectors
171
+ end
172
+ end
173
+
174
+ context 'with a property with a circular definition (ie, includes its own field)' do
175
+ let(:resource) { PostResource }
176
+
177
+ let(:properties) { {id: true, slug: true} }
178
+ let(:expected_selectors) do
179
+ {
180
+ PostModel => {
181
+ select: Set.new([:id, :slug, :title]),
182
+ track: Set.new
183
+ }
184
+ }
185
+ end
186
+ it 'generates the correct set of selectors' do
187
+ generator.selectors.should eq expected_selectors
188
+ end
189
+ end
190
+
191
+ context 'with a property without the :through option' do
192
+ let(:resource) { UserResource }
193
+ let(:properties) { {blogs_summary: {size: true}} }
194
+ let(:expected_selectors) do
195
+ {
196
+ BlogModel => {
197
+ select: Set.new([:owner_id]),
198
+ track: Set.new()
199
+ },
200
+ UserModel => {
201
+ select: Set.new([:id]),
202
+ track: Set.new([:blogs])
203
+ }
204
+ }
205
+ end
206
+ it 'ignores any subsequent fields when generating selectors' do
169
207
  generator.selectors.should eq expected_selectors
170
208
  end
171
209
  end
@@ -216,6 +254,7 @@ describe Praxis::Mapper::SelectorGenerator do
216
254
  it 'generates the correct set of selectors' do
217
255
  generator.selectors.should eq(expected_selectors)
218
256
  end
257
+
219
258
  end
220
259
 
221
260
  end
@@ -27,7 +27,10 @@ DB.create_table! :posts do
27
27
  Integer :author_id
28
28
 
29
29
  String :title
30
+ String :slug
30
31
  String :body
32
+
33
+ DateTime :created_at
31
34
  end
32
35
 
33
36
 
@@ -76,7 +79,7 @@ class UserModel < Sequel::Model(:users)
76
79
 
77
80
  repository_name :sequel
78
81
 
79
- one_to_many :posts, class: 'PostModel', key: :post_id
82
+ one_to_many :posts, class: 'PostModel', key: :author_id
80
83
  one_to_many :comments, class: 'CommentModel', key: :author_id
81
84
  one_to_many :blogs, class: 'BlogModel', key: :owner_id
82
85
 
@@ -37,10 +37,16 @@ end
37
37
  class UserResource < BaseResource
38
38
  model UserModel
39
39
 
40
+ property :full_name, dependencies: [:first_name, :last_name]
41
+ property :blogs_summary, dependencies: [:id, :blogs]
42
+
43
+ property :recent_posts, dependencies: ['posts.created_at'],
44
+ through: [:posts]
45
+
46
+
40
47
  def full_name
41
48
  "#{first_name} #{last_name}"
42
49
  end
43
- property :full_name, dependencies: [:first_name, :last_name]
44
50
 
45
51
  def blogs_summary
46
52
  {
@@ -49,9 +55,25 @@ class UserResource < BaseResource
49
55
  }
50
56
  end
51
57
 
52
- property :blogs_summary, dependencies: [:id, :blogs]
58
+ def recent_posts
59
+ posts.sort_by(&:created_at).reverse[2]
60
+ end
61
+
53
62
  end
54
63
 
55
64
  class CommentResource < BaseResource
56
65
  model CommentModel
57
66
  end
67
+
68
+ class PostResource < BaseResource
69
+ model PostModel
70
+
71
+ property :slug, dependencies: [:slug, :title]
72
+
73
+ # generate default slug from title if one wasn't set in the db
74
+ def slug
75
+ return record.slug if record.slug
76
+ record.title.gsub(" ", "-").downcase
77
+ end
78
+
79
+ 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.2'
4
+ version: '4.3'
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-10-19 00:00:00.000000000 Z
12
+ date: 2016-08-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: randexp
@@ -333,7 +333,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
333
333
  version: '0'
334
334
  requirements: []
335
335
  rubyforge_project:
336
- rubygems_version: 2.2.2
336
+ rubygems_version: 2.5.1
337
337
  signing_key:
338
338
  specification_version: 4
339
339
  summary: A multi-datastore library designed for efficiency in loading large datasets.