hexp 0.4.1 → 0.4.2

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
  SHA1:
3
- metadata.gz: e1f3d62af18c9ae3a5a4c28fdb30e44a082e58f4
4
- data.tar.gz: 4379e67e153aca5cb338f3b1f002c871b8f71010
3
+ metadata.gz: fe165e0149eb663683b10c6bbea75d2e594171c2
4
+ data.tar.gz: d0678b856d53b37e98652d7a987ced5be71ae220
5
5
  SHA512:
6
- metadata.gz: 8c0244e52ce84768cb92828ecdfb0021ebd6a62ae0c72ad3beaeb801f2386f5177f61008055d528b91444896aa26a47baa705269e04cc6baa2939d4c19fabc83
7
- data.tar.gz: db32b6b92696bd5953c666d5fcbaeb9190b9d87dac0320487eeade385d92536b98067373aeb3ffad753eb0cff9ee5c2ef25e67f77d71fe730da8b8c8a483627a
6
+ metadata.gz: 19bf3b836ac3a73e7b033d9e75c3323f85813660d56624b9f704b3d60c0ae74563c95ad6a64d77f6419330ce2592e4cecae4981e713e740ccb5e70545d37dc40
7
+ data.tar.gz: 7188623fe858bfe9c84f1c6c4c83061e6535cfae4bcd1558b7296d489116e4190f4a93ab5745b12fc24e18e45e48a98bbfb19749ef60a7978b3bd0ffb168899e
@@ -8,6 +8,9 @@ rvm:
8
8
  - 2.0.0
9
9
  - 2.1.1
10
10
  - 2.1.2
11
+ - 2.1.3
12
+ - 2.1
13
+ - rbx
11
14
  - jruby
12
15
  - jruby-head
13
16
  - ruby-head
@@ -1,6 +1,15 @@
1
1
  ### Development
2
2
 
3
- [full diff](http://github.com/plexus/hexp/compare/v0.4.1...master)
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 <script> tags
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 Ruby API for creating and manipulating HTML syntax trees. It enables a web application architecture where HTML is only ever represented as structured data, rather than as plain text.
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
- Only when the data needs to be serialized and sent over the network is it converted to a string representation. This has a number of advantages.
17
+ ## Fundamentals
18
18
 
19
- * Single responsibility : HTML generation is not mixed with business logic
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
- For a more in-depth explanation please see the slides of talk [Web Linguistics, Towards Higher Fluency](http://arnebrasseur.net/talks/eurucamp2013/presentation.html) given at Eurucamp 2013. (the video is not available yet.)
21
+ Take this bit of HTML
24
22
 
25
- **Creating Hexps**
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
- Hexps are basically snippets of HTML written in nothing but Ruby, here's an example.
33
+ If we would spell out all the objects, it would be represented as
28
34
 
29
- ````ruby
30
- @message = "Hexps are fun for the whole family, from 9 to 99 years old."
31
- @hexp = H[:div, {class: 'hexp-intro'}, [
32
- [:p, @message]
33
- ]
34
- ]
35
- ````
35
+ ``` ruby
36
+ include Hexp
36
37
 
37
- For more info to get you up and running have a look at the API documentation for [Hexp::Node](http://plexus.github.io/hexp/Hexp/Node.html).
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
- **Don't people use templates for this kind of thing?**
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
- They do, this is an alternative approach. With templates you need to think about which parts need to be HTML-escaped, or you can make errors like forgetting a closing tag. With hexps you no longer need to think about escaping.
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
- **Wait how is that?**
65
+ There's also a shorthand syntax:
44
66
 
45
- With traditional approaches you can insert plain text in your template, or snippets of HTML. The first must be escaped, the second should not. For your template they are all just strings, so you, the programmer, need to distinguish between the two in a way. For example by using `html_escape` on one (explicit escaping), or `html_safe` on the other (implicit escaping).
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
- When using hexps you never deal with strings that actually contain HTML. Helper methods would return hexps instead, and you can combine those into bigger hexps. Strings inside a hexp are always just that, so they will always be escaped without you thinking about it.
74
+ puts node.to_html
75
+ ```
48
76
 
49
- **So that's it, easier escaping?**
77
+ If the first argument to `H[...]` is a Symbol, then the result is a `Node`, otherwise it's a `List`.
50
78
 
51
- Well that's not all, by having a simple lightweight representation of HTML that is _a real data structure_, you can really start programming your HTML. If you have an object that responds to `to_hexp`, you can use that object inside a hexp, so you can use Object Orientation for your user interface. Like so
79
+ You can parse exisiting HTML to Hexp with `Hexp.parse(...)`.
52
80
 
53
- ````ruby
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
- class Layout < Struct.new(:content)
61
- def to_hexp
62
- H[:html, [
63
- [:body, [content]]
64
- ]
65
- end
66
- end
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
- render inline: Layout.new(ProfileLink.new(@user)).to_html
69
- ````
109
+ #### Attributes
70
110
 
71
- **Does it get any better?**
111
+ ``` ruby
112
+ # [] : As in Nokogiri/Hpricot, attributes can be accessed with hash syntax
113
+ node['class'] # => "bold"
72
114
 
73
- It does! The really neat part is having filters that process this HTML tree before it gets serialized to text. This could be good for
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
- - populating form fields
76
- - adding extra admin buttons when logged in
77
- - cleanly separate aspects of your app (e.g. discount codes) from 'core' implementation
78
- - becoming filthy rich and/or ridiculously happy
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
- **What's up with the funny name?**
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
- Hexp stands for HTML expressions. It's a reference to s-expressions as they are known in LISP languages, a simple way to represent data as nested lists.
132
+ #### Children
83
133
 
84
- How to use it
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
- Hexp objects come in two flavors : `Hexp::Node` and `Hexp::List`. A `Node` consists of three parts : its `tag`, `attributes` and `children`. A `List` is just that, a list (of nodes).
141
+ #### CSS Selectors
88
142
 
89
- To construct a `Node` use `H[tag, attributes, children]`. Use a `Symbol` for the `tag`, a `Hash` for the `attributes`, and an `Array` for the `children`. Attributes or children can be omitted when they are empty.
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 list of children will automatically be converted to `Hexp::List`, similarly for any nested nodes you can simply use `[tag, attributes, children]` without the `H`, all nodes in the tree will be converted to proper `Hexp::Node` objects.
149
+ #### The rest
92
150
 
93
- The entire API is centered around these two classes, and one of them you can think of as essentially just an `Array`, in other words Hexp is super easy to learn. Try it out in `irb`, have a look at the examples, and *build cool stuff*!
151
+ ``` ruby
152
+ puts node.pp
153
+ node.to_html
154
+ node.to_dom # => Convert to Nokogiri
155
+ ```
94
156
 
95
- A note on immutability
96
- ----------------------
157
+ ### Hexp::List
97
158
 
98
- All Hexp objects are frozen on creation, you can never alter them afterwards. Operations always return a new `Hexp::Node` rather than working in place.
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
- This might seem stringent when you are not used to this style of coding, but it's a pattern that generally promotes good code.
162
+ Additionally `Hexp::List` implements `to_html`, `append`, and `+`. Just like built-in collections, the class implements `[]` as an alternative constructor.
101
163
 
102
- Can I already use it
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
- Yes, but there are some things to keep in mind.
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
- For the 0.x line of versions Hexp is not restricted to [semantic versioning](http://semver.org). We are still designing the API, and small backwards incompatible changes may occur. However given that the project's aim is to only provide the lowest level of DOM manipulation upon which others can built, it is already quite feature complete. It shouldn't be too long before we release a 1.0.0, after which we will commit to semantic versioning.
172
+ ## hexp-rails
108
173
 
109
- Another thing is that Hexp is young. It hasn't been battle tested yet, and the ecosystem which will make this approach truly attractive is yet to emerge. Therefore better try it out on smaller, non-critical projects first, and give us your feedback.
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
- Is it any good?
112
- ---------------
176
+ ``` erb
177
+ <%= H[:p, legacy_helper] %>
178
+ ```
113
179
 
114
- Yes
180
+ You need to explicitly opt-in to this behaviour. The easiest is to add a 'require' to your Gemfile
115
181
 
116
- How to install
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
- At this point you're best off grabbing the Git repo, e.g. with bundler
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
- ````sh
122
- # Gemfile
226
+ ## Related projects
123
227
 
124
- gem 'hexp', github: 'plexus/hexp'
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.
@@ -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'
@@ -2,10 +2,11 @@ require 'delegate'
2
2
  require 'forwardable'
3
3
  require 'pathname'
4
4
 
5
- require 'nokogiri' # TODO => replace with Builder
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
@@ -91,5 +91,9 @@ module Hexp
91
91
  def +(other)
92
92
  self.class[*to_ary, *other.to_ary]
93
93
  end
94
+
95
+ def append(*args)
96
+ self + args
97
+ end
94
98
  end
95
99
  end
@@ -52,13 +52,14 @@ module Hexp
52
52
  # {Hexp::Node#select}.
53
53
  #
54
54
  class Node
55
- include Equalizer.new(:tag, :attributes, :children)
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
- @tag, @attributes, @children = Normalize.new(args).call
128
+ super(*Normalize.new(args).call)
128
129
  end
129
130
 
130
131
  # Standard hexp coercion protocol, return self
@@ -124,12 +124,10 @@ module Hexp
124
124
  def set_attrs(attrs)
125
125
  H[
126
126
  self.tag,
127
- self.attributes.merge(Hash[*attrs.flat_map{|k,v| [k.to_s, v]}]),
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Hexp
2
- VERSION = '0.4.1'
2
+ VERSION = '0.4.2'
3
3
  end
@@ -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.1
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-10-30 00:00:00.000000000 Z
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