hexp 0.4.1 → 0.4.2
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/.travis.yml +3 -0
- data/Changelog.md +11 -2
- data/README.md +177 -71
- data/hexp.gemspec +1 -0
- data/lib/hexp.rb +2 -1
- data/lib/hexp/list.rb +4 -0
- data/lib/hexp/node.rb +6 -5
- data/lib/hexp/node/attributes.rb +3 -3
- data/lib/hexp/unparser.rb +8 -2
- data/lib/hexp/version.rb +1 -1
- data/spec/unit/hexp/node/attributes_spec.rb +0 -4
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe165e0149eb663683b10c6bbea75d2e594171c2
|
4
|
+
data.tar.gz: d0678b856d53b37e98652d7a987ced5be71ae220
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19bf3b836ac3a73e7b033d9e75c3323f85813660d56624b9f704b3d60c0ae74563c95ad6a64d77f6419330ce2592e4cecae4981e713e740ccb5e70545d37dc40
|
7
|
+
data.tar.gz: 7188623fe858bfe9c84f1c6c4c83061e6535cfae4bcd1558b7296d489116e4190f4a93ab5745b12fc24e18e45e48a98bbfb19749ef60a7978b3bd0ffb168899e
|
data/.travis.yml
CHANGED
data/Changelog.md
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
### Development
|
2
2
|
|
3
|
-
[full diff](http://github.com/plexus/hexp/compare/v0.4.
|
3
|
+
[full diff](http://github.com/plexus/hexp/compare/v0.4.2...master)
|
4
|
+
|
5
|
+
### v0.4.2
|
6
|
+
|
7
|
+
* Added Hexp::List#append
|
8
|
+
* set_attr now simply replaces the full attribute hash, use
|
9
|
+
merge_attr for "smart" behavior. % is now an alias of merge_attr,
|
10
|
+
not set_attr
|
11
|
+
* Make the unparser aware of HTML "void" tags (tags that should not
|
12
|
+
have a closing tag)
|
4
13
|
|
5
14
|
### v0.4.1
|
6
15
|
|
@@ -8,7 +17,7 @@
|
|
8
17
|
* Add Hexp::Node#append as a convenient API for adding child nodes
|
9
18
|
* Make Unparser Adamantium-immutable so instances can be included in
|
10
19
|
Adamantiumized objects
|
11
|
-
* Skip escaping inside
|
20
|
+
* Skip escaping inside `<script>` tags
|
12
21
|
* Add a "rails integration" (ugh) to play nice with
|
13
22
|
ActiveSupport::SafeBuffer. use `gem 'hexp', require: 'hexp-rails'`
|
14
23
|
in your Gemfile
|
data/README.md
CHANGED
@@ -12,114 +12,220 @@
|
|
12
12
|
|
13
13
|
# Hexp
|
14
14
|
|
15
|
-
**Hexp** (pronounced [ˈɦækspi:]) is a
|
15
|
+
**Hexp** (pronounced [ˈɦækspi:]) is a DOM API for Ruby. It lets you treat HTML in your applications as objects, instead of strings. It is a standalone, framework independent library. You can use it to build full web pages, or to clean up your helpers and presenters.
|
16
16
|
|
17
|
-
|
17
|
+
## Fundamentals
|
18
18
|
|
19
|
-
|
20
|
-
* Security : Protection from XSS (cross-site scripting)
|
21
|
-
* Productivity : components that create or alter fragments of a HTML become generic, reusable parts
|
19
|
+
The three central classes are `Hexp::Node`, `Hexp::TextNode`, and `Hexp::List`. Instances of these classes are immutable. You can mostly treat `TextNode` as a `String` and `List` as an `Array`, except that they're immutable, and come with some extra convenience functions.
|
22
20
|
|
23
|
-
|
21
|
+
Take this bit of HTML
|
24
22
|
|
25
|
-
|
23
|
+
``` html
|
24
|
+
<nav id="menu">
|
25
|
+
<ul>
|
26
|
+
<li>Home</li>
|
27
|
+
<li>Lolcats</li>
|
28
|
+
<li>Games</li>
|
29
|
+
</ul>
|
30
|
+
</nav>
|
31
|
+
```
|
26
32
|
|
27
|
-
|
33
|
+
If we would spell out all the objects, it would be represented as
|
28
34
|
|
29
|
-
|
30
|
-
|
31
|
-
@hexp = H[:div, {class: 'hexp-intro'}, [
|
32
|
-
[:p, @message]
|
33
|
-
]
|
34
|
-
]
|
35
|
-
````
|
35
|
+
``` ruby
|
36
|
+
include Hexp
|
36
37
|
|
37
|
-
|
38
|
+
Node.new(:nav, {"id"=>"menu"},
|
39
|
+
List.new([
|
40
|
+
Node.new(:ul, {},
|
41
|
+
List.new([
|
42
|
+
Node.new(:li, {}, List.new([TextNode.new("Home")])),
|
43
|
+
Node.new(:li, {}, List.new([TextNode.new("lolcats")])),
|
44
|
+
Node.new(:li, {}, List.new([TextNode.new("Games")]))
|
45
|
+
])
|
46
|
+
)
|
47
|
+
])
|
48
|
+
)
|
49
|
+
```
|
38
50
|
|
39
|
-
|
51
|
+
The `Hexp::Node` constructor is lenient though. It knows how to wrap things in `TextNode` and `List` instances, and it will let you omit the attributes Hash if it's empty, so you never actually type all of that out.
|
40
52
|
|
41
|
-
|
53
|
+
The above simplifies to:
|
54
|
+
|
55
|
+
``` ruby
|
56
|
+
Node.new(:nav, {"id"=>"menu"},
|
57
|
+
Node.new(:ul,
|
58
|
+
Node.new(:li, "Home"),
|
59
|
+
Node.new(:li, "lolcats"),
|
60
|
+
Node.new(:li, "Games")
|
61
|
+
)
|
62
|
+
)
|
63
|
+
```
|
42
64
|
|
43
|
-
|
65
|
+
There's also a shorthand syntax:
|
44
66
|
|
45
|
-
|
67
|
+
``` ruby
|
68
|
+
node = H[:nav, {"id"=>"menu"},
|
69
|
+
H[:ul,
|
70
|
+
H[:li, "Home"],
|
71
|
+
H[:li, "Lolcats"],
|
72
|
+
H[:li, "Games"]]]
|
46
73
|
|
47
|
-
|
74
|
+
puts node.to_html
|
75
|
+
```
|
48
76
|
|
49
|
-
|
77
|
+
If the first argument to `H[...]` is a Symbol, then the result is a `Node`, otherwise it's a `List`.
|
50
78
|
|
51
|
-
|
79
|
+
You can parse exisiting HTML to Hexp with `Hexp.parse(...)`.
|
52
80
|
|
53
|
-
|
54
|
-
class ProfileLink < Struct.new(:user)
|
55
|
-
def to_hexp
|
56
|
-
H[:a, {class: "profile-link", href: "/user/#{user.id}"}, user.name]
|
57
|
-
end
|
58
|
-
end
|
81
|
+
### Hexp::Node
|
59
82
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
83
|
+
A `Node` has a `#tag`, `#attrs` and `#children`. The methods `#set_tag`, `#set_attrs` and `#set_children` return a new updated instance.
|
84
|
+
|
85
|
+
``` ruby
|
86
|
+
node = H[:p, { class: 'bold' }, "A lovely paragraph"]
|
87
|
+
node.tag # => :p
|
88
|
+
node.attrs # => {"class"=>"bold"}
|
89
|
+
node.children # => ["A lovely paragraph"]
|
90
|
+
|
91
|
+
node.set_tag(:div)
|
92
|
+
# => H[:div, {"class"=>"bold"}, ["A lovely paragraph"]]
|
93
|
+
|
94
|
+
node.set_attrs({id: 'para-1'})
|
95
|
+
# => H[:p, {"id"=>"para-1"}, ["A lovely paragraph"]]
|
96
|
+
|
97
|
+
node.set_children(H[:em, "Ginsberg said:"], "The starry dynamo in the machinery of night")
|
98
|
+
# => H[:p, {"class"=>"bold"}, [H[:em, ["Ginsberg said:"]], "The starry dynamo in the machinery of night"]]
|
99
|
+
```
|
100
|
+
|
101
|
+
#### Predicates
|
102
|
+
|
103
|
+
``` ruby
|
104
|
+
node.tag?(:p) # => true
|
105
|
+
node.text? # => false
|
106
|
+
node.children.first.text? # => true
|
107
|
+
```
|
67
108
|
|
68
|
-
|
69
|
-
````
|
109
|
+
#### Attributes
|
70
110
|
|
71
|
-
|
111
|
+
``` ruby
|
112
|
+
# [] : As in Nokogiri/Hpricot, attributes can be accessed with hash syntax
|
113
|
+
node['class'] # => "bold"
|
72
114
|
|
73
|
-
|
115
|
+
# attr : Analogues to jQuery's `attr`, read-write based on arity
|
116
|
+
node.attr('class') # => "bold"
|
117
|
+
node.attr('class', 'bourgeois') # => H[:p, {"class"=>"bourgeois"}, ["A lovely paragraph"]]
|
74
118
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
119
|
+
node.has_attr?('class') # => true
|
120
|
+
node.class?('bold') # => true
|
121
|
+
node.add_class('daring') # => H[:p, {"class"=>"bold daring"}, ["A lovely paragraph"]]
|
122
|
+
node.class_list # => ["bold"]
|
123
|
+
node.remove_class('bold') # => H[:p, ["A lovely paragraph"]]
|
124
|
+
node.remove_attr('class') # => H[:p, ["A lovely paragraph"]]
|
79
125
|
|
80
|
-
|
126
|
+
# merge_attrs : Does a Hash#merge on the attributes, but the class
|
127
|
+
# attribute is treated special. aliased to %
|
128
|
+
node.merge_attrs(class: 'daring', id: 'poem')
|
129
|
+
# => H[:p, {"class"=>"bold daring", "id"=>"poem"}, ["A lovely paragraph"]]
|
130
|
+
```
|
81
131
|
|
82
|
-
|
132
|
+
#### Children
|
83
133
|
|
84
|
-
|
85
|
-
|
134
|
+
``` ruby
|
135
|
+
node.empty? # => false
|
136
|
+
node.append(H[:blink, "ARRSOME"], H[:p, "bye"]) # => H[:p, {"class"=>"bold"}, ["A lovely paragraph", H[:blink, ["ARRSOME"]], H[:p, ["bye"]]]]
|
137
|
+
node.text # => "A lovely paragraph"
|
138
|
+
node.map_children { |ch| ch.text? ? ch.upcase : ch } # => H[:p, {"class"=>"bold"}, ["A LOVELY PARAGRAPH"]]
|
139
|
+
```
|
86
140
|
|
87
|
-
|
141
|
+
#### CSS Selectors
|
88
142
|
|
89
|
-
|
143
|
+
``` ruby
|
144
|
+
node.select('p')
|
145
|
+
# => #<Enumerator: #<Hexp::Node::CssSelection @node=H[:p, {"class"=>"bold"}, ["A lovely paragraph"]] @css_selector="p" matches=true>:each>
|
146
|
+
node.replace('.warn') {|warning| warning.add_class('bold') }
|
147
|
+
```
|
90
148
|
|
91
|
-
The
|
149
|
+
#### The rest
|
92
150
|
|
93
|
-
|
151
|
+
``` ruby
|
152
|
+
puts node.pp
|
153
|
+
node.to_html
|
154
|
+
node.to_dom # => Convert to Nokogiri
|
155
|
+
```
|
94
156
|
|
95
|
-
|
96
|
-
----------------------
|
157
|
+
### Hexp::List
|
97
158
|
|
98
|
-
|
159
|
+
A `Hexp::List` wraps and delegates to a Ruby Array, so it has the same
|
160
|
+
API as Array. Methods which mutate the Array will raise an exception.
|
99
161
|
|
100
|
-
|
162
|
+
Additionally `Hexp::List` implements `to_html`, `append`, and `+`. Just like built-in collections, the class implements `[]` as an alternative constructor.
|
101
163
|
|
102
|
-
|
103
|
-
--------------------
|
164
|
+
Equality checks with `==` only compare value equality, so comparing to an Array with the same content returns true. Use `eql?` for a stronger "type and value" equality.
|
104
165
|
|
105
|
-
|
166
|
+
``` ruby
|
167
|
+
list = Hexp::List[H[:p, "hello, world!"]]
|
168
|
+
list.append("what", "a", "nice", "day")
|
169
|
+
#=> [H[:p, ["hello, world!"]], "what", "a", "nice", "day"]
|
170
|
+
```
|
106
171
|
|
107
|
-
|
172
|
+
## hexp-rails
|
108
173
|
|
109
|
-
|
174
|
+
There is a thin layer of Rails integration included. This makes Hexp aware of the `html_safe` / `html_safe?` convention used to distinguish text from markup. It also aliases `to_html` to `to_s`, so Hexp nodes and lists can be used transparently in templates.
|
110
175
|
|
111
|
-
|
112
|
-
|
176
|
+
``` erb
|
177
|
+
<%= H[:p, legacy_helper] %>
|
178
|
+
```
|
113
179
|
|
114
|
-
|
180
|
+
You need to explicitly opt-in to this behaviour. The easiest is to add a 'require' to your Gemfile
|
115
181
|
|
116
|
-
|
117
|
-
|
182
|
+
``` ruby
|
183
|
+
gem 'hexp', require: 'hexp-rails'
|
184
|
+
```
|
185
|
+
|
186
|
+
## Builder
|
187
|
+
|
188
|
+
If you like the Builder syntax available in other gems like Builder and Hpricot, you can use `Hexp.build` to achieve the same
|
189
|
+
|
190
|
+
``` ruby
|
191
|
+
Hexp.build do
|
192
|
+
div id: 'warning-sign' do
|
193
|
+
span "It's happening!"
|
194
|
+
ul.warn_list do
|
195
|
+
li "Cats are taking over the world"
|
196
|
+
li "The price of lasagne has continued to rise"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# H[:div, {"id"=>"warning-sign"}, [
|
202
|
+
# H[:span, [
|
203
|
+
# "It's happening!"]],
|
204
|
+
# H[:ul, {"class"=>"warn_list"}],
|
205
|
+
# H[:li, [
|
206
|
+
# "Cats are taking over the world"]],
|
207
|
+
# H[:li, [
|
208
|
+
# "The price of lasagne has continued to rise"]]]]
|
209
|
+
```
|
210
|
+
|
211
|
+
## to_hexp
|
212
|
+
|
213
|
+
When an object implements `to_hexp` it can be used where you would otherwise use a node. This can be useful for instance to create components that know how to render themselves.
|
214
|
+
|
215
|
+
Yaks does not contain any core extensions, but there is an optional, opt-in, implementation of `to_hexp` for NilClass, so nils in a list of nodes won't raise an error. This lets you write things like
|
216
|
+
|
217
|
+
``` ruby
|
218
|
+
H[:p,
|
219
|
+
some_node if some_condition?,
|
220
|
+
other_node if other_condition?
|
221
|
+
]
|
222
|
+
```
|
118
223
|
|
119
|
-
|
224
|
+
You can use it with `require 'hexp/core_ext/nil'`. Loading `hexp-rails` will automatically include this because, let's be honest, if you're using Rails a single monkey patch won't make the difference.
|
120
225
|
|
121
|
-
|
122
|
-
# Gemfile
|
226
|
+
## Related projects
|
123
227
|
|
124
|
-
|
125
|
-
|
228
|
+
* [Hexp-Kramdown](https://github.com/plexus/hexp-kramdown) Convert Markdown documents of various flavors to Hexp
|
229
|
+
* [Slippery](https://github.com/plexus/slippery) Generate HTML/JS slides from Markdown. Supports backends for Reveal.js, Deck.js, and Impress.js.
|
230
|
+
* [Yaks-HTML](https://github.com/plexus/slippery) Uses Hexp to render hypermedia API resources to HTML
|
231
|
+
* [AssetPacker](https://github.com/plexus/asset_packer) Find all images, stylesheets, and javascript references in a HTML file, save them to local files, and return an updated HTML file pointing to the local resources.
|
data/hexp.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.add_runtime_dependency 'nokogiri', '~> 1.6'
|
22
22
|
gem.add_runtime_dependency 'adamantium', '~> 0.2'
|
23
23
|
gem.add_runtime_dependency 'equalizer', '~> 0.0'
|
24
|
+
gem.add_runtime_dependency 'concord', '~> 0.0'
|
24
25
|
|
25
26
|
gem.add_development_dependency 'rake'
|
26
27
|
gem.add_development_dependency 'rspec'
|
data/lib/hexp.rb
CHANGED
@@ -2,10 +2,11 @@ require 'delegate'
|
|
2
2
|
require 'forwardable'
|
3
3
|
require 'pathname'
|
4
4
|
|
5
|
-
require 'nokogiri'
|
5
|
+
require 'nokogiri'
|
6
6
|
require 'sass'
|
7
7
|
require 'adamantium'
|
8
8
|
require 'equalizer'
|
9
|
+
require 'concord'
|
9
10
|
|
10
11
|
module Hexp
|
11
12
|
ROOT = Pathname(__FILE__).dirname.parent
|
data/lib/hexp/list.rb
CHANGED
data/lib/hexp/node.rb
CHANGED
@@ -52,13 +52,14 @@ module Hexp
|
|
52
52
|
# {Hexp::Node#select}.
|
53
53
|
#
|
54
54
|
class Node
|
55
|
-
include
|
55
|
+
include Concord::Public.new(:tag, :attributes, :children)
|
56
56
|
include Adamantium
|
57
57
|
extend Forwardable
|
58
58
|
|
59
59
|
include Hexp::Node::Attributes
|
60
60
|
include Hexp::Node::Children
|
61
61
|
|
62
|
+
alias attrs attributes
|
62
63
|
memoize :class_list
|
63
64
|
|
64
65
|
# The HTML tag of this node
|
@@ -69,7 +70,7 @@ module Hexp
|
|
69
70
|
# @return [Symbol]
|
70
71
|
# @api public
|
71
72
|
#
|
72
|
-
attr_reader :tag
|
73
|
+
#attr_reader :tag
|
73
74
|
|
74
75
|
# The attributes of this node
|
75
76
|
#
|
@@ -79,7 +80,7 @@ module Hexp
|
|
79
80
|
# @return [Hash<String, String>]
|
80
81
|
# @api public
|
81
82
|
#
|
82
|
-
attr_reader :attributes
|
83
|
+
#attr_reader :attributes
|
83
84
|
|
84
85
|
# The child nodes of this node
|
85
86
|
#
|
@@ -90,7 +91,7 @@ module Hexp
|
|
90
91
|
# @return [Hexp::List]
|
91
92
|
# @api public
|
92
93
|
#
|
93
|
-
attr_reader :children
|
94
|
+
#attr_reader :children
|
94
95
|
|
95
96
|
# Main entry point for creating literal hexps
|
96
97
|
#
|
@@ -124,7 +125,7 @@ module Hexp
|
|
124
125
|
# @api public
|
125
126
|
#
|
126
127
|
def initialize(*args)
|
127
|
-
|
128
|
+
super(*Normalize.new(args).call)
|
128
129
|
end
|
129
130
|
|
130
131
|
# Standard hexp coercion protocol, return self
|
data/lib/hexp/node/attributes.rb
CHANGED
@@ -124,12 +124,10 @@ module Hexp
|
|
124
124
|
def set_attrs(attrs)
|
125
125
|
H[
|
126
126
|
self.tag,
|
127
|
-
|
127
|
+
Hash[*attrs.flat_map{|k,v| [k.to_s, v]}],
|
128
128
|
self.children
|
129
129
|
]
|
130
130
|
end
|
131
|
-
alias :% :set_attrs
|
132
|
-
alias :add_attributes :set_attrs
|
133
131
|
|
134
132
|
# Remove an attribute by name
|
135
133
|
#
|
@@ -188,6 +186,8 @@ module Hexp
|
|
188
186
|
end
|
189
187
|
result
|
190
188
|
end
|
189
|
+
alias :% :merge_attrs
|
190
|
+
|
191
191
|
end
|
192
192
|
end
|
193
193
|
end
|
data/lib/hexp/unparser.rb
CHANGED
@@ -27,7 +27,9 @@ module Hexp
|
|
27
27
|
|
28
28
|
DEFAULT_OPTIONS = {
|
29
29
|
encoding: Encoding.default_external,
|
30
|
-
no_escape: [:script]
|
30
|
+
no_escape: [:script],
|
31
|
+
void: [ :area, :base, :br, :col, :command, :embed, :hr, :img, :input,
|
32
|
+
:keygen, :link, :meta, :param, :source, :track, :wbr ]
|
31
33
|
}
|
32
34
|
|
33
35
|
attr_reader :options
|
@@ -59,7 +61,7 @@ module Hexp
|
|
59
61
|
end
|
60
62
|
buffer << GT
|
61
63
|
add_child_nodes(buffer, tag, children)
|
62
|
-
buffer << LT << FSLASH << tag.to_s << GT
|
64
|
+
buffer << LT << FSLASH << tag.to_s << GT unless void?(tag)
|
63
65
|
end
|
64
66
|
|
65
67
|
def add_child_nodes(buffer, tag, children)
|
@@ -84,5 +86,9 @@ module Hexp
|
|
84
86
|
def escape_text(text)
|
85
87
|
text.gsub(ESCAPE_TEXT_REGEX, ESCAPE_TEXT)
|
86
88
|
end
|
89
|
+
|
90
|
+
def void?(tag)
|
91
|
+
options[:void].include?(tag)
|
92
|
+
end
|
87
93
|
end
|
88
94
|
end
|
data/lib/hexp/version.rb
CHANGED
@@ -91,10 +91,6 @@ describe Hexp::Node::Attributes do
|
|
91
91
|
it 'should override attributes' do
|
92
92
|
expect(H[:foo, class: 'baz'].set_attrs(class: 'bar')).to eq H[:foo, class: 'bar']
|
93
93
|
end
|
94
|
-
|
95
|
-
it 'should merge keep both old and new attributes' do
|
96
|
-
expect(H[:foo, class: 'baz'].set_attrs(src: 'bar')).to eq H[:foo, class: 'baz', src: 'bar']
|
97
|
-
end
|
98
94
|
end
|
99
95
|
|
100
96
|
describe 'merge_attrs' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hexp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arne Brasseur
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sass
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: concord
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: rake
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|