measured 2.8.2 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +20 -0
- data/.github/workflows/ci.yml +12 -6
- data/.github/workflows/cla.yml +23 -0
- data/.github/workflows/dependabot_auto_merge.yml +93 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +2 -0
- data/README.md +114 -4
- data/cache/weight.json +230 -0
- data/dev.yml +1 -2
- data/gemfiles/rails-7.0.gemfile +6 -0
- data/gemfiles/rails-7.1.gemfile +6 -0
- data/gemfiles/rails-edge.gemfile +6 -0
- data/lib/measured/measurable.rb +3 -1
- data/lib/measured/rails/active_record.rb +130 -0
- data/lib/measured/rails/validations.rb +68 -0
- data/lib/measured/railtie.rb +12 -0
- data/lib/measured/units/weight.rb +3 -2
- data/lib/measured/version.rb +1 -1
- data/lib/measured.rb +2 -0
- data/lib/tapioca/dsl/compilers/measured_rails.rb +110 -0
- data/measured.gemspec +5 -0
- data/test/internal/app/models/thing.rb +14 -0
- data/test/internal/app/models/thing_with_custom_unit_accessor.rb +18 -0
- data/test/internal/app/models/thing_with_custom_value_accessor.rb +19 -0
- data/test/internal/app/models/validated_thing.rb +45 -0
- data/test/internal/config/database.yml +3 -0
- data/test/internal/config.ru +9 -0
- data/test/internal/db/.gitignore +1 -0
- data/test/internal/db/schema.rb +99 -0
- data/test/internal/log/.gitignore +1 -0
- data/test/measurable_test.rb +4 -0
- data/test/rails/active_record_test.rb +433 -0
- data/test/rails/validation_test.rb +252 -0
- data/test/tapioca/dsl/compilers/measured_rails_test.rb +220 -0
- data/test/test_helper.rb +15 -0
- data/test/units/weight_test.rb +77 -2
- metadata +84 -10
- data/gemfiles/activesupport-5.2.gemfile +0 -5
- data/gemfiles/activesupport-6.0.gemfile +0 -5
- data/gemfiles/activesupport-6.1.gemfile +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b304fbf20408beb13b8408d74c17747f00ccb991b586775b91249f2e90e9073
|
4
|
+
data.tar.gz: d8df7e3a16743234d79b1d8375f2f6d83e69f18824fdd1ff95c530fdd333d3a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f7e92b9cdc9165c11dca6857ae9bbc141b938eb071e89f56ca1e13198fc6d281bde0abe4d3436d2f102d869057c5b1f60562bdcb2f9376739a4927b11ccb248
|
7
|
+
data.tar.gz: 4b3d830a1814b6b64dd9123a40e444918adf3a546b925bb57088bc2c4324da0ccd7040253a838d24f0c53ea1c749cbade548dee29f2995678be903f87af04dcf
|
@@ -0,0 +1,20 @@
|
|
1
|
+
version: 2
|
2
|
+
registries:
|
3
|
+
ruby-shopify:
|
4
|
+
type: rubygems-server
|
5
|
+
url: https://pkgs.shopify.io/basic/gems/ruby
|
6
|
+
username: ${{secrets.RUBYGEMS_SERVER_PKGS_SHOPIFY_IO_USERNAME}}
|
7
|
+
password: ${{secrets.RUBYGEMS_SERVER_PKGS_SHOPIFY_IO_PASSWORD}}
|
8
|
+
github-com:
|
9
|
+
type: git
|
10
|
+
url: https://github.com
|
11
|
+
username: ${{secrets.DEPENDENCIES_GITHUB_USER}}
|
12
|
+
password: ${{secrets.DEPENDENCIES_GITHUB_TOKEN}}
|
13
|
+
updates:
|
14
|
+
- package-ecosystem: bundler
|
15
|
+
directory: "/"
|
16
|
+
schedule:
|
17
|
+
interval: weekly
|
18
|
+
open-pull-requests-limit: 100
|
19
|
+
insecure-external-code-execution: allow
|
20
|
+
registries: "*"
|
data/.github/workflows/ci.yml
CHANGED
@@ -10,14 +10,20 @@ jobs:
|
|
10
10
|
strategy:
|
11
11
|
matrix:
|
12
12
|
ruby:
|
13
|
-
- '
|
14
|
-
- '2
|
15
|
-
- '3.
|
13
|
+
- '3.1'
|
14
|
+
- '3.2'
|
15
|
+
- '3.3'
|
16
16
|
gemfile:
|
17
17
|
- Gemfile
|
18
|
-
- gemfiles/
|
19
|
-
- gemfiles/
|
20
|
-
- gemfiles/
|
18
|
+
- gemfiles/rails-7.0.gemfile
|
19
|
+
- gemfiles/rails-7.1.gemfile
|
20
|
+
- gemfiles/rails-edge.gemfile
|
21
|
+
exclude:
|
22
|
+
- ruby: '3.1'
|
23
|
+
gemfile: gemfiles/rails-edge.gemfile
|
24
|
+
- ruby: '3.2'
|
25
|
+
gemfile: gemfiles/rails-edge.gemfile
|
26
|
+
|
21
27
|
name: Ruby ${{ matrix.ruby }} ${{ matrix.gemfile }}
|
22
28
|
steps:
|
23
29
|
- uses: actions/checkout@v1
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# .github/workflows/cla.yml
|
2
|
+
name: Contributor License Agreement (CLA)
|
3
|
+
|
4
|
+
on:
|
5
|
+
pull_request_target:
|
6
|
+
types: [opened, synchronize]
|
7
|
+
issue_comment:
|
8
|
+
types: [created]
|
9
|
+
|
10
|
+
jobs:
|
11
|
+
cla:
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
if: |
|
14
|
+
(github.event.issue.pull_request
|
15
|
+
&& !github.event.issue.pull_request.merged_at
|
16
|
+
&& contains(github.event.comment.body, 'signed')
|
17
|
+
)
|
18
|
+
|| (github.event.pull_request && !github.event.pull_request.merged)
|
19
|
+
steps:
|
20
|
+
- uses: Shopify/shopify-cla-action@v1
|
21
|
+
with:
|
22
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
23
|
+
cla-token: ${{ secrets.CLA_TOKEN }}
|
@@ -0,0 +1,93 @@
|
|
1
|
+
name: Dependabot auto-merge
|
2
|
+
on: pull_request_target
|
3
|
+
|
4
|
+
jobs:
|
5
|
+
dependabot:
|
6
|
+
runs-on: shopify-ubuntu-latest
|
7
|
+
if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}
|
8
|
+
steps:
|
9
|
+
- name: Dependabot metadata
|
10
|
+
id: metadata
|
11
|
+
uses: dependabot/fetch-metadata@v1.6.0
|
12
|
+
with:
|
13
|
+
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
14
|
+
|
15
|
+
- name: Waiting for CI to finish
|
16
|
+
id: check_ci_failure
|
17
|
+
continue-on-error: true
|
18
|
+
if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.dependency-group == 'auto_merge' }}
|
19
|
+
uses: actions/github-script@v6
|
20
|
+
with:
|
21
|
+
script: |
|
22
|
+
function sleep(ms) {
|
23
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
24
|
+
}
|
25
|
+
const query = `query ($org: String!, $repo: String!, $pullRequestNumber: Int!) {
|
26
|
+
organization(login: $org) {
|
27
|
+
repository(name: $repo) {
|
28
|
+
pullRequest(number: $pullRequestNumber) {
|
29
|
+
commits(last: 1) {
|
30
|
+
nodes {
|
31
|
+
commit {
|
32
|
+
status {
|
33
|
+
state
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}`;
|
42
|
+
const variables = {
|
43
|
+
org: context.repo.owner,
|
44
|
+
repo: context.repo.repo,
|
45
|
+
pullRequestNumber: context.issue.number
|
46
|
+
}
|
47
|
+
// Try for 30 minutes
|
48
|
+
let attempts = 30
|
49
|
+
let ci_state = false
|
50
|
+
for (let i = 1; i <= attempts; i++) {
|
51
|
+
console.log(`Sleeping for 60 seconds`)
|
52
|
+
await sleep(60000)
|
53
|
+
const result = await github.graphql(query, variables)
|
54
|
+
const state = result["organization"]["repository"]["pullRequest"]["commits"]["nodes"][0]["commit"]["status"]["state"]
|
55
|
+
console.log(`Status is ${state} after ${i} attempts`)
|
56
|
+
if (state === "SUCCESS") {
|
57
|
+
ci_state = true
|
58
|
+
console.log("Proceeding with workflow as CI succeed")
|
59
|
+
break
|
60
|
+
}
|
61
|
+
}
|
62
|
+
core.setOutput("ci_state", ci_state)
|
63
|
+
- name: Send Slack notification if auto-merge failed
|
64
|
+
if: ${{ steps.check_ci_failure.outputs.ci_state == 'false' }}
|
65
|
+
uses: ruby/action-slack@v3.0.0
|
66
|
+
with:
|
67
|
+
payload: |
|
68
|
+
{
|
69
|
+
"attachments": [{
|
70
|
+
"text": "Auto-merge failed for pull request <${{ github.event.pull_request.html_url }}|#${{ github.event.pull_request.number }}> in repository ${{ github.repository }}",
|
71
|
+
"color": "danger"
|
72
|
+
}
|
73
|
+
]
|
74
|
+
}
|
75
|
+
env:
|
76
|
+
SLACK_WEBHOOK_URL: ${{ secrets.METRICS_SLACK_WEBHOOK_URL }}
|
77
|
+
|
78
|
+
- name: Approve and merge
|
79
|
+
if: ${{ steps.check_ci_failure.outputs.ci_state == 'true' && (steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.dependency-group == 'auto_merge') }}
|
80
|
+
uses: actions/github-script@v6
|
81
|
+
with:
|
82
|
+
script: |
|
83
|
+
await github.rest.pulls.createReview({
|
84
|
+
pull_number: context.issue.number,
|
85
|
+
owner: context.repo.owner,
|
86
|
+
repo: context.repo.repo,
|
87
|
+
event: 'APPROVE',
|
88
|
+
})
|
89
|
+
await github.rest.pulls.merge({
|
90
|
+
owner: context.repo.owner,
|
91
|
+
repo: context.repo.repo,
|
92
|
+
issue_number: context.issue.number,
|
93
|
+
})
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.2
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,19 @@ Unreleased
|
|
2
2
|
-----
|
3
3
|
|
4
4
|
|
5
|
+
3.1.0
|
6
|
+
-----
|
7
|
+
* Drop support for Rails 6 and Ruby 3.0.
|
8
|
+
* Add qunitals. Add aliases for UK ton/tonne. (@ragarwal6397)
|
9
|
+
|
10
|
+
|
11
|
+
3.0.0
|
12
|
+
-----
|
13
|
+
|
14
|
+
* Merge functionality of `measured-rails` into this gem. From this version on, this gem is able to automatically integrate with Active Record out of the box. (@paracycle)
|
15
|
+
* Add `:gm` and `:gms` as aliases to weight. (@kushagra-03)
|
16
|
+
* Adds support for initializing `Measured` objects with a rational value from string. (@dvisockas)
|
17
|
+
* Make `Measured` initialization faster by avoiding string substitution in certain cases. (@bitwise-aiden)
|
5
18
|
|
6
19
|
2.8.2
|
7
20
|
-----
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Encapsulates measurements with their units. Provides easy conversion between uni
|
|
4
4
|
|
5
5
|
Lightweight and easily extensible to include other units and conversions. Conversions done with `Rational` for precision.
|
6
6
|
|
7
|
-
|
7
|
+
Since version 3.0.0, the adapter to integrate `measured` with Ruby on Rails is also a part of this gem. If you had been using [`measured-rails`](https://github.com/Shopify/measured-rails) for that functionality, you should now remove `measured-rails` from your gem file.
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
@@ -158,6 +158,116 @@ Measured::Weight.new("3.14", "kg").format(with_conversion_string: false)
|
|
158
158
|
> "3.14 kg"
|
159
159
|
```
|
160
160
|
|
161
|
+
### Active Record
|
162
|
+
|
163
|
+
This gem also provides an Active Record adapter for persisting and retrieving measurements with their units, and model validations.
|
164
|
+
|
165
|
+
Columns are expected to have the `_value` and `_unit` suffix, and be `DECIMAL` and `VARCHAR`, and defaults are accepted. Customizing the column used to hold units is supported, see below for details.
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
class AddWeightAndLengthToThings < ActiveRecord::Migration
|
169
|
+
def change
|
170
|
+
add_column :things, :minimum_weight_value, :decimal, precision: 10, scale: 2
|
171
|
+
add_column :things, :minimum_weight_unit, :string, limit: 12
|
172
|
+
|
173
|
+
add_column :things, :total_length_value, :decimal, precision: 10, scale: 2, default: 0
|
174
|
+
add_column :things, :total_length_unit, :string, limit: 12, default: "cm"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
A column can be declared as a measurement with its measurement subclass:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
class Thing < ActiveRecord::Base
|
183
|
+
measured Measured::Weight, :minimum_weight
|
184
|
+
measured Measured::Length, :total_length
|
185
|
+
measured Measured::Volume, :total_volume
|
186
|
+
end
|
187
|
+
```
|
188
|
+
|
189
|
+
You can optionally customize the model's unit column by specifying it in the `unit_field_name` option, as follows:
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
class ThingWithCustomUnitAccessor < ActiveRecord::Base
|
193
|
+
measured_length :length, :width, :height, unit_field_name: :size_unit
|
194
|
+
measured_weight :total_weight, :extra_weight, unit_field_name: :weight_unit
|
195
|
+
measured_volume :total_volume, :extra_volume, unit_field_name: :volume_unit
|
196
|
+
end
|
197
|
+
```
|
198
|
+
|
199
|
+
Similarly, you can optionally customize the model's value column by specifying it in the `value_field_name` option, as follows:
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
class ThingWithCustomValueAccessor < ActiveRecord::Base
|
203
|
+
measured_length :length, value_field_name: :custom_length
|
204
|
+
measured_weight :total_weight, value_field_name: :custom_weight
|
205
|
+
measured_volume :volume, value_field_name: :custom_volume
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
209
|
+
There are some simpler methods for predefined types:
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
class Thing < ActiveRecord::Base
|
213
|
+
measured_weight :minimum_weight
|
214
|
+
measured_length :total_length
|
215
|
+
measured_volume :total_volume
|
216
|
+
end
|
217
|
+
```
|
218
|
+
|
219
|
+
This will allow you to access and assign a measurement object:
|
220
|
+
|
221
|
+
```ruby
|
222
|
+
thing = Thing.new
|
223
|
+
thing.minimum_weight = Measured::Weight.new(10, "g")
|
224
|
+
thing.minimum_weight_unit # "g"
|
225
|
+
thing.minimum_weight_value # 10
|
226
|
+
```
|
227
|
+
|
228
|
+
Order of assignment does not matter, and each property can be assigned separately and with mass assignment:
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
params = { total_length_unit: "cm", total_length_value: "3" }
|
232
|
+
thing = Thing.new(params)
|
233
|
+
thing.total_length.to_s # 3 cm
|
234
|
+
```
|
235
|
+
|
236
|
+
### Validations
|
237
|
+
|
238
|
+
Validations are available:
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
class Thing < ActiveRecord::Base
|
242
|
+
measured_length :total_length
|
243
|
+
|
244
|
+
validates :total_length, measured: true
|
245
|
+
end
|
246
|
+
```
|
247
|
+
|
248
|
+
This will validate that the unit is defined on the measurement, and that there is a value.
|
249
|
+
|
250
|
+
Rather than `true` the validation can accept a hash with the following options:
|
251
|
+
|
252
|
+
* `message`: Override the default "is invalid" message.
|
253
|
+
* `units`: A subset of units available for this measurement. Units must be in existing measurement.
|
254
|
+
* `greater_than`
|
255
|
+
* `greater_than_or_equal_to`
|
256
|
+
* `equal_to`
|
257
|
+
* `less_than`
|
258
|
+
* `less_than_or_equal_to`
|
259
|
+
|
260
|
+
All comparison validations require `Measured::Measurable` values, not scalars. Most of these options replace the `numericality` validator which compares the measurement/method name/proc to the column's value. Validations can also be combined with `presence` validator.
|
261
|
+
|
262
|
+
**Note:** Validations are strongly recommended since assigning an invalid unit will cause the measurement to return `nil`, even if there is a value:
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
thing = Thing.new
|
266
|
+
thing.total_length_value = 1
|
267
|
+
thing.total_length_unit = "invalid"
|
268
|
+
thing.total_length # nil
|
269
|
+
```
|
270
|
+
|
161
271
|
## Units and conversions
|
162
272
|
|
163
273
|
### SI units support
|
@@ -269,7 +379,7 @@ Existing alternatives which were considered:
|
|
269
379
|
* **Cons**
|
270
380
|
* Opens up and modifies `Array`, `Date`, `Fixnum`, `Math`, `Numeric`, `String`, `Time`, and `Object`, then depends on those changes internally.
|
271
381
|
* Lots of code to solve a relatively simple problem.
|
272
|
-
* No
|
382
|
+
* No Active Record adapter.
|
273
383
|
|
274
384
|
### Gem: [quantified](https://github.com/Shopify/quantified)
|
275
385
|
* **Pros**
|
@@ -278,7 +388,7 @@ Existing alternatives which were considered:
|
|
278
388
|
* All math done with floats making it highly lossy.
|
279
389
|
* All units assumed to be pluralized, meaning using unit abbreviations is not possible.
|
280
390
|
* Not actively maintained.
|
281
|
-
* No
|
391
|
+
* No Active Record adapter.
|
282
392
|
|
283
393
|
### Gem: [unitwise](https://github.com/joshwlewis/unitwise)
|
284
394
|
* **Pros**
|
@@ -287,7 +397,7 @@ Existing alternatives which were considered:
|
|
287
397
|
* **Cons**
|
288
398
|
* Lots of code. Good code, but lots of it.
|
289
399
|
* Many modifications to core types.
|
290
|
-
*
|
400
|
+
* Active Record adapter exists but is written and maintained by a different person/org.
|
291
401
|
* Not actively maintained.
|
292
402
|
|
293
403
|
## Contributing
|