Ruby REXML: Get Value Of An XML Element - ruby-on-rails

I am trying to put the values of some xml elements into an array using rexml. Here is an example of what I am doing:
doc = Document.new("<data><title>This is one title</title><title>This is another title</title></data>")
XPath.each( doc, "*/title") { |element|
puts element.text
}
However, that outputs:
[<title> ... </>, <title> ... </>]
How can I get it to output an array containing "This is one title" and "This is another title"?

Moving my comment to an answer, per request:
While puts may convert its argument its argument to a string anyway, you can have the XPath return the text node in the first place:
XPath.each(doc, "*/title/text()") {...

Are you sure about that? Here's a complete program:
#!/usr/bin/ruby
require 'rexml/document'
include REXML
doc = Document.new("<data><title>This is one title</title><title>This is another title</title></data>")
XPath.each( doc, "*/title") { |element|
puts element.text
}
Output:
This is one title
This is another title
Edit: It sounds like the OP has moved on, but I think there should be some clarification added here for future visitors. I upvoted #LarsH's good answer, but it should be noted that, given the OP's specific input, element.text should produce exactly the same output as would result from selecting the text() nodes in the first place. From the docs:
text( path = nil )
A convenience method which returns the String value
of the first child text element, if one exists, and nil otherwise.
The sample input given in the original question shows <title> elements containing only one text node in each case. Therefore, these two methods are the same (in this case).
However, pay attention to this important note:
Note that an element may have multiple Text elements, perhaps
separated by other children. Be aware that this method only returns
the first Text node.
You can get all of an element's child text nodes using texts() (plural).
What I suspect a lot of people are really looking for is an equivalent of the DOM's textContent (or its illegitimate cousin innerText). Here's how you might do that in Ruby:
XPath.each(doc, "*/title") { |el|
puts XPath.match(el,'.//text()').join
}
This joins all of the text descendants of each element into a single string.
The short answer is that there's no short answer. Which one of these you want, if any, is highly context-specific. The only requirement in the original question is to "put the values of some xml elements into an array", which isn't really much of a specification.

Related

How to append one Google Doc to another using APIv1

I'm writing a merging program. Instead of creating one document per input row, I want the merged (template with input values replaced) output from each input row added to a single document.
And the template is not just text. It's another Google doc with a standalone table.
I started with the mail merge example and it works fine but produces a separate doc for each input row. I've written a helper function that I call for each output doc, with "dest_doc" set to my summary doc.
Sadly, this does not work since the API seems only to permit unstyled text insertion, not contents-from-a-Doc.
It works fine when using 'hello world ' -- this is properly replicated in the output file. But when I replace it with body or body['contents'] it fails.
Is there any way of doing this? I'd balk at writing a contents-tree-to-UpdateRequest translator.
def append_contents(dest_doc, source_doc):
"""Copy the contents of the source doc to the end of the destination doc
"""
document = DOCS.documents().get(documentId=source_doc).execute()
body = document.get('body')
requests = [
{
'insertText': {
'endOfSegmentLocation': {
'segmentId': '',
},
'text': 'hello world '
}
},
]
DOCS.documents().batchUpdate(documentId=dest_doc, body={'requests': requests}).execute()
return
Thanks for your time.
Unfortunately you can't use the insertText request to append a complex object (as the Body of another Document) to your Document. This request only accepts text.
I'm afraid that the only solution here would be to parse the elements of your documents and create a request translator for each one of their types.
Even in Apps Script as you can see in this example, the merge is done element per element according to its type (Text, Table, Image, Paragraph, etc.).
However, if this feature is really important to you, you should definitely submit a feature request here

Access all fields in Zapier Code Step

Is it possible to access all the fields from a previous step as a collection like json rather than having to explicitly setting each one in the input data?
Hope the screenshot illustrates the idea:
https://www.screencast.com/t/TTSmUqz2auq
The idea is I have a step that lookup responses in a google form and I wish to parse the result to display all the Questions and Answer into an email.
Hope this is possible
Thanks
David here, from the Zapier Platform team. Unfortunately, what I believe you're describing right now isn't possible. Usually this works fine since users only map a few values. The worst case is when you want every value, which it sounds like you're facing. It would be cool to map all of them. I can pass that along to the team! In the meantime, you'll have to click everything you're going use in the code step.
If you really don't want to create a bunch of variables, but you could map them all into a single input and separate them with a separator like |, which (as long as it doesn't show up in the data), it's easy to split in the code step.
Hope that helps!
The simplest solution would be to create an additional field in the output object that is a JSON string of the output. In a Python code step, it would look like
import json
output = {'id': 123, 'hello': 'world'}
output['allfields'] = json.dumps(output)
or for returning a list
import json
output = [{'id': 123, 'hello': 'world'},{'id': 456, 'bye': 'world'}]
for x in output:
x['allfields'] = json.dumps(output[output.index(x)])
Now you have the individual value to use in steps as well as ALL the values to use in a code step (simply convert them from JSON). The same strategy holds for Javascript as well (I simply work in Python).
Zapier Result
Fields are accessible in an object called input_data by default. So a very simplistic way of grabbing a value (in Python) would be like:
my_variable = input_data['actual_field_name_from_previous_step']
This differs from explicitly naming the the field with Input Data (optional). Which as you know, is accessed like so:
my_variable = input['your_label_for_field_from_previous_step']
Here's the process description in Zapier docs.
Hope this helps.

AppiumLibrary: Element Should Not Be Visible Keyword?

In AppiumLibrary, as of version 1.4.5, a very handy keyword called Element Should Be Visible has been introduced. But I am looking for the opposite version of that keyword too, which would be something like Element Should Not Be Visible. Since AppiumLibrary doesn't have that yet, is there any way we could achieve this?
There's always the possibility of expanding the library's python code, this shouldn't be very difficult. You basically just need to clone the Element Should Be Visible keyword definition and change the condition to do the opposite.
But, if that's not a possibility for you, perhaps you could use the keyword Run Keyword And Expect Error, in combination with the keyword you mentioned, Element Should Be Visible. Using this keyword on an element that isn't visible would throw an error, which in this particular case would be the desired outcome.
Still, that's a fishy workaround which will not help the readability of your test, and you should consider first looking into expanding the library itself.
Thanks to Verv for the guidelines. I tried both approaches that he suggested, and both seem to be super easy. For future reference, I'm explaining both methods here.
Method 1:
Under AppiumeLibrary/keywords directory, there's a file called _element.py, which defines the Element Should Be Visible keyword. I was able to clone it to create the new keyword I was looking for.
Below is the code snippet that defines Element Should Be Visible
def element_should_be_visible(self, locator, loglevel='INFO'):
"""Verifies that element identified with locator is visible.
Key attributes for arbitrary elements are `id` and `name`. See
`introduction` for details about locating elements.
New in AppiumLibrary 1.4.5
"""
if not self._element_find(locator, True, True).is_displayed():
self.log_source(loglevel)
raise AssertionError("Element '%s' should be visible "
"but did not" % locator)
Next to the above code snippet, you can add the following code snippet to define a new keyword Element Should Not Be Visible
def element_should_not_be_visible(self, locator, loglevel='INFO'):
"""Verifies that element identified with locator is visible.
Key attributes for arbitrary elements are `id` and `name`. See
`introduction` for details about locating elements.
New in AppiumLibrary 1.4.5
"""
if self._element_find(locator, True, True).is_displayed():
self.log_source(loglevel)
raise AssertionError("Element '%s' should not be visible "
"but did" % locator)
Method 2
If you don't want to expand the existing library, you could just use the combination of existing keywords as follows:
${isVisible}= Run Keyword And Return Status Element Should Be Visible 'someElementSelector'
Should Be Equal ${isVisible} ${FALSE}

Creating placeholder tags for dynamic variables in Ruby

I looking for a way to define a set of placeholder/merge tags or implement a DSL where a set of template tags used in a view file would be dynamically substituted for conditional variable values.
For instance the template may look like this:
The quick <<COLOR>> <<ANIMAL_1>> jumps over the lazy <<ANIMAL_2>>
And the output of course resulting in:
The quick brown fox jumps over the lazy dog
This is beyond using <%= animal.color %> in an ERB template. In this case a user should ideally be able to use these tags in a form input with the tag alias saved with the rest of the text in the database.
I'm not sure how to refer to these tags so here's an example from the Mandrill documentation on their own "merge tag" feature: How to Use Merge Tags to Add Dynamic Content
Are there any existing gems that can do this? If not, how might I implement something similar?
Here's an adapted version of a previous answer. Given a String and a Hash, the expand method just iterates with sub until no more placeholders are found.
class String
def has_placeholder?
self=~/<<\w+>>/
end
end
wordbook = {
"<<COLOR>>"=> "brown",
"<<ANIMAL_1>>"=> "fox",
"<<ANIMAL_2>>"=> "dog"
}
def expand(sentence, wordbook)
while sentence.has_placeholder? do
sentence.sub!(/(<<\w+>>)/){wordbook[$1]}
end
sentence
end
puts expand("The quick <<COLOR>> <<ANIMAL_1>> jumps over the lazy <<ANIMAL_2>>", wordbook)
#=> The quick brown fox jumps over the lazy dog
It is also possible to have nested placeholders :
wordbook = {
"<<ADJECTIVE_1>>"=> "quick",
"<<ADJECTIVE_2>>"=> "lazy",
"<<COLOR>>"=> "brown",
"<<ANIMAL_1>>"=> "<<ADJECTIVE_1>> <<COLOR>> fox",
"<<ANIMAL_2>>"=> "the <<ADJECTIVE_2>> dog"
}
puts expand("The <<ANIMAL_1>> jumps over <<ANIMAL_2>>.", wordbook)
#=> The quick brown fox jumps over the lazy dog.
If you're interested, the linked answer picked a string randomly out of multiple possibilities.
Not sure if I understand the problem in depth, to me your question remains unclear: What is coming from the User when and what should be substituted by what (and how, by whom)? :)
Anyway, when speaking about String substitution and templating, sometimes plain old ruby is enough:
template = "The %{color} %{animal_1} jumps over the lazy %{animal_2}"
assignments = {color: 'black', animal_1: 'worm', animal_2: 'whitewalker'}
# pass Hash with substitution content
template % assignments #=> "The black worm jumps over the lazy whitewalker"
"String#%" is documented here: https://ruby-doc.org/core-2.3.0/String.html#method-i-25 .
But if you actually want a grammar, pick the other answer ;) . As stated, your question remains unclear to me.
I know this is many years later, but I came across this post, and now see it is built into Ruby. See here: https://idiosyncratic-ruby.com/49-what-the-format.html#referencing-template-style
Hope this helps someone who stumbles here.

rails break pasted code into lines and display

Lets say I have a rails application in which code is pasted into the content text box like the following.
Pasted Code
Person name
Person name
Person name
Person name
It is put into the database with the proper new lines after each line according to my server log.
What I want to do is in the show action I want to output this text after removing any blank spaces between names and remove any double names and put them in alphabetical order.
I also want to add an html option tag around them.
I have already written a program in Java to do this using sets. I was wondering how to approach this in rails. I would assume the code to do this would go in the controller.
My second question was in the index action. It shows all the pasted items. How can I only show a snippet of what was actually pasted. Lets say 20 characters long? Thanks!
removing any blank spaces between names
have no idea what 'blank spaces between' means, but I assume you want to remove 'blank lines':
lines = code.split(/\n\r?/).reject { |l| l.strip.empty? }
remove any double names
lines = lines.uniq # or uniq!
put them in alphabetical order
lines = lines.sort # or sort!
add an html option tag around them
options_str = lines.map { |l| "<option value='#{l}'>#{l}</option>" }.join('\n') # Do not forget to escape html
Or if you want it shorter:
code.split(/\n\r?/).reject { |l| l.strip.empty? }.uniq.sort.map do |l|
"<option value='#{l}'>#{l}</option>"
end.join('\n')
That should give you a tip.
The most important thing for you would probably be having String, Array and Enumerable documentation in front of you.

Resources