Injecting value into inner tag - grails

Having two nested tags, How is it possible to inject a variable into inner tag binding?
class CriteriaTagLib {
def criteria = { attrs, body ->
out << "start"
out << body.call()
out << "end"
}
def eq = {
out << "eq${group}"
}
}
And having in a GSP page :
<g:criteria>
<g:eq></g:eq>
<g:criteria>
The question is how to set a value for group (used inside of eq) from inside of criteria.

<q:criteria> can put own context/data as a request scope attribute (or page scope), and use it inside by <q:eq> (don't forget to remove it on closing tag). Like:
static final CONTEXT = this.class.name
def criteria = { attrs, body ->
def data = [
group: 'test 1'
]
request.setAttribute(CONTEXT, data)
out << "start"
out << body.call()
out << "end"
request.removeAttribute(CONTEXT)
}
def child = { attrs, body ->
def data = request.getAttribute(CONTEXT)
out << 'eq'
out << data.group
}

The inner tag eq has no knowledge of its outer tag criteria. But you could achieve something like below,
def criteria = { attrs, body ->
out << "start"
out << body('hello there')
out << "end"
}
def eq = {attrs->
out << "eq${attrs.group}"
}
and in the gsp page,
<g:criteria>
<g:eq group="${it}"/>
</g:criteria>

Related

Ruby: adding multiple rows to a hash key

I've got a class that looks like this:
class VariableStack
def initialize(document)
#document = document
end
def to_array
#document.template.stacks.each { |stack| stack_hash stack }
end
private
def stack_hash(stack)
stack_hash = {}
stack_hash['stack_name'] = stack.name
stack_hash['boxes'] = [stack.boxes.each { |box| box_hash box }]
stack_hash
end
def box_hash(box)
box_hash = {}
content = []
box.template_variables.indexed.each { |var| content << content_array(var) }
content.delete_if(&:blank?)
box_hash.store('content', content.join("\n"))
return if box_hash['content'].empty?
box_hash
end
def content_array(var)
v = #document.template_variables.where(master_id: var.id).first
return unless v
if v.text.present?
v.format_text
elsif v.photo_id.present?
v.image.uploaded_image.url
end
end
end
The document I'm testing with has two template_variables so the desired result should be a nested hash like so:
Instead I'm getting this result:
=> [#<Stack id: 1, name: "User information">]
i.e., I'm not getting the boxes key nor it's nested content. Why isn't my method looping through the box_hash and content fields?
That's because the to_array method uses each method, which returns the object it's been called on (in this case #document.template.stacks)
Change it to the map and you may get the desired result:
def to_array
#document.template.stacks.map { |stack| stack_hash stack }
end

Escape non HTML tags in plain text (convert plain text to HTML)

Using Rails, I need to get a plain text and show it as HTML, but I don't want to use <pre> tag, as it changes the format.
I needed to subclass HTML::WhiteListSanitizer to escape non whitelisted tags (by changing process_node), monkey patch HTML::Node to don't downcase tags' names and monkey patch HTML::Text to apply <wbr /> word splitting:
class Text2HTML
def self.convert text
text = simple_format text
text = auto_link text, :all, :target => '_blank'
text = NonHTMLEscaper.sanitize text
text
end
# based on http://www.ruby-forum.com/topic/87492
def self.wbr_split str, len = 10
fragment = /.{#{len}}/
str.split(/(\s+)/).map! { |word|
(/\s/ === word) ? word : word.gsub(fragment, '\0<wbr />')
}.join
end
protected
extend ActionView::Helpers::TagHelper
extend ActionView::Helpers::TextHelper
extend ActionView::Helpers::UrlHelper
class NonHTMLEscaper < HTML::WhiteListSanitizer
self.allowed_tags << 'wbr'
def self.sanitize *args
self.new.sanitize *args
end
protected
# Copy, just to reference this Node definition
def tokenize(text, options)
options[:parent] = []
options[:attributes] ||= allowed_attributes
options[:tags] ||= allowed_tags
tokenizer = HTML::Tokenizer.new(text)
result = []
while token = tokenizer.next
node = Node.parse(nil, 0, 0, token, false)
process_node node, result, options
end
result
end
# gsub <> instead of returning nil
def process_node(node, result, options)
result << case node
when HTML::Tag
if node.closing == :close
options[:parent].shift
else
options[:parent].unshift node.name
end
process_attributes_for node, options
options[:tags].include?(node.name) ? node : node.to_s.gsub(/</, "<").gsub(/>/, ">")
else
bad_tags.include?(options[:parent].first) ? nil : node.to_s
end
end
class Text < HTML::Text
def initialize(parent, line, pos, content)
super parent, line, pos, content
#content = Text2HTML.wbr_split content
end
end
# remove tag/attributes downcases and reference this Text
class Node < HTML::Node
def self.parse parent, line, pos, content, strict=true
if content !~ /^<\S/
Text.new(parent, line, pos, content)
else
scanner = StringScanner.new(content)
unless scanner.skip(/</)
if strict
raise "expected <"
else
return Text.new(parent, line, pos, content)
end
end
if scanner.skip(/!\[CDATA\[/)
unless scanner.skip_until(/\]\]>/)
if strict
raise "expected ]]> (got #{scanner.rest.inspect} for #{content})"
else
scanner.skip_until(/\Z/)
end
end
return HTML::CDATA.new(parent, line, pos, scanner.pre_match.gsub(/<!\[CDATA\[/, ''))
end
closing = ( scanner.scan(/\//) ? :close : nil )
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[^\s!>\/]+/)
unless closing
scanner.skip(/\s*/)
attributes = {}
while attr = scanner.scan(/[-\w:]+/)
value = true
if scanner.scan(/\s*=\s*/)
if delim = scanner.scan(/['"]/)
value = ""
while text = scanner.scan(/[^#{delim}\\]+|./)
case text
when "\\" then
value << text
value << scanner.getch
when delim
break
else value << text
end
end
else
value = scanner.scan(/[^\s>\/]+/)
end
end
attributes[attr] = value
scanner.skip(/\s*/)
end
closing = ( scanner.scan(/\//) ? :self : nil )
end
unless scanner.scan(/\s*>/)
if strict
raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
else
# throw away all text until we find what we're looking for
scanner.skip_until(/>/) or scanner.terminate
end
end
HTML::Tag.new(parent, line, pos, name, attributes, closing)
end
end
end
end
end
end

Create dynamically closures in Groovy from a String object

i would like to create a query with the Criteria API in Grails (GORM).
The query will have to be something like this:
MyEntity.createCriteria().list{
assoc{
parent{
eq("code", val)
}
}
}
What i need is to build the nested closure dynamically from a String object. The String for the example above will be "assoc.parent.code" . I splitted the String by dot (by doing String.split("\\.") ) but i don't know how to construct the nested closures:
assoc{
parent{
eq("code", val)
}
}
dynamically based on the array of the splitted Strings above.
What about createAlias?. You could try something like this:
def path = "assoc.parent.code"
def split = path.split(/\./)
MyEntity.createCriteria().list {
// this will get you 'createAlias( assoc.parent, alias1 )'
createAlias split.take( split.size() - 1 ), "alias1"
// this will get you 'eq(alias1.code, userInput)'
eq "alias1.${split[-1]}", userInput
}
This snippet is not generic, but you get the idea.
Update
Not conventional, but you can build a string containing the code with the closures and evaluate it using GroovyShell:
assoc = 'assoc.parent.child.name'
split = assoc.split( /\./ )
path = split[-2..0] // will get us 'child.parent.assoc';
// we will build it from inside-out
def firstClosure = "{ eq '${split[-1]}', 'john doe' }"
def lastClosure = firstClosure
for (entity in path) {
def criteriaClosure = "{ ${entity} ${lastClosure} }"
lastClosure = criteriaClosure
}
assert lastClosure == "{ assoc { parent { child { eq 'name', 'john doe' } } } }"
def builtClosure = new GroovyShell().evaluate("return " + lastClosure)
assert builtClosure instanceof Closure
A more generic approach would be to metaClass String as below, and use it for any kind of separator
. | , - ~ and more.
String.metaClass.convertToClosureWithValue = {op, val ->
split = delegate.split(op) as List
if(split.size() == 1) {return "Cannot split string '$delegate' on '$op'"}
items = []
split.each{
if(it == split.last()){
items << "{ eq '$it', $val }"
split.indexOf(it).times{items.push("}")}
} else {
items << "{$it"
}
}
println items.join()
new GroovyShell().evaluate("return " + items.join())
}
assert "assoc.parent.child.name".convertToClosureWithValue(/\./, "John Doe") instanceof Closure
assert "assoc-parent-child-name".convertToClosureWithValue(/\-/, "Billy Bob") instanceof Closure
assert "assoc|parent|child|grandChild|name".convertToClosureWithValue(/\|/, "Max Payne") instanceof Closure
assert "assoc~parent~child~grandChild~name".convertToClosureWithValue('\\~', "Private Ryan") instanceof Closure
assert "assocparentchildname".convertToClosureWithValue(/\|/, "Captain Miller") == "Cannot split string 'assocparentchildname' on '\\|'"
//Print lines from items.join()
{assoc{parent{child{ eq 'name', John Doe }}}}
{assoc{parent{child{ eq 'name', Billy Bob }}}}
{assoc{parent{child{grandChild{ eq 'name', Max Payne }}}}}
{assoc{parent{child{grandChild{ eq 'name', Private Ryan }}}}}

Dynamic namedQueries

Is their a dynamic namedquery on grails? Im not sure if its the right term but, I mean a namedquery that can be true to all.
Something like:
namedQueries = {
dynamicQuery{ term, name, value ->
term(name, value)
}
}
Then it can be called maybe like but not exactly:
def testClass = TestClass.dynamicQuery('eq', 'lastname', 'Bill').list()
and so you call it too like:
def testClass = TestClass.dynamicQuery('gt', 'id', 12).list()
This one might not work but is their something similar in grails?
UPDATE
The idea is that so I can chained it as many as I want like:
def testClass = TestClass.dynamicQuery('gt', 'id', 12).dynamicQuery('eq', 'stat', 11).list()
This is so that I dont have to create many namedqueries. I was hoping I can create one and use it multiple times.
Grails' createCriteria method generates Grails HibernateCriteriaBuilder instance, within which you can call invokeMethod method to dynamically create query criteria, which usually is defined by the standard DSL.
Here is a example in some controller:
private String dynamicCriteriaTest(String term, name, value) {
def c = TestClass.createCriteria()
def param = []
param << name
param << value
def result = c.list{
c.invokeMethod(term, param as Object[])
}
return result.toString()
}
def test() {
render dynamicCriteriaTest('eq','lastname','Bill')
}
That will get something you want.
update
If you want to call this method multiple times, pass the criteria parameters in an a List then execute the query:
private List dynamicCriteriaTest(List param) {
def c = TestClass.createCriteria()
def paramList = param.collate(3) //split the parameters into groups
def result = c.list{
paramList.each { paramInstance ->
def command = paramInstance[0]
paramInstance.remove(0)
c.invokeMethod(command, paramInstance as Object[])
}
}
return result
}
def test() {
ArrayList param = new ArrayList()
//the 1st criteria
param << 'gt'
param << 'id'
param << (long)12 //you have to check the Grails [HibernateCriteriaBuilder] API to make sure the parameter passed to `invokeMethod` is in the right type (e.g. **long** in this case)
//the 2nd one
param << 'eq'
param << 'stat'
param << (long)11
//even more
param << 'like'
param << 'description'
param << 'some text%'
render dynamicCriteriaTest(param)
}
In Grails you have NamedQueries and also Where Queries. The example you give can possibly be implemented by using a namedqueries and placing this in a abstract domain class. Your domain classes should extend this abstract domain.

Using Curry to Define Grails Tags

I have a grails tag library TpTagLib and in it I want to define 4 new tags that differ only in one constant value, so I tried to use curry.
But there is an exception: groovy.lang.MissingPropertyException: No such property: attr for class: TpTagLib
Does anyone have any idea why this exception occurs?
Here is the code:
def ifPermsTag = { permissions, attr, body ->
def user = attr?.user ?: session.userInstance
if( !user ) return false
if( !securityService.hasPermissions(user,permissions) ) return false
out << body()
return true
}
def canAdminRequestmaps = ifPermsTag.curry(Permission.CAN_ADMIN_REQUESTMAPS)
def canAdminCorporations = ifPermsTag.curry(Permission.CAN_ADMIN_CORPS)
def canAdminUsers = ifPermsTag.curry(Permission.CAN_ADMIN_USERS)
def canAdminDevices = ifPermsTag.curry(Permission.CAN_ADMIN_DEVICES)
Cool technique. You just need to make ifPermsTag private so it's not considered a candidate to be a usable tag method:
private ifPermsTag = { permissions, attr, body ->
...
}
Tags can have no parameters, or an 'attr' parameter, or an 'attr' and 'body' parameters but other signatures are invalid.

Resources