ruby readline nested autocompletion base on previous element - ruby

Hello I am struggling with code which provide autocompletion base on standard readline library
I tested scenario base on readline documentation example:
LIST = [
'search', 'download', 'open',
'help', 'history', 'quit',
'url', 'next', 'clear',
'prev', 'past'
].sort
comp = proc { |s| LIST.grep(/^#{Regexp.escape(s)}/) }
Readline.completion_append_character = " "
Readline.completion_proc = comp
while line = Readline.readline('> ', true)
p line
end
I plan to created nested autocompletion for previously "tabbed" element if exist
like for example lets look on 1 element on list "search", I want to create separated data structure which will hold subcommands for search and allow only elements confirmed to have options to be able to autocomplete base on source element
like search + tab = search instances
for now I tried to play with proc without success and with nested readline
any idea how to implement that ?

Related

Cypress: wrap chained commands in one

I use this long line to check the selected value on any list on my page (using Ember power-select which is not a but a complex set of ), selector being the parent so I can target the list I want, and trimming being there so I can chain a .should('eq', expected_value)
cy.get(selector).find('span[class="ember-power-select-selected-item"]').invoke("text").then((text) => text.trim())
I would love to shorten all the commands after get in one, and be able to call something like
cy.get(selector).selected_value()
I've started reading on custom commands, wrap, invoke... but am too new on Cypress to understand the right way to do this.
To add a custom command, you may add the following inside cypress/support/commands.js file:
Cypress.Commands.add('selected_value', { prevSubject: true}, (subject) => {
return cy.wrap(subject).find('span[class="ember-power-select-selected-item"]').invoke("text").then((text) => text.trim())
})
Note that we use the prevSubject option to be able to chain our command from an initial cy.get(selector) one.
And now you are able to use the command:
cy.get(selector).selected_value().should('eq', expected_value)
Also, it's better to provide a type script definition file for the new command so IDE can be aware of it and provide the autocomplete and other features.
Adding a command might look a bit complicated for a beginner or annoying for an experienced user. There is Cypress Pro plugin (I'm the author) for the IntelliJ platform that can simplify creating and maintaining custom commands.
Creating a custom command can make your test more readable, encapsulate a group of commands that are repeated and need to be parameterized.
For your example testing the text of dropdown items, you can pass in the text and return the select so that chaining is possible
NOTES
In your code, .find().invoke("text") returns all the item text in one string, so I added .contains() to select an individual item.
If you're only interested in a partial match, the command chain can stop at .contain(text.trim())
Cypress.Commands.add('hasSelectedItemText',
{ prevSubject: true },
(subject, text) => {
cy.wrap(subject)
.find('span[class="ember-power-select-selected-item"]')
.contains(text.trim())
.invoke("text")
.should('eq', text.trim())
cy.wrap(subject) // returns the original select
}
)
cy.get(selector)
.hasSelectedItemText('one')
.hasSelectedItemText('two')
.hasSelectedItemText('three')
A more complicated example using the dual type command. Here the command can be parent or child, so the parameters have different meanings depending on usage
Cypress.Commands.add("dropdownItemText",
{ prevSubject: "optional" },
(subject, arg1, arg2) => {
if (subject) {
const text = arg1
cy.wrap(subject)
.find('span[class="ember-power-select-selected-item"]')
.contains(text.trim())
.invoke("text")
.should('eq', text.trim())
cy.wrap(subject) // returns the original select
} else {
const text = arg2
cy.get(arg1)
.find('span[class="ember-power-select-selected-item"]')
.contains(text.trim())
.invoke("text")
.should('eq', text.trim())
cy.get(arg1) // make select the returned "subject" for further chaining
}
}
)
cy.dropdownItemText(selector, 'one')
.dropdownItemText('two')
.dropdownItemText('three')

ConditionalFormatting of cell containing string in CAxlsx

Using the CAxlsx gem (https://github.com/caxlsx/caxlsx), I'm trying to add conditional formatting to a range of cells, where the style should be applied if the cell contains the character -. Here's the snippet I'm using at the moment.
worksheet.add_conditional_formatting(range,
type: :containsText,
formula: "-",
dxfId: #styles[:invalid],
priority: 1)
Unfortunately, this doesn't seem to work. It does seem to apply the styling when the cell doesn't contain text, but a negative number, but that's not my use case. The documentation is severely lacking as well, and it doesn't offer a lot of explanation on what should be done in this case. (E.g., there's a cellIs type, with which the containsText operator can be used, but there's also a containsText type and no explanation as to what the difference between them is - and neither seem to work in my case.) Any pointers would be greatly appreciated, so far it's just been trial and error.
Assuming your range is something like "A1:A4" then formula you are looking for is NOT(ISERROR(SEARCH("-",A1))). Docs
Example:
require 'axlsx'
package = Axlsx::Package.new
workbook = package.workbook
s = workbook.styles.add_style({b:true, type: :dxf})
rows = ['a','b-c','d','e-f']
workbook.add_worksheet do |sheet|
rows.each do |row|
sheet.add_row [row]
end
sheet.add_conditional_formatting("A1:A4", { :type => :containsText,
:operator => :containsText,
:formula => 'NOT(ISERROR(SEARCH("-",A1)))',
:dxfId => s,
:priority => 1 })
end
package.serialize('conditional_test.xlsx')
I have found that the easiest way to determine the appropriate formula is to:
manually create a new excel work book
fill in an appropriate number of cells
conditionally format them in excel
save this workbook and close
change the extension to .zip (because xlsx is just zipped XML files)
open the zip file and navigate to /xl/worksheets/sheet1.xml
open this file and it will show you the formula used for the conditional formatting e.g.
<x14:conditionalFormattings>
<x14:conditionalFormatting xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
<x14:cfRule type="containsText" priority="1" operator="containsText" id="{E75522C8-BC6E-4142-B282-D21DF586C852}">
<xm:f>NOT(ISERROR(SEARCH("-",A1)))</xm:f>
<xm:f>"-"</xm:f>
<x14:dxf>
<font>
<color rgb="FF9C0006"/>
</font>
<fill>
<patternFill>
<bgColor rgb="FFFFC7CE"/>
</patternFill>
</fill>
</x14:dxf>
</x14:cfRule>
<xm:sqref>A1:A4</xm:sqref>
</x14:conditionalFormatting>
</x14:conditionalFormattings>

Puppet - create NESTED custom fact

I have successfully created a .rb custom fact that parses a built-in fact to create a new value, however I am now trying to use it as nested custom fact for Puppet.
The hierarchy I want to create is similar to built-in facts, eg running Facter (or Facter -p) would show:
custom_parent => {
custom_fact_1 => whatever
custom_fact_2 => whatever2
}
and usage in a puppet manifest would be:
$custom_parent.custom_fact_1
So far I have tried leading syntax such as:
Facter.add (:custom_parent)=>(custom_fact_1) do
Facter.add (:custom_parent)(:custom_fact_1) do
Facter.add (:custom_parent.custom_fact_1) do
Facter.add (:custom_parent:custom_fact_1) do
Facter.add (custom_parent:custom_fact_1) do
...and many other variations however cannot get a nested custom fact array to create. I've Googled for a while and if anyone knows if it is possible I would be very grateful.
I did find that nested facts can be created using an array in a .yaml file in the /etc/puppetlabs/facter/facts.d/ directory as below, however this sets FIXED values and does not process logic which I require in my custom fact.
{
"custom_parent":
{
"custom_fact_1": "whatever",
"custom_fact_2": "whatever2",
}
}
Thanks in advance.
The hierarchy I want to create is similar to built-in facts, eg
running Facter (or Facter -p) would show:
custom_parent => {
custom_fact_1 => whatever
custom_fact_2 => whatever2
}
There are no "nested" facts. There are "structured" facts, however, and these may have hashes as their values. To the extent that Facter presents output that you characterize as "nested", that's surely what you're looking at.
Since the elements of a structured fact's value are not facts in their own right, they need to be specified in the resolution of the fact itself:
Facter.add (:custom_parent) do
{
:custom_fact_1 => 'whatever',
:custom_fact_2 => 'whatever2',
}
end
The whatever and whatever2 do not need to be literal strings; they can be more or less arbitrary Ruby expressions. Of course, you can also set the members separately from creating the hash (but in the same fact resolution):
Facter.add (:custom_parent) do
value = new Hash()
value[:custom_fact_1] = 'whatever'
value[:custom_fact_2] = 'whatever2'
value
end
Thank you #John Bollinger. Your example was very close however I found that I needed to use type => aggregate and chunk to get it to work. I also combined it with a defined function, with the end result being based on the code below.
If you have any other suggestions to improve code conformity on this please feel free to point it out. Cheers
# define the function to process the input fact
def dhcp_octets(level)
dhcp_split = Facter.value(:networking)['dhcp'].split('.')
if dhcp_split[-level..-1]
result = dhcp_split[-level..-1].join('.')
result
end
end
# create the parent fact
Facter.add(:network_dhcp_octets, :type => :aggregate) do
chunk(:dhcp_ip) do
value = {}
# return a child => subchild array
value['child1'] = {'child2' => dhcp_octets(2)}
# return child facts based on array depth (right to left)
value['1_octets'] = dhcp_octets(1)
value['2_octets'] = dhcp_octets(2)
value['3_octets'] = dhcp_octets(3)
value['4_octets'] = dhcp_octets(4)
# this one should return an empty fact
value['5_octets'] = dhcp_octets(5)
value
end
end

How do I specify to prawn to truncate table cell content?

I'd like to instruct Prawn to truncate the contents of table cells instead of wrapping them.
I've tried setting the style as follows, but it has no effect:
options = {
cell_style: {
overflow: :truncate
},
}
pdf.table(entries, options)
If there is no direct way to specify truncation, I need a workaround recipe.
Please consider the following:
I cannot truncate the strings themselves, because the font is not fixed-width
calculating the width of each string is acceptable, but I would also need a way to retrieve the column width.
A fixed height could be used, but only if the height of the text is fixed. Only the first line of the text will be printed.
Example:
pdf.table(entries, :cell_style => {:height => 25})
Another option is to use a custom wrapper. Details are here:
https://github.com/prawnpdf/prawn/blob/master/lib/prawn/text/formatted/box.rb#L149
Example:
module MyWrap
def wrap(array)
initialize_wrap(array)
#line_wrap.wrap_line(:document => #document,
:kerning => #kerning,
:width => available_width,
:arranger => #arranger)
if enough_height_for_this_line?
move_baseline_down
print_line
end
#text = #printed_lines.join("\n")
#everything_printed = #arranger.finished?
#arranger.unconsumed
end
end
Prawn::Text::Formatted::Box.extensions << MyWrap
entries = [ ["very long text here", "another very long text here"] ]
Prawn::Document.generate("test.pdf") do
table entries, :cell_style => { :inline_format => true }
end
I just copy the original wrap method and remove the while loop in order to print only the first line of the text.
Note that :inline_format => true must be used in order to get Prawn::Text::Formatted::Box working.
http://prawn.majesticseacreature.com/docs/0.11.1/Prawn/Text/Formatted.html
That has a way to truncate text in a box with
formatted_text_box(array, options={})
But it's not a table. I don't think there is any way to do it with a table, you'll have to use textboxes.

Get all attributes or xPath of element in Webdriver

I'm trying to do simple monkey test for my web page, which get all active elements on page and click on them in random order.
When i do this I want to write a log to know, on which element my test click and on which test crashed
So I want log file to look like this
01.01.11 11.01.01 Clicked on Element <span id='myspan' class ='myclass .....>
01.01.11 11.01.01 Clicked on Element <span id='button' class ='myclass title = 'Button'.....>
or
01.01.11 11.01.01 Clicked on Element //*[#id='myspan']
01.01.11 11.01.01 Clicked on Element //*[#id='button']
Is it any way to do in Webdriver + Ruby?
I don't think there is a way but you could always do something like this (with watir-webdriver):
browser.divs.each do |div|
puts '<span ' + ['id','class','title'].map{|x| "#{x}='#{div.attribute_value(x)}'"}.join(' ') + '>'
end
WebDriver does not provide this type of functionality, you would have to get the page source and do some of your own parsing - I've done this in Html Agility Pack with C#, you would need to find a similar library for ruby (see: Options for HTML scraping?)
You can do this:
Get all elements, which are clickable
For example, find all links, find all clickable spans. Put those candidates in a list
Randomly pick a element in that candidate list
Click the very element and write some log
I tweaked #pguardiario's answer to come up with this method:
def get_element_dom_info(#e)
if #e.class != Selenium::WebDriver::Element
raise "No valid element passed: #{#e.class}"
end
#attrs = ['id', 'class', 'title', 'href', 'src', 'type', 'name']
return "<" + #e.tag_name + #attrs.map{ |x| " #{x}='#{#e.attribute(x)}'" if #e.attribute(x) && #e.attribute(x) != "" }.join('') + ">"
end
Of course, it expects that the single parameter you pass into it is an actual Selenium element. Also, it doesn't include every possible attribute, but that's the majority of them (and you can always add your extra attributes if needed).
I suppose you can integrate this via some code like this:
def clickElement(*args)
... # parse vars
#e = #driver.find_element(...)
puts get_timestamp + " Clicked on Element: " + get_element_dom_info(#e)
end
UPDATE
I recently realized that I could get the full html of the element using native javascript (d'oh!). You have to use a hack to get the "outerHTML". Here is my new method:
def get_element_dom_info(how, what)
e = #driver.find_element(how, what)
# Use native javascript to return element dom info by creating a wrapper
# that the element node is cloned to and we check the innerHTML of the parent wrapper.
return #driver.execute_script("var f = document.createElement('div').appendChild(arguments[0].cloneNode(true)); return f.parentNode.innerHTML", e)
end

Resources