remote_record 0.5.0 → 0.6.0

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
  SHA256:
3
- metadata.gz: 4bf471bd03525a3f6dfdfedc1d88e2681264c85e98447607259fec7a94d56ddd
4
- data.tar.gz: 076113352a325ee80c09cca58a00cdd8e703a584392319f008393fdae90ddbc2
3
+ metadata.gz: 8b0ea2bc5ed71919334a2a22d53f70a38ad06b116c7e76679e76bb0aeeb9ec9c
4
+ data.tar.gz: 4874762ea7e61fc354e99faa8d4cf6e79ba9b1451dcf5eefb029e2c173911b13
5
5
  SHA512:
6
- metadata.gz: 3dc11719e885adf6220ef9b351b1605943ce25220c3e95ee0ee8820175685783777ed7f1bd43f22249f83cb95e8031509e2b85f8be04b65dc33aaab0c4fdca22
7
- data.tar.gz: 478180ddc5b281c7a9873313608ad62368f9c947125fd1095b1c61a88e9cce5147637ca1391c801a0af1c2f53e4ac34418256a38563893f926a4f55ee37d62b4
6
+ metadata.gz: 603cbf7ecd366f2e3ba00c0be7d1ef793a484948a4940ddae511ab7e99583620d405cee4068958099dcf44aec52bdcc0df612771fc9b9243db530b643d5f1220
7
+ data.tar.gz: d5edee74efd6afd30db89d237674c5deb6953279636accc3e22b28b95e866f769631dc4a13fa70b30982e8f1422f2707bd9ccac2b0b1bdca2de5824e21e9c732
data/README.md CHANGED
@@ -124,6 +124,105 @@ caching by way of expiry or ETags, I recommend using `faraday-http-cache` for
124
124
  your clients and setting `memoize` to `false`. Remote Record will eventually
125
125
  gain support for caching.
126
126
 
127
+ ### `remote_all` and `remote_where`
128
+
129
+ If you're able to fetch multiple records at once from the API, implement the
130
+ `self.all` method on your remote record class. This should return an array of
131
+ hashes that can be used to initialize a set of references.
132
+
133
+ This can optionally take a block
134
+ for authorization - note that it won't use the auth you've configured and that
135
+ you'll always have to supply that inline. For example:
136
+
137
+ ```ruby
138
+ module RemoteRecord
139
+ module GitHub
140
+ # :nodoc:
141
+ class User < RemoteRecord::Base
142
+ def get
143
+ client.user(remote_resource_id)
144
+ end
145
+
146
+ def self.all
147
+ Octokit::Client.new(access_token: yield).users
148
+ end
149
+
150
+ private
151
+
152
+ def client
153
+ Octokit::Client.new(access_token: authorization)
154
+ end
155
+ end
156
+ end
157
+ end
158
+ ```
159
+
160
+ Now you can call `remote_all` on remote reference classes that use
161
+ `RemoteRecord::GitHub::User`, like this:
162
+
163
+ ```ruby
164
+ GitHub::UserReference.remote_all { GITHUB_PERSONAL_ACCESS_TOKEN }
165
+ ```
166
+
167
+ `remote_where` works in the same way, but with a parameter:
168
+
169
+ ```ruby
170
+ module RemoteRecord
171
+ module GitHub
172
+ # :nodoc:
173
+ class User < RemoteRecord::Base
174
+ def get
175
+ client.user(remote_resource_id)
176
+ end
177
+
178
+ def self.all
179
+ Octokit::Client.new(access_token: yield).users
180
+ end
181
+
182
+ def self.where(query)
183
+ Octokit::Client.new(access_token: yield).search_users(query)
184
+ end
185
+
186
+ private
187
+
188
+ def client
189
+ Octokit::Client.new(access_token: authorization)
190
+ end
191
+ end
192
+ end
193
+ end
194
+ ```
195
+
196
+ Now you can call `remote_where` on remote reference classes that use
197
+ `RemoteRecord::GitHub::User`, like this:
198
+
199
+ ```ruby
200
+ GitHub::UserReference.remote_where('q=tom+repos:%3E42+followers:%3E1000') { GITHUB_PERSONAL_ACCESS_TOKEN }
201
+ ```
202
+
203
+ It's recommended that you include something in `self.where` to filter incoming
204
+ params. Ideally, you want to expose an interface that's as ActiveRecord-like as
205
+ possible, e.g.:
206
+
207
+ ```ruby
208
+ GitHub::UserReference.remote_where(q: 'tom', repos: '>42', followers: '>1000') { GITHUB_PERSONAL_ACCESS_TOKEN }
209
+ ```
210
+
211
+ It's recommended that you write a `Transformer` to do this. Check out
212
+ `RemoteRecord::Transformers::SnakeCase` for an example.
213
+
214
+ ### `initial_attrs`
215
+
216
+ Behind the scenes, `remote_all` initializes references with a set of
217
+ `initial_attrs`. You can do the same! If you've already fetched the data for an
218
+ object, just pass it to `new` for your reference class under the
219
+ `initial_attrs:` keyword parameter, like this:
220
+
221
+ ```ruby
222
+ todo = { id: 1, title: 'Hello world' }
223
+ TodoReference.new(remote_resource_id: todo[:id], initial_attrs: todo)
224
+ ```
225
+
127
226
  ### Forcing a fresh request
128
227
 
129
228
  You might want to force a fresh request in some instances, even if you're using
@@ -131,4 +230,6 @@ You might want to force a fresh request in some instances, even if you're using
131
230
 
132
231
  ### Skip fetching
133
232
 
134
- You might not want to make a request on initialize sometimes. In this case, pass `fetching: false` to your query or `new` to make sure the resource isn't fetched.
233
+ You might not want to make a request on initialize sometimes. In this case, pass
234
+ `fetching: false` to your query or `new` to make sure the resource isn't
235
+ fetched.
@@ -9,10 +9,10 @@ module RemoteRecord
9
9
  Config.defaults.merge(remote_record_class: self)
10
10
  end
11
11
 
12
- def initialize(reference, options)
12
+ def initialize(reference, options = default_config, initial_attrs = {})
13
13
  @reference = reference
14
- @options = options.presence || default_config
15
- @attrs = HashWithIndifferentAccess.new
14
+ @options = options
15
+ @attrs = HashWithIndifferentAccess.new(initial_attrs)
16
16
  end
17
17
 
18
18
  def method_missing(method_name, *_args, &_block)
@@ -29,6 +29,14 @@ module RemoteRecord
29
29
  raise NotImplementedError.new, '#get should return a hash of data that represents the remote record.'
30
30
  end
31
31
 
32
+ def self.all
33
+ raise NotImplementedError.new, '#all should return an array of hashes of data that represent remote records.'
34
+ end
35
+
36
+ def self.where(_params)
37
+ raise NotImplementedError.new, '#where should return an array of hashes of data that represent remote records.'
38
+ end
39
+
32
40
  def fetch
33
41
  @attrs.update(get)
34
42
  end
@@ -23,19 +23,37 @@ module RemoteRecord
23
23
  def remote_record_config
24
24
  Config.new
25
25
  end
26
+
27
+ def remote_all(&authz_proc)
28
+ remote_record_class.all(&authz_proc).map do |remote_resource|
29
+ where(remote_resource_id: remote_resource['id']).first_or_initialize(initial_attrs: remote_resource)
30
+ end
31
+ end
32
+
33
+ def remote_where(params, &authz_proc)
34
+ remote_record_class.where(params, &authz_proc).map do |remote_resource|
35
+ where(remote_resource_id: remote_resource['id']).first_or_initialize(initial_attrs: remote_resource)
36
+ end
37
+ end
26
38
  end
27
39
 
28
40
  # rubocop:disable Metrics/BlockLength
29
41
  included do
30
42
  include ActiveSupport::Rescuable
31
43
  attr_accessor :fetching
44
+ attr_accessor :initial_attrs
32
45
 
33
46
  after_initialize do |reference|
34
47
  reference.fetching = true if reference.fetching.nil?
48
+ reference.fetching = false if reference.initial_attrs.present?
35
49
  config = reference.class.remote_record_class.default_config.merge(
36
50
  reference.class.remote_record_config.to_h
37
51
  )
38
52
  reference.instance_variable_set('@remote_record_config', config)
53
+ reference.instance_variable_set('@instance',
54
+ @remote_record_config.remote_record_class.new(
55
+ self, @remote_record_config, reference.initial_attrs.presence || {}
56
+ ))
39
57
  reference.fetch_remote_resource
40
58
  end
41
59
 
@@ -4,8 +4,11 @@ module RemoteRecord
4
4
  module Transformers
5
5
  # Base transformer class. Inherit from this and implement `#transform`.
6
6
  class Base
7
- def initialize(data)
7
+ def initialize(data, direction = :up)
8
+ raise ArgumentError, 'The direction should be one of :up or :down.' unless %i[up down].include? direction
9
+
8
10
  @data = data
11
+ @direction = direction
9
12
  end
10
13
 
11
14
  def transform
@@ -15,14 +15,19 @@ module RemoteRecord
15
15
  when Array
16
16
  value.map { |v| convert_hash_keys(v) }
17
17
  when Hash
18
- Hash[value.map { |k, v| [underscore_key(k), convert_hash_keys(v)] }]
18
+ Hash[value.map { |k, v| [transform_key(k), convert_hash_keys(v)] }]
19
19
  else
20
20
  value
21
21
  end
22
22
  end
23
23
 
24
- def underscore_key(key)
25
- key.to_s.underscore.to_sym
24
+ def transform_key(key)
25
+ case @direction
26
+ when :up
27
+ key.to_s.underscore.to_sym
28
+ when :down
29
+ key.to_s.camelize(:lower).to_sym
30
+ end
26
31
  end
27
32
  end
28
33
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RemoteRecord
4
- VERSION = '0.5.0'
4
+ VERSION = '0.6.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remote_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Fish
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-01-12 00:00:00.000000000 Z
12
+ date: 2021-02-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -39,20 +39,6 @@ dependencies:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
- - !ruby/object:Gem::Dependency
43
- name: database_cleaner
44
- requirement: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - ">="
47
- - !ruby/object:Gem::Version
48
- version: '0'
49
- type: :development
50
- prerelease: false
51
- version_requirements: !ruby/object:Gem::Requirement
52
- requirements:
53
- - - ">="
54
- - !ruby/object:Gem::Version
55
- version: '0'
56
42
  - !ruby/object:Gem::Dependency
57
43
  name: faraday
58
44
  requirement: !ruby/object:Gem::Requirement