objective_elements 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a96004d9cd00908af90c7f251458c7df1346a7d8be58d09f9518e6bb0c937c77
4
+ data.tar.gz: 245cee10433fc4479b5ddfba7b4bfe608fc777c8551f9de207609b663fa38d34
5
+ SHA512:
6
+ metadata.gz: 5ae990c5afac9ce516f7dc744dd5e5e1fac01a489af7c3a1bb4e5bdc196cd46682148c7a202666708cf7a54d08b388fd20a9798ad68c56137e11adccba499b02
7
+ data.tar.gz: be6296add683af618a92f7b8eaf64eb5534dafe01ac43ea843e5dc5ae55f26e65991876d7ece2faaa677ee62f0b2e3fea3bca0c7d29ee9459f0fb3aa43b50e41
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in elements.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,41 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ elements (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ coderay (1.1.2)
10
+ diff-lcs (1.3)
11
+ method_source (0.9.0)
12
+ pry (0.11.3)
13
+ coderay (~> 1.1.0)
14
+ method_source (~> 0.9.0)
15
+ rake (10.5.0)
16
+ rspec (3.8.0)
17
+ rspec-core (~> 3.8.0)
18
+ rspec-expectations (~> 3.8.0)
19
+ rspec-mocks (~> 3.8.0)
20
+ rspec-core (3.8.0)
21
+ rspec-support (~> 3.8.0)
22
+ rspec-expectations (3.8.1)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.8.0)
25
+ rspec-mocks (3.8.0)
26
+ diff-lcs (>= 1.2.0, < 2.0)
27
+ rspec-support (~> 3.8.0)
28
+ rspec-support (3.8.0)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ bundler (~> 1.16)
35
+ elements!
36
+ pry (~> 0.11.3)
37
+ rake (~> 10.0)
38
+ rspec (~> 3.8.0)
39
+
40
+ BUNDLED WITH
41
+ 1.16.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Robert Buchberger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # Objective Elements
2
+
3
+ This is a tiny gem that builds nicely formatted HTML using sane, readable Ruby. I use it for jekyll
4
+ plugins, but you can use it anywhere. It's ~100 lines, with no runtime dependencies beyond bundler
5
+ and rake.
6
+
7
+ Note: This gem doesn't actually know any HTML. It just knows how it should be formatted.
8
+
9
+ ## How it works:
10
+
11
+ * Instantiate a `SingleTag` or `DoubleTag`
12
+
13
+ * Attributes are a Hash, Content is an array. Define them on initialization, or later on.
14
+
15
+ * Render it with `.to_s`. Get normal HTML without the pain.
16
+
17
+ ## Motivation:
18
+
19
+ Have you ever tried to build HTML with string concatenation and interpolation? It starts out simply
20
+ enough, but once you account for all the what-ifs, the indentation, the closing tags, and the
21
+ spaces you only need sometimes, it turns into a horrible mess.
22
+
23
+ The problem, of course, is that building long, complex, varying blocks of text with string
24
+ concatenation and interpolation is fragile, unreadable, and painful. You know this, but you're not
25
+ going to write an entirely new class or pull in some big new dependency just for 10 lines of HTML.
26
+ Instead, you just hammer through it and end up with code like this:
27
+
28
+ ```ruby
29
+ picture_tag = "<picture>\n"\
30
+ "#{source_tags}"\
31
+ "#{markdown_escape * 4}<img src=\"#{url}#{instance['source_default'][:generated_src]}\" #{html_attr_string}>\n"\
32
+ "#{markdown_escape * 2}</picture>\n"
33
+ ```
34
+
35
+ or this:
36
+ ```ruby
37
+ def build_li(this_item_data, icon_location, label)
38
+ li = " <li#{@attributes['li']}>"
39
+ if this_item_data && this_item_data['url']
40
+ li << "<a href=\"#{this_item_data['url']}\"#{@attributes['a']}>"
41
+ end
42
+ li << build_image_tag(icon_location)
43
+ li << label
44
+ li << '</a>' if this_item_data['url']
45
+ li << "</li>\n"
46
+ end
47
+ ```
48
+
49
+ Which is why I sat down and wrote this gem. It's super simple, you probably could have written it
50
+ too, but hey! Now you don't have to. Here's a demo:
51
+
52
+ ## Demo
53
+
54
+ ```ruby
55
+ p = DoubleTag.new 'p'
56
+ p.to_s
57
+ # <p>
58
+ # </p>
59
+
60
+ # Add attributes and content however you like:
61
+ p.add_attributes class: 'stumpy grumpy', id: 'the-ugly-one'
62
+ p.add_attributes class: 'slimy'
63
+ p.add_content 'Bippity Boppity Boo!'
64
+ p.to_s
65
+ # <p class="stumpy grumpy slimy" id="the-ugly-one">
66
+ # Bippity Boppity Boo!
67
+ # </p>
68
+
69
+ # Want a oneliner?
70
+ p.oneline = true
71
+ p.to_s
72
+ # <p class="stumpy mopey grumpy slimy" id="the-ugly-one">Bippity Boppity Boo!</p>
73
+ p.oneline = false
74
+
75
+ # Build it up step by step, or all at once:
76
+ p.add_content DoubleTag.new(
77
+ 'a',
78
+ content: 'Link!',
79
+ attributes: {href: 'awesome-possum.com'},
80
+ oneline: true
81
+ )
82
+ # Add a parent tag!
83
+ div = p.add_parent DoubleTag.new 'div'
84
+
85
+ # Ruby implicitly calls .to_s on things when you try to perform string functions with them, meaning
86
+ # cool things like this work:
87
+ "#{div}"
88
+ # <div>
89
+ # <p class="stumpy mopey grumpy slimy" id="the-ugly-one">
90
+ # Bippity Boppity Boo!
91
+ # <a href="awesome-possum.com">Link!</a>
92
+ # </p>
93
+ # </div>
94
+
95
+ ```
96
+ ## Installation
97
+
98
+ - coming eventually. It'll be a gem and a require. For now, you can load it from this repo in your gemfile.
99
+
100
+ ## Usage
101
+
102
+ So we're on the same page, here's the terminology I'm using:
103
+ ```
104
+ <p class="stumpy">Hello</p>
105
+ |a| b | c | d |
106
+ ```
107
+ - a - element
108
+ - b - attributes
109
+ - a+b - opening tag
110
+ - c - content
111
+ - d - closing tag
112
+
113
+ There are 2 classes: `SingleTag` is the base class, and `DoubleTag` inherits from it. A `SingleTag` is a
114
+ self-closing tag, meaning it has no content and no closing tag. A `DoubleTag` is the other kind.
115
+
116
+ ### SingleTag Properties:
117
+
118
+ - element:
119
+ **String**, mandatory. What kind of tag it is; such as 'img' or 'hr'
120
+ - attributes:
121
+ **Hash** `{symbol: array or string}`, optional. Example: `{class: 'myclass plaid'}` or `{class:
122
+ ['myclass', 'plaid']}`
123
+
124
+ ### SingleTag Methods (that you care about)
125
+
126
+ `SingleTag.new(element, attributes: {})`
127
+
128
+ `.to_s` - The big one. Returns your HTML as a string, nondestructively.
129
+
130
+ `.reset_attributes(hash)` - Deletes all attributes, replaces with supplied argument if given.
131
+
132
+ `.add_attributes(hash)` - Appends attributes instead of overwriting them.
133
+
134
+ `.add_parent(DoubleTag)` - returns supplied DoubleTag, with self added as a child.
135
+
136
+ `attr_reader :attributes, :element`
137
+
138
+ ### DoubleTag Properties:
139
+
140
+ #### `DoubleTag` Inherits all of `SingleTag`'s properties and methods, but adds content and a closing tag.
141
+ - content:
142
+ **Array**, optional, containing anything (but probably just strings and tags. Anything else
143
+ will be turned into a string with `.to_s`, which is an alias for `.inspect` most of the time).
144
+
145
+ Each element in the array corresponds to at least one line of HTML; multiline child tags will
146
+ get as many lines as they need (like you'd expect).
147
+
148
+ Child elements are not rendered until the parent is rendered, meaning you can access and
149
+ modify them after defining a parent.
150
+
151
+ - oneline:
152
+ **Boolean**, optional, defaults to false. When true, the entire element and its content will be
153
+ rendered as a single line. Useful for anchor tags and list items.
154
+
155
+ ### DoubleTag Methods (that you care about)
156
+
157
+ `DoubleTag.new(element, attributes: {}, oneline: false, content: [])`
158
+ - You can initialize it with content.
159
+
160
+ `add_content(anything)`
161
+ - Smart enough to handle both arrays and not-arrays without getting dorked up.
162
+
163
+ `attr_accessor: content`
164
+ - You can modify the content array directly if you like. If you're just adding items, you should use
165
+ `.add_content`
166
+
167
+ `.to_a`
168
+ - Mostly used internally, but if you want an array of strings, each element a line with appropriate
169
+ indentation applied, this is how you can get it.
170
+
171
+ ## Configuration
172
+
173
+ Indentation is defined by the `indent` method on the DoubleTag class. If you'd like to change
174
+ it:
175
+
176
+ 1. Make a new class, inherit from DoubleTag.
177
+ 2. Override `indent` with whatever you want.
178
+ 3. Use your new class instead of DoubleTag.
179
+
180
+ Example:
181
+
182
+ ```ruby
183
+
184
+ class MyDoubleTag < DoubleTag
185
+ def indent
186
+ # 4 escaped spaces:
187
+ "\ \ \ \ "
188
+ end
189
+ end
190
+
191
+ MyDoubleTag.new('p', content: 'hello').to_s
192
+ # <p>
193
+ # hello
194
+ # </p>
195
+
196
+ ```
197
+
198
+ ## Limitations
199
+
200
+ * It actually doesn't know a single HTML element on its own, so it does nothing to ensure that
201
+ your HTML is valid. Garbage in, garbage out.
202
+
203
+ * A parent tag can't put siblings on the same line. You can either
204
+ do this (with `oneline: true` on the strong tag):
205
+
206
+ ```html
207
+
208
+ Here is some
209
+ <strong>strong</strong>
210
+ text.
211
+
212
+ ```
213
+ or this (default behavior):
214
+
215
+ ```html
216
+
217
+ Here is some
218
+ <strong>
219
+ strong
220
+ </strong>
221
+ text.
222
+
223
+ ```
224
+ But you can't do this without string interpolation:
225
+
226
+ ```html
227
+
228
+ Here is some <strong>strong</strong> text.
229
+
230
+ ```
231
+ It doesn't affect how the browser will render it, but it might bug you if you're particular about
232
+ source code layout.
233
+
234
+ * If you set 'oneline: true' on a parent DoubleTag, but not all its children DoubleTags, the output
235
+ will not be pretty. I advise against it.
236
+
237
+ * It doesn't wrap long lines of text, and it doesn't indent text with newlines embedded. It's on the
238
+ TODO list.
239
+
240
+ ## Contributing
241
+
242
+ For code style, I've been using rubocop with the default settings and would appreciate if you did the
243
+ same.
244
+
245
+ If you add new functionality, or change existing functionality, please update the rspec tests to
246
+ reflect it.
247
+
248
+ https://github.com/rbuchberger/objective_elements
249
+
250
+ contact:
251
+ robert@robert-buchberger.com
252
+
253
+ ## License
254
+
255
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "elements"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ module ObjectiveElements
2
+ require_relative 'objective_elements/single_tag'
3
+ require_relative 'objective_elements/double_tag'
4
+ end
@@ -0,0 +1,48 @@
1
+ # Non-Self-Closing tag. Can have content, but doesn't have to.
2
+ class DoubleTag < SingleTag
3
+ attr_accessor :content, :oneline
4
+ # content is an array of anything. Entries will be inserted sequentially in
5
+ # between the opening and closing tags.
6
+
7
+ def initialize(element, attributes: nil, content: [], oneline: false)
8
+ @oneline = oneline
9
+ reset_content(content)
10
+ super(element, attributes: attributes)
11
+ end
12
+
13
+ def closing_tag
14
+ "</#{element}>"
15
+ end
16
+
17
+ # Deletes all content, replaces with parameter (if supplied)
18
+ def reset_content(new = nil)
19
+ @content = []
20
+ add_content(new) if new
21
+ end
22
+
23
+ def add_content(addition)
24
+ @content << addition
25
+ @content.flatten!
26
+ self
27
+ end
28
+
29
+ def indent
30
+ "\ \ "
31
+ end
32
+
33
+ def to_a
34
+ lines = content.map do |c|
35
+ if c.is_a? SingleTag
36
+ c.to_a
37
+ else
38
+ c.to_s.dup
39
+ end
40
+ end
41
+ lines = lines.flatten.map { |l| l.prepend oneline ? '' : indent }
42
+ lines.unshift(opening_tag).push(closing_tag)
43
+ end
44
+
45
+ def to_s
46
+ to_a.join(oneline ? '' : "\n") + "\n"
47
+ end
48
+ end
@@ -0,0 +1,67 @@
1
+ # Collection of HTML element tags
2
+ # Describes a basic, self-closing HTML tag.
3
+ class SingleTag
4
+ attr_reader :attributes
5
+ attr_accessor :element
6
+
7
+ # element is a string, such as 'div' or 'p'.
8
+
9
+ # Attributes are a hash. Keys are symbols, values are arrays. Will render as
10
+ # key="value1 value2 value3"
11
+
12
+ def initialize(element, attributes: nil)
13
+ @element = element
14
+ reset_attributes(attributes)
15
+ end
16
+
17
+ # Deletes all current attributes, overwrites them with supplied hash.
18
+ def reset_attributes(new = nil)
19
+ @attributes = {}
20
+ add_attributes(new) if new
21
+ end
22
+
23
+ # This is the only way we add new attributes. Flexible about what you give
24
+ # it-- accepts both strings and symbols for the keys, and both strings and
25
+ # arrays for the values.
26
+ def add_attributes(new)
27
+ formatted_new = {}
28
+ new.each_pair do |k, v|
29
+ v = v.split ' ' if v.is_a? String
30
+ formatted_new[k.to_sym] = v
31
+ end
32
+
33
+ @attributes.merge! formatted_new do |_key, oldval, newval|
34
+ oldval.concat newval
35
+ end
36
+ end
37
+ alias_method :add_attribute, :add_attributes
38
+
39
+ # Turns attributes into a string we can insert.
40
+ def render_attributes
41
+ attribute_string = ''
42
+ @attributes.each_pair do |k, v|
43
+ attribute_string << "#{k}=\"#{v.join ' '}\" "
44
+ end
45
+ attribute_string.strip
46
+ end
47
+
48
+ # Returns parent, with self added as a child
49
+ def add_parent(parent)
50
+ parent.add_content(self)
51
+ end
52
+
53
+ def opening_tag
54
+ output = '<' + @element
55
+ output << ' ' + render_attributes unless @attributes.empty?
56
+ output << '>'
57
+ end
58
+
59
+ def to_a
60
+ [opening_tag]
61
+ end
62
+
63
+ # Renders our HTML.
64
+ def to_s
65
+ opening_tag + "\n"
66
+ end
67
+ end
@@ -0,0 +1,3 @@
1
+ module ObjectiveElements
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,31 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'objective_elements/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'objective_elements'
7
+ spec.version = ObjectiveElements::VERSION
8
+ spec.authors = ['Robert Buchberger']
9
+ spec.email = ['robert@robert-buchberger.com']
10
+
11
+ spec.summary = 'Build HTML without interpolating strings.'
12
+ spec.homepage = 'https://github.com/rbuchberger/objective_elements'
13
+ spec.license = 'MIT'
14
+
15
+ # Specify which files should be added to the gem when it is released. The
16
+ # `git ls-files -z` loads the files in the RubyGem that have been added into
17
+ # git.
18
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
19
+ `git ls-files -z`.split("\x0").reject do |f|
20
+ f.match(%r{^(test|spec|features)/})
21
+ end
22
+ end
23
+ spec.bindir = 'exe'
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ['lib']
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.16'
28
+ spec.add_development_dependency 'pry', '~>0.11.3'
29
+ spec.add_development_dependency 'rake', '~> 10.0'
30
+ spec.add_development_dependency 'rspec', '~>3.8.0'
31
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: objective_elements
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Robert Buchberger
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-10-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.11.3
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.11.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.8.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.8.0
69
+ description:
70
+ email:
71
+ - robert@robert-buchberger.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - Gemfile.lock
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - bin/console
83
+ - bin/setup
84
+ - lib/objective_elements.rb
85
+ - lib/objective_elements/double_tag.rb
86
+ - lib/objective_elements/single_tag.rb
87
+ - lib/objective_elements/version.rb
88
+ - objective_elements.gemspec
89
+ homepage: https://github.com/rbuchberger/objective_elements
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.7.3
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Build HTML without interpolating strings.
113
+ test_files: []