Creating methods with a macro based on the names of an enum - macros

Having a struct with the following enum:
struct Payment
enum Status
Open
Paid
Failed
def to_s
super.downcase
end
end
getter status : String
end
How can I access the enum names in a macro to create methods like open?, paid? and failed??
SOLUTION
Based on #Jonne Haß' proposal, this is what I wanted to achieve:
{% for value in Status.constants %}
{% downcased = value.stringify.downcase %}
def {{ downcased.id }}?
{{ downcased }} == status
end
{% end %}

With TypeNode#constants:
struct Payment
enum Status
Open
Paid
Failed
end
getter status : Status = Status::Open
{% begin %}
delegate({{Status.constants.map {|value| value.stringify.downcase + "?" }.join(", ").id}}, to: status)
{% end %}
end

Related

jinja2: passing a string as a parameter to a macro?

I have the following python code to invoke jinja2. In this code, I want to pass a string as an argument to a macro ...
#!/usr/local/bin/python3
import jinja2
test_template = '''
{% macro testmacro(start, arg1, arg2, arg3) -%}
start_{{arg1}}_{{arg2}}_{{arg3}}
{%- endmacro %}
result = {{ testmacro('begin', A, B, C) }}
'''
template = jinja2.Template(test_template)
rendered = template.render(dict(A='AAA', B='BBB', C='CCC'))
print(rendered)
The result is start_AAA_BBB_CCC. However, I want it to be begin_AAA_BBB_CCC.
And if I call the macro with 'xyz' as its first parameter, then I want the output to be xyz_AAA_BBB_CCC.
I don't want the first parameter to be passed to jinja2 from the caller as another variable. I want it hard-coded inside of the template, itself, as a parameter to the macro call.
How can I do this in jinja2 ?
Thank you very much in advance.
Oh, never mind. The answer is simple:
{% macro testmacro(start, arg1, arg2, arg3) -%}
{{start}}_{{arg1}}_{{arg2}}_{{arg3}}
{%- endmacro %}
I thought I tried that before, and it didn't work, but I must have made some other error that I didn't realize.
As long as the string argument is passed within quotes, this works.

Wagtail: Rendering tags from Fieldblock

I am really stuck on this: (sorry, newbie problems)
Following the method exposed in this post I need to render the tags in a page, but I am not able to get the tag values through the #property
The code is as follow, the models:
class TagsBlock(blocks.FieldBlock):
"""
Basic Stream Block that will use the Wagtail tags system.
Stores the tags as simple strings only.
"""
def __init__(self, required=False, help_text=None, **kwargs):
# note - required=False is important if you are adding this tag to an existing streamfield
self.field = forms.CharField(widget=AdminTagWidget, required=False)
super().__init__(**kwargs)
class ServicesPage(Page):
services = StreamField([
('services',blocks.StructBlock([
('entries',PortfolioBlock()),
('tags',TagsBlock(required=False)),
]))], null=True, blank=True)
#property
def get_tags(self):
"""
Helpful property to pull out the tags saved inside the struct value
Important: makes some hard assumptions about the names & structure
Does not get the id of the tag, only the strings as a list
"""
tags_all = [block.value.entries.get('tags', '').split(',') for block in self]
tags = list(chain.from_iterable(tags_all))
return tags
content_panels = Page.content_panels + [
StreamFieldPanel('services'),
]
Then in the template im just doing:
<div class="row-4">
{% for tag in page.services.get_tags %}
{{tag}}
{% endfor %}
</div>
However, I can't get any result. I have really tried everything, but I can't figure how to call the property to give me the list of values.
Thank you very much in advance for your time,
Francisco
In your template, the line
{% for tag in page.services.get_tags %}
would be trying to access a property named get_tags on the 'services' field of the page. However, get_tags isn't defined there - it's defined as a property of the page object - so this line should be
{% for tag in page.get_tags %}
Secondly, in the line
tags_all = [block.value.entries.get('tags', '').split(',') for block in self]
you're intending to loop over all the items in the StreamField, but the self in for block in self refers to the page object. This should be for block in self.services.
Finally, in the same line, block.value will give you the value of each block, which in this case will be a dictionary of two items, entries and tags. If you wanted to access entries (the PortfolioBlock), you would write block.value['entries'] or block.value.get('entries') rather than block.value.entries - but really you don't want that, you want to access the tags item instead - so block.value.entries.get('tags', '').split(',') should be block.value.get('tags', '').split(',').

View error in Flask when retrieving item in MongoDB

I am trying to populate a page built in Flask from items in MongoDB. My route looks like:
#app.route('/job/<posting>', methods=["GET"])
def job_post(posting):
posting = db.openings.find({'Id': posting})
title = db.openings.find_one({'Id': posting}, {'Title': 1, '_id':0})
return render_template('post_page.html', posting=posting, title=title)
My template page for post_page.html is:
{% extends "layout.html" %}
{% block content%}
<div class="container">
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<h3><a class="mr-2" href="#">{{ title }}</a></h3>
</div> -->
</article>
</div>
{% endblock content %}
When I attempt to access a page, I get an error saying "bson.errors.InvalidDocument: cannot encode object: <pymongo.cursor.Cursor object at 0x10dd402e8>, of type: <class 'pymongo.cursor.Cursor'>"
When I research that error, most of the issues have come from using find instead of find_one. I am using find_one here, and when I test it in a Python shell, the variable type for title is a dictionary, so I'm confused as to why it's passing a cursor object to the template.
You are passing posting in to the function and then immediately setting it in the first function posting = db.openings.find({'Id': posting}). I suspect you don't mean to do this.
find() return a cursor which you're then passing into the find_one() function which is causing the error you see.
Not sure what you are trying to achieve by calling find() then find_one() on the same collection. My suggestion is you remove the line posting = db.openings.find({'Id': posting})

mailjet mjml blank array throws error

mailjet mjml blank array throws error
In my var:low_price_low_stock_goods is empty array for loop should not execute but it throws error: No value for "var:low_price_low_stock_goods"
{% for item in var:low_price_low_stock_goods %}
{% endfor %}
As answered on Github and your other StackOverflow question: it's a known issue on Mailjet's templating language side, sorry for that. To avoid it, just pass a valid object in the API call for the low_price_low_stock_goods value. This will work:
{
item.good_name: "",
item.value: "",
item.count: ""
}

Pyrocms tag as parameter in another tag

I tried to use {{ page:slug }} as a parameter in my page to get the blog articles from the category of the same name. For example:
Pagename = About me
Slug = about-me
Then create a category with the same name and slugname in Blog with associated articles. Now in pagelayouts I thought I could create the following, but it doesn't seem to work. Does anyone know why not?
{{ blog:posts order-by="created_on" dir="asc" category="{{ page:slug }}" }}
<section class="title">
<h4>
{{ title }}
</h4>
</section>
<section class="item">
<p>{{ intro }}</p>
<p>Read more..</p>
</section>
{{ /blog:posts }}
Solved
I found the answer by asking it face to face to another developer. Since this is a templating language, it doesn't support functionality. It just reads pre-made variables. So I will have to solve this problem by creating another method in pages/plugins.php.
You don't need to try and embed a tag in a string, just pass the tag straight to the attribute.
{{ blog:posts order-by="created_on" dir="asc" category="{{ page:slug }}" }}
Should be:
{{ blog:posts order-by="created_on" dir="asc" category=page:slug }}
Easier than you thought ey?
This is how I solved it using PHP. The below edit checks if the page parameter from the {{ blog:posts }} tag is set. When it is, it grabs the last segment and uses it as category filter in the database query to retreive only those posts:
In system/cms/modules/blog/plugin.php look for the 'posts' function and add a parameter:
$page = $this->attribute('page');
Then use the following statement to check if the parameter has been set and then add a 'where' statement:
if($page) //check if page is set
{
$segment = end($this->uri->segment_array()); //get the last segment from the url
$this->db->where('blog_categories.slug', $segment); //use the segment as filter
}
Now you can create a page containing blog posts from which the categories refer to its pagename like for example: www.website.com/pagename/subpagename/subsubpagename/awesome then use this as pagelayout and it will load a list of blogposts that have 'awesome' as category:
<h3>{{ page:title }}</h3>
{{ blog:posts order-by="created_on" dir="asc" page="true" }}
<h4>{{ title }}</h4>
<p>
{{ intro }}</p>
<p>
Read more..</p>
{{ /blog:posts }}
Instead of using tags i have found a simple solution to avoid tags as much as we can. Here is it.
Instead of using tags call a view in plugin and pass the third parameter as TRUE so that it return string instead of loading view than do any kind of looping and conditional checking in the view as usuall as you do with php o course. No need to meet tags there. After that in plugin where you are calling this view simply return a single variable and use your variable in tags in the page to display content of view.
Here is an example
class Plugin_Home extends Plugin
{
function test()
{
$this->load->model('test/test_m');
$data['test'] = $this->test_m->index();
return $this->load->view('test/test_view',$data , TRUE);
}
}
And in the page you can call it like this
{{ Home:test }}
And get rid of tags for conditioning and looping