Multiple method overrides in Mason2 - perl

Building a website using Mason2. Each page should call 3 filters (methods, called by content):
<% $.filter1(args...) {{ %>
<% $.filter2(args...) {{ %>
<% $.filter3(args...) {{ %>
I have 3 different implementation of those filters (methods). The default set of those 3 filters, are defined in the top level /Base.mc component.
Now, for a different parts of the site - (different routes), say for the
/a/all/paths>/...>
/b/.....
need use the above default set of filters/methods, but for another routes,
/c/...
/d/...
/e/...
need use another set of filter1, filter2, filter3 and for the
/x/....
/y/...
want use the third set.
The methods can be easily redefined, in lower level components, but doing it as in (this question), isn't honors the DRY principe, e.g. in each
/c/Base.mc
/d/Base.mc
/e/Base.mc
Need repeat the same 3
<%override filter1>
...
<%override filter2>
...
<%override filter3>
The question is:
how to write only once the 3 different implementations of the methods, and how to use them at once?
I tried make an compomponent, like: /set2.mi, /set3.mi, where I tried override those filter-methods, and called it in the needed /{c,d,e}/Base.mc as
<& /set2.mi &>
But this doesn't works.
How to write the 3 different methods implementatinons and call them at once in the needed deeprr Base.mc? Is it possible?
In standard perl i would probably use roles, so in the needed packages I would use something like:
with 'Set1';
with 'Set2';
Where the packages Set1 and Set2 would contain the implementations of the needed methods, or for dynamic loading would use the require ... . Is something like possible in the Mason2, or i must repeat the %ovverride?
Hope this make sense... if not, please add an comment and i will try update the question.
EDIT
Example, for shorting the code, using only one filter not 3.
The /Base.mc
<%augment wrap><% inner() %></%augment>
% #this is the "default" MyHead filter
<%filter MyHead($arg)>
<!-- in the real code it is more complicated, but for an illustration it is enough -->
<h1 class="<% $arg %>"><% $yield->() %></h1>
</%filter>
When in the /a/index.mc using it as
% $.MyHead('big') {{
some head text
% }}
will output like:
<h1 class="big">some head text</h1>
now, have an another MyHead.
<%filter MyHead($arg)>
<!-- in the real code it is more complicated - basically want output different thing -->
<h2 id="<% $arg %>"><% $yield->() %></h2>
</%filter>
If I add the above code to my /b/Base.mc it will work, and calling the MyHead filter in the /b/index.mc
% $.MyHead('modal') {{
some other text
% }}
will call the redefined filter, and will output what i want
<h2 id="modal">some other text</h2>
The problem is,
I don't want repeat the above filter code, in N other Base.mc components, like in /c/Base.mc and /d/Base.mc and so on.
How to achieve, to write the filter once and "use" it in many other components for "redefine" the default one.

One solution could be (not a nice one and produces an "spaghetty" like code) playing with the inheritance chain, as next:
Remove your "default" filter from the /Base.mc, so it would contains only the <%augment wrap
Create one component called for example: BaseSetDefault.mc and enter into it, your "default" filter and explicitly set the inheritance chain to the top level /Base.mc
<%augment wrap><% inner() %></%augment>
<%filter MyHead($arg)><h1 class="<% $arg %>"><% $yield->() %></h1></%filter>
<%flags>
extends => '/Base.mc'
</%flags>
Similarly, create an another component say called as /BaseSet2.mc and put here your "Set2" filters, like:
<%augment wrap><% inner() %></%augment>
<%filter MyHead($arg)><h2 id="<% $arg %>"><% $yield->() %></h2></%filter>
<%flags>
extends => '/Base.mc'
</%flags>
Now, in the /a/Base.mc and /b/Base.mc - everywhere, you want the "default" set, change the chain
<%flags>
extends => '/BaseSetDefault.mc'
</%flags>
and in the /c/Base.mc and /d/Base.mc - everywhere you want the "Set2" filters, use the
<%flags>
extends => '/BaseSet2.mc'
</%flags>
From now, the inheritance chain for /c/index.mc will be:
/c/index.mc -> /c/Base.mc -> /BaseSet2.mc -> /Base.mc
and the execution would be done as
/Base.mc -augment-> /BaseSet2.mc -augment-> /c/Base.mc -main-> /c/index.mc
^^^^^^^^^^^^ - defines the Set2 filters
and for the /a/index.mc inheritance chain
/a/index.mc -> /a/Base.mc -> /BaseSetDefault.mc -> /Base.mc
the execution
/Base.mc -augment-> /BaseSetDefault.mc -augment-> /a/Base.mc -main-> /a/index.mc
^^^^^^^^^^^^^^^^^^ - defines the "Default" filters
It is not very nice solution, but works...

Related

FlaskWTF same form on every view / page

in my Flask Project i have configured a basepage.html Jinja template every other page inherits from.
In this basepage.html i want to have a Search bar at the top, so that it also appears at every page inheriting from it.
The input from this search form (is a form even the best way to do it?) should be processed
by the routing function search() I defined as follows:
views.py
#app.route('/')
#app.route('/dashboard')
def dashboard():
return render_template('Dashboard.html') #Dashboard.html inherits from basepage.html
#app.route('/search', methods=['GET' ,'POST'])
def search():
search_query = #input from search-bar
result = #get data matching search_query from database
return render_template('Search.html', results = results) #Search.html extends basepage.html
forms.py excerpt:
class Search(FlaskForm):
search_query = StringField('Search for ...')
submit = SubmitField('Search')
Is there a way i can have my Search form on every page, so when i enter my query in the bar at the top of the Dashboard form and press enter the search_query gets processed by the search() view?
Just create your searchbar template code in a partial file located at /templates/includes/my_searchbar.html or whatever you want to name it and then in your base template just place an includes directive wherever you want the search to render in, something like:
{% include 'includes/my_searchbar.html' %}
If the searchbar is included in base and all your other templates inherit from base, then search will be included on all pages.
All of your routes will also need to have the Search() form class included so they have access to the form, so you will need to modify your routes something like:
from app.forms import Search #if you defined Search in /app/forms.py
#app.route('/dashboard')
def dashboard():
search_form = Search() #initialize an instance of the form
return render_template('Dashboard.html', search_form=search_form)
Then you can use search_form in your includes/my_searchbar.html partial to render the form. Maybe something like:
<form class="form" method="post" role="form">
{{ search_form.hidden_tag() }}
{{ search_form.search_query }}
{{ search_form.submit }}
</form>

How to access a method and pass an argument within the template?

In my template I want check whether an entity has a relation to another one. Meaning one object is in an attached Object Storage of another one.
In the controller I can simply call:
if ($product->getCategory()->offsetExists($category) {
print 'In category ' . $category->getName();
}
But I can't figure out the correct syntax in the template. I tried those without luck (both evaluate to true everytime):
<f:if condition="{product.category.offsetExists(category)}">true</f:if>
<f:if condition="{product.category.offsetExists({category})}">true</f:if>
Is this even possible within the template?
You can only access properties via Getter from Fluid with no parameters, but you can implement an own ViewHelper to check that. As parameters you can use your Product and the Category. Then you can call your ViewHelper from Fluid this way:
<vh:checkOffset product="{product}" category="{category}" />
or inline
{vh:checkOffset(product: product, category: category)}
Within your ViewHelper you can check this in the way you've done it in your Controller:
public function render($product, $category){
return ($product->getCategory()->offsetExists($category));
}
Additionally to sretuer's answer, I'll only mention that you can create VH which will display block conditionally like:
File typo3conf/ext/your_ext/ViewHelpers/CheckOffsetViewHelper.php
<?php
namespace VENDORNAME\YourExt\ViewHelpers;
class CheckOffsetViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper {
public function render() {
return ($product->getCategory()->offsetExists($category))
? $this->renderChildren()
: '';
}
}
?>
So you can use it in the view:
{namespace vh=VENDORNAME\YourExt\ViewHelpers}
<vh:checkOffset product="{product}" category="{category}" >
Display this only if product is in category
</vh:checkOffset>
Of course you need to fix VENDORNAME and YourExt according to your extension, can be found at the beginning of every controller, model, repository etc.
You may consider https://fluidtypo3.org/viewhelpers/vhs/master/Condition/Iterator/ContainsViewHelper.html which is designed for creating conditions in Fluid that check if an array or Iterator contains another object and works exactly like f:if regarding then and else arguments and f:then and f:else child nodes.

Is this joining coffeescript classes over files a valid aproach?

I want to join (use) classe in Coffescript files, and i found some usefull ideas here (sry for not including all links), since none fitted my needs (at least not as i like) I tried to find an own solution, this will work fine, but is there something I have overseen?
following "base" and "base class" are not OO words, I just did not find better names
I have a base (class) file TechDraw
class TechDraw
constructor: ->
$(window).load () =>
... do somthing
wave_to_me: ->
say 'I wave' # say is a basic func, I always use for debugging (console.log)
#TechDraw = new TechDraw
this works fine
Now I want to expand/extend my class with "sub classes/modules" in other files; ie. I have a TechDrawLine, and a TechDrawCalc, ans so on
What I did, was building a coffee file for each of them like:
class TechDrawConnector
constructor: (p)->
#parent=p
wave: (msg) ->
say 'yes its me, the connector:',msg
`window.TechDrawConnector = function(p) { return new TechDrawConnector(p) };`
# the last instead of a simple new like
# #TechDrawConnector = new TechDrawConnector
and my base(ic) class/module I extendet like this:
class TechDraw
constructor: ->
$(window).load () =>
#Connector=window.TechDrawConnector(this)
#LineGuy=window.TechDrawLineGuy(this)
#Connector.wave('init')
Since I am a newbee in coffeescript (yes javascript also) my solution feels to simple ...
Have I overseen somthing? The Polution of the global namespace is not so bad I think
You cant create an "extension" that way.
If you define the same class in the same namespace a second time the first class will simply be overwritten and become in accessible. This will mostly be dependent on the order of loading of the compiled JavaScript files.
However you could either later add an method to the prototype of the class
#file a.coffee
class A
constructor: ->
#foo()
#file B.coffee
A::foo = -> #do something
However this is no good style and can certainly be very confusing some time and lead to brittle errors.
Better would be to use a form of dependency injection
#file a.coffee
class A
constructor: (#closure) ->
$(window).load () => #closure()
#file B.coffee
new A () ->
#Connector=window.TechDrawConnector(#)
#LineGuy=window.TechDrawLineGuy(#)
#Connector.wave('init')

How to define a `separator` tag in play-1.x without modifing play's source code

I want to define a tag separator tag, which inside a list tag, can add separator between items.
The sample code is:
List<String> users = new ArrayList<String>();
users.add("Jeff");
users.add("Mike");
#{list users, as: 'user'}
#{separator ' + ' /}
<span>${user}</span>
#{/list}
If I don't use the separator tag, the code will be:
#{list users, as: 'user'}
${user_isFirst ? '' : ' + '}
<span>${user}</span>
#{/list}
The generated html code will be:
<span>Jeff</span> + <span>Mike</span>
I tried defined a fastTag:
public static void _separator(Map<?, ?> args, Closure body, PrintWriter out, GroovyTemplate.ExecutableTemplate template, int fromLine) {
Object value = args.get("arg");
// TODO how to get the value of `as` defined in parent `list` tag?
out.print(value);
}
But the problem is I can't get the value of as defined in list tag (which is user) in this case.
You can create a custom list tag in groovy like this
#{list items:_arg, as:'tmp'}
%{
attrs = [:]
attrs.put(_as, tmp)
}%
#{ifnot tmp_isFirst}${_sep}#{/ifnot}
#{doBody vars:attrs /}
#{/list}
and use it like this
#{myList users, as:'user', sep:','}
${user}
#{/myList}
You should trace into your FastTag implementation. I think you'll see all the variables in scope inside the args map. This is from memory - so, sorry if not.
That said, I think it might be simpler if you copy the Java code for #{list} and add a new parameter, like
#{list users, as: 'user', separator: '+' }
and handle the logic in there. It seems a bit cleaner too from a design point of view - if it is a separator, how come you can put it anywhere you like in the code (and why not put it in twice!).
A final option is to look at Groovy or Java collection operators. http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html

More-efficient way to pass the Rails params hash to named route

I need a more-efficient way to pass the params hash to a named route, including the ability to add/remove/modify a key/value pair.
Adding a key (the :company symbol), while preserving the remainder of the params hash (manually specify each symbol/value):
# adds the company filter
link_to_unless params[:company]==company, company, jobs_path(:company=>company, :posted=>params[:posted],:sort=>params[:sort],:dir=>params[:dir])
Removing a key (eliminates the :company symbol), while preserving the remainder of the params hash (manually specify each symbol/value):
# create a link that removes the company filter
link_to_unless_current 'x', jobs_path(:posted=>params[:posted],:sort=>params[:sort],:dir=>params[:dir])
I thought of just passing the params hash directly, but that throws an exception:
link_to_unless params[:company]==company, company, jobs_path( params )
I'm hoping for some DRYer alternatives.
Refactored the helper function:
def options(hash)
o={:employer=>params[:employer],:location=>params[:location],:posted=>params[:posted],:starting=>params[:starting],:sort=>params[:sort],:dir=>params[:dir],:fav=>params[:fav]}
# add the contents of hash, overwriting entries with duplicate keys
o.merge!(hash)
end
Refactored the view code to pass hash instead of key/value pair; greater flexibility:
<%= link_to_unless params[:employer]==employer, employer, jobs_path( options({:employer=>employer}) ) %>
and
<%= link_to_unless_current '✗', jobs_path( options({:employer=>nil}) ) %>
This approach works, but doesn't seem optimal:
helper:
def options(key, value)
# create a hash of all params
o={:employer=>params[:employer],:posted=>params[:posted],:starting=>params[:starting],:sort=>params[:sort],:dir=>params[:dir]}
# set value
o[key]=value
# return hash
o
end
view:
# change symbol's value
<%= link_to_unless params[:employer]==employer, employer, jobs_path( options(:employer, employer) ) %>
# remove symbol
<%= link_to_unless_current '✗', jobs_path( options(:employer, nil) ) %>
It seems like I should be able to work with the params hash directly, but this will work for now.
Refactored version. Put this in the target controller so it won't be global:
# new_params: new parameters you wish to pass on
# white_list: array of symbols, representing additional keys from existing params which you wish to pass on
def options(new_params, white_list = [])
white_list += [ :employer,:location,:posted,:starting,:sort,:dir,:fav ]
white_list.uniq!
new_params.reverse_merge( params.slice(white_list) )
end
helper_method :options