random_unique_id 1.0.1 → 1.1.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 +4 -4
- data/Appraisals +2 -1
- data/CHANGELOG.md +5 -0
- data/Gemfile +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +33 -2
- data/Rakefile +1 -1
- data/gemfiles/rails_3_2.gemfile +1 -0
- data/gemfiles/rails_3_2.gemfile.lock +27 -27
- data/gemfiles/rails_4_0.gemfile.lock +28 -33
- data/gemfiles/rails_4_1.gemfile.lock +34 -39
- data/gemfiles/rails_4_2.gemfile.lock +3 -3
- data/lib/random_unique_id.rb +94 -28
- data/lib/random_unique_id/util.rb +2 -2
- data/lib/random_unique_id/version.rb +2 -2
- data/random_unique_id.gemspec +2 -2
- data/test/random_unique_id/util_test.rb +2 -2
- data/test/random_unique_id_test.rb +112 -9
- data/test/test_helper.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4fd33354ff7f7dc83c2049861a1c1834180a701
|
4
|
+
data.tar.gz: e2d3966da120622d465f8ee44a8eafbfa34a07d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fc7fff7df4603a17669ba6e3a5daf6c8b564f2f1d895497e91081de7b6ef4c2c70668f34b5d554d9ff9b09e9a972d13ca3614111812967bfa5121026ee659dc
|
7
|
+
data.tar.gz: 4ef6aece9431d658ad3f034ad709a7a91ca4b3021870bec305c8e8147e3cef0b30fb3f49d16f79136947a0a12d6a40bef066750789b83bdd47d878400571d910
|
data/Appraisals
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
Authoritative changelog in README.md.
|
2
2
|
|
3
|
+
## Version 1.1.0 (Jan 7, 2015)
|
4
|
+
- Added global configuration.
|
5
|
+
- Added UUID generation.
|
6
|
+
- Allow changing the name of the field used for the random id.
|
7
|
+
|
3
8
|
## Version 1.0.1 (Dec 22, 2014)
|
4
9
|
- Added support for Rails 4.2.
|
5
10
|
|
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -69,15 +69,44 @@ records. For example:
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
+
## Configuration
|
73
|
+
|
74
|
+
There are 2 settings that are configurable for RandomUniqueId:
|
75
|
+
|
76
|
+
- `min_rid_length:` If you have a large table, the default of 5 characters for an initial length may be a problem, since it may lead to
|
77
|
+
several "bounces" until a new unique RID gets generated. The gem scales well with this problem, since it makes
|
78
|
+
each subsequent try longer than the one before, but it may still may 3 or 4 DB hits for each record creation.
|
79
|
+
In that case, you may want to set it to start with a higher number directly.
|
80
|
+
|
81
|
+
- `random_generation_method:` If you have a very large table, and having very long IDs is not a problem, you can choose to generate UUIDs instead
|
82
|
+
of short-ish Random IDs. This looks worse if you're displaying these IDs in a URL, but it allows to skip the check
|
83
|
+
for existence, which in a large table can make a large difference.
|
84
|
+
random_generation_method can be either `:short` (the default) or `:uuid`
|
85
|
+
|
86
|
+
Both of these settings can be specified on a per-model basis, when adding RandomUniqueId to the model:
|
87
|
+
|
88
|
+
has_random_unique_id(min_rid_length: 10)
|
89
|
+
has_random_unique_id(random_generation_method: :uuid)
|
90
|
+
|
91
|
+
Or globally in an initializer:
|
92
|
+
|
93
|
+
RandomUniqueId.config(min_rid_length: 10)
|
94
|
+
|
72
95
|
## Users
|
73
96
|
|
74
97
|
This gem is being used by:
|
75
98
|
|
76
99
|
- [Watu](https://watuapp.com)
|
100
|
+
- [MSTY](https://www.msty.com)
|
77
101
|
- You? please, let us know if you are using this gem.
|
78
102
|
|
79
103
|
## Changelog
|
80
104
|
|
105
|
+
### Version 1.1.0 (Jan 7, 2015)
|
106
|
+
- Added global configuration.
|
107
|
+
- Added UUID generation.
|
108
|
+
- Allow changing the name of the field used for the random id.
|
109
|
+
|
81
110
|
### Version 1.0.1 (Dec 22, 2014)
|
82
111
|
- Added support for Rails 4.2.
|
83
112
|
|
@@ -102,12 +131,14 @@ This gem is being used by:
|
|
102
131
|
|
103
132
|
1. Fork it
|
104
133
|
1. Create your feature branch (`git checkout -b my-new-feature`)
|
105
|
-
1. Code your thing
|
106
|
-
1.
|
134
|
+
1. Code your thing
|
135
|
+
1. Write and run tests:
|
107
136
|
|
108
137
|
bundle install
|
109
138
|
appraisal
|
110
139
|
appraisal rake test
|
140
|
+
1. Write documentation and make sure it looks good: yard server --reload
|
141
|
+
1. Add items to the changelog, both in the README and the CHANGELOG file.
|
111
142
|
1. Commit your changes (`git commit -am "Add some feature"`)
|
112
143
|
1. Push to the branch (`git push origin my-new-feature`)
|
113
144
|
1. Create new Pull Request
|
data/Rakefile
CHANGED
data/gemfiles/rails_3_2.gemfile
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
random_unique_id (1.0
|
4
|
+
random_unique_id (1.1.0)
|
5
5
|
activerecord (>= 3.2.0)
|
6
6
|
activesupport (>= 3.2.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
actionmailer (3.2.
|
12
|
-
actionpack (= 3.2.
|
11
|
+
actionmailer (3.2.21)
|
12
|
+
actionpack (= 3.2.21)
|
13
13
|
mail (~> 2.5.4)
|
14
|
-
actionpack (3.2.
|
15
|
-
activemodel (= 3.2.
|
16
|
-
activesupport (= 3.2.
|
14
|
+
actionpack (3.2.21)
|
15
|
+
activemodel (= 3.2.21)
|
16
|
+
activesupport (= 3.2.21)
|
17
17
|
builder (~> 3.0.0)
|
18
18
|
erubis (~> 2.7.0)
|
19
19
|
journey (~> 1.0.4)
|
@@ -21,15 +21,15 @@ GEM
|
|
21
21
|
rack-cache (~> 1.2)
|
22
22
|
rack-test (~> 0.6.1)
|
23
23
|
sprockets (~> 2.2.1)
|
24
|
-
activemodel (3.2.
|
25
|
-
activesupport (= 3.2.
|
24
|
+
activemodel (3.2.21)
|
25
|
+
activesupport (= 3.2.21)
|
26
26
|
builder (~> 3.0.0)
|
27
|
-
activerecord (3.2.
|
28
|
-
activemodel (= 3.2.
|
29
|
-
activesupport (= 3.2.
|
27
|
+
activerecord (3.2.21)
|
28
|
+
activemodel (= 3.2.21)
|
29
|
+
activesupport (= 3.2.21)
|
30
30
|
arel (~> 3.0.2)
|
31
31
|
tzinfo (~> 0.3.29)
|
32
|
-
activesupport (3.2.
|
32
|
+
activesupport (3.2.21)
|
33
33
|
i18n (~> 0.6, >= 0.6.4)
|
34
34
|
multi_json (~> 1.0)
|
35
35
|
ansi (1.4.3)
|
@@ -39,7 +39,7 @@ GEM
|
|
39
39
|
thor (>= 0.14.0)
|
40
40
|
arel (3.0.3)
|
41
41
|
builder (3.0.4)
|
42
|
-
codeclimate-test-reporter (0.4.
|
42
|
+
codeclimate-test-reporter (0.4.4)
|
43
43
|
simplecov (>= 0.7.1, < 1.0.0)
|
44
44
|
coveralls (0.7.1)
|
45
45
|
multi_json (~> 1.3)
|
@@ -49,9 +49,9 @@ GEM
|
|
49
49
|
thor
|
50
50
|
docile (1.1.5)
|
51
51
|
erubis (2.7.0)
|
52
|
-
hashie (3.3.
|
52
|
+
hashie (3.3.2)
|
53
53
|
hike (1.2.3)
|
54
|
-
i18n (0.
|
54
|
+
i18n (0.7.0)
|
55
55
|
journey (1.0.4)
|
56
56
|
json (1.8.1)
|
57
57
|
mail (2.5.4)
|
@@ -74,7 +74,7 @@ GEM
|
|
74
74
|
mocha (1.1.0)
|
75
75
|
metaclass (~> 0.0.1)
|
76
76
|
multi_json (1.10.1)
|
77
|
-
netrc (0.
|
77
|
+
netrc (0.10.2)
|
78
78
|
polyglot (0.3.5)
|
79
79
|
powerbar (1.0.11)
|
80
80
|
ansi (~> 1.4.0)
|
@@ -86,14 +86,14 @@ GEM
|
|
86
86
|
rack
|
87
87
|
rack-test (0.6.2)
|
88
88
|
rack (>= 1.0)
|
89
|
-
railties (3.2.
|
90
|
-
actionpack (= 3.2.
|
91
|
-
activesupport (= 3.2.
|
89
|
+
railties (3.2.21)
|
90
|
+
actionpack (= 3.2.21)
|
91
|
+
activesupport (= 3.2.21)
|
92
92
|
rack-ssl (~> 1.3.2)
|
93
93
|
rake (>= 0.8.7)
|
94
94
|
rdoc (~> 3.4)
|
95
95
|
thor (>= 0.14.6, < 2.0)
|
96
|
-
rake (10.
|
96
|
+
rake (10.4.2)
|
97
97
|
rdoc (3.12.2)
|
98
98
|
json (~> 1.4)
|
99
99
|
rest-client (1.7.2)
|
@@ -105,26 +105,26 @@ GEM
|
|
105
105
|
shoulda-context (1.2.1)
|
106
106
|
shoulda-matchers (2.7.0)
|
107
107
|
activesupport (>= 3.0.0)
|
108
|
-
simplecov (0.9.
|
108
|
+
simplecov (0.9.1)
|
109
109
|
docile (~> 1.1.0)
|
110
|
-
multi_json
|
110
|
+
multi_json (~> 1.0)
|
111
111
|
simplecov-html (~> 0.8.0)
|
112
112
|
simplecov-html (0.8.0)
|
113
|
-
sprockets (2.2.
|
113
|
+
sprockets (2.2.3)
|
114
114
|
hike (~> 1.2)
|
115
115
|
multi_json (~> 1.0)
|
116
116
|
rack (~> 1.0)
|
117
117
|
tilt (~> 1.1, != 1.3.0)
|
118
|
-
sqlite3 (1.3.
|
118
|
+
sqlite3 (1.3.10)
|
119
119
|
term-ansicolor (1.3.0)
|
120
120
|
tins (~> 1.0)
|
121
121
|
thor (0.19.1)
|
122
122
|
tilt (1.4.1)
|
123
|
-
tins (1.3.
|
123
|
+
tins (1.3.3)
|
124
124
|
treetop (1.4.15)
|
125
125
|
polyglot
|
126
126
|
polyglot (>= 0.3.1)
|
127
|
-
tzinfo (0.3.
|
127
|
+
tzinfo (0.3.42)
|
128
128
|
|
129
129
|
PLATFORMS
|
130
130
|
ruby
|
@@ -135,7 +135,7 @@ DEPENDENCIES
|
|
135
135
|
bundler
|
136
136
|
codeclimate-test-reporter
|
137
137
|
coveralls
|
138
|
-
minitest
|
138
|
+
minitest (~> 4.7.5)
|
139
139
|
minitest-rails
|
140
140
|
minitest-reporters
|
141
141
|
mocha
|
@@ -1,32 +1,32 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
random_unique_id (1.0
|
4
|
+
random_unique_id (1.1.0)
|
5
5
|
activerecord (>= 3.2.0)
|
6
6
|
activesupport (>= 3.2.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
actionmailer (4.0.
|
12
|
-
actionpack (= 4.0.
|
13
|
-
mail (~> 2.5.4)
|
14
|
-
actionpack (4.0.
|
15
|
-
activesupport (= 4.0.
|
11
|
+
actionmailer (4.0.13)
|
12
|
+
actionpack (= 4.0.13)
|
13
|
+
mail (~> 2.5, >= 2.5.4)
|
14
|
+
actionpack (4.0.13)
|
15
|
+
activesupport (= 4.0.13)
|
16
16
|
builder (~> 3.1.0)
|
17
17
|
erubis (~> 2.7.0)
|
18
18
|
rack (~> 1.5.2)
|
19
19
|
rack-test (~> 0.6.2)
|
20
|
-
activemodel (4.0.
|
21
|
-
activesupport (= 4.0.
|
20
|
+
activemodel (4.0.13)
|
21
|
+
activesupport (= 4.0.13)
|
22
22
|
builder (~> 3.1.0)
|
23
|
-
activerecord (4.0.
|
24
|
-
activemodel (= 4.0.
|
23
|
+
activerecord (4.0.13)
|
24
|
+
activemodel (= 4.0.13)
|
25
25
|
activerecord-deprecated_finders (~> 1.0.2)
|
26
|
-
activesupport (= 4.0.
|
26
|
+
activesupport (= 4.0.13)
|
27
27
|
arel (~> 4.0.0)
|
28
28
|
activerecord-deprecated_finders (1.0.3)
|
29
|
-
activesupport (4.0.
|
29
|
+
activesupport (4.0.13)
|
30
30
|
i18n (~> 0.6, >= 0.6.9)
|
31
31
|
minitest (~> 4.2)
|
32
32
|
multi_json (~> 1.3)
|
@@ -39,7 +39,7 @@ GEM
|
|
39
39
|
thor (>= 0.14.0)
|
40
40
|
arel (4.0.2)
|
41
41
|
builder (3.1.4)
|
42
|
-
codeclimate-test-reporter (0.4.
|
42
|
+
codeclimate-test-reporter (0.4.4)
|
43
43
|
simplecov (>= 0.7.1, < 1.0.0)
|
44
44
|
coveralls (0.7.1)
|
45
45
|
multi_json (~> 1.3)
|
@@ -49,13 +49,12 @@ GEM
|
|
49
49
|
thor
|
50
50
|
docile (1.1.5)
|
51
51
|
erubis (2.7.0)
|
52
|
-
hashie (3.3.
|
53
|
-
i18n (0.
|
54
|
-
mail (2.
|
55
|
-
mime-types (
|
56
|
-
treetop (~> 1.4.8)
|
52
|
+
hashie (3.3.2)
|
53
|
+
i18n (0.7.0)
|
54
|
+
mail (2.6.3)
|
55
|
+
mime-types (>= 1.16, < 3)
|
57
56
|
metaclass (0.0.4)
|
58
|
-
mime-types (
|
57
|
+
mime-types (2.4.3)
|
59
58
|
minitest (4.7.5)
|
60
59
|
minitest-rails (1.0.1)
|
61
60
|
minitest (~> 4.7)
|
@@ -71,20 +70,19 @@ GEM
|
|
71
70
|
mocha (1.1.0)
|
72
71
|
metaclass (~> 0.0.1)
|
73
72
|
multi_json (1.10.1)
|
74
|
-
netrc (0.
|
75
|
-
polyglot (0.3.5)
|
73
|
+
netrc (0.10.2)
|
76
74
|
powerbar (1.0.11)
|
77
75
|
ansi (~> 1.4.0)
|
78
76
|
hashie (>= 1.1.0)
|
79
77
|
rack (1.5.2)
|
80
78
|
rack-test (0.6.2)
|
81
79
|
rack (>= 1.0)
|
82
|
-
railties (4.0.
|
83
|
-
actionpack (= 4.0.
|
84
|
-
activesupport (= 4.0.
|
80
|
+
railties (4.0.13)
|
81
|
+
actionpack (= 4.0.13)
|
82
|
+
activesupport (= 4.0.13)
|
85
83
|
rake (>= 0.8.7)
|
86
84
|
thor (>= 0.18.1, < 2.0)
|
87
|
-
rake (10.
|
85
|
+
rake (10.4.2)
|
88
86
|
rest-client (1.7.2)
|
89
87
|
mime-types (>= 1.16, < 3.0)
|
90
88
|
netrc (~> 0.7)
|
@@ -94,21 +92,18 @@ GEM
|
|
94
92
|
shoulda-context (1.2.1)
|
95
93
|
shoulda-matchers (2.7.0)
|
96
94
|
activesupport (>= 3.0.0)
|
97
|
-
simplecov (0.9.
|
95
|
+
simplecov (0.9.1)
|
98
96
|
docile (~> 1.1.0)
|
99
|
-
multi_json
|
97
|
+
multi_json (~> 1.0)
|
100
98
|
simplecov-html (~> 0.8.0)
|
101
99
|
simplecov-html (0.8.0)
|
102
|
-
sqlite3 (1.3.
|
100
|
+
sqlite3 (1.3.10)
|
103
101
|
term-ansicolor (1.3.0)
|
104
102
|
tins (~> 1.0)
|
105
103
|
thor (0.19.1)
|
106
104
|
thread_safe (0.3.4)
|
107
|
-
tins (1.3.
|
108
|
-
|
109
|
-
polyglot
|
110
|
-
polyglot (>= 0.3.1)
|
111
|
-
tzinfo (0.3.41)
|
105
|
+
tins (1.3.3)
|
106
|
+
tzinfo (0.3.42)
|
112
107
|
|
113
108
|
PLATFORMS
|
114
109
|
ruby
|
@@ -1,34 +1,34 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
random_unique_id (1.0
|
4
|
+
random_unique_id (1.1.0)
|
5
5
|
activerecord (>= 3.2.0)
|
6
6
|
activesupport (>= 3.2.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
actionmailer (4.1.
|
12
|
-
actionpack (= 4.1.
|
13
|
-
actionview (= 4.1.
|
14
|
-
mail (~> 2.5.4)
|
15
|
-
actionpack (4.1.
|
16
|
-
actionview (= 4.1.
|
17
|
-
activesupport (= 4.1.
|
11
|
+
actionmailer (4.1.9)
|
12
|
+
actionpack (= 4.1.9)
|
13
|
+
actionview (= 4.1.9)
|
14
|
+
mail (~> 2.5, >= 2.5.4)
|
15
|
+
actionpack (4.1.9)
|
16
|
+
actionview (= 4.1.9)
|
17
|
+
activesupport (= 4.1.9)
|
18
18
|
rack (~> 1.5.2)
|
19
19
|
rack-test (~> 0.6.2)
|
20
|
-
actionview (4.1.
|
21
|
-
activesupport (= 4.1.
|
20
|
+
actionview (4.1.9)
|
21
|
+
activesupport (= 4.1.9)
|
22
22
|
builder (~> 3.1)
|
23
23
|
erubis (~> 2.7.0)
|
24
|
-
activemodel (4.1.
|
25
|
-
activesupport (= 4.1.
|
24
|
+
activemodel (4.1.9)
|
25
|
+
activesupport (= 4.1.9)
|
26
26
|
builder (~> 3.1)
|
27
|
-
activerecord (4.1.
|
28
|
-
activemodel (= 4.1.
|
29
|
-
activesupport (= 4.1.
|
27
|
+
activerecord (4.1.9)
|
28
|
+
activemodel (= 4.1.9)
|
29
|
+
activesupport (= 4.1.9)
|
30
30
|
arel (~> 5.0.0)
|
31
|
-
activesupport (4.1.
|
31
|
+
activesupport (4.1.9)
|
32
32
|
i18n (~> 0.6, >= 0.6.9)
|
33
33
|
json (~> 1.7, >= 1.7.7)
|
34
34
|
minitest (~> 5.1)
|
@@ -41,7 +41,7 @@ GEM
|
|
41
41
|
thor (>= 0.14.0)
|
42
42
|
arel (5.0.1.20140414130214)
|
43
43
|
builder (3.2.2)
|
44
|
-
codeclimate-test-reporter (0.4.
|
44
|
+
codeclimate-test-reporter (0.4.4)
|
45
45
|
simplecov (>= 0.7.1, < 1.0.0)
|
46
46
|
coveralls (0.7.1)
|
47
47
|
multi_json (~> 1.3)
|
@@ -51,18 +51,17 @@ GEM
|
|
51
51
|
thor
|
52
52
|
docile (1.1.5)
|
53
53
|
erubis (2.7.0)
|
54
|
-
i18n (0.
|
54
|
+
i18n (0.7.0)
|
55
55
|
json (1.8.1)
|
56
|
-
mail (2.
|
57
|
-
mime-types (
|
58
|
-
treetop (~> 1.4.8)
|
56
|
+
mail (2.6.3)
|
57
|
+
mime-types (>= 1.16, < 3)
|
59
58
|
metaclass (0.0.4)
|
60
|
-
mime-types (
|
61
|
-
minitest (5.
|
62
|
-
minitest-rails (2.1.
|
59
|
+
mime-types (2.4.3)
|
60
|
+
minitest (5.5.0)
|
61
|
+
minitest-rails (2.1.1)
|
63
62
|
minitest (~> 5.4)
|
64
63
|
railties (~> 4.1)
|
65
|
-
minitest-reporters (1.0.
|
64
|
+
minitest-reporters (1.0.8)
|
66
65
|
ansi
|
67
66
|
builder
|
68
67
|
minitest (>= 5.0)
|
@@ -70,41 +69,37 @@ GEM
|
|
70
69
|
mocha (1.1.0)
|
71
70
|
metaclass (~> 0.0.1)
|
72
71
|
multi_json (1.10.1)
|
73
|
-
netrc (0.
|
74
|
-
polyglot (0.3.5)
|
72
|
+
netrc (0.10.2)
|
75
73
|
rack (1.5.2)
|
76
74
|
rack-test (0.6.2)
|
77
75
|
rack (>= 1.0)
|
78
|
-
railties (4.1.
|
79
|
-
actionpack (= 4.1.
|
80
|
-
activesupport (= 4.1.
|
76
|
+
railties (4.1.9)
|
77
|
+
actionpack (= 4.1.9)
|
78
|
+
activesupport (= 4.1.9)
|
81
79
|
rake (>= 0.8.7)
|
82
80
|
thor (>= 0.18.1, < 2.0)
|
83
|
-
rake (10.
|
81
|
+
rake (10.4.2)
|
84
82
|
rest-client (1.7.2)
|
85
83
|
mime-types (>= 1.16, < 3.0)
|
86
84
|
netrc (~> 0.7)
|
87
|
-
ruby-progressbar (1.
|
85
|
+
ruby-progressbar (1.7.1)
|
88
86
|
shoulda (3.5.0)
|
89
87
|
shoulda-context (~> 1.0, >= 1.0.1)
|
90
88
|
shoulda-matchers (>= 1.4.1, < 3.0)
|
91
89
|
shoulda-context (1.2.1)
|
92
90
|
shoulda-matchers (2.7.0)
|
93
91
|
activesupport (>= 3.0.0)
|
94
|
-
simplecov (0.9.
|
92
|
+
simplecov (0.9.1)
|
95
93
|
docile (~> 1.1.0)
|
96
|
-
multi_json
|
94
|
+
multi_json (~> 1.0)
|
97
95
|
simplecov-html (~> 0.8.0)
|
98
96
|
simplecov-html (0.8.0)
|
99
|
-
sqlite3 (1.3.
|
97
|
+
sqlite3 (1.3.10)
|
100
98
|
term-ansicolor (1.3.0)
|
101
99
|
tins (~> 1.0)
|
102
100
|
thor (0.19.1)
|
103
101
|
thread_safe (0.3.4)
|
104
|
-
tins (1.3.
|
105
|
-
treetop (1.4.15)
|
106
|
-
polyglot
|
107
|
-
polyglot (>= 0.3.1)
|
102
|
+
tins (1.3.3)
|
108
103
|
tzinfo (1.2.2)
|
109
104
|
thread_safe (~> 0.1)
|
110
105
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
random_unique_id (1.0
|
4
|
+
random_unique_id (1.1.0)
|
5
5
|
activerecord (>= 3.2.0)
|
6
6
|
activesupport (>= 3.2.0)
|
7
7
|
|
@@ -50,7 +50,7 @@ GEM
|
|
50
50
|
thor (>= 0.14.0)
|
51
51
|
arel (6.0.0)
|
52
52
|
builder (3.2.2)
|
53
|
-
codeclimate-test-reporter (0.4.
|
53
|
+
codeclimate-test-reporter (0.4.4)
|
54
54
|
simplecov (>= 0.7.1, < 1.0.0)
|
55
55
|
coveralls (0.7.1)
|
56
56
|
multi_json (~> 1.3)
|
@@ -70,7 +70,7 @@ GEM
|
|
70
70
|
mime-types (>= 1.16, < 3)
|
71
71
|
metaclass (0.0.4)
|
72
72
|
mime-types (2.4.3)
|
73
|
-
mini_portile (0.6.
|
73
|
+
mini_portile (0.6.2)
|
74
74
|
minitest (5.5.0)
|
75
75
|
minitest-rails (2.1.1)
|
76
76
|
minitest (~> 5.4)
|
data/lib/random_unique_id.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
# Copyright © 2011, 2012, 2013, 2014, Watu
|
2
|
+
# Copyright © 2011, 2012, 2013, 2014, 2015, Watu
|
3
3
|
|
4
4
|
require "random_unique_id/version"
|
5
5
|
require "securerandom"
|
@@ -9,6 +9,27 @@ require "active_record"
|
|
9
9
|
module RandomUniqueId
|
10
10
|
extend ActiveSupport::Concern
|
11
11
|
|
12
|
+
@@config = nil
|
13
|
+
|
14
|
+
# The global configuration for RandomUniqueID.
|
15
|
+
# Set it in initializers
|
16
|
+
#
|
17
|
+
# RandomUniqueId.config(field: :rid,
|
18
|
+
# random_generation_method: :short,
|
19
|
+
# min_rid_length: 5)
|
20
|
+
#
|
21
|
+
# @param [Hash] options
|
22
|
+
# @option options [Symbol] field the name of the field where the random unique id is stored.
|
23
|
+
# @option options [Symbol] random_generation_method the method to generate random IDs, `:short` or `:uuid`.
|
24
|
+
# `:short` will generate a short-ish random ID, and check that it is unique
|
25
|
+
# `:uuid` will generate a UUID, and skip the check. This is better for performance, and bad for readability of IDs
|
26
|
+
# @option options [FixNum] min_rid_length the minimum length RandomUniqueID will generate. Defaults to 5
|
27
|
+
# @return [Hash] the configuration.
|
28
|
+
def self.config(options={})
|
29
|
+
@@config ||= {field: :rid, random_generation_method: :short, min_rid_length: 5}
|
30
|
+
@@config = @@config.merge(options)
|
31
|
+
end
|
32
|
+
|
12
33
|
# Collection of methods that will end as class methods of ActiveRecord::Base.
|
13
34
|
#
|
14
35
|
# @see ActiveSupport::Concern
|
@@ -25,10 +46,14 @@ module RandomUniqueId
|
|
25
46
|
# has_random_unique_id
|
26
47
|
# # ... other stuff
|
27
48
|
# end
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
49
|
+
#
|
50
|
+
# @param options [Hash] generation options, same as RandomUniqueID.config, in case the generation method or minimum
|
51
|
+
# length needs to be overridden for one specific model
|
52
|
+
def has_random_unique_id(options={})
|
53
|
+
options = RandomUniqueId.config.merge(options)
|
54
|
+
before_validation :populate_rid_field, if: Proc.new { |r| r.send(options[:field]).blank? }
|
55
|
+
add_rid_related_validations(options)
|
56
|
+
add_rid_related_methods(options)
|
32
57
|
end
|
33
58
|
|
34
59
|
# Augment the ActiveRecord belongs_to to also define rid accessors. For example: if you blog post belongs_to an
|
@@ -70,9 +95,9 @@ module RandomUniqueId
|
|
70
95
|
def populate_random_unique_ids
|
71
96
|
find_each do |record|
|
72
97
|
rid_just_populated = false
|
73
|
-
if record.
|
74
|
-
record.
|
75
|
-
record.update_column(:
|
98
|
+
if record.send(record.random_unique_id_options[:field]).blank?
|
99
|
+
record.populate_rid_field
|
100
|
+
record.update_column(record.random_unique_id_options[:field], record.send(record.random_unique_id_options[:field]))
|
76
101
|
rid_just_populated = true
|
77
102
|
end
|
78
103
|
yield(record, rid_just_populated) if block_given?
|
@@ -81,6 +106,20 @@ module RandomUniqueId
|
|
81
106
|
|
82
107
|
private
|
83
108
|
|
109
|
+
# Add the rid related methods to the model.
|
110
|
+
# @param options [Hash] same as in RandomUniqueID.config
|
111
|
+
def add_rid_related_methods(options)
|
112
|
+
define_method(:to_param) { send(options[:field]) }
|
113
|
+
define_method(:random_unique_id_options) { options } # I don't think this is the best way to store this, but I didn't find a better one.
|
114
|
+
end
|
115
|
+
|
116
|
+
# Add the rid related validations to the model.
|
117
|
+
# @param options [Hash] same as in RandomUniqueID.config
|
118
|
+
def add_rid_related_validations(options)
|
119
|
+
validates(options[:field], presence: true)
|
120
|
+
validates(options[:field], uniqueness: true) if options[:random_generation_method] != :uuid # If we're generating UUIDs, don't check for uniqueness
|
121
|
+
end
|
122
|
+
|
84
123
|
# Defines the setter and getter for the RID of a relationship.
|
85
124
|
#
|
86
125
|
# @param related_class [Class] class in which the RID methods are going to be defined.
|
@@ -88,7 +127,7 @@ module RandomUniqueId
|
|
88
127
|
# @see RandomUniqueId::ClassMethods.belongs_to
|
89
128
|
def define_rid_accessors(related_class, relationship_name)
|
90
129
|
define_method("#{relationship_name}_rid") do
|
91
|
-
self.send(relationship_name).try(:
|
130
|
+
self.send(relationship_name).try(random_unique_id_options[:field])
|
92
131
|
end
|
93
132
|
|
94
133
|
define_method("#{relationship_name}_rid=") do |rid|
|
@@ -101,43 +140,70 @@ module RandomUniqueId
|
|
101
140
|
|
102
141
|
# Generate and store the random unique id for the object.
|
103
142
|
#
|
104
|
-
# @param
|
143
|
+
# @param length [Integer] how long should the random string be. Only applicable for `:short` type.
|
105
144
|
# @param field [String] name of the field that contains the rid.
|
106
145
|
# @return [String] the random string.
|
107
146
|
# @see RandomUniqueId::ClassMethods#has_random_unique_id
|
108
147
|
# @see RandomUniqueId.generate_random_id
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
klass = k
|
118
|
-
end
|
148
|
+
def populate_rid_field(length=random_unique_id_options[:min_rid_length], field=random_unique_id_options[:field])
|
149
|
+
case random_unique_id_options[:random_generation_method]
|
150
|
+
when :short
|
151
|
+
self.send("#{field}=", generate_short_random_unique_id(length, field))
|
152
|
+
when :uuid
|
153
|
+
self.send("#{field}=", RandomUniqueId.generate_uuid)
|
154
|
+
else
|
155
|
+
raise "Invalid random generation method: #{self.random_unique_id_options[:random_generation_method]}"
|
119
156
|
end
|
157
|
+
end
|
120
158
|
|
159
|
+
# Generate random ids, increasing their size, until one is found that is not used for another record in the database.
|
160
|
+
# @param length [Integer] how long should the random string be.
|
161
|
+
# @param field [String] name of the field that contains the rid.
|
162
|
+
def generate_short_random_unique_id(length, field)
|
163
|
+
potential_unique_random_id = nil
|
121
164
|
begin
|
122
|
-
|
123
|
-
|
124
|
-
end while
|
165
|
+
potential_unique_random_id = RandomUniqueId.generate_short_random_id(length)
|
166
|
+
length += 1
|
167
|
+
end while topmost_model_class.unscoped.where(field => potential_unique_random_id).exists?
|
168
|
+
potential_unique_random_id
|
169
|
+
end
|
170
|
+
|
171
|
+
# Find the topmost class before ActiveRecord::Base so that when we do queries, we don't end up with type=Whatever in
|
172
|
+
# the where clause.
|
173
|
+
# @return [Class] the class object
|
174
|
+
def topmost_model_class
|
175
|
+
@topmost_model_class ||= begin
|
176
|
+
klass = self.class
|
177
|
+
self.class.ancestors.select { |k| k.is_a? Class }.each do |k|
|
178
|
+
if k == ActiveRecord::Base
|
179
|
+
return klass
|
180
|
+
end
|
181
|
+
klass = k
|
182
|
+
end
|
183
|
+
end
|
125
184
|
end
|
126
185
|
|
127
186
|
# By a cunning use of SecureRandom.urlsafe_base64, quickly generate an alphanumeric random string.
|
128
187
|
#
|
129
|
-
# @param
|
188
|
+
# @param length [Integer] how long should the random string be.
|
130
189
|
# @return [String] the random string.
|
131
|
-
# @see RandomUniqueId#
|
132
|
-
def self.
|
190
|
+
# @see RandomUniqueId#populate_rid_field
|
191
|
+
def self.generate_short_random_id(length=10)
|
133
192
|
# IMPORTANT: don't ever generate dashes or underscores in the RIDs as they are likely to end up in the UI in Rails
|
134
193
|
# and they'll be converted to something else by jquery ujs or something like that.
|
135
194
|
generated_rid = ""
|
136
|
-
while generated_rid.length <
|
137
|
-
generated_rid = (generated_rid + SecureRandom.urlsafe_base64(
|
195
|
+
while generated_rid.length < length
|
196
|
+
generated_rid = (generated_rid + SecureRandom.urlsafe_base64(length * 3).downcase.gsub(/[^a-z0-9]/, ""))[0..(length-1)]
|
138
197
|
end
|
139
198
|
return generated_rid
|
140
199
|
end
|
200
|
+
|
201
|
+
# Generate a UUID. Just a wrapper around SecureRandom.uuid
|
202
|
+
# @return [String] the new UUID.
|
203
|
+
# @see RandomUniqueId#populate_rid_field
|
204
|
+
def self.generate_uuid
|
205
|
+
SecureRandom.uuid
|
206
|
+
end
|
141
207
|
end
|
142
208
|
|
143
209
|
ActiveRecord::Base.send(:include, RandomUniqueId)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright © 2014, Watu
|
1
|
+
# Copyright © 2014, 2015, Watu
|
2
2
|
|
3
3
|
# Some utilities that might be useful if you are using RandomUniqueId. This file needs to be explicitely included:
|
4
4
|
#
|
@@ -35,4 +35,4 @@ module RandomUniqueId::Util
|
|
35
35
|
raise "Don't know how to set initial ids for #{sql_connection.adapter_name}. Would you like to contribute? https://github.com/watu/random_unique_id"
|
36
36
|
end
|
37
37
|
end
|
38
|
-
end
|
38
|
+
end
|
data/random_unique_id.gemspec
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
# Copyright © 2013, 2014, Watu
|
2
|
+
# Copyright © 2013, 2014, 2015, Watu
|
3
3
|
|
4
4
|
lib = File.expand_path("../lib", __FILE__)
|
5
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
@@ -8,7 +8,7 @@ require "random_unique_id/version"
|
|
8
8
|
Gem::Specification.new do |spec|
|
9
9
|
spec.name = "random_unique_id"
|
10
10
|
spec.version = RandomUniqueId::VERSION
|
11
|
-
spec.authors = ["J. Pablo Fernández"]
|
11
|
+
spec.authors = ["J. Pablo Fernández", "Daniel Magliola"]
|
12
12
|
spec.email = ["pupeno@watuapp.com"]
|
13
13
|
spec.homepage = "https://github.com/watu/random_unique_id"
|
14
14
|
spec.summary = %q{Generate random but unique ids for your active record records.}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright © 2014, Watu
|
1
|
+
# Copyright © 2014, 2015, Watu
|
2
2
|
|
3
3
|
require_relative "../test_helper"
|
4
4
|
|
@@ -25,4 +25,4 @@ class RandomUniqueId::UtilTest < MiniTest::Unit::TestCase
|
|
25
25
|
ActiveRecord::Base.expects(:connection).returns(sql_connection)
|
26
26
|
RandomUniqueId::Util.set_initial_ids
|
27
27
|
end
|
28
|
-
end
|
28
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
# Copyright © 2011, 2012, 2013, 2014, Watu
|
2
|
+
# Copyright © 2011, 2012, 2013, 2014, 2015, Watu
|
3
3
|
|
4
4
|
require_relative "test_helper"
|
5
5
|
|
@@ -18,6 +18,20 @@ ActiveRecord::Schema.define(version: 0) do
|
|
18
18
|
t.integer :blog_id
|
19
19
|
end
|
20
20
|
add_index :posts, :rid, unique: true
|
21
|
+
|
22
|
+
create_table :comments do |t|
|
23
|
+
t.string :random_id
|
24
|
+
t.string :text
|
25
|
+
t.integer :post_id
|
26
|
+
end
|
27
|
+
add_index :comments, :random_id, unique: true
|
28
|
+
|
29
|
+
create_table :post_views do |t|
|
30
|
+
t.string :rid
|
31
|
+
t.string :ip_address
|
32
|
+
t.integer :post_id
|
33
|
+
end
|
34
|
+
add_index :post_views, :rid # NOT unique, for performance, it'll store UUIDs so we don't need to check
|
21
35
|
end
|
22
36
|
|
23
37
|
class Blog < ActiveRecord::Base
|
@@ -25,6 +39,28 @@ class Blog < ActiveRecord::Base
|
|
25
39
|
has_random_unique_id
|
26
40
|
end
|
27
41
|
|
42
|
+
class BlogWithInvalidGenerationMethod < ActiveRecord::Base
|
43
|
+
self.table_name = "blogs"
|
44
|
+
has_random_unique_id(random_generation_method: :invalid) # Used to test the exception when you specify the wrong method
|
45
|
+
end
|
46
|
+
|
47
|
+
original_config = RandomUniqueId.config
|
48
|
+
RandomUniqueId.config(random_generation_method: :uuid)
|
49
|
+
|
50
|
+
class BlogWithUuid < ActiveRecord::Base
|
51
|
+
self.table_name = "blogs"
|
52
|
+
has_random_unique_id
|
53
|
+
end
|
54
|
+
|
55
|
+
RandomUniqueId.config(random_generation_method: :short, min_rid_length: 12)
|
56
|
+
|
57
|
+
class BlogWithLongRid < ActiveRecord::Base
|
58
|
+
self.table_name = "blogs"
|
59
|
+
has_random_unique_id
|
60
|
+
end
|
61
|
+
|
62
|
+
RandomUniqueId.config(original_config)
|
63
|
+
|
28
64
|
class Post < ActiveRecord::Base
|
29
65
|
belongs_to :blog
|
30
66
|
has_random_unique_id
|
@@ -36,6 +72,15 @@ end
|
|
36
72
|
class ImagePost < Post
|
37
73
|
end
|
38
74
|
|
75
|
+
class Comment < ActiveRecord::Base
|
76
|
+
belongs_to :post
|
77
|
+
has_random_unique_id(field: :random_id, min_rid_length: 10) # Comments have longer RIDs, since it's a big table and we were getting lots of collissions
|
78
|
+
end
|
79
|
+
|
80
|
+
class PostView < ActiveRecord::Base
|
81
|
+
belongs_to :post
|
82
|
+
has_random_unique_id(random_generation_method: :uuid) # Post Views have UUIDs instead of RIDs, since the table is ginormous, so it can't check for existence every time.
|
83
|
+
end
|
39
84
|
|
40
85
|
class RandomUniqueIdTest < MiniTest::Unit::TestCase
|
41
86
|
context "With a record with random id" do
|
@@ -51,9 +96,9 @@ class RandomUniqueIdTest < MiniTest::Unit::TestCase
|
|
51
96
|
|
52
97
|
should "resolve random id collision" do
|
53
98
|
# Mock RandomUniqueId to return a collision on the first call, and hopefully a non collision on the second, expecting n to grow by one.
|
54
|
-
RandomUniqueId.expects(:
|
99
|
+
RandomUniqueId.expects(:generate_short_random_id).with(5).returns(@text_post.rid)
|
55
100
|
new_rid = @text_post.rid + "i"
|
56
|
-
RandomUniqueId.expects(:
|
101
|
+
RandomUniqueId.expects(:generate_short_random_id).with(6).returns(new_rid)
|
57
102
|
|
58
103
|
new_record = TextPost.create! # No exception should be raised.
|
59
104
|
assert_equal new_rid, new_record.rid
|
@@ -61,9 +106,9 @@ class RandomUniqueIdTest < MiniTest::Unit::TestCase
|
|
61
106
|
|
62
107
|
should "resolve random id collision in different classes of the same table (due to STI)" do
|
63
108
|
# Mock RandomUniqueId to return a collision on the first call, and hopefully a non collision on the second, expecting n to grow by one.
|
64
|
-
RandomUniqueId.expects(:
|
109
|
+
RandomUniqueId.expects(:generate_short_random_id).with(5).returns(@text_post.rid)
|
65
110
|
new_rid = @text_post.rid + "i"
|
66
|
-
RandomUniqueId.expects(:
|
111
|
+
RandomUniqueId.expects(:generate_short_random_id).with(6).returns(new_rid)
|
67
112
|
|
68
113
|
new_record = ImagePost.create! # No exception should be raised.
|
69
114
|
assert_equal new_rid, new_record.rid
|
@@ -80,18 +125,76 @@ class RandomUniqueIdTest < MiniTest::Unit::TestCase
|
|
80
125
|
end
|
81
126
|
|
82
127
|
should "populate a table with rids" do
|
83
|
-
# Create a bunch of blogs without rid by manually inserting them into the
|
128
|
+
# Create a bunch of blogs without rid by manually inserting them into the table.
|
84
129
|
rid_less_records = 10
|
85
130
|
5.times { Blog.create! }
|
86
131
|
existing_rids = Blog.all.map(&:rid).compact
|
87
132
|
rid_less_records.times { Blog.connection.execute("INSERT INTO blogs (name) VALUES ('Blag')") }
|
88
|
-
assert_equal rid_less_records, Blog.where(:
|
133
|
+
assert_equal rid_less_records, Blog.where(rid: nil).count # Just to be sure this test is being effective.
|
89
134
|
|
90
135
|
rids_populated = 0
|
91
136
|
Blog.populate_random_unique_ids { |_, rid_just_populated| rids_populated += 1 if rid_just_populated }
|
92
137
|
assert_equal rid_less_records, rids_populated
|
93
|
-
assert_equal 0, Blog.where(:
|
94
|
-
assert_equal existing_rids.count, Blog.where(:
|
138
|
+
assert_equal 0, Blog.where(rid: nil).count
|
139
|
+
assert_equal existing_rids.count, Blog.where(rid: existing_rids).count # Make sure the existing rids where not touched.
|
140
|
+
end
|
141
|
+
|
142
|
+
should "populate a table with rids with custom field name" do
|
143
|
+
# Create a bunch of comments without rid by manually inserting them into the table
|
144
|
+
rid_less_records = 10
|
145
|
+
blog = Blog.create!
|
146
|
+
post = Post.create!(blog: blog)
|
147
|
+
5.times { Comment.create!(post: post) }
|
148
|
+
existing_rids = Comment.all.map(&:random_id).compact
|
149
|
+
rid_less_records.times { Comment.connection.execute("INSERT INTO comments (post_id) VALUES (#{post.id})") }
|
150
|
+
assert_equal rid_less_records, Comment.where(random_id: nil).count # Just to be sure this test is being effective.
|
151
|
+
|
152
|
+
rids_populated = 0
|
153
|
+
Comment.populate_random_unique_ids { |_, rid_just_populated| rids_populated += 1 if rid_just_populated }
|
154
|
+
assert_equal rid_less_records, rids_populated
|
155
|
+
assert_equal 0, Comment.where(random_id: nil).count
|
156
|
+
assert_equal existing_rids.count, Comment.where(random_id: existing_rids).count # Make sure the existing rids where not touched.
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "With an invalid generation method" do
|
161
|
+
should "Raise exception on RID geneartion" do
|
162
|
+
assert_raises RuntimeError do
|
163
|
+
BlogWithInvalidGenerationMethod.create!
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Tests for configuration options, both global and model overrides
|
169
|
+
context "With a global configuration for UUIDs" do
|
170
|
+
should "generate UUID" do
|
171
|
+
blog = BlogWithUuid.create!
|
172
|
+
assert_match /(\w{8}(-\w{4}){3}-\w{12}?)/, blog.rid
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context "With a global configuration for long RIDs" do
|
177
|
+
should "generate long RID" do
|
178
|
+
blog = BlogWithLongRid.create!
|
179
|
+
assert blog.rid.length >= 12, "RID must be at least 12 chars long"
|
180
|
+
assert !(blog.rid =~ /(\w{8}(-\w{4}){3}-\w{12}?)/), "RID must not be a UUID"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context "With models that have overrides for RID configuration" do
|
185
|
+
should "Generate short RID for normal model" do
|
186
|
+
blog = Blog.create!
|
187
|
+
assert_equal 5, blog.rid.length
|
188
|
+
end
|
189
|
+
|
190
|
+
should "Generate long RID for model that requested min_rid_length" do
|
191
|
+
comment = Comment.create!
|
192
|
+
assert_equal 10, comment.random_id.length
|
193
|
+
end
|
194
|
+
|
195
|
+
should "Generate UUID for model that requested UUID random_generation_method" do
|
196
|
+
postview = PostView.create!
|
197
|
+
assert_match /(\w{8}(-\w{4}){3}-\w{12}?)/, postview.rid
|
95
198
|
end
|
96
199
|
end
|
97
200
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: random_unique_id
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- J. Pablo Fernández
|
8
|
+
- Daniel Magliola
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date:
|
12
|
+
date: 2015-01-07 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: activesupport
|