How to check if a constant is defined in Crystal - metaprogramming

I need to verify if a constant is defined to do a conditional.
I was trying this but "defined" method not exists on this language:
if defined(constant)
value = :foo
else
value = :bar
end

You can use macro and TypeNode#has_constant?:
FOO = 1
value = nil
{% if #type.has_constant? "FOO" %}
value = :foo
{% else %}
value = :bar
{% end %}
pp value #=> :foo
Or even better, you can write a short custom macro for this:
macro toplevel_constant_defined?(c)
{{ #type.has_constant? c }}
end
pp toplevel_constant_defined? "FOO" # => true
pp toplevel_constant_defined? "BAR" # => false
Note: as mentioned by Jonne Haß, you only ever should need this in advanced macro programming, everywhere else it's a huge code smell, regardless of the language used.

Related

Searching for a defined set of values in a string

I'm trying to search a string for a set of values and return the value ONLY if the string matches exactly with one of the set.
My original expression was as so:
title = "MrS"
setTitles = {"Miss", "Mr", "Mrs", "Dr", "Ms"}
title = (title:gsub("%w",string.lower)):gsub("^l", string.upper)
if string.match(title, setTitles) ~= nil then title = title else title = "XX" end
I then realised I needed some way of cycling through the values so got to here:
title = "MrS"
setTitles = {"Miss", "Mr", "Mrs", "Dr", "Ms"}
title = (title:gsub("%w",string.lower)):gsub("^%l", string.upper)
for i = 1, 5 do
if string.match(title, setTitles[i]) ~= nil
then title = title
else title = "XX"
end
end
Except that just returns "XX" every time.
I know it's probably quite simple and obvious, but I can't seem to find a solution and would really appreciate a hand!
Here is why your code is not working. Your first iteration of the loop uses Mrs and checks if it matches Miss, and it does not so it changes title to XX and thus no following checks can ever match.
You cant change title until you have checked all your possible values first.
By adjusting your code to use a matchFound variable to determine if the change is needed you can fix this problem:
local matchFound = false
for i = 1, 5 do
if string.match(title, setTitles[i]) ~= nil then
matchFound = true
break
end
end
if matchFound == false then
title = "XX"
end
print(title)
Additionally your code can give false matches for Mr rather then Mrs this is because Mr will match inside Mrs or any string that starts with Mr. To change this you can adjust your call of string.match to:
string.match(title, "^".. setTitles[i] .. "$")
This forces string.match to insure that the first and last chars of the pattern are also the first and last chars of the string passed to it.
As a suggestion, Rather then using string.match, make your setTitles a proper set like:
local setTitles = {["Miss"] = true, ["Mr"] = true, ["Mrs"] = true, ["Dr"] = true, ["Ms"] = true}
Then your check becomes:
title = setTitles[title] and title or "XX"
Lua resource on Sets:
https://www.lua.org/pil/11.5.html
http://lua-users.org/wiki/SetOperations
You shouldn't change the title variable in the for loop.
You can try this code:
--title = "MrS"
title = "MrX"
setTitles = {"Miss", "Mr", "Mrs", "Dr", "Ms"}
title = title:gsub("%w", string.lower) -- mrs
title = title:gsub("^%l", string.upper) -- Mrs
ismatch = false
for i = 1, 5 do
print(title, setTitles[i])
if tostring(title) == tostring(setTitles[i]) then
ismatch = true
print("matched")
return
end
end
if ismatch then title = title else title = "XX" end
print(title)
Hope this helps.

Beautiful soup match tag content in html object to variable

I have soup object
apt_object = soup.find(string="Data about object:").find_next('ul')
which gives following html:
<ul>
<li>New building</li>
<li>Lift</li>
<li>City pluming</li>
<li>City sanitary</li>
</ul>
For various pages this will give list with predefined values defined in li tag.
I wan't match all values to predefined variables like
ads_object_newBuilding and assign "Yes" to variable if <li>New building</li> exists in html object or "No" if it doesn't.
I have so far solution that lists every li tag in html object and check is and than doing assignment:
for li in apt_object:
if li.string.strip().find("New building"):
ads_object_newBuilding= "Yes"
else: ads_object_newBuilding= "No"
if li.string.strip().find("Lift"):
apt_object_lift = "Yes"
else: apt_object_lift = "No"
I wonder if check and assignment to variable can be done in one line.
This is one approach.
Ex:
apt_object = soup.find(string="Data about object:").find_next('ul').text
apt_objekt_novogradnja, apt_objekt_lift = "No", "No"
if "New building" in apt_object:
apt_objekt_novogradnja = "Yes"
if "Lift" in apt_object:
apt_objekt_lift = "Yes"
print(apt_objekt_novogradnja, apt_objekt_lift)
You can use the ternary operator:
ads_object_newBuilding = "Yes" if li.string.strip().find("New building") else "No"

Formatting Excel cell from number to text in rails

I have made an application on which I have provide the feature to import the records from CSV and Excel file. I am using roo gem for it. The record added successfully but the problem is at the time of importing records from excel, it adds .0 to every field which is number. I don't want it because i have some fields like enrollment_no, roll_no, contact_no and it adds .0 to every filed like it made 23 to 23.0. I already had converted these filed to varchar in database and now i want to format the excel cell from number to text. It will solve my problem. Tell me how i will format the excel cell from number to string using rails.
Here is my code for importing the file:
student.rb :
def self.import(file, current_organization_id)
spreadsheet = open_spreadsheet(file)
header = spreadsheet.row(1)
(2..spreadsheet.last_row).each do |i|
row = Hash[[header, spreadsheet.row(i)].transpose]
record = Student.find_by(:organization_id => current_organization_id,:enrollment_no => row["enrollment_no"]) || new
record.organization_id= current_organization_id
record.attributes = row.to_hash.slice(*row.to_hash.keys)
record.save!
end
end
def self.open_spreadsheet(file)
case File.extname(file.original_filename)
when ".csv" then Roo::CSV.new(file.path)
when ".xls" then Roo::Excel.new(file.path)
when ".xlsx" then Roo::Excelx.new(file.path)
else raise "Unknown file type: #{file.original_filename}"
end
end
students_controller.rb :
def import
Student.import(params[:file], session[:current_organization_id])
#puts #session[:current_organization_id].inspect
redirect_to students_path, notice: "Record imported Successfully."
end
new.html.erb :
<%= form_tag import_students_path, multipart: true do %>
<%= file_field_tag :file , :required=> true%> <br/>
<%= submit_tag "Import" , :class => "btn btn-primary btn-block" %>
<% end %>
I am doing something similar in my application but the import is made easier by importing only from csv.
It seems that cell type is a pretty common problem in Roo and there are few workaround suggested using regex or char to include in your cell.
My solution it would be much easier:
# student.rb
COLUMNS_TO_STRING = ["organization_id", "enrollment_no", "contact_no"] # and so on
def self.import(file, current_organization_id)
spreadsheet = open_spreadsheet(file)
header = spreadsheet.row(1)
(2..spreadsheet.last_row).each do |i|
row = Hash[[header, spreadsheet.row(i)].transpose]
row = clean_for row, COLUMNS_TO_STRING
record = Student.find_by(:organization_id => current_organization_id,:enrollment_no => row["enrollment_no"]) || new
record.organization_id= current_organization_id
record.attributes = row.to_hash.slice(*row.to_hash.keys)
record.save!
end
end
def self.clean_for row_as_hash, string_columns_array
row_as_hash.each do |key, value|
if string_columns_array.include?key
row_as_hash[key] = value.to_i.to_s
end
end
end
def self.open_spreadsheet(file)
case File.extname(file.original_filename)
when ".csv" then Roo::CSV.new(file.path)
when ".xls" then Roo::Excel.new(file.path)
when ".xlsx" then Roo::Excelx.new(file.path)
else raise "Unknown file type: #{file.original_filename}"
end
end
get the index of the columns you want to format differently
convert the value imported from float to integer
convert the integer to string

Showing a MsgBox with variables depending on boolean

Im kind of a beginner with programming but here is the thing:
I have different variables set as local, these variables are strings of chars, messages and at the same time I have a set of Booleans that will fill with True or False depending of some circumstances.
The idea is to show a single Message Box containing these variables ONLY IF the booleans are False.
An example in pseudocode:
Local string Greetings = "Hi, my name is"
Local string Name1 = "John"
Local string Name2 = "James"
Local Boolean name1 = .T.
Local Boolean name2 = .T.
If Name1 (Have some conditions)
name1 = .T.
endif
If name2 (Some conditions)
name2 = .F.
If name1 == .T. OR name2 == .T.
MsgBox(Greetings+":"+name1+name2,"Messagebox","Alert")
Endif
Sorry I cant describe a lot about which code it is. but it is like Clipper with Xbase.
The problem I have is than even if the condition of these variables are false, the message box will show the both of them, Do I need to put all the contingencies there? for example if I have 3 booleans, Do I need to do this with True,False,False - False,True,False etc?
Best Regards.
I do not see how name1 can possibly be FALSE. It starts as TRUE and if your first condition evaluates to TRUE, you assign it TRUE again. So name1 must be TRUE.
Depending on your logic you might want to have Local Boolean name1 = .F. in the beginning or name1 = .F. inside your first IF.

Using excel vba to change the value of a dropdown menu on a website

I am writing an Excel macro to fill out a form on a website. I have written the code that populate the text boxes easily enough, and found code to chose radio boxes, but I am having problems with choosing info from dropdown menus.
Example 'Gender':
The combo box has three options:
Select / Male / Female
I've tried a few variations on this:
doc.getElementsByName("xs_r_gender").Item(0).Value="Male"
...but with no luck.
This is the web source code:
<td> <select name="xs_r_gender" id="xs_r_gender">
<option value="" selected>Select</option>
<option value="male">Male</option>
<option value="female">Female</option> </select></td>
Thanks.
doc.getElementById("xs_r_gender").selectedindex=1
seems to do the trick. (Where 1 represents male)
Though it means I will need to do alot of lookups to determine what the value is for the items in my dropdown. (Easy enough for Sex, where there are only two options, but I have some comboboxes with up to 50 options). If anyone knows of a faster solution, that'd be great. In the meantime, Ill start doing up some tables!!!
thanks.
Try below code assuming doc = ie.document
doc.getElementById("xs_r_gender").value = "Male"
Use this in your code to call the function below.
xOffset = SetSelect(IE.Document.all.Item("shipToStateValue"), "Texas")
doc.getElementById("shipToStateValue").selectedindex = xOffset
Then use this for your function
Function SetSelect(xComboName, xComboValue) As Integer
'Finds an option in a combobox and selects it.
Dim x As Integer
For x = 0 To xComboName.options.Length - 1
If xComboName.options(x).Text = xComboValue Then
xComboName.selectedindex = x
Exit For
End If
Next x
SetSelect = x
End Function
Thanks Stack, works for me! My solution to operate an IE HTML combobox drop down turned out to be two parts.
Part 1 was to click the pull down, here's code:
Dim eUOM1 As MSHTML.HTMLHtmlElement
Set eUOM1 = ie.document.getElementsByTagName("input")(27).NextSibling
eUOM1.Focus
eUOM1.Click
Part 2 was to choose and click the value, like this (*actual element name changed):
Dim eUOM2 As MSHTML.HTMLHtmlElement
Set eUOM2 = ie.document.getElementsByName("[*PutNameHere]")(0)
eUOM2.Value = "EA"
eUOM2.Click
Here are references:refs
You can try the querySelector method of document to apply a CSS selector of option tag with attribute value = 'male':
doc.querySelector("option[value='male']").Click
or
doc.querySelector("option[value='male']").Selected = True
Function SetSelect(s, val) As Boolean
'Selects an item (val) from a combobox (s)
'Usage:
'If Not SetSelect(IE.Document.all.Item("tspan"), "Custom") Then
'something went wrong
'Else
'continue...
'End If
Dim x As Integer
Dim r As Boolean
r = False
For x = 0 To s.Options.Length - 1
If s.Options(x).Text = val Then
s.selectedIndex = x
r = True
Exit For
End If
Next x
SetSelect = r
End Function
Try this code :
doc.getElementById("xs_r_gender").value = "Male"
doc.getElementById("xs_r_gender").FireEvent("onchange")
You can do something like this:
doc.getElementsByName("xs_r_gender").Item(1).Selected=True
or
doc.getElementById("xs_r_gender").selectedindex = 1
Where 1 is the male option (in both cases).
If the dropbox needs to fire some event in order to aknowledge your choice, it is likely that it will be the "onchange" event. You can fire it like so:
doc.getElementById("xs_r_gender").FireEvent("onchange")
If you ever want to be able to select an option based on the option's text you can use the function given by Lansman (here) .
Based on the same answer, if you want to call the option by it's value property (instead of the text, you can just change the line If xComboName.Options(x).Text = xComboValue Then to If xComboName.Options(x).value = xComboValue Then).
This should cover all bases.
Copy from Here till last line:
Sub Filldata()
Set objShell = CreateObject("Shell.Application")
IE_count = objShell.Windows.Count
For X = 0 To (IE_count - 1)
On Error Resume Next ' sometimes more web pages are counted than are open
my_url = objShell.Windows(X).document.Location
my_title = objShell.Windows(X).document.Title
If my_title Like "***Write your page name***" Then
Set IE = objShell.Windows(X)
Exit For
Else
End If
Next
With IE.document.forms("***write your form name***")
' Assuming you r picking values from MS Excel Sheet1 cell A2
i=sheet1.range("A2").value
.all("xs_r_gender").Item(i).Selected = True
End with
End sub

Resources