Descriptive Analysis of MLST Data for MRSA
Original post date: January 24 2017
During one of my summers, I had the opportunity to conduct some research on the prevalence of methicillin-resistant Staphylococcus aureus (MRSA) in vulnerable populations and examining US emergency department data and I thought this would be a pretty interesting topic to expand on for my thesis in light of the increasing concerns of antimicrobial resistance, both at the domestic and international level. This resulted in an systematic review of the recent literature of MRSA in the major Far East countries (China, Hong Kong, Japan, South Korea and Taiwan) which has a wide variety of genotypes and strains. If you’re interested in understanding S. aureus in Asia, a good starting point is this article . An extensive and great review on community-associated MRSA written by two researchers from Chicago if you really want to dig deep into this topic.
What is MRSA?
MRSA is a gram-positive bacteria which is found on almost every continent on earth, which developed resistance to methicillin (a narrow spectrum β-lactam antibiotic from the penicillin class) in the early 60s. MRSA was originally considered to only cause infections within hospital settings (ICUs, EDs, surgery) but towards the end of the 1980s and into the 1990s, cases of severe MRSA infections were being reported in communities outside of healthcare centers . MRSA typically causes skin and soft tissue infections but some cases when left untreated can cause havoc to respiratory system, bloodstream and eventually cause sepsis – not a pretty picture. Over the next 20 years, reports of different strains of this “new” MRSA started to appear around the globe. Some of these strains successfully disseminated from their country of origin to neighboring regions and even across continents with the help of globalization. With the advent and advancement of genotyping technology – researchers were able to track and identify the strains causing outbreaks across the world. One classification system is called Multi Locus Sequence Typing ( MLST ) which was developed by researchers at Imperial College, UK. In short, it is based on creating a profile or sequence type (ST) of each S. aureus strain based on sequencing specific internal nucleotide fragments of seven house-keeping genes . Using this method, investigators are able to discriminate between strains and identify evolutionary changes over time.
Reading in the data using R
This post has nothing too fancy in R – I’m just going to do a step-by-step walkthrough of data cleaning and asking a couple questions, then running descriptive analyses to find some answers. For those you are proficient in doing this might find this a little boring (you can just scroll down to the end and see the results). The data is taken from https://pubmlst.org/saureus/ by selecting the link “download as MS excel” and downloading the spreadsheet to a local directory. The data is complied from submissions to the database which report new MLSTs with some demographic information. This database doesn’t really provide information of a specific strain’s prevalence – it’s just a timestamp report for novel strains. This file contains nine sheets. The one using for this post is called “profile”. I saved this as a comma-delimited file manually but there are lots of resources on the internet of doing this in R using some packages.
So now you should have a .csv file someone on your local directory. You first want to set your working directory. You can read a brief description of how to do this
here
. I’m using R 3.3.2
on Mac OS as an FYI, the link before talks about the differences when you set your working directory in Windows and Mac OS.
getwd() #shows what your current working directory is
setwd("/Users/myname/Documents/Folder") #sets your working directory
Next, we’re gonna read the comma delimited file into R using the read.csv()
function and assigning it the name “profile”. The first argument you want input the filename with the .csv extension. The second argument tells the function that the first row in the file contains the headers or column names. Based on a quick visual inspection of spreadsheet file using Excel, there are many missing values just left as blank cells. The final argument I used specifies to convert any blank cells to NA type so that R can just treat these as missing data.
profile <- read.csv("name_of_your_file.csv", header=T, na.strings=c("", NA))
A good practice I always do when I read in spreadsheets into R is to get a idea of what I’m dealing with. I want to know how many columns and rows there are, the names of the headers/columns and the type of data in each column. To accomplish this, we run the dim()
, names()
and str()
functions for each respective question.
dim(profile) #dimensions of the dataset
names(profile) # names of the columns in dataset
str(profile) #structure of the data frame
When we do this, the data frame should have 4703 rows and 25 columns. The 25 headers include the following…
# [1] "id" "strain" "other_name" "st" "country"
# [6] "region" "year" "age_yr" "age_mth" "sex"
# [11] "disease" "sourcce" "epidemiology" "species" "oxsau"
# [16] "methicillin" "vancomycin" "spa_type" "reference" "comments"
# [21] "sender" "curator" "date_entered" "datestamp" "username"
Most of the time the columns names can be cryptic. You always want to have the metadata or the documentation files for the datasets so that you know exactly what each column represents. In this case for this file, it is pretty straightforward – we can see that for each submission there is an id number, strain name, sequence type (st), who submitted the info, time/date stamps and some other demographic information.
When we run str(profile)
, our output shows the class type of each column. This will be important as R tends to have functions that run only for specific types of data. A good overview the different types of data in R can be found
here
.
Two major things stick out to me when I look through the data – first the column with country names is type factor. I want to convert into a string type, so later on I can subset the data by country by referring to the actual name, versus the factor level (which would be confusing and annoying to keep track of).
# Change country to character from factor type
profile$country <- as.character(profile$country)
Second, I noticed the classification of a lot of categorical columns have multiple levels. Sex has 6 levels!? Methicillin resistance has 9 levels? What does this mean…? Well, the problem for databases that are derived from public submissions is that there is no standardization with the data input. When we inspect what the levels are for sex by running the line of code below, we see that due to the lack of data input standardization, we have F
, female
and Female
for those categorized as female sex. For male sex we have M
and Male
, and then Unspecified for those contributors who didn’t leave it blank.
# > summary(profile$sex)
# F female Female M Male Unspecified NA's
# 17 1 814 19 842 1343 1667
I’m not particularly interested in distribution of gender with these reports so I’m okay with leaving this alone. But we have to same issue with the column for methicillin resistance and I want to simplify it to a binary category of resistant and susceptible. When we look at the levels for the the methicillin column we see this…
# > summary(profile$methicillin)
# I MIC 4mg/L MIC 64mg/L r R s S
# 1 2 1 1 1533 3 2175
# Unknown Unspecified NA's
# 4 199 784
Since we want to simplify this, we can use the levels()
function to set and rename the levels in this column. First, we’re gonna merge the lower and uppercase letters together (r will be R and s will be S)
levels(profile$methicillin) <- c("I", "MIC4", "MIC64", "R", "R", "S", "S", "Unknown", "Unspecified")
Minimum Inhibitory Concentration (MIC) is the lowest concentration of an antibiotic that inhibits the growth of an organism. If you have two strains of S. aureus (#1 and #2) where #1 has a higher MIC to methicillin or oxacillin (another antibiotic) compared to strain #2 strain, #1 is considered to be more resistant to that tested antibiotic compared to #2. This is a method used to provide more granularity in defining antimicrobial resistance. Here in this case, I recognize MIC’s above 4mg/L are considered resistant (I’m making an assumption that all the reports used the same standard and technique to make this definition, a limitation is simplifying this). There’s more information on MIC’s for MRSA on the CDC’s website . So with all this in mind, I going to merge the MIC4 and MIC64 into the resistant category. After doing this, we only have four levels in the methicillin column: R, S, Unknown and Unspecified. We can check this by running
levels(profile$methicillin) <- c("R","R","R","R","S","Unknown","Unspecified")
factor(profile$methicillin)
Before we move on, another good practice is to be aware (and quantify) missing values in each of the columns. Lets say we want to know how many missing sequence types (STs) are there? We can use the is.na()
and which()
together to figure out which rows are missing values under the st column.
# > which(is.na(profile$st))
# [1] 1596 1897 4602 4605
Using this, we now know that there are four rows missing STs. Row numbers 1596, 1897, 4602 and 4605. If we wanted to subset the data for those missing STs, we can just use square brackets [...]
to accomplish this. If you’re unfamiliar with this, you can read this post to learn more on these accessors.
profile[which(is.na(profile$st)),]
Let’s ask some questions
Now that we’re done with the appropriate cleaning of the data, let’s ask some (random) questions
1. Which countries reported sequence type 239 (ST-239)? Based on my literature review, ST-239 is a common hospital-associated strain in Asia and it’s prevalence has increased in China, where is it the most common strain in the entire country. Other variants of ST-239 are common in Eastern Europe and South America as well, in particular Hungary and Brazil respectively.
To answer this question, we use the logic statements with subsetting to accomplish this.
id.239 <- which(profile$st==239) #index of rows with ST-239
st.239 <-profile[id.239,] #new dataframe of only ST-239
sort(unique(st.239$country)) # alphabetical list of countries with ST-239
When we run the sort()
function we get a vector of 29 countries in alphabetical order which have reported ST-239.
# [1] "Algeria" "Australia" "Brazil"
# [4] "China" "Czech republic" "Eire"
# [7] "Finland" "Germany" "Greece"
# [10] "Hungary" "India" "Iran"
# [13] "Malaysia" "Pakistan" "Poland"
# [16] "Portugal" "Scotland" "Slovenia"
# [19] "South Africa" "Spain" "Sweden"
# [22] "Switzerland" "Taiwan" "Thailand"
# [25] "The Netherlands" "Turkey" "UK"
# [28] "United Arab Emirates" "USA"
2. Which countries reported ST8 and spa type t008? spa typing is another classification technique used for S. aureus strains. It is a single-locus typing method, so it’s much more cost-effective compared to MLST.
We use the similar code as above except we add another condition to the logic statement to specify the spa type.
id.ST8.t008 <- which(profile$st==8 & profile$spa_type=="t008")
ST8.t008 <- profile[id.ST8.t008,]
sort(unique(ST8.t008$country))
We see that 4 countries that have reported ST-8 with spa type t008.
# [1] "Algeria" "France" "Portugal" "Switzerland"
3. What is the proportion of methicillin-susceptible vs. -resistant S. aureus reports are there in North America? I was honestly just curious, the results we get from this are definitely limited due to the assumptions we had with the definitions and simplification discussed earlier.
First, since there is no column specifying the region of North America, we have to specify which countries to include to this subset. By visual inspection of the countries using sort(unique(profile$country))
, we see that there Canada and USA but no Mexico. So we’ll subset the data to only Canada and USA.
#selection of North American countries
NorthA.MRSA <- profile[profile$country=="USA" | profile$country=="Canada",]
Then running the following line, we get the counts of methicillin-resistant and methicillin-susceptible in North America.
# table(NorthA.MRSA$methicillin,useNA="always")
# R S Unknown Unspecified <NA>
# 138 148 0 15 75
4. How many different STs are there in China, S Korea, Taiwan and Japan? This is just coming from my thesis and getting an idea of distributions in the region of the world.
F.east <- profile[profile$country=="China" | profile$country=="South Korea" | profile$country=="Japan" | profile$country=="Taiwan",]
The following lines of code will answer some other questions related to this…
factor(F.east$st) #find the number of STs in all these countries
sum(is.na(F.east$st)) #number of missing values
table(F.east$country) #counts of STs by country
Based on 573 entries, we find that 289 different STs; China has 241, Japan has 259, South Korea has 26 and Taiwan 59.
If we want to only look those without missing values…
#removal of all rows that are missing an entry for country
FE.final<-F.east[complete.cases(F.east$country),]
To go further we can look at each specific country, lets ask what are the top 5 most reported STs in China?
# FE.China <- FE.final[FE.final$country=="China",] #subset for China
# sort(table(FE.China$st),decreasing=T)[1:5] #top 5 most reported
#
# 97 239 398 88 2154
# 21 13 5 3 3
5. What are the 10 most frequently STs reported in North America in this database?
Let’s also visualize this in a basic pie chart? First need to setup the data so that it is easier to visualize using the ggplot2
package. So we setup the STs for North America and then order the the counts/frequencies of STs. *note you can also accomplish this using the count()
function in plyr
package
c.NorthA.MRSA <-as.data.frame(table(NorthA.MRSA$st))
names(c.NorthA.MRSA) <- c("ST","count") #data frame containing count frequencies of the STs
c.NorthA.MRSA<-c.NorthA.MRSA[order(-c.NorthA.MRSA$count),] #order the STs by count frequencies
c.NorthA.MRSA[1:10,] # top ten
Then we use ggplot2 to create the pie chart! First specifying the theme so we can tinker around with font size, remove the background grid, etc. I used the scales package to display the percentages in the pie chart for each ST. I stumbled across this incredible resource for color palettes for data visualization where you can choose distinct colors (with color-blind friendly options as well!). This specific case I choose the pastel color option.
We use ggplot2
to create the pie chart! First specifying the theme so we can tinker around with font size, remove the background grid, etc. I used the scales package to display the percentages in the pie chart for each ST. I stumbled across this incredible resource for color palettes for data visualization where you can choose distinct colors (with color-blind friendly options as well!). This specific case I choose the pastel color option.
blank_t <-theme(
axis.title.x = element_blank(),
axis.title.y = element_blank(),
panel.border = element_blank(),
panel.grid=element_blank(),
axis.ticks = element_blank(),
plot.title=element_text(size=20, face="bold",hjust=0.6),
plot.subtitle=element_text(size=15,face=c("bold","italic"),hjust=0.6),
axis.text.x=element_blank(),
legend.title=element_text(size=14,face="bold",vjust=0.5),
panel.margin=unit(2,"cm")
)
pie1<-ggplot(c.NorthA.MRSA[1:10,], aes(x="",y=count,fill=ST,order=ST)) +
geom_bar(width=1, stat="identity") +
ggtitle(element_text(size=50))+
labs(title = "STs in North America", subtitle = "Canada and United States",y=NULL) +
coord_polar(theta="y")+
geom_text(aes(label = scales::percent(count/sum(c.NorthA.MRSA[1:10,2]))),size=4, position = position_stack(vjust = 0.6)) +
blank_t +
scale_fill_manual(guide_legend(title="STs"),
values=c("#e6b4c9",
"#bce5c2", # color palette hexcodes
"#c7b5de",
"#e3e4c5",
"#a1bbdd",
"#e5b6a5",
"#99d5e5",
"#bdbda0",
"#dcd1e1",
"#99c6b8"))
print(pie1)
ggsave(plot=pie1,filename="North_America_STs_MLSTdatabase.png", width=5.75, height=7, dpi=120)
Then we get the pie chart that looks like this… {width=95%}
We can see that ST-5 and ST-8 have the most number of reports, while the remainder are ≤10%. All the code is on Github, open for anyone to use and adapt. I’m always open for suggestions to how to improve the methodology and code. I know pie charts are “controversial” so any input on other graphics to display this type of information is always welcome.
Hopefully this post demonstrates the power and flexibility of R when you have some questions and how easily you can find the answers in your data. It is always important to remember where the data came from, who collected it, when you received it, how you processed it and any assumptions you make along the way – this can change the interpretation for the results dramatically. The results from this specific dataset are just informative and descriptive of the MLST database and potentially lead to some hypotheses. Another possible extension of this data is attaching the strain information by country and displaying it in a map. There is a wealth of resources on mapping in R (the link is from the R-Bloggers website) and many packages exist for displaying spatial data – so this might be something I might tinker with in the near future.


|
Dendro
5.01
Dendro in Greek language means tree. The Dendro library is a large scale (262K cores on ORNL's Titan) distributed memory adaptive octree framework. The main goal of Dendro is to perform large scale multiphysics simulations efficeiently in mordern supercomputers. Dendro consists of efficient parallel data structures and algorithms to perform variational ( finite element) methods and finite difference mthods on 2:1 balanced arbitary adaptive octrees which enables the users to perform simulations raning from black holes (binary black hole mergers) to blood flow in human body, where applications ranging from relativity, astrophysics to biomedical engineering.
|
Public Member Functions | |
| AsyncExchangeContex (const void *var) | |
| creates an async ghost exchange contex | |
| void | allocateSendBuffer (size_t bytes) |
| allocates send buffer for ghost exchange | |
| void | allocateRecvBuffer (size_t bytes) |
| allocates recv buffer for ghost exchange | |
| void | deAllocateSendBuffer () |
| allocates send buffer for ghost exchange | |
| void | deAllocateRecvBuffer () |
| allocates recv buffer for ghost exchange | |
| void * | getSendBuffer () |
| void * | getRecvBuffer () |
| const void * | getBuffer () |
| std::vector< MPI_Request * > & | getRequestList () |
| bool | operator== (AsyncExchangeContex other) const |


|
|
| MemBuffer (MemBuffer &&) |
| |
|
Sint64 | tellp () const noexcept |
| |
|
Sint64 | seek (int64_t offset, int whence) |
| |
|
size_t | write (const void *buf, size_t num, size_t n) |
| |
|
size_t | read (void *buf, size_t num, size_t n) |
| |
|
size_t | size () const noexcept |
| |
|
size_t | capcitity () const noexcept |
| |
|
| RWops (SDL_RWops *r) |
| |
|
| RWops (const RWops &)=delete |
| |
|
| RWops (RWops &&rw) |
| |
|
SDL_RWops * | get () const noexcept |
| |
| bool | close () |
| | Close the rwops. More...
|
| |
|
size_t | write (const void *buf, size_t size, size_t n) |
| |
|
size_t | read (void *buf, size_t size, size_t n) |
| |
|
size_t | tell () const |
| |
|
size_t | size () const |
| |
|
size_t | seek (Sint64 offset, int whence) |
| |
|
RWops & | operator= (RWops &&) |
| |
|
|
std::ostream & | operator<< (std::ostream &, const MemBuffer &) |
| |
|
|
static RWops | FromStdIstream (std::istream &) |
| |
|
static RWops | FromStdOstream (std::ostream &) |
| |
|
static RWops | FromStdFstream (std::fstream &) |
| |
|
static RWops | FromFile (const char *fname, const char *modes) |
| |
|
static RWops | FromFD (int fd, const char *modes) |
| |
The documentation for this class was generated from the following files:


Specifies a request for versions of cells from a column family.
A specification describing how to write data from a scalding tuple field to a family of columns in a Kiji table.
A specification describing a column filter to use when reading data from a Kiji table.
A request for data from a Kiji table.
A specification describing how to write data from a scalding tuple field to a Kiji table.
An entity ID, or row key, that can be used to address a row in a Kiji table.
A runtime exception thrown when two EntityIds with different formats are compared.
A map from keys to values backed by a data store.
A container for data from a Kiji table.
This exception is thrown when a KijiTap cannot validate that the Kiji tables or columns it requires exist.
KijiJob is KijiExpress's extension of Scalding's Job
, and users should extend it when writing
their own jobs in KijiExpress.
A class that adds Kiji-specific functionality to a Cascading pipe, allowing the user to pack fields into an Avro record.
A read or write view of a Kiji table.
A specification of the type of paging to use.
Specifies a request for versions of cells from a fully-qualified column.
A specification describing how to write data from a scalding tuple field to a column in a Kiji table.
A specification describing a row filter to use when reading data from a Kiji table.
A specification of the range of rows that should be read from a Kiji table.
A specification describing the schema to use when reading or writing values to a column in a Kiji table.
A specification of the range cell versions that should be read from a column in a Kiji table.
TransientStream
is a special case of scala.collection.Seq meant to be backed by a source
which is larger than memory.
Provides factory functions for creating org.kiji.express.flow.ColumnFamilyInputSpec instances.
Provides factory functions for creating org.kiji.express.flow.ColumnFamilyOutputSpec instances.
Provides org.kiji.express.flow.ColumnFilterSpec implementations.
Provides convenience factory methods for creating org.kiji.express.flow.ColumnInputSpec instances.
Provides factory functions for creating org.kiji.express.flow.ColumnOutputSpec instances.
Companion object for EntityId.
A factory for key-value stores backed by specific KijiMR key-value store implementations.
Special factory methods for KijiTable key-value stores.
The main-method entry point for running an Express job.
Companion object containing factory methods for creating flow cells and orderings for sorting cells.
Factory methods for constructing org.kiji.express.flow.KijiSources that will be used as inputs to a KijiExpress flow.
Factory methods for constructing org.kiji.express.flow.KijiSources that will be used as outputs of a KijiExpress flow.
Provides org.kiji.express.flow.PagingSpec implementations.
Provides factory functions for creating org.kiji.express.flow.QualifiedColumnInputSpec instances.
Provides factory functions for creating org.kiji.express.flow.QualifiedColumnOutputSpec instances.
Provides org.kiji.express.flow.RowFilterSpec implementations.
Provides org.kiji.express.flow.RowRangeSpec implementations.
Provides org.kiji.express.flow.SchemaSpec implementations.
Provides org.kiji.express.flow.TimeRangeSpec implementations.
Used with a column input spec to indicate that all cells of a column should be retrieved.
Package containing supporting classes and objects for the KijiExpress flow API.
Used with a column input spec to indicate that only the latest cell of a column should be retrieved.
Package containing utility classes for the KijiExpress flow API.
Module providing the ability to write Scalding using data stored in Kiji tables.
KijiExpress users should import the members of this module to gain access to factory methods that produce org.kiji.express.flow.KijiSources that can perform data processing operations that read from or write to Kiji tables.
import org.kiji.express.flow._
Reading from columns and map-type column families.
Specify columns to read from a Kiji table using instances of the org.kiji.express.flow.QualifiedColumnInputSpec and org.kiji.express.flow.ColumnFamilyInputSpec classes, which contain fields for specifying the names of the columns to read, as well as what data to read back (e.g., only the latest version of a cell, or a certain number of recent versions) and how it is read back (e.g., using paging to limit the amount of data in memory).
Specify a fully-qualified column with an instance of
QualifiedColumnInputSpec
. Below are several examples for specifying the columninfo:name
:To request cells from all of the columns in a family, use the
ColumnFamilyInputSpec
class, which, likeQualifiedColumnInputSpec
, provides options on the input spec such as the maximum number of cell versions to return, filters to use, etc. A user can specify a filter, for example, to specify a regular expression such that a column in the family will only be retrieved if its qualifier matches the regular expression:See org.kiji.express.flow.QualifiedColumnInputSpec and org.kiji.express.flow.ColumnFamilyInputSpec for a full list of options for column input specs.
When specifying a column for writing, the user can likewise use the
QualifiedColumnOutputSpec
andColumnFamilyOutputSpec
classes to indicate the name of the column and any options. The following, for example, specifies a column to use for writes with the default reader schema:When writing to a family, you specify a Scalding field that contains the name of the qualifier to use for your write. For example, to use the value in the Scalding field
as the name of the column qualifier, use the following:'terms
See org.kiji.express.flow.QualifiedColumnOutputSpec and org.kiji.express.flow.ColumnFamilyOutputSpec for a full list of options for column output specs.
Getting input from a Kiji table.
The factory
KijiInput
can be used to obtain a org.kiji.express.flow.KijiSource to process rows from the table (represented as tuples) using various operations. When usingKijiInput
, users specify a table (using a Kiji URI) and use column specs and other options to control how data is read from Kiji into tuple fields.
contains different factories that allow for abbreviated column specifications, as illustrated in the examples below:KijiInput
See org.kiji.express.flow.KijiInput and org.kiji.express.flow.ColumnInputSpec for more information on how to create and use time ranges for requesting data.
Writing to a Kiji table.
Data from any Cascading
Source
can be written to a Kiji table. Tuples to be written to a Kiji table must have a field namedentityId
which contains an entity id for a row in a Kiji table. The contents of a tuple field can be written as a cell at the most current timestamp to a column in a Kiji table. To do so, you specify a mapping from tuple field names to qualified Kiji table column names.Specifying ranges of time.
Instances of org.kiji.express.flow.TimeRangeSpec are used to specify a range of timestamps that should be retrieved when reading data from Kiji. There are five implementations of
TimeRange
that can be used when requesting data.These implementations can be used with org.kiji.express.flow.KijiInput to specify a range that a Kiji cell's timestamp must be in to be retrieved. For example, to read cells from the column
info:word
that have timestamps between0L
and10L
, you can do the following.


vignettes/yourData.Rmd
yourData.Rmd
In this vignette we describe how you can use BactDating on your own data.
The first required input (let’s call it t
) is a phylogenetic tree of the class phylo
from the ape
package, which can be loaded from a Newick or Nexus file using respectively the following commands from the ape
package:
library(ape) t=read.tree('filename.nwk') t=read.nexus('filename.nex')
This tree should have branch lengths measured in units of substitutions. In other words, the branch lengths should not be measured in substitutions per site. If you are not sure whether the branch lengths are measured correctly or not, try to compute sum(t$edge.length)
. This should be the total number of substitutions throughout the tree, so if you get a value below one or even only a bit above one, your branch lengths are probably not in the correct unit.
If the branch lengths are measured per site, you will need to rescale them using the command:
t$edge.length=t$edge.length*L
where L
is the number of sites which were used to build this tree.
Alternatively, you can load directly from the output of ClonalFrameML or Gubbins using the following commands respectively:
t=loadCFML(prefix) t=loadGubbins(prefix)
This will have the advantage to account for the recombination events detected by ClonalFrameML or Gubbins when performing the dating.
The second required input (let’s call it d
) is the dates at which the isolates were sampled. Dates need to be expressed decimally, for example 2015.5
means 1st July 2015. If needed, you can convert into decimal years from other date formats using the decimal_date
function of the lubridate
package. This input can be simply a vector of decimal dates, in which case the order needs to be the same as the order of the tree tips in t$tip.label
. For example, if t$tip.label
is (A,B,C)
then d=c(2010,2011,2012)
indicates that A
was isolated in 2010, B in 2011 and C in 2012.
Alternatively, the vector d
does not have to be ordered if names(d)
corresponds to the tip names. For example, d=c(2011,2010,2012)with names(d)=c('B','A','C')
would have the same meaning as in the previous example.
If some isolation dates are unknown, this can be indicated using NA
in the vector, in which case the date is considered unknown but within the range of known dates, ie with the range return by the command range(d,na.rm=T)
.
Finally, the isolation dates can be specified as an interval rather than an exact value. This is useful for example if the isolation year is 2015 but with unknown month or day. To do so, d
needs to be a matrix with two columns, with the first column indicating the lower bound and the second column indicating the upper bound of the interval. For example, if year
is the vector of isolation years, but the exact date in each year is unknown, you can specify this by building the matrix d
as follows:
d=cbind(year,year+1)
Once the two inputs t
and d
described above are in the correct format, you are ready to run BactDating using the main command:
res=bactdate(t,d)
See other vignettes for additional functionality. In particular, if your tree is unrooted you can find an optimal root using:
rooted=initRoot(t,d)
Before running the main bactdate
analysis, it is also a good idea to assess the strength of the temporal signal using a root-to-tip linear regression:
r=roottotip(t,d)
BactDating is a MCMC algorithm which needs to be run for long enough to ensure that the results are meaningful. The length of the algorithm can be controlled using the nbIts
argument of the bactdate
function, which by default is 10e4 but may need increasing for example ten folds:
res=bactdate(t,d,nbIts=1e5)
To test the MCMC convergence, a good start is to check that the traces of the parameters look stable using the command:
plot(res,'trace')
For further testing of convergence, you can export the BactDating result to the format required by the coda
package using the command:
mcmc=as.mcmc(res)
You can then compute for example the effective sample size of the parameters which should be at least 100 for each paramater, using:
effectiveSize(mcmc)


Active Record objects don‘t specify their attributes directly, but rather infer them from the table definition with which they‘re linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
See the mapping rules in table_name and the full example in files/README.html for more insight.
Active Records accept constructor parameters either in a hash or as a block. The hash method is especially useful when you‘re receiving the data from somewhere else, like an HTTP request. It works like this:
user = User.new(:name => "David", :occupation => "Code Artist") user.name # => "David"
You can also use block initialization:
user = User.new do |u| u.name = "David" u.occupation = "Code Artist" end
And of course you can just create a bare object and specify the attributes after the fact:
user = User.new user.name = "David" user.occupation = "Code Artist"
Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement. The array form is to be used when the condition input is tainted and requires sanitization. The string form can be used for statements that don‘t involve tainted data. The hash form works much like the array form, except only equality and range is possible. Examples:
class User < ActiveRecord::Base def self.authenticate_unsafely(user_name, password) where("user_name = '#{user_name}' AND password = '#{password}'").first end def self.authenticate_safely(user_name, password) where("user_name = ? AND password = ?", user_name, password).first end def self.authenticate_safely_simply(user_name, password) where(:user_name => user_name, :password => password).first end end
The authenticate_unsafely method inserts the parameters directly into the query and is thus susceptible to SQL-injection attacks if the user_name and password parameters come directly from an HTTP request. The authenticate_safely and authenticate_safely_simply both will sanitize the user_name and password before inserting them in the query, which will ensure that an attacker can‘t escape the query and fake the login (or worse).
When using multiple parameters in the conditions, it can easily become hard to read exactly what the fourth or fifth question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That‘s done by replacing the question marks with symbols and supplying a hash with values for the matching symbol keys:
Company.where( "id = :id AND name = :name AND division = :division AND created_at > :accounting_date", { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' } ).first
Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND operator. For instance:
Student.where(:first_name => "Harvey", :status => 1) Student.where(params[:student])
A range may be used in the hash to use the SQL BETWEEN operator:
Student.where(:grade => 9..12)
An array may be used in the hash to use the SQL IN operator:
Student.where(:grade => [9,11,12])
When joining tables, nested hashes or keys written in the form ‘table_name.column_name’ can be used to qualify the table name of a particular condition. For instance:
Student.joins(:schools).where(:schools => { :type => 'public' }) Student.joins(:schools).where('schools.type' => 'public' )
All column values are automatically available through basic accessors on the Active Record object, but sometimes you want to specialize this behavior. This can be done by overwriting the default accessors (using the same name as the attribute) and calling read_attribute(attr_name) and write_attribute(attr_name, value) to actually change things. Example:
class Song < ActiveRecord::Base # Uses an integer of seconds to hold the length of the song def length=(minutes) write_attribute(:length, minutes.to_i * 60) end def length read_attribute(:length) / 60 end end
You can alternatively use self[:attribute]=(value) and self[:attribute] instead of write_attribute(:attribute, value) and read_attribute(:attribute) as a shorter form.
In addition to the basic accessors, query methods are also automatically available on the Active Record object. Query methods allow you to test whether an attribute value is present.
For example, an Active Record User with the name attribute has a name? method that you can call to determine whether the user has a name:
user = User.new(:name => "David") user.name? # => true anonymous = User.new(:name => "") anonymous.name? # => false
Sometimes you want to be able to read the raw attribute data without having the column-determined typecast run its course first. That can be done by using the <attribute>_before_type_cast accessors that all attributes have. For example, if your Account model has a balance attribute, you can call account.balance_before_type_cast or account.id_before_type_cast.
This is especially useful in validation situations where the user might supply a string for an integer field and you want to display the original string back in an error message. Accessing the attribute normally would typecast the string to 0, which isn‘t what you want.
Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by appending the name of an attribute to find_by_, find_last_by_, or find_all_by_, so you get finders like Person.find_by_user_name, Person.find_all_by_last_name, and Payment.find_by_transaction_id. So instead of writing Person.where(:user_name => user_name).first, you just do Person.find_by_user_name(user_name). And instead of writing Person.where(:last_name => last_name).all, you just do Person.find_all_by_last_name(last_name).
It‘s also possible to use multiple attributes in the same find by separating them with "and", so you get finders like Person.find_by_user_name_and_password or even Payment.find_by_purchaser_and_state_and_country. So instead of writing Person.where(:user_name => user_name, :password => password).first, you just do Person.find_by_user_name_and_password(user_name, password).
It‘s even possible to call these dynamic finder methods on relations and named scopes. For example :
Payment.order("created_on").find_all_by_amount(50) Payment.pending.find_last_by_amount(100)
The same dynamic finder style can be used to create the object if it doesn‘t already exist. This dynamic finder is called with find_or_create_by_ and will return the object if it already exists and otherwise creates it, then returns it. Protected attributes won‘t be set unless they are given in a block. For example:
# No 'Summer' tag exists Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer") # Now the 'Summer' tag does exist Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer") # Now 'Bob' exist and is an 'admin' User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true }
Use the find_or_initialize_by_ finder if you want to return a new record without saving it first. Protected attributes won‘t be set unless they are given in a block. For example:
# No 'Winter' tag exists winter = Tag.find_or_initialize_by_name("Winter") winter.new_record? # true
To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of a list of parameters. For example:
Tag.find_or_create_by_name(:name => "rails", :creator => current_user)
That will either find an existing tag named "rails", or create a new one while setting the user that created it.
Active Record can serialize any object in text columns using YAML. To do so, you must specify this with a call to the class method serialize. This makes it possible to store arrays, hashes, and other non-mappable objects without doing any additional work. Example:
class User < ActiveRecord::Base serialize :preferences end user = User.create(:preferences => { "background" => "black", "display" => large }) User.find(user.id).preferences # => { "background" => "black", "display" => large }
You can also specify a class option as the second parameter that‘ll raise an exception if a serialized object is retrieved as a descendant of a class not in the hierarchy. Example:
class User < ActiveRecord::Base serialize :preferences, Hash end user = User.create(:preferences => %w( one two three )) User.find(user.id).preferences # raises SerializationTypeMismatch
Active Record allows inheritance by storing the name of the class in a column that by default is named "type" (can be changed by overwriting Base.inheritance_column). This means that an inheritance looking like this:
class Company < ActiveRecord::Base; end class Firm < Company; end class Client < Company; end class PriorityClient < Client; end
When you do Firm.create(:name => "37signals"), this record will be saved in the companies table with type = "Firm". You can then fetch this row again using Company.where(:name => ‘37signals’).first and it will return a Firm object.
If you don‘t have a type column defined in your table, single-table inheritance won‘t be triggered. In that case, it‘ll work just like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
Note, all the attributes for all the cases are kept in the same table. Read more: www.martinfowler.com/eaaCatalog/singleTableInheritance.html
Connections are usually created through ActiveRecord::Base.establish_connection and retrieved by ActiveRecord::Base.connection. All classes inheriting from ActiveRecord::Base will use this connection. But you can also set a class-specific connection. For example, if Course is an ActiveRecord::Base, but resides in a different database, you can just say Course.establish_connection and Course and all of its subclasses will use this connection instead.
This feature is implemented by keeping a connection pool in ActiveRecord::Base that is a Hash indexed by the class. If a connection is requested, the retrieve_connection method will go up the class-hierarchy until a connection is found in the connection pool.
Note: The attributes listed are class-level attributes (accessible from both the class and instance level). So it‘s possible to assign a logger to the class through Base.logger= which will then be used by all instances in the current object space.
| colorize_logging | -> | colorize_logging= |
| set_table_name | -> | table_name= |
| set_inheritance_column | -> | inheritance_column= |
| set_sequence_name | -> | sequence_name= |
| sanitize_sql_for_conditions | -> | sanitize_sql |
| sanitize_sql_hash_for_conditions | -> | sanitize_sql_hash |
| sanitize_sql | -> | sanitize_conditions |
| abstract_class | [RW] | Set this to true if this is an abstract class (see abstract_class?). |
Overwrite the default class equality method to provide support for association proxies.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 892 892: def ===(object) 893: object.is_a?(self) 894: end
Returns whether this class is a base AR class. If A is a base class and B descends from A, then B.base_class will return B.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 908 908: def abstract_class? 909: defined?(@abstract_class) && @abstract_class == true 910: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 935 935: def arel_engine 936: @arel_engine ||= begin 937: if self == ActiveRecord::Base 938: Arel::Table.engine 939: else 940: connection_handler.connection_pools[name] ? Arel::Sql::Engine.new(self) : superclass.arel_engine 941: end 942: end 943: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 931 931: def arel_table 932: @arel_table ||= Arel::Table.new(table_name, :engine => arel_engine) 933: end
Specifies a white list of model attributes that can be set via mass-assignment, such as new(attributes), update_attributes(attributes), or attributes=(attributes)
This is the opposite of the attr_protected macro: Mass-assignment will only set attributes in this list, to assign to the rest of attributes you can use direct writer methods. This is meant to protect sensitive attributes from being overwritten by malicious users tampering with URLs or forms. If you‘d rather start from an all-open default and restrict attributes as needed, have a look at attr_protected.
class Customer < ActiveRecord::Base attr_accessible :name, :nickname end customer = Customer.new(:name => "David", :nickname => "Dave", :credit_rating => "Excellent") customer.credit_rating # => nil customer.attributes = { :name => "Jolly fellow", :credit_rating => "Superb" } customer.credit_rating # => nil customer.credit_rating = "Average" customer.credit_rating # => "Average"
If the access logic of your application is richer you can use Hash#except or Hash#slice to sanitize the hash of parameters before they are passed to Active Record.
For example, it could be the case that the list of accessible attributes for a given model depends on the role of the user:
# Assumes plan_id is accessible because it depends on the role. params[:account] = params[:account].except(:plan_id) unless admin? @account.update_attributes(params[:account])
Note that attr_accessible is still applied to the received hash. Thus, with this technique you can at most narrow the list of accessible attributes for a particular mass-assignment call.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 570 570: def attr_accessible(*attributes) 571: write_inheritable_attribute(:attr_accessible, Set.new(attributes.map(&:to_s)) + (accessible_attributes || [])) 572: end
Attributes named in this macro are protected from mass-assignment, such as new(attributes), update_attributes(attributes), or attributes=(attributes).
Mass-assignment to these attributes will simply be ignored, to assign to them you can use direct writer methods. This is meant to protect sensitive attributes from being overwritten by malicious users tampering with URLs or forms.
class Customer < ActiveRecord::Base attr_protected :credit_rating end customer = Customer.new("name" => David, "credit_rating" => "Excellent") customer.credit_rating # => nil customer.attributes = { "description" => "Jolly fellow", "credit_rating" => "Superb" } customer.credit_rating # => nil customer.credit_rating = "Average" customer.credit_rating # => "Average"
To start from an all-closed default and enable attributes as needed, have a look at attr_accessible.
If the access logic of your application is richer you can use Hash#except or Hash#slice to sanitize the hash of parameters before they are passed to Active Record.
For example, it could be the case that the list of protected attributes for a given model depends on the role of the user:
# Assumes plan_id is not protected because it depends on the role. params[:account] = params[:account].except(:plan_id) unless admin? @account.update_attributes(params[:account])
Note that attr_protected is still applied to the received hash. Thus, with this technique you can at most extend the list of protected attributes for a particular mass-assignment call.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 522 522: def attr_protected(*attributes) 523: write_inheritable_attribute(:attr_protected, Set.new(attributes.map {|a| a.to_s}) + (protected_attributes || [])) 524: end
Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 580 580: def attr_readonly(*attributes) 581: write_inheritable_attribute(:attr_readonly, Set.new(attributes.map(&:to_s)) + (readonly_attributes || [])) 582: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 830 830: def attribute_method?(attribute) 831: super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, ''))) 832: end
Returns the base AR subclass that this class descends from. If A extends AR::Base, A.base_class will return A. If B descends from A through some arbitrarily deep hierarchy, B.base_class will return A.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 899 899: def base_class 900: class_of_active_record_descendant(self) 901: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 396 396: def colorize_logging(*args) 397: ActiveSupport::Deprecation.warn "ActiveRecord::Base.colorize_logging and " << 398: "config.active_record.colorize_logging are deprecated. Please use " << 399: "Rails::LogSubscriber.colorize_logging or config.colorize_logging instead", caller 400: end
Returns an array of column names as strings.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 770 770: def column_names 771: @column_names ||= columns.map { |column| column.name } 772: end
Returns an array of column objects for the table associated with this class.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 756 756: def columns 757: unless defined?(@columns) && @columns 758: @columns = connection.columns(table_name, "#{name} Columns") 759: @columns.each { |column| column.primary = column.name == primary_key } 760: end 761: @columns 762: end
Returns a hash of column objects for the table associated with this class.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 765 765: def columns_hash 766: @columns_hash ||= columns.inject({}) { |hash, column| hash[column.name] = column; hash } 767: end
Returns true if ActiveRecord is connected.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/abstract/connection_specification.rb, line 107 107: def connected? 108: connection_handler.connected?(self) 109: end
Returns the connection currently associated with the class. This can also be used to "borrow" the connection to do database work unrelated to any of the specific Active Records.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/abstract/connection_specification.rb, line 94 94: def connection 95: retrieve_connection 96: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/abstract/connection_specification.rb, line 98 98: def connection_pool 99: connection_handler.retrieve_connection_pool(self) 100: end
Returns an array of column objects where the primary id, all columns ending in "_id" or "_count", and columns used for single table inheritance have been removed.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 776 776: def content_columns 777: @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column } 778: end
Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part. The use of this method should be restricted to complicated SQL queries that can‘t be executed using the ActiveRecord::Calculations class methods. Look into those before using this.
Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 478 478: def count_by_sql(sql) 479: sql = sanitize_conditions(sql) 480: connection.select_value(sql, "#{name} Count").to_i 481: end
Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
The attributes parameter can be either be a Hash or an Array of Hashes. These Hashes describe the attributes on the objects that are to be created.
# Create a single new object User.create(:first_name => 'Jamie') # Create an Array of new objects User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) # Create a single object and pass it into a block to set other attributes. User.create(:first_name => 'Jamie') do |u| u.is_admin = false end # Creating an Array of new objects using a block, where the block is executed for each object: User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u| u.is_admin = false end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 456 456: def create(attributes = nil, &block) 457: if attributes.is_a?(Array) 458: attributes.collect { |attr| create(attr, &block) } 459: else 460: object = new(attributes) 461: yield(object) if block_given? 462: object.save 463: object 464: end 465: end
True if this isn‘t a concrete subclass needing a STI type condition.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 855 855: def descends_from_active_record? 856: if superclass.abstract_class? 857: superclass.descends_from_active_record? 858: else 859: superclass == Base || !columns_hash.include?(inheritance_column) 860: end 861: end
Establishes the connection to the database. Accepts a hash as input where the :adapter key must be specified with the name of a database adapter (in lower-case) example for regular databases (MySQL, Postgresql, etc):
ActiveRecord::Base.establish_connection( :adapter => "mysql", :host => "localhost", :username => "myuser", :password => "mypass", :database => "somedatabase" )
Example for SQLite database:
ActiveRecord::Base.establish_connection( :adapter => "sqlite", :database => "path/to/dbfile" )
Also accepts keys as strings (for parsing from YAML for example):
ActiveRecord::Base.establish_connection( "adapter" => "sqlite", "database" => "path/to/dbfile" )
The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError may be returned on an error.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/abstract/connection_specification.rb, line 51 51: def self.establish_connection(spec = nil) 52: case spec 53: when nil 54: raise AdapterNotSpecified unless defined?(Rails.env) 55: establish_connection(Rails.env) 56: when ConnectionSpecification 57: self.connection_handler.establish_connection(name, spec) 58: when Symbol, String 59: if configuration = configurations[spec.to_s] 60: establish_connection(configuration) 61: else 62: raise AdapterNotSpecified, "#{spec} database is not configured" 63: end 64: else 65: spec = spec.symbolize_keys 66: unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end 67: 68: begin 69: require 'rubygems' 70: gem "activerecord-#{spec[:adapter]}-adapter" 71: require "active_record/connection_adapters/#{spec[:adapter]}_adapter" 72: rescue LoadError 73: begin 74: require "active_record/connection_adapters/#{spec[:adapter]}_adapter" 75: rescue LoadError 76: raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{$!})" 77: end 78: end 79: 80: adapter_method = "#{spec[:adapter]}_connection" 81: if !respond_to?(adapter_method) 82: raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" 83: end 84: 85: remove_connection 86: establish_connection(ConnectionSpecification.new(spec, adapter_method)) 87: end 88: end
Executes a custom SQL query against your database and returns all the results. The results will be returned as an array with columns requested encapsulated as attributes of the model you call this method from. If you call Product.find_by_sql then the results will be returned in a Product object with the attributes you specified in the SQL query.
If you call a complicated SQL query which spans multiple tables the columns specified by the SELECT will be attributes of the model, whether or not they are columns of the corresponding table.
The sql parameter is a full SQL query as a string. It will be called as is, there will be no database agnostic conversions performed. This should be a last resort because using, for example, MySQL specific terms will lock you to using that particular database engine or require you to change your call if you switch engines.
# A simple SQL query spanning multiple tables Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id" > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...] # You can use the same string replacement techniques as you can with ActiveRecord#find Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date] > [#<Post:0x36bff9c @attributes={"first_name"=>"The Cheap Man Buys Twice"}>, ...]
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 430 430: def find_by_sql(sql) 431: connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) } 432: end
Defines the column name for use with single table inheritance — can be set in subclasses like so: self.inheritance_column = "type_id"
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 681 681: def inheritance_column 682: @inheritance_column ||= "type".freeze 683: end
Returns a string like ‘Post id:integer, title:string, body:text‘
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 869 869: def inspect 870: if self == Base 871: super 872: elsif abstract_class? 873: "#{super}(abstract)" 874: elsif table_exists? 875: attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', ' 876: "#{super}(#{attr_list})" 877: else 878: "#{super}(Table doesn't exist)" 879: end 880: end
New objects can be instantiated as either empty (pass no construction parameter) or pre-set with attributes but not yet saved (pass a hash with key names matching the associated table column names). In both instances, valid attribute keys are determined by the column names of the associated table — hence you can‘t have attributes that aren‘t part of the table columns.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1427 1427: def initialize(attributes = nil) 1428: @attributes = attributes_from_column_definition 1429: @attributes_cache = {} 1430: @new_record = true 1431: @readonly = false 1432: @destroyed = false 1433: @marked_for_destruction = false 1434: @previously_changed = {} 1435: @changed_attributes = {} 1436: 1437: ensure_proper_type 1438: 1439: if scope = self.class.send(:current_scoped_methods) 1440: create_with = scope.scope_for_create 1441: create_with.each { |att,value| self.send("#{att}=", value) } if create_with 1442: end 1443: self.attributes = attributes unless attributes.nil? 1444: 1445: result = yield self if block_given? 1446: _run_initialize_callbacks 1447: result 1448: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 650 650: def quoted_table_name 651: @quoted_table_name ||= connection.quote_table_name(table_name) 652: end
Returns an array of all the attributes that have been specified as readonly.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 585 585: def readonly_attributes 586: read_inheritable_attribute(:attr_readonly) || [] 587: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/abstract/connection_specification.rb, line 111 111: def remove_connection(klass = self) 112: connection_handler.remove_connection(klass) 113: end
Resets all the cached information about columns, which will cause them to be reloaded on the next request.
The most common usage pattern for this method is probably in a migration, when just after creating a table you want to populate it with some default values, eg:
class CreateJobLevels < ActiveRecord::Migration def self.up create_table :job_levels do |t| t.integer :id t.string :name t.timestamps end JobLevel.reset_column_information %w{assistant executive manager director}.each do |type| JobLevel.create(:name => type) end end def self.down drop_table :job_levels end end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 820 820: def reset_column_information 821: undefine_attribute_methods 822: @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil 823: @arel_engine = @unscoped = @arel_table = nil 824: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 912 912: def respond_to?(method_id, include_private = false) 913: if match = DynamicFinderMatch.match(method_id) 914: return true if all_attributes_exists?(match.attribute_names) 915: elsif match = DynamicScopeMatch.match(method_id) 916: return true if all_attributes_exists?(match.attribute_names) 917: end 918: 919: super 920: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/abstract/connection_specification.rb, line 102 102: def retrieve_connection 103: connection_handler.retrieve_connection(self) 104: end
If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object, then specify the name of that attribute using this method and it will be handled automatically. The serialization is done through YAML. If class_name is specified, the serialized object must be of that class on retrieval or SerializationTypeMismatch will be raised.
# Serialize a preferences attribute class User serialize :preferences end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 604 604: def serialize(attr_name, class_name = Object) 605: serialized_attributes[attr_name.to_s] = class_name 606: end
Returns a hash of all the attributes that have been specified for serialization as keys and their class restriction as values.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 609 609: def serialized_attributes 610: read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {}) 611: end
Sets the name of the inheritance column to use to the given value, or (if the value # is nil or false) to the value returned by the given block.
class Project < ActiveRecord::Base set_inheritance_column do original_inheritance_column + "_id" end end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 718 718: def set_inheritance_column(value = nil, &block) 719: define_attr_method :inheritance_column, value, &block 720: end
Sets the name of the sequence to use when generating ids to the given value, or (if the value is nil or false) to the value returned by the given block. This is required for Oracle and is useful for any database which relies on sequences for primary key generation.
If a sequence name is not explicitly set when using Oracle or Firebird, it will default to the commonly used pattern of: #{table_name}_seq
If a sequence name is not explicitly set when using PostgreSQL, it will discover the sequence corresponding to your primary key for you.
class Project < ActiveRecord::Base set_sequence_name "projectseq" # default would have been "project_seq" end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 737 737: def set_sequence_name(value = nil, &block) 738: define_attr_method :sequence_name, value, &block 739: end
Sets the table name to use to the given value, or (if the value is nil or false) to the value returned by the given block.
class Project < ActiveRecord::Base set_table_name "project" end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 703 703: def set_table_name(value = nil, &block) 704: @quoted_table_name = nil 705: define_attr_method :table_name, value, &block 706: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 922 922: def sti_name 923: store_full_sti_class ? name : name.demodulize 924: end
Returns a list of all subclasses of this class, meaning all descendants.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1180 1180: def subclasses 1181: @@subclasses[self] ||= [] 1182: @@subclasses[self] + @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses } 1183: end
Indicates whether the table associated with this class exists
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 751 751: def table_exists? 752: connection.table_exists?(table_name) 753: end
Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy looks like: Reply < Message < ActiveRecord::Base, then Message is used to guess the table name even when called on Reply. The rules used to do the guess are handled by the Inflector class in Active Support, which knows almost all common English inflections. You can add new inflections in config/initializers/inflections.rb.
Nested classes are given table names prefixed by the singular form of the parent‘s table name. Enclosing modules are not considered.
class Invoice < ActiveRecord::Base; end; file class table_name invoice.rb Invoice invoices class Invoice < ActiveRecord::Base; class Lineitem < ActiveRecord::Base; end; end; file class table_name invoice.rb Invoice::Lineitem invoice_lineitems module Invoice; class Lineitem < ActiveRecord::Base; end; end; file class table_name invoice/lineitem.rb Invoice::Lineitem lineitems
Additionally, the class-level table_name_prefix is prepended and the table_name_suffix is appended. So if you have "myapp_" as a prefix, the table name guess for an Invoice class becomes "myapp_invoices". Invoice::Lineitem becomes "myapp_invoice_lineitems".
You can also overwrite this class method to allow for unguessable links, such as a Mouse class with a link to a "mice" table. Example:
class Mouse < ActiveRecord::Base set_table_name "mice" end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 646 646: def table_name 647: reset_table_name 648: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 926 926: def unscoped 927: @unscoped ||= Relation.new(self, arel_table) 928: finder_needs_type_condition? ? @unscoped.where(type_condition) : @unscoped 929: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1278 1278: def aggregate_mapping(reflection) 1279: mapping = reflection.options[:mapping] || [reflection.name, reflection.name] 1280: mapping.first.is_a?(Array) ? mapping : [mapping] 1281: end
Returns the class descending directly from ActiveRecord::Base or an abstract class, if any, in the inheritance hierarchy.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1236 1236: def class_of_active_record_descendant(klass) 1237: if klass.superclass == Base || klass.superclass.abstract_class? 1238: klass 1239: elsif klass.superclass.nil? 1240: raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord" 1241: else 1242: class_of_active_record_descendant(klass.superclass) 1243: end 1244: end
Returns the class type of the record using the current module as a prefix. So descendants of MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1208 1208: def compute_type(type_name) 1209: if type_name.match(/^::/) 1210: # If the type is prefixed with a scope operator then we assume that 1211: # the type_name is an absolute reference. 1212: type_name.constantize 1213: else 1214: # Build a list of candidates to search for 1215: candidates = [] 1216: name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" } 1217: candidates << type_name 1218: 1219: candidates.each do |candidate| 1220: begin 1221: constant = candidate.constantize 1222: return constant if candidate == constant.to_s 1223: rescue NameError => e 1224: # We don't want to swallow NoMethodError < NameError errors 1225: raise e unless e.instance_of?(NameError) 1226: rescue ArgumentError 1227: end 1228: end 1229: 1230: raise NameError, "uninitialized constant #{candidates.first}" 1231: end 1232: end
Sets the default options for the model. The format of the options argument is the same as in find.
class Person < ActiveRecord::Base default_scope order('last_name, first_name') end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1193 1193: def default_scope(options = {}) 1194: self.default_scoping << construct_finder_arel(options, default_scoping.pop) 1195: end
Accepts a hash of SQL conditions and replaces those attributes that correspond to a composed_of relationship with their expanded aggregate attribute values. Given:
class Person < ActiveRecord::Base composed_of :address, :class_name => "Address", :mapping => [%w(address_street street), %w(address_city city)] end
Then:
{ :address => Address.new("813 abc st.", "chicago") } # => { :address_street => "813 abc st.", :address_city => "chicago" }
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1294 1294: def expand_hash_conditions_for_aggregates(attrs) 1295: expanded_attrs = {} 1296: attrs.each do |attr, value| 1297: unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil? 1298: mapping = aggregate_mapping(aggregation) 1299: mapping.each do |field_attr, aggregate_attr| 1300: if mapping.size == 1 && !value.respond_to?(aggregate_attr) 1301: expanded_attrs[field_attr] = value 1302: else 1303: expanded_attrs[field_attr] = value.send(aggregate_attr) 1304: end 1305: end 1306: else 1307: expanded_attrs[attr] = value 1308: end 1309: end 1310: expanded_attrs 1311: end
Accepts an array of conditions. The array has each value sanitized and interpolated into the SQL statement.
["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1348 1348: def sanitize_sql_array(ary) 1349: statement, *values = ary 1350: if values.first.is_a?(Hash) and statement =~ /:\w+/ 1351: replace_named_bind_variables(statement, values.first) 1352: elsif statement.include?('?') 1353: replace_bind_variables(statement, values) 1354: else 1355: statement % values.collect { |value| connection.quote_string(value.to_s) } 1356: end 1357: end
Accepts an array, hash, or string of SQL conditions and sanitizes them into a valid SQL fragment for a SET clause.
{ :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1270 1270: def sanitize_sql_for_assignment(assignments) 1271: case assignments 1272: when Array; sanitize_sql_array(assignments) 1273: when Hash; sanitize_sql_hash_for_assignment(assignments) 1274: else assignments 1275: end 1276: end
Accepts an array, hash, or string of SQL conditions and sanitizes them into a valid SQL fragment for a WHERE clause.
["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'" { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'" "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1256 1256: def sanitize_sql_for_conditions(condition, table_name = self.table_name) 1257: return nil if condition.blank? 1258: 1259: case condition 1260: when Array; sanitize_sql_array(condition) 1261: when Hash; sanitize_sql_hash_for_conditions(condition, table_name) 1262: else condition 1263: end 1264: end
Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
{ :status => nil, :group_id => 1 } # => "status = NULL , group_id = 1"
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1339 1339: def sanitize_sql_hash_for_assignment(attrs) 1340: attrs.map do |attr, value| 1341: "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}" 1342: end.join(', ') 1343: end
Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
{ :name => "foo'bar", :group_id => 4 } # => "name='foo''bar' and group_id= 4" { :status => nil, :group_id => [1,2,3] } # => "status IS NULL and group_id IN (1,2,3)" { :age => 13..18 } # => "age BETWEEN 13 AND 18" { 'other_records.id' => 7 } # => "`other_records`.`id` = 7" { :other_records => { :id => 7 } } # => "`other_records`.`id` = 7"
And for value objects on a composed_of relationship:
{ :address => Address.new("123 abc st.", "chicago") } # => "address_street='123 abc st.' and address_city='chicago'"
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1327 1327: def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name) 1328: attrs = expand_hash_conditions_for_aggregates(attrs) 1329: 1330: table = Arel::Table.new(self.table_name, :engine => arel_engine, :as => default_table_name) 1331: builder = PredicateBuilder.new(arel_engine) 1332: builder.build_from_hash(attrs, table).map(&:to_sql).join(' AND ') 1333: end
Works like with_scope, but discards any nested properties.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1175 1175: def with_exclusive_scope(method_scoping = {}, &block) 1176: with_scope(method_scoping, :overwrite, &block) 1177: end
Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash. method_name may be :find or :create. :find parameter is Relation while :create parameters are an attributes hash.
class Article < ActiveRecord::Base def self.create_with_scope with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1 a = create(1) a.blog_id # => 1 end end end
In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of where, includes, and joins operations in Relation, which are merged.
joins operations are uniqued so multiple scopes can join in the same table without table aliasing problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the array of strings format for your joins.
class Article < ActiveRecord::Base def self.find_with_scope with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do with_scope(:find => limit(10)) do all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10 end with_scope(:find => where(:author_id => 3)) do all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1 end end end end
You can ignore any previous scopings by using the with_exclusive_scope method.
class Article < ActiveRecord::Base def self.find_with_exclusive_scope with_scope(:find => where(:blog_id => 1).limit(1)) do with_exclusive_scope(:find => limit(10)) all # => SELECT * from articles LIMIT 10 end end end end
Note: the +:find+ scope also has effect on update and deletion methods, like update_all and delete_all.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1134 1134: def with_scope(method_scoping = {}, action = :merge, &block) 1135: method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping) 1136: 1137: if method_scoping.is_a?(Hash) 1138: # Dup first and second level of hash (method and params). 1139: method_scoping = method_scoping.inject({}) do |hash, (method, params)| 1140: hash[method] = (params == true) ? params : params.dup 1141: hash 1142: end 1143: 1144: method_scoping.assert_valid_keys([ :find, :create ]) 1145: relation = construct_finder_arel(method_scoping[:find] || {}) 1146: 1147: if current_scoped_methods && current_scoped_methods.create_with_value && method_scoping[:create] 1148: scope_for_create = if action == :merge 1149: current_scoped_methods.create_with_value.merge(method_scoping[:create]) 1150: else 1151: method_scoping[:create] 1152: end 1153: 1154: relation = relation.create_with(scope_for_create) 1155: else 1156: scope_for_create = method_scoping[:create] 1157: scope_for_create ||= current_scoped_methods.create_with_value if current_scoped_methods 1158: relation = relation.create_with(scope_for_create) if scope_for_create 1159: end 1160: 1161: method_scoping = relation 1162: end 1163: 1164: method_scoping = current_scoped_methods.merge(method_scoping) if current_scoped_methods && action == :merge 1165: 1166: self.scoped_methods << method_scoping 1167: begin 1168: yield 1169: ensure 1170: self.scoped_methods.pop 1171: end 1172: end
Returns true if the comparison_object is the same object, or is of the same type and has the same id.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1639 1639: def ==(comparison_object) 1640: comparison_object.equal?(self) || 1641: (comparison_object.instance_of?(self.class) && 1642: comparison_object.id == id && !comparison_object.new_record?) 1643: end
Returns the value of the attribute identified by attr_name after it has been typecast (for example, "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). (Alias for the protected read_attribute method).
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1546 1546: def [](attr_name) 1547: read_attribute(attr_name) 1548: end
Updates the attribute identified by attr_name with the specified value. (Alias for the protected write_attribute method).
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1552 1552: def []=(attr_name, value) 1553: write_attribute(attr_name, value) 1554: end
Returns an inspect-like string for the value of the attribute attr_name. String attributes are elided after 50 characters, and Date and Time attributes are returned in the :db format. Other attributes return the value of inspect without modification.
person = Person.create!(:name => "David Heinemeier Hansson " * 3) person.attribute_for_inspect(:name) # => '"David Heinemeier Hansson David Heinemeier Hansson D..."' person.attribute_for_inspect(:created_at) # => '"2009-01-12 04:48:57"'
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1614 1614: def attribute_for_inspect(attr_name) 1615: value = read_attribute(attr_name) 1616: 1617: if value.is_a?(String) && value.length > 50 1618: "#{value[0..50]}...".inspect 1619: elsif value.is_a?(Date) || value.is_a?(Time) 1620: %("#{value.to_s(:db)}") 1621: else 1622: value.inspect 1623: end 1624: end
Returns an array of names for the attributes available on this object sorted alphabetically.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1539 1539: def attribute_names 1540: @attributes.keys.sort 1541: end
Returns true if the specified attribute has been set by the user or by a database load and is neither nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1628 1628: def attribute_present?(attribute) 1629: value = read_attribute(attribute) 1630: !value.blank? 1631: end
Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1595 1595: def attributes 1596: attrs = {} 1597: attribute_names.each { |name| attrs[name] = read_attribute(name) } 1598: attrs 1599: end
Allows you to set all the attributes at once by passing in a hash with keys matching the attribute names (which again matches the column names).
If guard_protected_attributes is true (the default), then sensitive attributes can be protected from this form of mass-assignment by using the attr_protected macro. Or you can alternatively specify which attributes can be accessed with the attr_accessible macro. Then all the attributes not included in that won‘t be allowed to be mass-assigned.
class User < ActiveRecord::Base attr_protected :is_admin end user = User.new user.attributes = { :username => 'Phusion', :is_admin => true } user.username # => "Phusion" user.is_admin? # => false user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false) user.is_admin? # => true
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1576 1576: def attributes=(new_attributes, guard_protected_attributes = true) 1577: return if new_attributes.nil? 1578: attributes = new_attributes.stringify_keys 1579: 1580: multi_parameter_attributes = [] 1581: attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes 1582: 1583: attributes.each do |k, v| 1584: if k.include?("(") 1585: multi_parameter_attributes << [ k, v ] 1586: else 1587: respond_to?("#{k}=""#{k}=") ? send("#{k}=""#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}") 1588: end 1589: end 1590: 1591: assign_multiparameter_attributes(multi_parameter_attributes) 1592: end
Returns a cache key that can be used to identify this record.
Product.new.cache_key # => "products/new" Product.find(5).cache_key # => "products/5" (updated_at not available) Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1518 1518: def cache_key 1519: case 1520: when new_record? 1521: "#{self.class.model_name.cache_key}/new" 1522: when timestamp = self[:updated_at] 1523: "#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}" 1524: else 1525: "#{self.class.model_name.cache_key}/#{id}" 1526: end 1527: end
Returns the column object for the named attribute.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1634 1634: def column_for_attribute(name) 1635: self.class.columns_hash[name.to_s] 1636: end
Returns the connection currently associated with the class. This can also be used to "borrow" the connection to do database work that isn‘t easily done without going straight to SQL.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/connection_adapters/abstract/connection_specification.rb, line 19 19: def connection 20: self.class.connection 21: end
Returns duplicated record with unfreezed attributes.

# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1657 1657: def freeze 1658: @attributes.freeze; self 1659: end
Returns true if the attributes hash has been frozen.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1662 1662: def frozen? 1663: @attributes.frozen? 1664: end
Returns true if the given attribute is in the attributes hash
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1534 1534: def has_attribute?(attr_name) 1535: @attributes.has_key?(attr_name.to_s) 1536: end
Delegates to id in order to allow two records of the same type and id to work with something like:
[ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1652 1652: def hash 1653: id.hash 1654: end
Cloned objects have no id assigned and are treated as new records. Note that this is a "shallow" clone as it copies the object‘s attributes only, not its associations. The extent of a "deep" clone is application specific and is therefore left to the application to implement according to its need.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1453 1453: def initialize_copy(other) 1454: # Think the assertion which fails if the after_initialize callback goes at the end of the method is wrong. The 1455: # deleted clone method called new which therefore called the after_initialize callback. It then went on to copy 1456: # over the attributes. But if it's copying the attributes afterwards then it hasn't finished initializing right? 1457: # For example in the test suite the topic model's after_initialize method sets the author_email_address to 1458: #<EMAIL_ADDRESS>I would have thought this would mean that all cloned models would have an author email address 1459: # of<EMAIL_ADDRESS>However the test_clone test method seems to test that this is not the case. As a result the 1460: # after_initialize callback has to be run *before* the copying of the atrributes rather than afterwards in order 1461: # for all tests to pass. This makes no sense to me. 1462: callback(:after_initialize) if respond_to_without_attributes?(:after_initialize) 1463: cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast) 1464: cloned_attributes.delete(self.class.primary_key) 1465: 1466: @attributes = cloned_attributes 1467: 1468: @changed_attributes = {} 1469: attributes_from_column_definition.each do |attr, orig_value| 1470: @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr]) 1471: end 1472: 1473: clear_aggregation_cache 1474: @attributes_cache = {} 1475: @new_record = true 1476: ensure_proper_type 1477: 1478: if scope = self.class.send(:current_scoped_methods) 1479: create_with = scope.scope_for_create 1480: create_with.each { |att,value| self.send("#{att}=", value) } if create_with 1481: end 1482: end
Returns the contents of the record as a nicely formatted string.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1685 1685: def inspect 1686: attributes_as_nice_string = self.class.column_names.collect { |name| 1687: if has_attribute?(name) || new_record? 1688: "#{name}: #{attribute_for_inspect(name)}" 1689: end 1690: }.compact.join(", ") 1691: "#<#{self.class} #{attributes_as_nice_string}>" 1692: end
Marks this record as read only.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1680 1680: def readonly! 1681: @readonly = true 1682: end
Returns true if the record is read only. Records loaded through joins with piggy-back attributes will be marked as read only since they cannot be saved.
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1675 1675: def readonly? 1676: @readonly 1677: end
Returns a String, which Action Pack uses for constructing an URL to this object. The default implementation returns this record‘s id as a String, or nil if this record‘s unsaved.
For example, suppose that you have a User model, and that you have a resources :users route. Normally, user_path will construct a path with the user object‘s ‘id’ in it:
user = User.find_by_name('Phusion') user_path(user) # => "/users/1"
You can override to_param in your model to make user_path construct a path using the user‘s name instead of the user‘s id:
class User < ActiveRecord::Base def to_param # overridden name end end user = User.find_by_name('Phusion') user_path(user) # => "/users/Phusion"
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1506 1506: def to_param 1507: # We can't use alias_method here, because method 'id' optimizes itself on the fly. 1508: id && id.to_s # Be sure to stringify the id for routes 1509: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1702 1702: def clone_attribute_value(reader_method, attribute_name) 1703: value = send(reader_method, attribute_name) 1704: value.duplicable? ? value.clone : value 1705: rescue TypeError, NoMethodError 1706: value 1707: end
# File /usr/lib/ruby/gems/1.8/gems/activerecord-3.0.0.beta4/lib/active_record/base.rb, line 1695 1695: def clone_attributes(reader_method = :read_attribute, attributes = {}) 1696: attribute_names.each do |name| 1697: attributes[name] = clone_attribute_value(reader_method, name) 1698: end 1699: attributes 1700: end