How to let a user render a partial?

David T. wrote:

==========
def dropify(content)
s = StringScanner.new(content)
output = “”
previous_end = 0
while s.scan_until(/{/)
output << content[previous_end, s.pointer - previous_end - 1]
partial = s.scan(/\w+/)
s.skip /\s+/
arguments = {}
while argument = s.scan(/\w+:\w+/)
name, value = argument.split(/:/)
arguments[name.to_sym] = value
s.skip /\s+/
end
s.skip_until /}/
previous_end = s.pointer
output << render(:partial => “drops/#{partial}”, :locals =>
arguments)
end
output << content[s.pointer, content.length - s.pointer]
end

Okay, I actually have a concern about this method. Right now it only
accepts this kind of syntax:

{test foo:foo_bar}

Since spaces are not covered by \w+ this is not accepted:

{test foo:foo bar}

So I tried changing \w+ to .+ but of course that is never going to work.
E.g. if I try to pass multiple arguments to the partial like this:

{test foo:foo bar bar:bar foo}

then only one argument will only be passed (foo) and it will have this
value: “foo bar bar” because the .+ includes that.

But what about putting quotation marks around it?

“.+”

Still the same problem. Now foo will just have this value: ““foo bar”
bar”. So, here is my question: How can I make this method accept
multiple arguments without being limited to that the values have to
match “\w+”?

how about doing something like this logic

scan your content looking for “{”
store everything until your next “}” into a string variable # now
string = “test foo:foo bar bar:bar foo”
split your string by spaces into an array # now array = [“test”,
“foo:foo”, “bar”, “bar:bar”, “foo”]

loop through your array
new_array = []
index = 0
array.each do |a|
if a does not contain “:”
index = index + 1
partial = a
args = {}
new_array[index] = [partial, args]
else
args = new_array[index][2] # should be a hash
name, value = a.split(/:/)
args[name.to_sym] = value # add new args to hash
new_array[index][2] = args # store hash back into the array
end
end

now new_array = [[“foo”, {foo => “foo”}], [“bar”, {bar => “bar”}],

[“foo”, {}]]
loop through this and render partials
new_array.each do |a|
partial = a[1]
arguments = a[2]
output << render(:partial => “drops/#{partial}”, :locals =>
arguments)
end

i am sure there are errors and bugs but i think the basic logic should
work. it will probably turn out to be fairly clean and readable if
refactored into a few methods.

On Oct 15, 6:01 am, David T. [email protected]

Scott Nj wrote:

how about doing something like this logic

It’s good to hear your oppinion on it, but I actually like Fred’s
approach on it a little better. It is clean and fairly readable.

i am sure there are errors and bugs but i think the basic logic should
work. it will probably turn out to be fairly clean and readable if
refactored into a few methods.

The thing is that Fred helped me putting together a method and he’s
spent a lot of time fixing all the bugs I found. Why would I completely
remake the method just to find a bunch of new bugs?

Okay, I think I should describe my tricky problem a little further. This
is it: The method does a scan_until(/{/) which means it stops when
meeting a curly brace and starts looking for something matching \w+
immediately after the curly brace. That is the partial to render.

The string looks like this: {test_partial foo:“bar foo” bar:“foo bar”}

Fine. Then it starts looking for arguments immediately after the partial
and a space. My problem is that the method only supports values that
matches \w+. I would like values to be able to match .* or .+.

Now, my problem is that when I change the method so that the value
should match .+ it doesn’t only take the characters between the first
and the second quotation sign (name: “foo”, value: “bar foo”), it
actually goes on at takes the next quotation sign too (name: “foo”,
value: “bar foo” bar:"). So that we actually get a value that contains
the right value and then the name of the NEXT argument afterwards.

How can I solve this? If you need further details, just ask.

On 16 Oct 2008, at 08:36, David T. wrote:

partial
and a space. My problem is that the method only supports values that
matches \w+. I would like values to be able to match .* or .+.

Greedy things like .* are almost always dangerous as they make you
consume the entire string. typically you’d want to scan or scan_until
the first occurence of something

the following modification to the previous solution does that:

def dropify(content)
s = StringScanner.new(content)
output = “”
previous_end = 0
while s.scan_until(/{/)
output << content[previous_end, s.pointer - previous_end - 1]
partial = s.scan(/\w+/)
s.skip /\s+/
arguments = {}
while label= s.scan(/(\w+):"/)
name=s[1]
value = s.scan /[^"]+/
arguments[name.to_sym] = value
s.skip /"\s+/
end
s.skip_until /}/
previous_end = s.pointer
puts arguments.inspect
end
end

Having scanned \w+:" (ie a label and the opening quote mark) we
consume all non " characters and says those are the arguments. then we
skip over the closing " and any whitespace

dropify(’{test_partial foo:“bar foo” bar:“foo bar”}’)

outputs

{:foo=>“bar foo”, :bar=>“foo bar”}

Fred

Frederick C. wrote:

On 16 Oct 2008, at 08:36, David T. wrote:

partial
and a space. My problem is that the method only supports values that
matches \w+. I would like values to be able to match .* or .+.

Greedy things like .* are almost always dangerous as they make you
consume the entire string. typically you’d want to scan or scan_until
the first occurence of something

Yes.

the following modification to the previous solution does that:

def dropify(content)
s = StringScanner.new(content)
output = “”
previous_end = 0
while s.scan_until(/{/)
output << content[previous_end, s.pointer - previous_end - 1]
partial = s.scan(/\w+/)
s.skip /\s+/
arguments = {}
while label= s.scan(/(\w+):"/)
name=s[1]
value = s.scan /[^"]+/
arguments[name.to_sym] = value
s.skip /"\s+/
end
s.skip_until /}/
previous_end = s.pointer
puts arguments.inspect
end
end

This works perfectly. I modified it a little bit for my needs. This
method supports multiple arguments with values containing any kind of
characters:

def dropify(content)
s = StringScanner.new(content)
output = “”
previous_end = 0
while s.scan_until(/{/)
output << content[previous_end, s.pointer - previous_end - 1]
partial = s.scan(/\w+/)
s.skip /\s+/
arguments = {}
while label= s.scan(/(\w+):"/)
name = s[1]
value = s.scan /[^"]+/
arguments[name.to_sym] = value
s.skip /"\s+/
end
s.skip_until /}/
previous_end = s.pointer
output << render(:partial => “drops/#{partial}”, :locals =>
arguments)
end
output << content[s.pointer, content.length - s.pointer]
end

Having scanned \w+:" (ie a label and the opening quote mark) we
consume all non " characters and says those are the arguments. then we
skip over the closing " and any whitespace

dropify(’{test_partial foo:“bar foo” bar:“foo bar”}’)

outputs

{:foo=>“bar foo”, :bar=>“foo bar”}

Great! Thanks again for your help, Fred.

David T.