Following is my RSpec code snippet:
describe UsersController do
def mock_authentication_token(user, token_string)
...
...
end
def create_data
#date_format = '%Y-%m-%d %H:%M:%S'
today = Time.now
#today_str = today.strftime(#date_format)
...
..
..
end
before do
#current_user = Factory(:client)
authtoken_str = "client auth token string"
mock_authentication_token(#current_user, authtoken_str)
end
context "action: index" do
before do
create_data
#params = #params.merge(limit: 5)
end
it "should return the more link with date set to 1 second ahead of #{#today_str}" do
get :index, #params
body = JSON.parse response.body
...
...
...
end
end
This example it "should return the more link with date set to 1 second ahead of #{#today_str}" do when fails it doesn't print the value of instance variable #today_str set by helper method create_data, in the failed example description.
It just prints: should return the more link with date set to 1 second ahead of
It seems that it method doesn't allow string interpolation.Is this really the case? If yes how do I achieve the desired behavior.
Thanks,
Jignesh
Rspec resets the class instance # variables after each it block.
For instance:
describe 'it blocks' do
before :all
#reset = 0
##global = 'will break tests'
end
it 'should increment' do
#reset += 1
end
it "shouldn't forget it, either" do
# but it does
#reset.should eql 0
end
it 'does remember class-level variables, though' do
##global += ' for sure'
end
it 'can be demonstrated via' do
##global.split(' ').should > 3
end
# this is not the same #reset as what's in `before :all`.
this_is_blank = #reset
it "won't interpolate #{this_is_blank} because it's an instance level variable" do
true.should be true
end
local = 'might as well hard code them into your descriptions at this point'
it "Doesn't matter anymore because you #{local}" do
true.should eql true
end
it "won't get here because class-level variables #{##global}" do
(2 + 2).should eql 5
end
end
Looks like you'll have to name your spec tests more generically. I have, anyway.
Related
I have data in a .txt file that looks like this:
04:31 Yuri Kane feat Jeza – Love Comes (Original Mix) [PREMIER]
25:31 Heatbeat & Quilla – Secret (Original Mix) [ARMADA CAPTIVATING]
All of them have this pattern:
00:00 artist - title [studio]
I want to remove the time stamp and the studio, so the output looks like this:
1. Yuri Kane feat Jeza – Love Comes (Original Mix)
Here is what I tried:
function remove_time_from(str::String)
return last(split(str,"0 "))
end
function remove_url(str::String)
return first(rsplit(str,"["))
end
function main()
tracks = String[]
local number = 0
for line in eachline("track-list.txt")
number += 1
removed_time = remove_time_from(line)
cleaned = remove_url(removed_time)
push!(tracks,"$number.$cleaned")
end
open("track-list-cleaned.txt", "w") do io
for line in tracks
write(io, "$line\n")
end
end
end
main()
but it returns:
MethodError: no method matching remove_url(::SubString{String})
When you use the function remove_time_from() it uses first() which returns a SubString{String}:
track = "04:31 Yuri Kane feat Jeza – Love Comes (Original Mix) [PREMIER]"
println(typeof(remove_time_from(track))) # Output: SubString{String}
You have 2 ways to fix it:
Have both remove_time_from() and remove_url() convert the SubString to String before returning it. This way, no matter which function you use first, you'll get a String:
return convert(String,last(split(str,"0 ")))
Use AbstractString instead of String as the function parameter, because SubString is a subtype of AbstractString:
println(SubString <: AbstractString) # Output: true
This way, no matter which function you use first, it would accept a String (the variable type of line) or SubString (the type you end up with after using one of the functions).
Suggestions:
Using split(str,"0 ") won't remove the time stamp:
last(split("04:31 Yuri Kane feat Jeza – Love Comes (Original Mix) [PREMIER]", "0 "))
Output: 04:31 Yuri Kane feat Jeza – Love Comes (Original Mix) [PREMIER]
What you need is chop() and you can specify how many characters to ignore from the head, so in this case 5 (includes the leading whitespace).
chop(str, head = 5)
You don't need to read in the lines, clean it, and then store it in a Vector to write later. You can clean it (do it in one line), and write it out to the file:
open("track-list-cleaned.txt", "w") do io
for line in eachline("track-list.txt")
number += 1
cleaned = (remove_url(remove_time_from(line)))
write(io, "$number.$cleaned\n")
end
end
Use enumerate() to number the lines as you're reading them in:
for (number,line) in enumerate(eachline("track-list.txt"))
Code:
# Using the assignment form because each function has only one line.
remove_time_from(str::AbstractString) = chop(str, head = 5)
remove_url(str::AbstractString) = first(rsplit(str," https"))
function main()
open("track-list-cleaned.txt", "w") do io
for (number,line) in enumerate(eachline("track-list.txt"))
cleaned = strip(remove_url(remove_time_from(line)))
write(io,"$number.$cleaned\n")
end
end
end
main()
I have a need to crawl a set of data of indeterminate size and build a table key/value index of it. Since I don't know the dimensions in advance, seems I have to use a recursive function. My Lua skills are very new and superficial. I'm having difficulty understanding how to deal with returning a table from the function call.
Note this is for a Lua 5.1 script processor
API = function(tbl)
local table_api = {}
-- do stuff here with target data and add to table_api
table_api["key"] = value
-- then later there is a need to recurse deeper into target data
table_api["lower"] = API(var)
return table_api
end
result = API(source_data)
In most every other language I know there would be some way to make the recurse line table_api["lower"] = API(var) work but since Lua does table variables by reference, my return sub-table just keeps getting overwritten and my result is a tiny last bit of what it should be.
Just for background on my purpose: there's a commercial application I'm working with that has a weakly documented Lua scripting interface. It's running Lua 5.1 and the API is not well documented and frequently updated. As I understand it, everything is stored in _G, so I wanted to write something to reverse engineer the API. I have a working recursive function (not shown here) that enumerates all of _G. For that return value, it just builds up an annotated string and progressively builds on the string. That all works fine and is really useful, but it shows much more that the API interface; all the actual data elements are included, so I have to sift through like 30,000 records to determine an API set of about 500 terms. In order to determine the API, I am trying to use this sub-table return value recursive function being discussed in this question. The code I'm showing here is just a small distilled subset of the larger function.
I'll go ahead and include the full code here. I was hoping to incrementally build a large'ish table of each API level, any sublevels, and finally whatever keys used at the lowest level.
In the end, I was expecting to have a table that I could address like this:
result["api"]["label"]["api"]["sublabel"]["value"]["valuename"]
Full code:
tableAPIShow = function(tbl, table_track)
table_track = table_track or {}
local table_api = {}
if type(tbl) == 'table' then
-- Check if values are tables.
local parent_table_flag = true
for ind,val in pairs(tbl) do
if type(val) ~= 'table' then
parent_table_flag = false
break
end
end
-- If all children are table type, check each of them for subordinate commonality
local api_flag = false
if parent_table_flag == true then
local child_table = {}
local child_table_flag = false
api_flag = true
for ind,val in pairs(tbl) do
-- For each child table, store the names of the indexes.
for sub_ind,sub_val in pairs(val) do
if child_table_flag == false then -- First time though, create starting template view of typical child table.
child_table[sub_ind] = true -- Store the indexes as a template table.
elseif child_table[sub_ind] == nil then -- Otherwise, test this child table compared to the reference template.
api_flag = false
break
end
end
if api_flag == false then -- need to break out of nested loop
break
end
child_table_flag = true
end
end
if api_flag == true then
-- If everything gets to here, then this level is an API with matching child tables below.
for ind,val in pairs(tbl) do
if table_api["api"] == nil then
table_api["api"] = {}
end
table_api["api"][ind] = tableAPIShow(val, table_track)
end
else
-- This level is not an API level, determine how to process otherwise.
for ind,val in pairs(tbl) do
if type(val) == 'table' then
if table_track[val] ~= nil then -- Have we already recursed this table?
else
table_track[val] = true
if table_api["table"] == nil then
table_api["table"] = {}
end
table_api["table"][ind] = tableAPIShow(val, table_track)
end
else -- The children are not tables, they are values
if table_api["value"] == nil then
table_api["value"] = {}
end
table_api["value"][ind] = val
end
end
end
else
-- It's not a table, just return it.
-- Probably never use this portion because it's caught on upper level recurse and not called
return tbl
end
return table_api
end
And I was calling this function in the main script like this:
local str = tableAPIShow(_G)
I've got another function that recursively shows a table so I can look inside my results and see I only get a return value that contains only the values of the top-level of _G (I have it excluded built-in Lua functions/values because I'm only interested in the Application API):
{
[value] = table: 00000000F22CB700 {
[value][_VERSION] = Application/5.8.1 (x86_64; Windows NT 10.0.16299),
[value][tableAPIShow] = "function: 00000000F22C6DE0, defined in (121-231) C:\\Users\\user\\AppData\\Local\\Temp\\APP\\/~mis00002690 ",
[value][_FINAL_VERSION] = true,
[value][Path] = ./Scripts/Database/elements/,
[value][class] = "function: 00000000F1953C40, defined in (68-81) Scripts/Common/Class.lua ",
[value][db_path] = ./Scripts/Database/,
[value][merge_all_units] = "function: 00000000F20D20C8, defined in (2242-2250) Scripts/Database/db_merge.lua ",
}
You just need to localize the variable you store your table in and it will work as you expect:
local table_api = {}
(note that you are passing table variable that conflicts with the global table variable and is not currently used in the function.)
I am inclined to believe that your tableAPIShow function code is working correctly and the
another function that recursively shows a table
fails to serialize your table fully. Hence you don't see the deeper levels of the table returned by tableAPIShow().
I got your initial and current code (tableAPIShow) to work with my simple table serialize function: the whole _Global table is exported completely and formatted as you implemented it in your tableAPIShow().
Code for testing:
apiMassiveTable = {
api = {
get = {
profile = {"profileID", "format"},
name = {"profileID", "encoding"},
number = {"profileID", "binary"}
},
set = {
name = {"apikey", "profileID", "encoding", "newname"},
number = {"apikey", "profileID", "binary", "newnumber"}
},
retrieve = {}
},
metadata = {version="1.4.2", build="nightly"}
}
-- tableAPIShow implemenation here
table.serialize = dofile("serialize.lua")
print(table.serialize("myNameForAHugeTable", tableAPIShow(_G)))
PS: Whatever serialize function you're using, it should enquote strings like Application/5.8.1 (x86_64; Windows NT 10.0.16299) which it does not.
Like #Paul Kulchenko said, you need to learn to use locals (https://www.lua.org/pil/4.2.html). Global variable post-exist until a new lua_State is loaded (a new environment, could be a new process depending on what interpreter you are using). So a tip is to always use local variables for anything you don't want to leave the function or leave the compilation unit.
Think of tables like dictionaries: a word is attached to a definition. Thusly the definition is the data.
What I think you are trying to do is serialization of a table of data. However, that isn't really necessary. You can either shadow copy or deep copy the given table. A shadow copy is when you don't delve into the depths of tables found in the keys, etc. A deep copy is when you copy tables in keys of tables in keys of tables... etc.
local shallow_copy = function(tab)
local rep_tab = {}
for index, value in pairs(tab)do
rep_tab[index] = value
end
return rep_tab
end
-- because local variable is not defined or declared immediately on a 1-liner,
-- a declaration has to exist so that deep_copy can be used
-- lets metatable execute functions
local deep_copy
deep_copy = function(tab)
local rep_tab = {}
for index, value in pairs(tab)do
if(type(value) == "table")then
rep_tab[index] = deep_copy(value)
else
rep_tab[index] = value
end
end
return rep_tab
end
Deco's deepcopy.lua
https://gist.github.com/Deco/3985043
You can also index tables using periods:
local tab = {}
tab.abc = 123
tab["def"] = 456
print(tab.abc, tab["def"])
To serialize the entire _G, you would just filter out the junk you don't need and recurse each table encountered. Watch out for _G, package, and _ENV because if defined it will recurse back to the start.
-- use cap as a whitelist
enumerate_dir = function(dir, cap)
local base = {}
for i, v in pairs(dir) do
-- skip trouble
if(i ~= "_G" and i ~= "_ENV" and i ~= "package")then
if(type(v) == "table")then -- if we have a table
base[i] = enumerate_dir(v, cap)
else
for k, n in pairs(cap) do
if(type(v) == n)then -- if whitelisted
base[i] = tostring(v)
end
end
end
end
end
return base
end
-- only functions and tables from _G please
local enumeration = enumerate_dir(_G, {"function", "table"})
-- do some cool quips to get a basic tree system
prefix = ""
recursive_print = function(dir)
for i, v in pairs(dir)do
if(type(v) == "table")then
print(prefix, i, v)
prefix = prefix .. ">"
recursive_print(v)
else
print(prefix, i, v)
end
end
if(#prefix > 0)then
prefix = prefix:sub(1, #prefix-1)
end
end
recursive_print(test)
I have a dexterity content type (my.product.myobject) that uses the IEventBasic behavior (implemented in the xml file with other behaviors) and I'm trying to make the end field be set within the same week of the start field. I attempt to correct the end date in an IObjectAddedEvent (zope.lifecycleevent.interfaces.IObjectAddedEvent).
I first get the week range from the start field:
def getWeekRangeFromDate(a_date,offset=1):
start = (a_date - timedelta(days=(a_date.weekday() + offset) % 7)).replace(hour=0,minute=0)
end = (start + timedelta(days=6)).replace(hour=23,minute=59)
return {'start':start,
'end':end,
}
def myObjectAdded(myObject, event):
week_range = getWeekRangeFromDate(myObject.start)
if myObject.end > week_range['end']:
myObject.end = week_range['end']
I have the end field printed in the browserview and I use IEventAccessor to extract the end date from the myObject:
class View(BrowserView):
def __init__(self,context,request):
...
self.context = self.context
def getFormattedEnd(self):
if self.context.whole_day == False:
return IEventAccessor(self.context).end.strftime('%m/%d %I:%M %p')
...
When it doesn't need to be programmatically corrected, the end field displays correctly, but when it does, it appears 5 hours off.
In my myObjectAdded event, I tried:
if myObject.end > week_range['end']:
myObject.end = week_range['end'] - timedelta(hours = IEventAccessor(myObject).end.offset().total_seconds()/3600)
This does appear right actually, but when I go to the edit form and change the start field, the end field ends up changing itself seemingly random. Changing the starting hour field to 16 changed the end's month about two weeks ahead.
How can I set it without it acting up? Am I misundertanding how to use IEventBasic?
Edit: I'm coming across something really interesting. In the edit form, the Start End Validator is failing.
Event Starts - 25/November/2016 9:00
Event Ends - 25/Novermber/2016 20:00
I click submit and the status message says End date must be after the start date.
Edit: The version of Plone I am using is 4.3.
I have a vbscript function to create a tinyurl from a regular url.
FUNCTION GetShortURL(strUrl)
Dim oXml,strTinyUrl,strReturnVal
strTinyUrl = "http://tinyurl.com/api-create.php?url=" & strUrl
set oXml = Server.CreateObject("Msxml2.ServerXMLHTTP.3.0")
oXml.Open "GET", strTinyUrl, false
oXml.Send
strReturnVal = oXml.responseText
Set oXml = nothing
GetShortURL = strReturnVal
END FUNCTION
I have come across the problem when the tinyurl api is down or inaccessible, making my script fail:
msxml3.dll
error '80072efe'
The connection with the server was terminated abnormally
Is there a safeguard I can add to this function to prevent the error and use the long url it has..?
Many thanks in advance,
neojakey
If you want to just return strUrl if the call fails, you can use On Error Resume Next
FUNCTION GetShortURL(strUrl)
on error resume next
Dim oXml,strTinyUrl,strReturnVal
strTinyUrl = "http://tinyurl.com/api-create.php?url=" & strUrl
set oXml = Server.CreateObject("Msxml2.ServerXMLHTTP.3.0")
oXml.Open "GET", strTinyUrl, false
oXml.Send
strReturnVal = oXml.responseText
Set oXml = nothing
'Check if an error occurred.
if err.number = 0 then
GetShortURL = strReturnVal
else
GetShortURL = strUrl
end if
END FUNCTION
Obviously, you need some kind of error handling. In general, VBScript's error handling is no fun, but for your specs - get the right thing or the default in case of any/don't care what problem - you can use a nice and simple approach:
Reduce your original function to
assignment of default value to function name (to prepare for the failures)
On Error Resume Next (to disable VBScript's default error handling, i.e. crash)
assignment of the return value of a helper function to function name (to prepare for success)
In code:
Function GetShortURL2(strUrl)
GetShortURL2 = strUrl
On Error Resume Next
GetShortURL2 = [_getshorturl](GetShortURL2)
End Function
I use [] to be able to use an 'illegal' function name, because I want to emphasize that _getshorturl() should not be called directly. Any error in _getshorturl() will cause a skip of the assignment and a resume on/at the next line (leaving the function, returning the default, reset to default error handling).
The helper function contains the 'critical parts' of your original function:
Function [_getshorturl](strUrl)
Dim oXml : Set oXml = CreateObject("Msxml2.ServerXMLHTTP.3.0")
oXml.Open "GET", "http://tinyurl.com/api-create.php?url=" & strUrl, False
oXml.Send
[_getshorturl] = oXml.responseText
End Function
No matter which operation fails, the program flow will resume on/at the "End Function" line of GetShortURL2().
Added: Comments on the usual way to do it (cf. Daniel Cook's proposal)
If you switch off error handling (and "On Error Resume Next" is just that) you are on thin ice: all errors are hidden, not just the ones you recon with; your script will do actions in all the Next lines whether necessary preconditions are given or preparations done.
So you should either restrict the scope to one risky line:
Dim aErr
...
On Error Resume Next
SomeRiskyAction
aErr = Array(Err.Number, Err.Description, Err.Source)
On Error Goto 0
If aErr(0) Then
deal with error
Else
normal flow
End If
or check after each line in the OERN scope
On Error Resume Next
SomeRiskyAction1
If Err.Number Then
deal with error
Else
SomeRiskyAction2
If Err.Number Then
deal with error
Else
...
End If
End If
That's what I meant, when I said "no fun". Putting the risky code in a function will avoid this hassle.
OK I'm a complete newbie to ASP.
I have a client with different content loading depending on what is passed in an array.
select case lcase(arURL(4))
Sometimes though, arURL(4) might be empty, in them cases I'm getting the following error:
Error running function functionName(), the error was:
Subscript out of range
Does anybody know a way to fix this?
Thanks
OK further code as requested. It is horrible code and I don't mean to cause anybody a headache, so please excuse it. Thanks again ........
function GetContent()
dim strURL, arURL, strRetval
select case lcase(request.ServerVariables("URL"))
case "/content.asp"
strURL = ""
arURL = split(request.querystring("url"), "/")
if request("page") = "" then
select case lcase(arURL(2))
case "searches"
select case lcase(arURL(1))
case "looking"
select case lcase(arURL(3))
case "ohai"
strRetval = "Lorem"
case "blahblah"
strRetval = "Lorem Ipsum"
case "edinburgh"
select case lcase(arURL(4))
case "ohai"
strRetval = "Ipsum"
case "ohno"
strRetval = "Lorem"
end select
case "bristol"
select case lcase(arURL(4))
case "some_blahblah"
strRetval = "LOREM"
case "overthere"
strRetval = "LOREM"
case "blahblah"
strRetval = "LOREM"
end select
case "cambridge"
select case lcase(arURL(4))
case "some_rubbish"
strRetval = "Lorem"
end select
case else
strRetval = " "
end select
case else
strRetval = " "
end select
case else
strRetval = " "
end select
end if
end select
strRetval = strRetval & "<style>h2{border: 0px);</style>"
GetContent = strRetval
end function
You are using value passed over the querystring and split it by "/" character - when the value does not contain "enough" slashes, you will get error and the code will crash.
For example, if the querystring parameter url will be only "/something" then even arURL(2) will fail since the array has only two items. (First one is empty string, second is "something")
To avoid all this mess, best way I can advice is writing custom function that will take array and index as its arguments and return either the item in the given index if exists otherwise empty string:
Function GetItemSafe(myArray, desiredIndex, defValue)
If (desiredIndex < LBound(myArray)) Or (desiredIndex > UBound(myArray)) Then
If IsObject(defValue) Then
Set GetItemSafe = defValue
Else
GetItemSafe = defValue
End If
Else
If IsObject(myArray(desiredIndex)) Then
Set GetItemSafe = myArray(desiredIndex)
Else
GetItemSafe = myArray(desiredIndex)
End If
End If
End Function
(ended up with more generic version, letting the calling code decide what is the default value in case index is out of array range)
Having this, change your code to use the function instead of accessing the array directly.
This line for example:
select case lcase(arURL(2))
Should become this instead:
select case lcase(GetItemSafe(arURL, 2, ""))
Change the rest of those lines accordingly and you'll no longer get errors when the given value won't be valid.
What that error is saying at the most basic level is that you're trying to get information from an array element that doesn't exist, eg arURL may have been declared for 3 elements, but accessing the 4th generates the "subscript out of range" error.
If you're keying on the last element in the array, you might look at the UBound() function, which returns the high index element in an array, eg:
select case lcase(arURL(ubound(arURL))
However, there might be something else going on in the code that would change how you determine which element should be used as the target of the "select case," hence the suggestion to post more of the code.