Using a Volt::Model as a dictionary of Key/Value pairs - ruby

I would like to use a Volt::Model as a reactive dictionary in a view.
Ideally, I would like something like this:
<dl>
{{ dictionary.each do |key, val| }}
<dt>Term: {{ key }}</dt>
<dd>Definition: {{ val }}</dd>
{{ end }}
</dl>
Is this possible in Volt without using an ArrayModel?

Sorry, I don't have .each_pair working in bindings in Volt yet, its on the todo list. Yea, you can use .keys.each do |key| in the mean time.

Figured it out. This works:
<dl>
{{ dictionary.keys.each do |key| }}
<dt>Term: {{ key }}</dt>
<dd>Definition: {{ dictionary.get(key) }}</dd>
{{ end }}
</dl>

The version in the question should almost work. You just have to use each_pair instead of each.

Related

How do I determine what the data type is of a value in Ruby?

I'm using Jekyll, and have stored some information in _data/some_data.yml, which contains data like this:
---
links:
- http://example.org/some/data
- http://example.org/some/other/data
Another record, _data/more_data.yml, contains data like this:
---
links: http://example.com
A third record, _data/yet_more_data.yml, contains data like this:
---
links:
"A title-based link": http://example.net
"Another title-based link": https://example.net/some/other/page
I will be parsing the data sources in the template, like this:
<pre>
{% for a_record in site.data %}
Parsing: {{ a_record[0] }}
Links:
<< PSUDOCODE >>
{% if a_record[1].links is a string %}
* [{{ a_record[1].links }}]({{ a_record[1].links }})
{% elseif a_record[1].links is an array %}
{% for link in a_record[1].links %}
* [{{ link }}]({{ link }}]
{% endfor %}
{% elseif a_record[1].links is a hash %}
{% for link in a_record[1].links %}
* [{{ link[0] }}]({{ link[1] }})
{% endfor %}
{% endif %}
<< /PSUDOCODE >>
- End Record -
{% endfor %}
</pre>
How can I work out if I'm looking at a string, an array or a hash?
I started writing a plugin to support this check, but it's not working out the way I expected - at all!
module Jekyll
module IsAFilter
def is_a_string(value)
if not value.instance_of?(::Hash)
if not value.instance_of?(::Array)
if value.instance_of?(::String)
return true
end
end
end
return false
end
def is_a_hash(value)
if not value.instance_of?(::Array)
if value.instance_of?(::Hash)
return true
end
end
return false
end
def is_an_array(value)
if not value.instance_of?(::Hash)
if value.instance_of?(::Array)
return true
end
end
return false
end
end
end
Liquid::Template.register_filter(Jekyll::IsAFilter)
To use that, I imagined that I could do {% if a_record[1].links | is_a_string %}{{ a_record[1].links }}{% endif %} but that always renders it as a string, even if it's an array or a hash?
And, before it's suggested, yes, I could say "you must always use the YAML hash format", but once I've finished writing this, I'm going to be handing control of the data to someone else, so I want to be sure that whatever they end up putting in there, it'll work.
In ruby you can use .is_a? DataType. E.g.
[1] pry(main)> "hello".is_a? String
=> true
[2] pry(main)> {key: 1}.is_a? Hash
=> true
[3] pry(main)> [1,2,3].is_a? Array
=> true
I had it so close! Thanks to #bashford7's answer, I amended my code to use the form value.is_a? String, but the problem was actually in my understanding of Jekyll...
I had to use this stanza:
{% assign check_variable = some_variable | some_filter_that_returns_true %}
{% if check_variable %}
USE THIS STANZA - IT WORKS
{% endif %}
Instead of what I assumed I could use, which was this stanza:
{% if some_variable | some_filter_that_returns_true %}
DON'T USE THIS STANZA - IT DOESN'T WORK
{% endif %}
My final plugin, if it's useful, looks like this:
module Jekyll
module IsAFilters
def is_a(value)
return value.inspect
end
def is_a_string(value)
if value.is_a? String
return true
end
return false
end
def is_a_hash(value)
if value.is_a? Hash
return true
end
return false
end
def is_an_array(value)
if value.is_a? Array
return true
end
return false
end
end
end
Liquid::Template.register_filter(Jekyll::IsAFilters)

Access site or post variables inside a jekyll converter

In liquid, I can access, say, {{ post.title }} or any variables that are defined in the markdown. How can I get at the equivalent of {{ post.title }} from a converter or a generator or a filter?
from a converter
No way. They are just converting content to hmtl.
from a generator
In the Generate method you receive the site object, you can then loop in site.pages and site.posts.
from a filter
Depending on what you pass to the filter you can get any page/post variable
module Jekyll
module MyFilters
def filter_name(page)
# do whatever with the page variables
end
end
end
Liquid::Template.register_filter(Jekyll::MyFilters)
Example call : {{ page | filter_name }}

Navigation in Jekyll sorted by weight

I want to make a navigation sorted by weight on my Jekyll site. I'm using this plugin, but I want to show in navigation only pages with weight, instead of showing pages without weight at the end of the list.
So I changed the plugin like this:
module Jekyll
class WeightedPagesGenerator < Generator
safe true
def generate(site)
site.pages.each do |page|
if page.data["weight"] != nil
site.config['weighted_pages'] = site.pages.sort_by { |a|
a.data['weight'] }
end
end
end
end
end
But I get an error: Generating... /_plugins/sorted_navigation.rb:10:in `sort_by': comparison of NilClass with 2 failed (ArgumentError).
Any idea how to make this work?
Since Jekyll 2.2.0 you can sort an array of objects by any object property. Sorting by weight is now possible and far more efficient than older solutions (see https://stackoverflow.com/a/25513956/1548376)
I ended up not using this plugin. Instead I use liquid tags from this answer and now my navigation looks like this:
<nav>
<ul>
{% for weight in (1..5) %}
{% unless p.weight %}
{% for p in site.pages %}
{% if p.weight == weight %}
<li><a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}" title="{{ p.title }}">{{ p.title }}</a></li>
{% endif %}
{% endfor %}
{% endunless %}
{% endfor %}
</ul>
</nav>

What is the erb equivalent of macros in jinja2?

In jinja2 I can specify frequently used template code in macros, essentially like template functions:
{% macro input(name, value='', type='text', size=20) -%}
<input type="{{ type }}" name="{{ name }}" value="{{value|e }}" size="{{ size }}">
{%- endmacro %}
and then use it like so:
{% input("hello") %}
Is there any way to accomplish something similar with erb templates?
Thanks in advance!
If you use lambdas/procs instead of methods then you can:
require 'erb'
doc = <<ERB
<% input = lambda do |name, value='', type='text', size=20| %>
<input type="<%= type %>" name="<%= name %>" value="<%= value || 'e' %>" size="<%= size %>">
<% end %>
<% input["hello"] %>
<% input["HELLO", 123, 'select', 50] %>
ERB
puts ERB.new(doc, 0, '>').result
# >> <input type="text" name="hello" value="" size="20">
# >> <input type="select" name="HELLO" value="123" size="50">
The problem is that erb (and erubis) create these strings by parsing the document and creating another piece of code in which the values inside and outside the tags are inverted. Then the template between the tags is just appended to some local variable. (there are actually ways around this, but you have to go somewhat deep, I've had to extend erubis before to get the alternative behaviour I wanted). When you create methods, that changes scope and the local variable can no longer be seen, so it gives you some obscure error NameError: undefined local variable or method ‘_erbout’ for main:Object When you use closures, the environment is captured, including the local variable, so you can then write to it.

How to make a value available in all Liquid templates

I'm using Liquid with Sinatra and would like to make a certain value (Sinatra::Application.environment, specifically) available in all templates without defining it as a local in every get/post. Like so:
In app.rb (my main application file):
# nothing in here about the variable
get '/some/route' do
# or here
liquid :my_template
end
In app.rb--my main application file, or something I can require/include:
some_awesome_technique do
def app_env
Sinatra::Application.environment
end
end
In any template:
<p>
{% if environment == :development %}
Never see this in production
{% end %}
</p>
<!-- or even -->
<p>
{% if dev_mode %}
Or this...
{% endif %}
</p>
I don't really care about the implementation as long as I don't have to put redundant code in every route. Thanks in advance!
Something like this will work
before do
#env = Sinatra::Application.environment
end
then in your template:
{% if #env == :development %}
Boo!
{% endif %}

Resources