Skip to content

Commit

Permalink
Add data attribute api to all tag types.
Browse files Browse the repository at this point in the history
This introduces a new feature that enhances the api to accept a data
attribute.

```
meta(
  tag_type: {
    value: 'value',
    data: { 'data-attr-name' => 'data attr value' }
  }
)
```

Multiple data attributes can be specified:

```
meta(
  tag_type: {
    value: 'value',
    data: { 'attr-1' => 'one', 'attr-2' => 'two' }
  }
)
```

Custom tags can be registered:

```
Metamagic::Renderer.register_tag_type :custom, ->(key, value, data) do
  tag(:custom_tag, name: key, value: value, data: data)
end
```

This is backwards compatible with the existing api.
  • Loading branch information
JaredShay committed Nov 8, 2022
1 parent 1301f20 commit bd67db8
Show file tree
Hide file tree
Showing 12 changed files with 263 additions and 18 deletions.
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ In your *Gemfile*:
```ruby
gem 'metamagic'
```

Then run `bundle install`.

Examples
Expand Down Expand Up @@ -217,7 +217,7 @@ For custom meta tags, you can use it like this:
meta my_custom_name: "My custom value"
%>
```

This will generate the following:

```html
Expand Down Expand Up @@ -404,6 +404,46 @@ custom key_one: "My first key",
%>
```

### Adding data attributes to tags

All tag types can add data attributes.

```erb
<%
meta title: "My Title",
description: {
value: "content",
data: { "attr-name" => "data attr value" }
}
%>
```

This will render the following:

```html
<title>My Title</title>
<meta name="description" content="content" data-attr-name="data attr value" />
```

Multiple data attributes can be specified:

```erb
<%
meta title: "My Title",
description: {
value: "content",
data: { "attr-name-1" => "one", "attr-name-2" => "two" }
}
%>
```

This will render the following:

```html
<title>My Title</title>
<meta name="description" content="content" data-attr-1="one" data-attr-2="two" />
```

Requirements
------------

Expand Down
3 changes: 2 additions & 1 deletion lib/metamagic.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
%w{
version
value
tag
tags/meta_tag
tags/title_tag
Expand All @@ -12,4 +13,4 @@
view_helper
}.each { |f| require "metamagic/#{f}" }

ActionView::Base.send :include, Metamagic::ViewHelper
ActionView::Base.send :include, Metamagic::ViewHelper
4 changes: 2 additions & 2 deletions lib/metamagic/renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,12 @@ def transform_hash(hash, path = "")
hash.each_with_object({}) do |(k, v), ret|
key = path + k.to_s

if v.is_a?(Hash)
if v.is_a?(Hash) && !v.key?(:value)
ret.merge! transform_hash(v, "#{key}:")
else
ret[key] = v
end
end
end
end
end
end
6 changes: 4 additions & 2 deletions lib/metamagic/tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ def interpolated_values
else
raise "Unknown template type #{template.class}."
end
end.flatten.compact.uniq.map { |value| ERB::Util.html_escape(value) }
end.flatten.compact.uniq.map do |value|
Metamagic::Value.create_escaped(value)
end
end

def ==(other)
Expand All @@ -53,4 +55,4 @@ def method_missing(method, *args, &block)
context.send(method, *args, &block)
end
end
end
end
17 changes: 15 additions & 2 deletions lib/metamagic/tags/custom_tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,20 @@ def remove_prefix?
end

def to_html
instance_exec key, interpolated_values, &render_proc
if render_proc.arity == 2
instance_exec(
key,
interpolated_values.map(&:value),
&render_proc
)
else
instance_exec(
key,
interpolated_values.map(&:value),
interpolated_values.first.data,
&render_proc
)
end
end
end
end
end
15 changes: 13 additions & 2 deletions lib/metamagic/tags/link_tag.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
module Metamagic
class LinkTag < Tag
def to_html
interpolated_values.map { |value| tag(:link, rel: key, href: value) }.join("\n").html_safe.presence
interpolated_values.map do |value|
options = {
rel: key,
href: value.value,
data: value.data
}.compact

tag(:link, **options)
end
.join("\n")
.html_safe
.presence
end
end
end
end
11 changes: 9 additions & 2 deletions lib/metamagic/tags/meta_tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ module Metamagic
class MetaTag < Tag
def to_html
return if interpolated_values.empty?
tag(:meta, name: key, content: interpolated_values.join(", ").html_safe)

options = {
name: key,
content: interpolated_values.map(&:value).join(", ").html_safe,
data: interpolated_values.first.data
}.compact

tag(:meta, **options)
end

def sort_order
Expand All @@ -13,4 +20,4 @@ def remove_prefix?
false
end
end
end
end
15 changes: 13 additions & 2 deletions lib/metamagic/tags/property_tag.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
module Metamagic
class PropertyTag < Tag
def to_html
interpolated_values.map { |value| tag(:meta, property: key, content: value) }.join("\n").html_safe.presence
interpolated_values.map do |value|
options = {
property: key,
content: value.value,
data: value.data
}.compact

tag(:meta, **options)
end
.join("\n")
.html_safe
.presence
end

def sort_order
3
end
end
end
end
6 changes: 4 additions & 2 deletions lib/metamagic/tags/title_tag.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
module Metamagic
class TitleTag < Tag
def to_html
content_tag(:title, interpolated_values.join(separator).html_safe) if interpolated_values.any?
values = interpolated_values.map(&:value).join(separator).html_safe

content_tag(:title, values) if values.length > 0
end

def sort_order
1
end
end
end
end
46 changes: 46 additions & 0 deletions lib/metamagic/value.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module Metamagic
class Value
def self.create_escaped(value)
value.is_a?(Hash) ? ValueHash.new(value) : ValueString.new(value)
end

def escape(string)
ERB::Util.html_escape(string)
end
end

class ValueString < Value
attr_reader :value
def initialize(value)
@value = escape(value)
end

def data
nil
end
end

class ValueHash < Value
attr_reader :value_hash
def initialize(value_hash)
@value_hash = value_hash
@value_hash[:value] = if value.is_a?(Array)
value.map { |v| escape(v) }
else
escape(value)
end

if @value_hash.key?(:data)
@value_hash[:data] = data.map { |k, v| [escape(k), escape(v)] }.to_h
end
end

def value
value_hash[:value]
end

def data
value_hash[:data]
end
end
end
2 changes: 1 addition & 1 deletion test/custom_tag_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ class CustomTagTest < ActionView::TestCase
assert_equal %{<title>Test Title</title>\n<custom_tag one="custom:first" two="This is the first" />\n<custom_tag one="custom:second" two="This is the second" />},
metamagic
end
end
end
112 changes: 112 additions & 0 deletions test/data_attribute_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
require 'test_helper'

class DataAttributeTest < ActionView::TestCase
include Metamagic::ViewHelper

test "title" do
meta title: {
value: "Test Title",
data: { "test-attr" => "title" }
}

assert_equal_segment %{<title data-test-attr="title">Test Title</title>},
metamagic
end

test "title with multiple data attrs" do
meta title: {
value: "Test Title",
data: { "test-attr-1" => "one", "test-attr-2" => "two" }
}

assert_equal_segment %{<title data-test-1="one" data-test-2="two">Test Title</title>},
metamagic
end

test "title with data attr api and no data attribute specified" do
meta title: { value: "Test Title" }

assert_equal_segment %{<title>Test Title</title>},
metamagic
end

test "link rel with data attr" do
meta rel: {
author: {
value: "http://test.com/author.html",
data: { "test-attr" => "test" }
}
}
rel publisher: {
value: "http://test.com/publisher.html",
data: { "test-attr" => "test" }
}

assert_equal_segment %{<link href="http://test.com/author.html" rel="author" data-test-attr="test"/>\n<link href="http://test.com/publisher.html" rel="publisher" data-test-attr="test"/>},
metamagic
end

test "twitter tag with data attribute" do
meta title: "Test Title",
twitter: {
card: {
value: :summary,
data: { "test-attr" => "test" }
}
}
twitter site: {
value: "@flickr",
data: { "test-attr" => "test" }
}

assert_equal_segment %{<title>Test Title</title>\n<meta content="summary" name="twitter:card" data-test-attr="test" />\n<meta content="@flickr" name="twitter:site" data-test-attr="test" />},
metamagic
end

test "meta tags with data attr" do
meta keywords: {
value: %w{one two three},
data: { "test-attr" => "test" }
},
description: {
value: "My description",
data: { "test-attr" => "test" }
}

assert_equal_segment %{<meta content="one, two, three" name="keywords" data-test-attr="test" />\n<meta content="My description" name="description" data-test-attr="test" />},
metamagic
end

test "open graph tags with data attr" do
meta og: {
image: {
url: {
value: "http://test.com/image.jpg",
data: { "test-attr" => "test" }
}
}
}

assert_equal_segment %{<meta content="http://test.com/image.jpg" property="og:image:url" data-test-attr="test" />},
metamagic
end

test "custom tags with data attr" do
Metamagic::Renderer.register_tag_type :custom, ->(key, value, data) { tag(:custom_tag, one: key, two: value, data: data) }

meta title: "Test Title",
custom: {
first: {
value: "This is the first",
data: { "test-attr" => "test" }
},
second: {
value: "This is the second",
data: { "test-attr" => "test" }
}
}

assert_equal %{<title>Test Title</title>\n<custom_tag one="custom:first" two="This is the first" data-test-attr="test" />\n<custom_tag one="custom:second" two="This is the second" data-test-attr="test" />},
metamagic
end
end

0 comments on commit bd67db8

Please sign in to comment.