Testing ActionMailer views in a Rails engine - actionmailer

I have a rails gem/engine called how_slow that contains a view for my embedded ActionMailer subclass. When the gem is installed in an application the view is triggered and an email is sent. However, in the tests (I'm using minitest) the view doesn't get picked up or is inaccessible such that a call to:
HowSlow::Mailer.metrics_email.body.to_s
...returns an empty string ("") rather than the content of the email, as I intend it to.
The same call inside an app that uses a prerelease version of the gem/engine does, indeed, return the content of the text email as I would expect.
Am I missing something in terms of getting engine views to be visible to tests inside the gem/engine itself?
Example:
Inside an app that contains the current version of the gem on this branch I get (I've broken up the line breaks for you so it's easier to read):
> HowSlow::Mailer.metrics_email.body.to_s
=> "Slowest 12 requests, sorted by total_runtime:
--------------------
2013-06-22T11:42:21-05:00 :: sections/show 270 / 12 / 249
2013-06-21T22:50:21-05:00 :: log_books/index 263 / 23 / 225
2013-06-22T11:42:18-05:00 :: log_books/index 257 / 24 / 217
2013-06-22T11:42:21-05:00 :: log_books/show 58 / 9 / 0
2013-06-22T11:42:24-05:00 :: sections/show 56 / 4 / 14
2013-06-22T11:42:20-05:00 :: log_books/index 39 / 11 / 27
2013-06-22T11:42:26-05:00 :: log_books/index 38 / 12 / 25
2013-06-22T11:42:31-05:00 :: sections/show 38 / 5 / 30
2013-06-22T11:42:25-05:00 :: sections/show 28 / 4 / 20
2013-06-22T11:42:28-05:00 :: sections/show 21 / 4 / 14
2013-06-22T11:42:32-05:00 :: sections/show 20 / 3 / 12
2013-06-22T11:42:28-05:00 :: log_books/show 5 / 1 / 0
Counters sorted by alpha_asc:
--------------------"
The view in question is here: https://github.com/normalocity/how_slow/blob/12-mailer/app/views/how_slow/mailer/metrics_email.text.erb
In the test framework, however, a call to HowSlow::Mailer.metrics_email.body.to_s returns "" (empty string).

Related

Error in post hoc test for lmer(): both multcomp() and emmeans()

I have a dataset of measurements of "Y" at different locations, and I am trying to determine how variable Y is influenced by variables A, B, and D by running a lmer() model and analyzing the results. However, when I reach the post hoc step, I receive an error when trying to analyze.
Here is an example of my data:
table <- " ID location A B C D Y
1 1 AA 0 0.6181587 -29.67 14.14 168.041
2 2 AA 1 0.5816176 -29.42 14.21 200.991
3 3 AA 2 0.4289670 -28.57 13.55 200.343
4 4 AA 3 0.4158891 -28.59 12.68 215.638
5 5 AA 4 0.3172721 -28.74 12.28 173.299
6 6 AA 5 0.1540603 -27.86 14.01 104.246
7 7 AA 6 0.1219355 -27.18 14.43 128.141
8 8 AA 7 0.1016643 -26.86 13.75 179.330
9 9 BB 0 0.6831649 -28.93 17.03 210.066
10 10 BB 1 0.6796935 -28.54 18.31 280.249
11 11 BB 2 0.5497743 -27.88 17.33 134.023
12 12 BB 3 0.3631052 -27.48 16.79 142.383
13 13 BB 4 0.3875498 -26.98 17.81 136.647
14 14 BB 5 0.3883785 -26.71 17.56 142.179
15 15 BB 6 0.4058061 -26.72 17.71 109.826
16 16 CC 0 0.8647298 -28.53 11.93 220.464
17 17 CC 1 0.8664036 -28.39 11.59 326.868
18 18 CC 2 0.7480748 -27.61 11.75 322.745
19 19 CC 3 0.5959143 -26.81 13.27 170.064
20 20 CC 4 0.4849077 -26.77 14.68 118.092
21 21 CC 5 0.3584687 -26.65 15.65 95.512
22 22 CC 6 0.3018285 -26.33 16.11 71.717
23 23 CC 7 0.2629121 -26.39 16.16 60.052
24 24 DD 0 0.8673077 -27.93 12.09 234.244
25 25 DD 1 0.8226558 -27.96 12.13 244.903
26 26 DD 2 0.7826429 -27.44 12.38 252.485
27 27 DD 3 0.6620447 -27.23 13.84 150.886
28 28 DD 4 0.4453213 -27.03 15.73 102.787
29 29 DD 5 0.3720257 -27.13 16.27 109.201
30 30 DD 6 0.6040217 -27.79 16.41 101.509
31 31 EE 0 0.8770987 -28.62 12.72 239.036
32 32 EE 1 0.8504547 -28.47 12.92 220.600
33 33 EE 2 0.8329484 -28.45 12.94 174.979
34 34 EE 3 0.8181102 -28.37 13.17 138.412
35 35 EE 4 0.7942685 -28.32 13.69 121.330
36 36 EE 5 0.7319724 -28.22 14.62 111.851
37 37 EE 6 0.7014828 -28.24 15.04 110.447
38 38 EE 7 0.7286984 -28.15 15.18 121.831"
#Create a dataframe with the above table
df <- read.table(text=table, header = TRUE)
df
# Make sure location is a factor
df$location<-as.factor(df$location)
Here is my model:
# Load libraries
library(ggplot2)
library(pscl)
library(lmtest)
library(lme4)
library(car)
mod = lmer(Y ~ A * B * poly(D, 2) * (1|location), data = df)
summary(mod)
plot(mod)
I now need to determine what variables significantly influence Y. Thus I ran Anova() from the package car (output pasted here).
Anova(mod)
# Analysis of Deviance Table (Type II Wald chisquare tests)
#
# Response: Y
# Chisq Df Pr(>Chisq)
# A 8.2754 1 0.004019 **
# B 0.0053 1 0.941974
# poly(D, 2) 40.4618 2 1.636e-09 ***
# A:B 0.1709 1 0.679348
# A:poly(D, 2) 1.6460 2 0.439117
# B:poly(D, 2) 5.2601 2 0.072076 .
# A:B:poly(D, 2) 0.6372 2 0.727175
# Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
This suggests that:
A significantly influences Y
B does not significantly influence Y
D significantly influences Y
So next I would run a post hoc test for each of these variables, but this is where I run into issues. I have tried using both emmeans and multcomp packages below:
library(emmeans)
emmeans(mod, list(pairwise ~ A), adjust = "tukey")
# NOTE: Results may be misleading due to involvement in interactions
# Error in if ((misc$estType == "pairs") && (paste(c("", by), collapse = ",") != :
# missing value where TRUE/FALSE needed
pairs(emmeans(mod, "A"))
# NOTE: Results may be misleading due to involvement in interactions
# Error in if ((misc$estType == "pairs") && (paste(c("", by), collapse = ",") != :
# missing value where TRUE/FALSE needed
library(multcomp)
summary(glht(mod, linfct = mcp(A = "Tukey")), test = adjusted("fdr"))
# Error in h(simpleError(msg, call)) :
# error in evaluating the argument 'object' in selecting a method for function 'summary': Variable(s) ‘depth’ of class ‘integer’ is/are not contained as a factor in ‘model’.
This is the first time I've run an ANOVA/post hoc test on a lmer() model, and though I've read a few introductory sites for this model, I'm not sure I am testing it correctly. Any help would be appreciated.
If I am looking at the data correctly, A is the variable that has values of 0, 1, ..., 7. Now look at your anova table, where you see that A has only 1 d.f., not 7 as it should for a factor having 8 levels. That means your model is taking A to be a numerical predictor -- which is rather meaningless. Make A into a factor and re-fit he model. You'll have better luck.
I also think you meant to have + (1|location) at the end of the model formula, rather than having the random effects interacting with some of the polynomial effects.

Converting integer to binary for SPI transfer

I am trying to convert an integer to 3 bytes to send via the SPI protocol to control a MAX6921 serial chip. I am using spi.xfer2() but cannot get it to work as expected.
In my code, I can call spi.xfer2([0b00000000, 0b11101100, 0b10000000]) it displays the letter "H" on my display, but when I try and convert the int value for this 79616, it doesn't give the correct output:
val = 79616
spi.xfer2(val.to_bytes(3, 'little'))
My full code so far is on GitHub, and for comparison, this is my working code for Arduino.
More details
I have an IV-18 VFD tube driver module, which came with some example code for an ESP32 board. The driver module has a 20 output MAX6921 serial chip to drive the vacuum tube.
To sent "H" to the second grid position (as the first grid only displays a dot or a dash) the bits are sent to MAX6921 in order OUT19 --> OUT0, so using the LSB in my table below. The letter "H" has an int value 79616
I can successfully send this, manually, via SPI using:
spi.xfer2([0b00000000, 0b11101100, 0b10000000])
The problem I have is trying to convert other letters in a string to the correct bits. I can retrieve the integer value for any character (0-9, A-Z) in a string, but can't then work out how to convert it to the right format for spi.xfer() / spi.xfer2()
My Code
def display_write(val):
spi.xfer2(val.to_bytes(3, 'little'))
# Loops over the grid positions
def update_display():
global grid_index
val = grids[grid_index] | char_to_code(message[grid_index:grid_index+1])
display_write(val)
grid_index += 1
if (grid_index >= 9):
grid_index = 0
The full source for my code so far is on GitHub
Map for MAX6921 Data out pins to the IV-18 tube pins:
BIT
24
23
22
21
20
19
18
17
16
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
IV-18
G9
G8
G7
G6
G5
G4
G3
G2
G1
A
B
C
D
E
F
G
DP
MAX6921
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
IV-18 Pinout diagram

Amend cross sections for single element

I'm playing around with an example on https://code.kx.com/q/ref/amend/#cross-sections
$ q
KDB+ 3.6 2019.04.02 Copyright (C) 1993-2019 Kx Systems
q)d:((1 2 3;4 5 6 7);(8 9;10;11 12);(13 14;15 16 17 18;19 20))
q)i:(2 0; 0 1 0)
q)y:(100 200 300; 400 500 600)
q)r:.[d; i; ,; y]
It all works fine except if I'll try to reference a single element d[1;1]:
q)i:(1 0; 0 1 0)
q)r:.[d; i; ,; y]
'type
[0] r:.[d; i; ,; y]
But if I use join , for lists and for just single element it works as supposed:
q)10,200
10 200
q)10,((),200)
10 200
So why the amend operation breaks on this simple join?
Upd:
One more example:
q)#[(1; 2; 3);1;,;10]
'type
[0] #[(1; 2; 3);1;,;10]
^
but it's ok if one use lists:
q)#[(1; (),2; 3);1;,;10]
1
2 10
3
The answer lies in examining the data you are extracting to join, here the operator -3! is your friend to help reveal the actual structure through multiple layers of enlistment
q)d:((1 2 3;4 5 6 7);(8 9;10;11 12);(13 14;15 16 17 18;19 20))
q)i1:(2 0; 0 1 0)
q)i2:(1 0; 0 1 0)
q)y:(100 200 300; 400 500 600)
q)-3!r1:.[d; i1]
"((13 14;15 16 17 18;13 14);(1 2 3;4 5 6 7;1 2 3))"
q)q){type each x} each .[d; i1]
7 7 7
7 7 7
q)-3!r2:.[d; i2]
"((8 9;10;8 9);(1 2 3;4 5 6 7;1 2 3))"
q){type each x} each .[d; i2]
7 -7 7
7 7 7
No here we can see that in the first case, each element of r1 is a list of lists, but for r2 the first element is 2 lists of longs with an atomic long 10.
From the cross section documentation
The shape of y is 2 3, the same shape as the cross-section selected by d . i
i.e., Shape should be the counts and types matching, the type of each item of y is a 7h, that should match the type of each selection from d.
Essentially when you are using the amendment operators , and # it will be expecting conformity as it is using an amend in place. a:1;a,:1 2 3 will also fail.
We can confirm this with your other examples
q)type #[(1; (),2; 3);1]
7h
Changing that example to adjust the first element
q)#[(1; (),2; 3);0;,;10]
'type
The reason that just using the , operator as 10,((),200) didn't cause any errors, is because you are using it outside the amend overloads, within the amend overload , is expecting to be working with matching shape. When used directly it can promote and adjust shape.
The only explanation I can offer is that kdb+ tries to use amend-in-place (also known as assignment through the operator) when it can.
For example, this works:
q)l:(13 14;15 16 17 18;19 20)
q)l[1],:200
q)l
13 14
15 16 17 18 200
19 20
But this doesn't:
q)l:(8 9;10;11 12)
q)l[1],:200
'type
[0] l[1],:200
^
The latter fails because kdb can't substitute the vector 10 200 for the atom 10 -- changing types is not something that amend-in-place is supposed to do.
If you used your own function {x,y} instead of the plus operator the second
expression from your example would work as expected too because kdb will replace existing values with return values of the function (which, in contrast to the built-in ,, is a black box as far as kdb is concerned):
q)i:(1 0; 0 1 0)
q)r:.[d; i; ,; y]
q).[d; ii; {x,y}; y]
(1 2 3 400 600;4 5 6 7 500)
(8 9 100 300;10 200;11 12)
(13 14;15 16 17 18;19 20)

(q/kdb+) Generating an automated list

Example 1)
I have the code below
5#10+1*2
that generates
index value
0 12
1 12
2 12
3 12
4 12
How can I replace the number "1" by the index?
then generating
5#10+index*2
index value
0 10
1 12
2 14
3 16
4 18
update Example 2)
Now, if I have, let's say
mult:5;
t:select from ([]numC:1 3 6 4 1;[]s:50 16 53 6 33);
update lst:(numC#'s) from t
the last update will generate
numC s lst
1 50 50
3 16 16 16 16
6 53 53 53 53 53 53 53
4 6 6 6 6 6
1 33 33
How can I generate the "lst" column as per below?
numC s lst
1 50 50+0*mult
3 16 16+0*mult 16+1*mult 16+2*mult
6 53 53+0*mult 53+1*mult 53+2*mult 53+3*mult 53+4*mult 53+5*mult
4 6 6+0*mult 6+1*mult 6+2*mult 6+3*mult
1 33 33+0*mult
I tried something like
update lst:(numC#'s + (til numC)*mult) from t
but I am getting an error
ERROR: 'type
Thanks vm
Is this what you're looking for:
q)x:5
q)x#10+(til x)*2
10 12 14 16 18
http://code.kx.com/q/ref/arith-integer/#til
You can remove take # and use til to simplify to:
q)10+2*til 5
10 12 14 16 18
Using til will create a list of a list of 5 elements (0->4), so you will not need take 5 elements from the resulting list. Take will only be required if your list of indices is greater than 5.
Update:
For your second example the following should work:
q)update lst:{y+x*til z}'[mult;s;numC] from t
q)update lst:s+mult*til each numC from t
numC s lst
-------------------------
1 50 ,50
3 16 16 21 26
6 53 53 58 63 68 73 78
4 6 6 11 16 21
1 33 ,33
There are many ways with which we can get achieve this:
1) 10+2*til 5
2) (2*til 5) + 10
/ take operator: The dyadic take function creates lists. The left argument specifies the count and shape and the right argument provides the data.
It is useful for selecting from the front or end of a list.
https://code.kx.com/wiki/Reference/NumberSign
q)5#0 1 2 3 4 5 6 7 8 / take the first 5 items
0 1 2 3 4
q)-5#0 1 2 3 4 5 6 7 8 / take the last 5 elements
4 5 6 7 8
use take operator # only when it is required.
say we have 10 elements, of which we need five on output, then we can use:
5#10+2*til 10
/ The til function takes a non-negative integer argument X and returns the first X integers

KDB '.' operator

The . operator in the simplest form is used to index a list. How would you explain its use in english in this code?
if[x~"last";upd:{[t;x].[t;();,;r::select by sym from x]}]
I also don't understand the empty list and the :: operator in this line, but maybe they will make sense once the . is cleared up.
In plain english I would explain it as:
modify the table t at all () indices by applying the append/comma function with the value r.
First consider a few simpler cases of #:
q)l:3 5 7 9
q)l:1.1 2.2 3.3
q)#[l; 0 2; +; 10]
11.1 2.2 13.3
q)d:`p`o`i!4.4 5.5 6.6
q)#[d; `p`i; -; 10]
p| -5.6
o| 5.5
i| -3.4
As you can see the format is
#[dataStructure; indices; function; y-arg]
means to the dataStructure at indices apply the function with the given y-arguments. Notice for the list l the indices 0 2 meant index both 0 and 2 at the topmost level. There's no way using # to index at depth. e.g. given matrix m:(1 2 3; 4 5 6; 7 8 9) how can we use this format to modify just the values 4 and 6?
q)/ # indexes repeatedly at topmost level
q)/ definitely not what we want
q)#[m;(1;0 2);+;100]
101 102 103
104 105 106
107 108 109
q)/ **. indexes into the data structure**
q).[m;1 2;+;100]
1 2 3
4 5 106
7 8 9
q).[m;(1;0 2);+;100]
1 2 3
104 5 106
7 8 9
Lastly the empty list () is a short way of saying, apply to all indices:
q).[m;();+;100]
101 102 103
104 105 106
107 108 109
. In this case means apply , to t and r. r is global updated on each call and contains last values recieved by sym. :: is assignment to global in most cases.
code.kx.com describe . function in great details