cable_ready 1.0.0 → 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/Gemfile.lock +2 -2
- data/README.md +131 -31
- data/lib/cable_ready/broadcaster.rb +67 -15
- data/lib/cable_ready/version.rb +1 -1
- data/vendor/assets/javascripts/cable_ready.js +71 -29
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4af592b2c92fa517e06a99e0f7c352be90c65ca7
|
4
|
+
data.tar.gz: 6f471df22a630daed758c14b8a141dfc4584a3bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e33789c536e4d878808de59eac1db026224bb0c61eb948d3adc13ff93f0768ed3adc7f0dd971c51f13b82765c5e1bbb443e5f0b8c311b543d6f092b7109b814d
|
7
|
+
data.tar.gz: 005ce8accba916cf3e297d5262c4e51c1ddd1eba242a8e189c4548055c68492b977c4640046d8aaa66bef40522101a3af16496c006dffd8c85ab84eef4912aef
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,81 +1,181 @@
|
|
1
|
-
[](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
|
2
2
|
[](https://codeclimate.com/github/hopsoft/cable_ready)
|
3
3
|
[](https://gemnasium.com/hopsoft/cable_ready)
|
4
4
|
|
5
5
|
# CableReady
|
6
6
|
|
7
7
|
CableReady provides a standard interface for invoking common client-side DOM operations
|
8
|
-
from the server via
|
8
|
+
from the server via [ActionCable](http://guides.rubyonrails.org/action_cable_overview.html).
|
9
9
|
|
10
|
-
|
10
|
+
Learn more about CableReady by reading through & running the [CableReady Test](https://github.com/hopsoft/cable_ready_test) project.
|
11
11
|
|
12
12
|
## Supported DOM Operations
|
13
13
|
|
14
|
-
|
14
|
+
> The `selector` options use [Document.querySelector()](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) to find elements.
|
15
|
+
|
16
|
+
> It's possible to invoke multiple DOM operations with a single ActionCable broadcast.
|
17
|
+
|
18
|
+
### DOM Events
|
19
|
+
|
20
|
+
#### [dispatchEvent](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent)
|
15
21
|
|
16
22
|
Dispatches a DOM event in the browser.
|
17
23
|
|
18
24
|
```ruby
|
19
25
|
cable_ready_broadcast "MyChannel", dispatch_event: [{
|
20
|
-
|
21
|
-
|
22
|
-
|
26
|
+
name: "string", # required - the name of the DOM event to dispatch (can be custom)
|
27
|
+
detail: "object", # [null] - assigned to event.detail
|
28
|
+
selector: "string" # [window] - string containing one or more CSS selectors separated by commas
|
23
29
|
}]
|
24
30
|
```
|
25
31
|
|
26
|
-
###
|
32
|
+
### Element Mutations
|
33
|
+
|
34
|
+
#### [innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)
|
27
35
|
|
28
36
|
Sets the innerHTML of a DOM element.
|
29
37
|
|
30
38
|
```ruby
|
31
39
|
cable_ready_broadcast "MyChannel", inner_html: [{
|
32
|
-
|
33
|
-
|
40
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
41
|
+
focusSelector: "string", # [null] - string containing one or more CSS selectors separated by commas
|
42
|
+
html: "string" # [null] - the HTML to assign
|
34
43
|
}]
|
35
44
|
```
|
36
45
|
|
37
|
-
|
46
|
+
#### [textContent](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)
|
47
|
+
|
48
|
+
Sets the text content of a DOM element.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
cable_ready_broadcast "MyChannel", text_content: [{
|
52
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
53
|
+
text: "string" # [null] - the text to assign
|
54
|
+
}]
|
55
|
+
```
|
56
|
+
|
57
|
+
#### [insertAdjacentHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML)
|
38
58
|
|
39
59
|
Inserts HTML into the DOM relative to an element.
|
40
60
|
Supports behavior akin to prepend & append.
|
41
61
|
|
42
62
|
```ruby
|
43
63
|
cable_ready_broadcast "MyChannel", insert_adjacent_html: [{
|
44
|
-
|
45
|
-
|
46
|
-
|
64
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
65
|
+
focusSelector: "string", # [null] - string containing one or more CSS selectors separated by commas
|
66
|
+
position: "string", # [beforeend] - the relative position to the DOM element (beforebegin, afterbegin, beforeend, afterend)
|
67
|
+
html: "string" # [null] - the HTML to insert
|
47
68
|
}]
|
48
69
|
```
|
49
70
|
|
50
|
-
|
71
|
+
#### [insertAdjacentText](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentText)
|
72
|
+
|
73
|
+
Inserts text into the DOM relative to an element.
|
74
|
+
Supports behavior akin to prepend & append.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
cable_ready_broadcast "MyChannel", insert_adjacent_text: [{
|
78
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
79
|
+
position: "string", # [beforeend] - the relative position to the DOM element (beforebegin, afterbegin, beforeend, afterend)
|
80
|
+
text: "string" # [null] - the text to insert
|
81
|
+
}]
|
82
|
+
```
|
83
|
+
|
84
|
+
#### [remove](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove)
|
51
85
|
|
52
86
|
Removes an element from the DOM.
|
53
87
|
|
54
88
|
```ruby
|
55
89
|
cable_ready_broadcast "MyChannel", remove: [{
|
56
|
-
|
90
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
91
|
+
focusSelector: "string" # [null] - string containing one or more CSS selectors separated by commas
|
57
92
|
}]
|
58
93
|
```
|
59
94
|
|
60
|
-
|
95
|
+
#### [replace](https://developer.mozilla.org/en-US/docs/Web/API/Node/replaceChild)
|
61
96
|
|
62
97
|
Replaces a DOM element with new HTML.
|
63
98
|
|
64
99
|
```ruby
|
65
100
|
cable_ready_broadcast "MyChannel", replace: [{
|
66
|
-
|
67
|
-
|
101
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
102
|
+
focusSelector: "string", # [null] - string containing one or more CSS selectors separated by commas
|
103
|
+
html: "string" # [null] - the HTML to use as replacement
|
68
104
|
}]
|
69
105
|
```
|
70
106
|
|
71
|
-
|
107
|
+
#### [setValue](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement)
|
72
108
|
|
73
|
-
Sets the
|
109
|
+
Sets the value of an element.
|
74
110
|
|
75
111
|
```ruby
|
76
|
-
cable_ready_broadcast "MyChannel",
|
77
|
-
|
78
|
-
|
112
|
+
cable_ready_broadcast "MyChannel", set_value: [{
|
113
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
114
|
+
value: "string" # [null] - the value to assign to the attribute
|
115
|
+
}]
|
116
|
+
```
|
117
|
+
|
118
|
+
### Attribute Mutations
|
119
|
+
|
120
|
+
#### [setAttribute](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute)
|
121
|
+
|
122
|
+
Sets an attribute on an element.
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
cable_ready_broadcast "MyChannel", set_attribute: [{
|
126
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
127
|
+
name: "string", # required - the attribute to set
|
128
|
+
value: "string" # [null] - the value to assign to the attribute
|
129
|
+
}]
|
130
|
+
```
|
131
|
+
|
132
|
+
#### [removeAttribute](https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute)
|
133
|
+
|
134
|
+
Removes an attribute from an element.
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
cable_ready_broadcast "MyChannel", remove_attribute: [{
|
138
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
139
|
+
name: "string" # required - the attribute to remove
|
140
|
+
}]
|
141
|
+
```
|
142
|
+
|
143
|
+
### CSS Class Mutations
|
144
|
+
|
145
|
+
#### [addCssClass](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList)
|
146
|
+
|
147
|
+
Adds a css class to an element.
|
148
|
+
This is a `noop` if the css class is already assigned.
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
cable_ready_broadcast "MyChannel", add_css_class: [{
|
152
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
153
|
+
name: "string" # [null] - the CSS class to add
|
154
|
+
}]
|
155
|
+
|
156
|
+
```
|
157
|
+
#### [removeCssClass](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList)
|
158
|
+
|
159
|
+
Removes a css class from an element.
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
cable_ready_broadcast "MyChannel", add_css_class: [{
|
163
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
164
|
+
name: "string" # [null] - the CSS class to remove
|
165
|
+
}]
|
166
|
+
```
|
167
|
+
|
168
|
+
### Dataset Mutations
|
169
|
+
|
170
|
+
#### [setDatasetProperty](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset)
|
171
|
+
|
172
|
+
Sets an dataset property (data-* attribute) on an element.
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
cable_ready_broadcast "MyChannel", set_dataset_property: [{
|
176
|
+
selector: "string", # required - string containing one or more CSS selectors separated by commas
|
177
|
+
name: "string", # required - the property to set
|
178
|
+
value: "string" # [null] - the value to assign to the dataset
|
79
179
|
}]
|
80
180
|
```
|
81
181
|
|
@@ -89,7 +189,7 @@ class User < ApplicationRecord
|
|
89
189
|
include CableReady::Broadcaster
|
90
190
|
|
91
191
|
def broadcast_name_change
|
92
|
-
cable_ready_broadcast "UserChannel", text_content: [{
|
192
|
+
cable_ready_broadcast "UserChannel", text_content: [{ selector: "#user-name", text: name }]
|
93
193
|
end
|
94
194
|
end
|
95
195
|
```
|
@@ -114,11 +214,11 @@ App.cable.subscriptions.create({ channel: "UserChannel" }, {
|
|
114
214
|
});
|
115
215
|
```
|
116
216
|
|
117
|
-
## Testing & Experimenting
|
118
|
-
|
119
|
-
Learn more about CableReady by running the [CableReady Test](https://github.com/hopsoft/cable_ready_test) project.
|
120
|
-
|
121
217
|
## Advanced Usage
|
122
218
|
|
123
|
-
|
124
|
-
[SelfRenderer](https://github.com/hopsoft/self_renderer)
|
219
|
+
Consider using CableReady in concert with a gem like
|
220
|
+
[SelfRenderer](https://github.com/hopsoft/self_renderer) to create a powerful SPA style user experience with the simplicity of server side rendering.
|
221
|
+
|
222
|
+
<a target='_blank' rel='nofollow' href='https://app.codesponsor.io/link/QMSjMHrtPhvfmCnk5Hbikhhr/hopsoft/cable_ready'>
|
223
|
+
<img alt='Sponsor' width='888' height='68' src='https://app.codesponsor.io/embed/QMSjMHrtPhvfmCnk5Hbikhhr/hopsoft/cable_ready.svg' />
|
224
|
+
</a>
|
@@ -5,36 +5,88 @@ module CableReady
|
|
5
5
|
# Example Payload:
|
6
6
|
#
|
7
7
|
# {
|
8
|
+
# # DOM Events ..................................................................................................
|
9
|
+
#
|
8
10
|
# dispatch_event: [{
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
11
|
+
# name: "string",
|
12
|
+
# detail: "object",
|
13
|
+
# selector: "string",
|
12
14
|
# }, ...],
|
13
15
|
#
|
16
|
+
# # Element Mutations ...........................................................................................
|
17
|
+
#
|
14
18
|
# inner_html: [{
|
15
|
-
#
|
16
|
-
#
|
19
|
+
# selector: "string",
|
20
|
+
# focusSelector: "string",
|
21
|
+
# html: "string"
|
17
22
|
# }, ...],
|
18
23
|
#
|
24
|
+
# text_content: [{
|
25
|
+
# selector: "string",
|
26
|
+
# text: "string"
|
27
|
+
# }, ...]
|
28
|
+
#
|
19
29
|
# insert_adjacent_html: [{
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
30
|
+
# selector: "string",
|
31
|
+
# focusSelector: "string",
|
32
|
+
# position: "string",
|
33
|
+
# html: "string"
|
34
|
+
# }, ...],
|
35
|
+
#
|
36
|
+
# insert_adjacent_text: [{
|
37
|
+
# selector: "string",
|
38
|
+
# position: "string",
|
39
|
+
# text: "string"
|
23
40
|
# }, ...],
|
24
41
|
#
|
25
42
|
# remove: [{
|
26
|
-
#
|
43
|
+
# selector: "string",
|
44
|
+
# focusSelector: "string,
|
27
45
|
# }, ...],
|
28
46
|
#
|
29
47
|
# replace: [{
|
30
|
-
#
|
31
|
-
#
|
48
|
+
# selector: "string",
|
49
|
+
# focusSelector: "string",
|
50
|
+
# html: "string"
|
32
51
|
# }, ...],
|
33
52
|
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
# }, ...]
|
53
|
+
# set_value: [{
|
54
|
+
# selector: "string",
|
55
|
+
# value: "string"
|
56
|
+
# }, ...],
|
57
|
+
#
|
58
|
+
# # Attribute Mutations .........................................................................................
|
59
|
+
#
|
60
|
+
# set_attribute: [{
|
61
|
+
# selector: "string",
|
62
|
+
# name: "string",
|
63
|
+
# value: "string"
|
64
|
+
# }, ...],
|
65
|
+
#
|
66
|
+
# remove_attribute: [{
|
67
|
+
# selector: "string",
|
68
|
+
# name: "string"
|
69
|
+
# }, ...],
|
70
|
+
#
|
71
|
+
# # CSS Class Mutations .........................................................................................
|
72
|
+
#
|
73
|
+
# add_css_class: [{
|
74
|
+
# selector: "string",
|
75
|
+
# name: "string"
|
76
|
+
# }, ...],
|
77
|
+
#
|
78
|
+
# remove_css_class: [{
|
79
|
+
# selector: "string",
|
80
|
+
# name: "string"
|
81
|
+
# }, ...],
|
82
|
+
#
|
83
|
+
# # Dataset Mutations ...........................................................................................
|
84
|
+
#
|
85
|
+
# set_dataset_property: [{
|
86
|
+
# selector: "string",
|
87
|
+
# name: "string",
|
88
|
+
# value: "string"
|
89
|
+
# }, ...],
|
38
90
|
# }
|
39
91
|
def cable_ready_broadcast(channel, operations={})
|
40
92
|
operations ||= {}
|
data/lib/cable_ready/version.rb
CHANGED
@@ -1,52 +1,90 @@
|
|
1
1
|
(function () {
|
2
2
|
"use strict";
|
3
3
|
|
4
|
-
function log (operation, config) {
|
5
|
-
if (window.CableReady.debug) {
|
6
|
-
console.log("CableReady", operation, config);
|
7
|
-
}
|
8
|
-
}
|
9
|
-
|
10
4
|
var CableReadyOperations = {
|
5
|
+
// DOM Events .....................................................................................................
|
6
|
+
|
11
7
|
dispatchEvent: function (config) {
|
12
|
-
log("dispatchEvent", config);
|
13
|
-
var
|
14
|
-
var
|
15
|
-
var event = new Event(config.eventName);
|
8
|
+
if (CableReady.debug) { console.log("CableReady.dispatchEvent", config); }
|
9
|
+
var target = document.querySelector(config.selector) || window;
|
10
|
+
var event = new Event(config.name);
|
16
11
|
event.detail = config.detail;
|
17
12
|
target.dispatchEvent(event);
|
18
13
|
},
|
19
14
|
|
15
|
+
// Element Mutations ..............................................................................................
|
16
|
+
|
20
17
|
innerHtml: function (config) {
|
21
|
-
log("innerHTML", config);
|
22
|
-
|
23
|
-
|
18
|
+
if (CableReady.debug) { console.log("CableReady.innerHTML", config); }
|
19
|
+
document.querySelector(config.selector).innerHTML = config.html;
|
20
|
+
if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
|
21
|
+
},
|
22
|
+
|
23
|
+
textContent: function (config) {
|
24
|
+
if (CableReady.debug) { console.log("CableReady.textContent", config); }
|
25
|
+
document.querySelector(config.selector).textContent = config.text;
|
24
26
|
},
|
25
27
|
|
26
28
|
insertAdjacentHtml: function (config) {
|
27
|
-
log("insertAdjacentHTML", config);
|
28
|
-
|
29
|
-
|
29
|
+
if (CableReady.debug) { console.log("CableReady.insertAdjacentHTML", config); }
|
30
|
+
document.querySelector(config.selector).insertAdjacentHTML(config.position || "beforeend", config.html);
|
31
|
+
if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
|
32
|
+
},
|
33
|
+
|
34
|
+
insertAdjacentText: function (config) {
|
35
|
+
if (CableReady.debug) { console.log("CableReady.insertAdjacentText", config); }
|
36
|
+
document.querySelector(config.querySelector).insertAdjacentText(config.position || "beforeend", config.text);
|
30
37
|
},
|
31
38
|
|
32
39
|
remove: function (config) {
|
33
|
-
log("remove", config);
|
34
|
-
|
35
|
-
|
40
|
+
if (CableReady.debug) { console.log("CableReady.remove", config); }
|
41
|
+
document.querySelector(config.selector).remove();
|
42
|
+
if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
|
36
43
|
},
|
37
44
|
|
38
45
|
replace: function (config) {
|
39
|
-
log("replace", config);
|
40
|
-
var element
|
41
|
-
var
|
42
|
-
|
43
|
-
element.parentNode.replaceChild(
|
46
|
+
if (CableReady.debug) { console.log("CableReady.replace", config); }
|
47
|
+
var element = document.querySelector(config.selector);
|
48
|
+
var div = document.createElement("div");
|
49
|
+
div.innerHTML = config.html;
|
50
|
+
element.parentNode.replaceChild(div.firstElementChild, element);
|
51
|
+
if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
|
44
52
|
},
|
45
53
|
|
46
|
-
|
47
|
-
log("
|
48
|
-
|
49
|
-
|
54
|
+
setValue: function (config) {
|
55
|
+
if (CableReady.debug) { console.log("CableReady.setValue", config); }
|
56
|
+
document.querySelector(config.selector).value = config.value;
|
57
|
+
},
|
58
|
+
|
59
|
+
// Attribute Mutations ............................................................................................
|
60
|
+
|
61
|
+
setAttribute: function (config) {
|
62
|
+
if (CableReady.debug) { console.log("CableReady.setAttribute", config); }
|
63
|
+
document.querySelector(config.selector).setAttribute(config.name, config.value);
|
64
|
+
},
|
65
|
+
|
66
|
+
removeAttribute: function (config) {
|
67
|
+
if (CableReady.debug) { console.log("CableReady.removeAttribute", config); }
|
68
|
+
document.querySelector(config.selector).removeAttribute(config.name);
|
69
|
+
},
|
70
|
+
|
71
|
+
// CSS Class Mutations ............................................................................................
|
72
|
+
|
73
|
+
addCssClass: function (config) {
|
74
|
+
if (CableReady.debug) { console.log("CableReady.addCssClass", config); }
|
75
|
+
document.querySelector(config.selector).classList.add(config.name);
|
76
|
+
},
|
77
|
+
|
78
|
+
removeCssClass: function (config) {
|
79
|
+
if (CableReady.debug) { console.log("CableReady.removeCssClass", config); }
|
80
|
+
document.querySelector(config.selector).classList.remove(config.name);
|
81
|
+
},
|
82
|
+
|
83
|
+
// Dataset Mutations ..............................................................................................
|
84
|
+
|
85
|
+
setDatasetProperty: function (config) {
|
86
|
+
if (CableReady.debug) { console.log("CableReady.setDatasetProperty", config); }
|
87
|
+
document.querySelector(config.selector).dataset[config.name] = config.value;
|
50
88
|
}
|
51
89
|
};
|
52
90
|
|
@@ -57,7 +95,11 @@
|
|
57
95
|
if (operations.hasOwnProperty(name)) {
|
58
96
|
var entries = operations[name];
|
59
97
|
for (var i = 0; i < entries.length; i++) {
|
60
|
-
|
98
|
+
try {
|
99
|
+
CableReadyOperations[name](entries[i]);
|
100
|
+
} catch (e) {
|
101
|
+
console.log("CableReady detected an error! " + e.message);
|
102
|
+
}
|
61
103
|
}
|
62
104
|
}
|
63
105
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cable_ready
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Hopkins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
11
|
+
date: 2017-09-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.0.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 5.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|