airrecord 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e5fbb9f72ea43cf78e32e2a6dbfb0138b5732f3171cfe4416630b84e26d643e
4
- data.tar.gz: b232483322295e61cd75a09cb56a04a67e673b01ecd27b8aee551e54b91ee45e
3
+ metadata.gz: 1224190a9be6b7349af9ab26640516b13961d88bc3b42d188c5879777e90fe04
4
+ data.tar.gz: 119beef65efdc6d110cc4e5c1865e92308c3e2a043b0e2e853484480488307d2
5
5
  SHA512:
6
- metadata.gz: ce0d4a831cd0e2de8d5725b7ad66298ed061d920ee713b2bb0a54bde1c17e5b123e5edc00760b832c1f36ae2f3586bf1cc218fb4982bea265fbc6703c897d358
7
- data.tar.gz: 115ab85c06ad0934d11e9f8d1a246a155f374300f60cf4c32019c3624e72794ea01d659464649b440ff793296ac2ce9b757feea3d41644fcf10b33770da10ead
6
+ metadata.gz: 577ae326c6cb761f7557c9b75a6a3e1fc46373aba67b6300430d9d0efac54e21eea30885237d40c233ef1c33f2d4bd3766984f0697211ebbff8bd6d7af160bcf
7
+ data.tar.gz: 11c1ce89e39a1f0e66f8c3b34cbf53eb21f69a2e91f98ae5460af85f1df2b62e37a1c01940263da8dba7c0499e822f8a3280ef55345cc93f76401c2d36579252
@@ -1,3 +1,11 @@
1
+ # 1.0.0 (unreleased)
2
+
3
+ * 1.0.0 will introduce breaking changes, including removing support for symbols. To update, change snake-case symbols to their correct column names (for example, `record["First Name"]` instead of `record[:first_name]`)
4
+
5
+ # 0.2.5
6
+
7
+ * Deprecate using symbols instead of strings
8
+
1
9
  # 0.2.4
2
10
 
3
11
  * Don't flag as dirty if change is equal
data/README.md CHANGED
@@ -20,7 +20,7 @@ class Tea < Airrecord::Table
20
20
  self.base_key = "app1"
21
21
  self.table_name = "Teas"
22
22
 
23
- has_many :brews, class: 'Brew', column: "Brews"
23
+ has_many "Brews", class: 'Brew', column: "Brews"
24
24
 
25
25
  def self.chinese
26
26
  all(filter: '{Country} = "China"')
@@ -31,11 +31,11 @@ class Tea < Airrecord::Table
31
31
  end
32
32
 
33
33
  def location
34
- [self[:village], self[:country], self[:region]].compact.join(", ")
34
+ [self["Village"], self["Country"], self["Region"]].compact.join(", ")
35
35
  end
36
36
 
37
37
  def green?
38
- self[:type] == "Green"
38
+ self["Type"] == "Green"
39
39
  end
40
40
  end
41
41
 
@@ -43,22 +43,22 @@ class Brew < Airrecord::Table
43
43
  self.base_key = "app1"
44
44
  self.table_name = "Brews"
45
45
 
46
- belongs_to :tea, class: 'Tea', column: 'Tea'
46
+ belongs_to "Tea", class: 'Tea', column: 'Tea'
47
47
 
48
48
  def self.hot
49
49
  all(filter: "{Temperature} > 90")
50
50
  end
51
51
 
52
52
  def done_brewing?
53
- self[:created_at] + self[:duration] > Time.now
53
+ self["Created At"] + self["Duration"] > Time.now
54
54
  end
55
55
  end
56
56
 
57
57
  teas = Tea.all
58
58
  tea = teas.first
59
- tea[:country] # access atribute
59
+ tea["Country"] # access atribute
60
60
  tea.location # instance methods
61
- tea[:brews] # associated brews
61
+ tea["Brews"] # associated brews
62
62
  ```
63
63
 
64
64
  A short-hand API for definitions and more ad-hoc querying is also available:
@@ -67,7 +67,7 @@ A short-hand API for definitions and more ad-hoc querying is also available:
67
67
  Tea = Airrecord.table("api_key", "app_key", "Teas")
68
68
 
69
69
  Tea.all.each do |record|
70
- puts "#{record.id}: #{record[:name]}"
70
+ puts "#{record.id}: #{record["Name"]}"
71
71
  end
72
72
 
73
73
  Tea.find("rec3838")
@@ -107,7 +107,7 @@ class Tea < Airrecord::Table
107
107
  self.table_name = "Teas"
108
108
 
109
109
  def location
110
- [self[:village], self[:country], self[:region]].compact.join(", ")
110
+ [self["Village"], self["Country"], self["Region"]].compact.join(", ")
111
111
  end
112
112
  end
113
113
  ```
@@ -130,13 +130,13 @@ To use `filterbyFormula` to filter returned records:
130
130
 
131
131
  ```ruby
132
132
  # Retrieve all teas from China
133
- Tea.all(filter: '{Country} == "China"')
133
+ Tea.all(filter: '{Country} = "China"')
134
134
 
135
135
  # Retrieve all teas created in the past week
136
136
  Tea.all(filter: "DATETIME_DIFF(CREATED_TIME(), TODAY(), 'days') < 7")
137
137
 
138
138
  # Retrieve all teas that don't have a country defined
139
- Tea.all(filter: "{Country} == \"\"")
139
+ Tea.all(filter: "{Country} = \"\"")
140
140
  ```
141
141
 
142
142
  This filtering can, of course, also be done in Ruby directly after calling
@@ -158,17 +158,16 @@ The `sort` option can be used to sort results returned from the Airtable API.
158
158
 
159
159
  ```ruby
160
160
  # Sort teas by the Name column in ascending order
161
- Tea.all(sort: { Name: "asc" })
161
+ Tea.all(sort: { "Name" => "asc" })
162
162
 
163
163
  # Sort teas by Type (green, black, oolong, ..) in descending order
164
- Tea.all(sort: { Type: "desc" })
164
+ Tea.all(sort: { "Type" => "desc" })
165
165
 
166
166
  # Sort teas by price in descending order
167
- Tea.all(sort: { Price: "desc" })
167
+ Tea.all(sort: { "Price" => "desc" })
168
168
  ```
169
169
 
170
- Note again that the key _must_ be the full column name. Snake-cased variants do
171
- not work here.
170
+ Note again that the key _must_ be the full column name.
172
171
 
173
172
  As mentioned above, by default Airrecord will return results from all pages.
174
173
  This can be slow if you have 1000s of records. You may wish to use the `view`
@@ -181,7 +180,7 @@ calls. Airrecord will _always_ fetch the maximum possible amount of records
181
180
  Tea.all(paginate: false)
182
181
 
183
182
  # Give me only the most recent teas
184
- Tea.all(sort: { "Created At": "desc" }, paginate: false)
183
+ Tea.all(sort: { "Created At" => "desc" }, paginate: false)
185
184
  ```
186
185
 
187
186
  ### Creating
@@ -192,16 +191,15 @@ Creating a new record is done through `#create`.
192
191
  tea = Tea.new("Name" => "Feng Gang", "Type" => "Green", "Country" => "China")
193
192
  tea.create # creates the record
194
193
  tea.id # id of the new record
195
- tea[:name] # "Feng Gang", accessed through snake-cased name
194
+ tea["Name"] # "Feng Gang"
196
195
  ```
197
196
 
198
- Note that when instantiating the new record the column names (keys of the passed
199
- named parameters) need to match the exact column names in Airtable, otherwise
200
- Airrecord will throw an error that no column matches it.
197
+ Note that column names need to match the exact column names in Airtable,
198
+ otherwise Airrecord will throw an error that no column matches it.
201
199
 
202
- In the future I hope to provide more convient names for these (snake-cased),
203
- however, this is error-prone without a proper schema API from Airtable which has
204
- still not been released.
200
+ _Earlier versions of airrecord provided methods for snake-cased column names
201
+ and symbols, however this proved error-prone without a proper schema API from
202
+ Airtable which has still not been released._
205
203
 
206
204
  ### Updating
207
205
 
@@ -210,11 +208,7 @@ Airtable with `#save`.
210
208
 
211
209
  ```ruby
212
210
  tea = Tea.find("someid")
213
- tea[:name] = "Feng Gang Organic"
214
-
215
- # Since the Village column is not set, we do not have access to a snake-cased
216
- # variant since the mapping is not determined. For all we know, the correct column
217
- # name could be "VilLlaGe". Therefore, we must use the proper column name.
211
+ tea["Name"] = "Feng Gang Organic"
218
212
  tea["Village"] = "Feng Gang"
219
213
 
220
214
  tea.save # persist to Airtable
@@ -236,7 +230,7 @@ providing the URL. Unfortunately, it does not allow uploading directly.
236
230
 
237
231
  ```ruby
238
232
  word = World.find("cantankerous")
239
- word["Pronounciation"] = [{url: "https://s3.ca-central-1.amazonaws.com/word-pronunciations/cantankerous.mp3}]
233
+ word["Pronounciation"] = [{url: "https://s3.ca-central-1.amazonaws.com/word-pronunciations/cantankerous.mp3"}]
240
234
  word.save
241
235
  ```
242
236
 
@@ -274,14 +268,14 @@ class Tea < Airrecord::Table
274
268
  self.base_key = "app1"
275
269
  self.table_name = "Teas"
276
270
 
277
- has_many :brews, class: 'Brew', column: "Brews"
271
+ has_many "Brews", class: 'Brew', column: "Brews"
278
272
  end
279
273
 
280
274
  class Brew < Airrecord::Table
281
275
  self.base_key = "app1"
282
276
  self.table_name = "Brews"
283
277
 
284
- belongs_to :tea, class: 'Tea', column: 'Tea'
278
+ belongs_to "Tea", class: 'Tea', column: 'Tea'
285
279
  end
286
280
  ```
287
281
 
@@ -296,14 +290,14 @@ To retrieve records from associations to a record:
296
290
 
297
291
  ```ruby
298
292
  tea = Tea.find('rec84')
299
- tea[:brews] # brews associated with tea
293
+ tea["Brews"] # brews associated with tea
300
294
  ```
301
295
 
302
296
  This in turn works the other way too:
303
297
 
304
298
  ```ruby
305
299
  brew = Brew.find('rec849')
306
- brew[:tea] # the associated tea instance
300
+ brew["Tea"] # the associated tea instance
307
301
  ```
308
302
 
309
303
  ### Creating associated records
@@ -313,7 +307,8 @@ You can easily associate records with each other:
313
307
  ```ruby
314
308
  tea = Tea.find('rec849829')
315
309
  # This will create a brew associated with the specific tea
316
- Brew.create("Tea" => tea, "Temperature" => "80", "Time" => "4m", "Rating" => "5")
310
+ brew = Brew.new("Tea" => tea, "Temperature" => "80", "Time" => "4m", "Rating" => "5")
311
+ brew.create
317
312
  ```
318
313
 
319
314
  ### Ad-hoc API
@@ -327,20 +322,12 @@ around.
327
322
  Tea = Airrecord.table("api_key", "app_key", "Teas")
328
323
 
329
324
  Tea.all.each do |record|
330
- puts "#{record.id}: #{record[:name]}"
325
+ puts "#{record.id}: #{record["Name"]}"
331
326
  end
332
327
 
333
328
  Tea.find("rec3838")
334
329
  ```
335
330
 
336
- ### Snake-cased helper methods
337
-
338
- When retrieving an existing record from Airtable, snake-cased helper names are
339
- available to index attributes. These are _only_ available on retrieved records,
340
- and _only_ if the column was set. If it's `nil`, it will not exist. That means
341
- if you want to set column that has a `nil` value for a column type, you'll have
342
- to fully type it out.
343
-
344
331
  ### Production Middlewares
345
332
 
346
333
  For production use-cases, it's worth considering adding retries and circuit
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
 
12
12
  spec.summary = %q{Airtable client}
13
13
  spec.description = %q{Airtable client to make Airtable interactions a breeze}
14
- spec.homepage = "https://github.com/sirupsen/airtable"
14
+ spec.homepage = "https://github.com/sirupsen/airrecord"
15
15
  spec.license = "MIT"
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
@@ -8,10 +8,24 @@ module Airrecord
8
8
  # Right now I bet there's a bunch of bugs around similar named column keys (in
9
9
  # terms of capitalization), it's inconsistent and non-obvious that `create`
10
10
  # doesn't use the same column keys as everything else.
11
+ #
12
+ # 2018-11-01
13
+ # deprecate_symbols: long-term plan is to force everyone to use raw strings,
14
+ # to match the Airtable behavior. For now we'll just warn when using symbols
15
+ # with a deprecation notice.
16
+
11
17
  class Table
18
+ def deprecate_symbols
19
+ self.class.deprecate_symbols
20
+ end
21
+
12
22
  class << self
13
23
  attr_accessor :base_key, :table_name, :api_key, :associations
14
24
 
25
+ def deprecate_symbols
26
+ warn Kernel.caller.first + ": warning: Using symbols with airrecord is deprecated."
27
+ end
28
+
15
29
  def client
16
30
  @@clients ||= {}
17
31
  @@clients[api_key] ||= Client.new(api_key)
@@ -20,7 +34,7 @@ module Airrecord
20
34
  def has_many(name, options)
21
35
  @associations ||= []
22
36
  @associations << {
23
- field: name.to_sym,
37
+ field: name.to_sym, # todo: deprecate_symbols
24
38
  }.merge(options)
25
39
  end
26
40
 
@@ -49,6 +63,7 @@ module Airrecord
49
63
 
50
64
  if sort
51
65
  options[:sort] = sort.map { |field, direction|
66
+ deprecate_symbols if field.is_a? Symbol
52
67
  { field: field.to_s, direction: direction }
53
68
  }
54
69
  end
@@ -106,8 +121,10 @@ module Airrecord
106
121
  value = nil
107
122
 
108
123
  if fields[key]
124
+ deprecate_symbols if key.is_a? Symbol
109
125
  value = fields[key]
110
126
  elsif column_mappings[key]
127
+ deprecate_symbols if key.is_a? Symbol
111
128
  value = fields[column_mappings[key]]
112
129
  end
113
130
 
@@ -125,11 +142,13 @@ module Airrecord
125
142
  end
126
143
 
127
144
  def []=(key, value)
145
+ deprecate_symbols if key.is_a? Symbol
128
146
  if fields[key]
129
147
  return if fields[key] == value # no-op
130
148
  @updated_keys << key
131
149
  fields[key] = value
132
150
  elsif column_mappings[key]
151
+ deprecate_symbols
133
152
  return if fields[column_mappings[key]] == value # no-op
134
153
  @updated_keys << column_mappings[key]
135
154
  fields[column_mappings[key]] = value
@@ -196,19 +215,25 @@ module Airrecord
196
215
  value = [ value ] unless value.is_a?(Enumerable)
197
216
  assocs = value.map { |assoc|
198
217
  assoc.respond_to?(:id) ? assoc.id : assoc
199
- }
218
+ }
200
219
  [key, assocs]
201
220
  else
202
221
  [key, value]
203
222
  end
204
223
  }]
205
224
  end
206
-
225
+
207
226
  def ==(other)
208
227
  self.class == other.class &&
209
228
  serializable_fields == other.serializable_fields
210
229
  end
211
230
 
231
+ alias_method :eql?, :==
232
+
233
+ def hash
234
+ serializable_fields.hash
235
+ end
236
+
212
237
  protected
213
238
 
214
239
  def association(key)
@@ -221,15 +246,15 @@ module Airrecord
221
246
 
222
247
  def fields=(fields)
223
248
  @updated_keys = []
224
- @column_mappings = Hash[fields.keys.map { |key| [underscore(key), key] }]
249
+ @column_mappings = Hash[fields.keys.map { |key| [underscore(key), key] }] # TODO remove (deprecate_symbols)
225
250
  @fields = fields
226
251
  end
227
252
 
228
- def self.underscore(key)
253
+ def self.underscore(key) # TODO remove (deprecate_symbols)
229
254
  key.to_s.strip.gsub(/\W+/, "_").downcase.to_sym
230
255
  end
231
256
 
232
- def underscore(key)
257
+ def underscore(key) # TODO remove (deprecate_symbols)
233
258
  self.class.underscore(key)
234
259
  end
235
260
 
@@ -249,6 +274,7 @@ module Airrecord
249
274
  value
250
275
  end
251
276
  end
277
+
252
278
  end
253
279
 
254
280
  def self.table(api_key, base_key, table_name)
@@ -1,3 +1,3 @@
1
1
  module Airrecord
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.5"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: airrecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Eskildsen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-15 00:00:00.000000000 Z
11
+ date: 2018-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -115,7 +115,7 @@ files:
115
115
  - lib/airrecord/client.rb
116
116
  - lib/airrecord/table.rb
117
117
  - lib/airrecord/version.rb
118
- homepage: https://github.com/sirupsen/airtable
118
+ homepage: https://github.com/sirupsen/airrecord
119
119
  licenses:
120
120
  - MIT
121
121
  metadata: {}