[med-svn] [r-cran-tibble] 08/10: New upstream version 1.2

Andreas Tille tille at debian.org
Fri Sep 29 21:43:25 UTC 2017


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository r-cran-tibble.

commit f2871df8781f34af2d3df74b11f40fb7f60fa78d
Author: Andreas Tille <tille at debian.org>
Date:   Fri Sep 29 23:40:36 2017 +0200

    New upstream version 1.2
---
 DESCRIPTION                                        |  32 ++
 LICENSE                                            |   2 +
 MD5                                                |  94 ++++++
 NAMESPACE                                          |  96 ++++++
 NEWS.md                                            | 199 ++++++++++++
 R/RcppExports.R                                    |   7 +
 R/add.R                                            | 153 ++++++++++
 R/all-equal.r                                      | 103 +++++++
 R/check-names.R                                    |  85 ++++++
 R/dataframe.R                                      | 333 +++++++++++++++++++++
 R/enframe.R                                        |  24 ++
 R/glimpse.R                                        |  99 ++++++
 R/has-name.R                                       |  18 ++
 R/repair-names.R                                   |  40 +++
 R/rownames.R                                       |  94 ++++++
 R/tbl-df.r                                         |  75 +++++
 R/tibble.R                                         |  65 ++++
 R/tribble.R                                        | 102 +++++++
 R/type-sum.r                                       | 134 +++++++++
 R/utils-format.r                                   | 288 ++++++++++++++++++
 R/utils.r                                          |  86 ++++++
 README.md                                          | 136 +++++++++
 build/vignette.rds                                 | Bin 0 -> 230 bytes
 debian/README.test                                 |   8 -
 debian/changelog                                   |  17 --
 debian/compat                                      |   1 -
 debian/control                                     |  26 --
 debian/copyright                                   |  32 --
 debian/docs                                        |   4 -
 debian/rules                                       |   8 -
 debian/source/format                               |   1 -
 debian/tests/control                               |   3 -
 debian/tests/run-unit-test                         |  20 --
 debian/watch                                       |   3 -
 inst/doc/formatting.R                              |  53 ++++
 inst/doc/formatting.Rmd                            | 106 +++++++
 inst/doc/formatting.html                           | 170 +++++++++++
 inst/doc/tibble.R                                  |  50 ++++
 inst/doc/tibble.Rmd                                | 128 ++++++++
 inst/doc/tibble.html                               | 204 +++++++++++++
 man/add_column.Rd                                  |  40 +++
 man/add_row.Rd                                     |  47 +++
 man/all_equal.Rd                                   |  54 ++++
 man/as_tibble.Rd                                   |  87 ++++++
 man/enframe.Rd                                     |  26 ++
 man/formatting.Rd                                  |  52 ++++
 man/glimpse.Rd                                     |  41 +++
 man/has_name.Rd                                    |  29 ++
 man/is.tibble.Rd                                   |  21 ++
 man/knit_print.trunc_mat.Rd                        |  13 +
 man/obj_sum.Rd                                     |  41 +++
 man/repair_names.Rd                                |  32 ++
 man/rownames.Rd                                    |  51 ++++
 man/tibble-package.Rd                              |  54 ++++
 man/tibble.Rd                                      |  78 +++++
 man/tribble.Rd                                     |  44 +++
 src/RcppExports.cpp                                |  18 ++
 src/matrixToDataFrame.cpp                          | 103 +++++++
 tests/testthat.R                                   |   3 +
 tests/testthat/helper-data.R                       |  12 +
 tests/testthat/helper-output.R                     |  18 ++
 tests/testthat/helper-unknown-rows.R               |  21 ++
 tests/testthat/output/glimpse/5.txt                |   1 +
 tests/testthat/output/glimpse/all-35.txt           |  11 +
 tests/testthat/output/glimpse/all-50.txt           |  11 +
 tests/testthat/output/glimpse/all-70.txt           |  11 +
 tests/testthat/output/glimpse/iris-70.txt          |   7 +
 tests/testthat/output/glimpse/iris-empty-70.txt    |   1 +
 tests/testthat/output/glimpse/mtcars-70.txt        |  13 +
 tests/testthat/output/trunc_mat/POSIXlt-8-60.txt   |  12 +
 tests/testthat/output/trunc_mat/all--30.txt        |  10 +
 tests/testthat/output/trunc_mat/all--300.txt       |  11 +
 tests/testthat/output/trunc_mat/all-1-30-0.txt     |   6 +
 tests/testthat/output/trunc_mat/all-1-30-2.txt     |   7 +
 tests/testthat/output/trunc_mat/all-knit-120.txt   |  12 +
 tests/testthat/output/trunc_mat/all-knit-60.txt    |  12 +
 tests/testthat/output/trunc_mat/iris--70.txt       |  14 +
 tests/testthat/output/trunc_mat/iris-3-5.txt       |  23 ++
 tests/testthat/output/trunc_mat/iris-5-30.txt      |  13 +
 tests/testthat/output/trunc_mat/iris_unk-10-70.txt |  13 +
 tests/testthat/output/trunc_mat/long-5-30.txt      |   9 +
 tests/testthat/output/trunc_mat/long_unk-5-30.txt  |   8 +
 tests/testthat/output/trunc_mat/mtcars-8-30.txt    |  17 ++
 tests/testthat/output/trunc_mat/mtcars-knit-60.txt |  20 ++
 tests/testthat/output/trunc_mat/wide-8-60.txt      |   6 +
 .../output/trunc_mat/zero-cols_unk-5-30.txt        |   2 +
 .../output/trunc_mat/zero-rows_unk-5-30.txt        |   6 +
 tests/testthat/output/trunc_mat/zero_cols-5-30.txt |   1 +
 tests/testthat/output/trunc_mat/zero_rows--30.txt  |   3 +
 tests/testthat/test-add.R                          | 216 +++++++++++++
 tests/testthat/test-data-frame.R                   | 210 +++++++++++++
 tests/testthat/test-enframe.R                      |  22 ++
 tests/testthat/test-equality.R                     |  92 ++++++
 tests/testthat/test-glimpse.R                      |  66 ++++
 tests/testthat/test-has-name.R                     |  24 ++
 tests/testthat/test-lst.R                          |  13 +
 tests/testthat/test-matrix.R                       |  70 +++++
 tests/testthat/test-nibble.R                       | 106 +++++++
 tests/testthat/test-obj-sum.R                      |  43 +++
 tests/testthat/test-options.R                      |  22 ++
 tests/testthat/test-repair_names.R                 |  42 +++
 tests/testthat/test-rownames.R                     |  53 ++++
 tests/testthat/test-tbl-df.R                       | 153 ++++++++++
 tests/testthat/test-trunc-mat.R                    | 125 ++++++++
 vignettes/formatting.Rmd                           | 106 +++++++
 vignettes/tibble.Rmd                               | 128 ++++++++
 106 files changed, 5612 insertions(+), 123 deletions(-)

diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..05f0746
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,32 @@
+Package: tibble
+Encoding: UTF-8
+Version: 1.2
+Title: Simple Data Frames
+Description: Provides a 'tbl_df' class that offers better checking and
+    printing capabilities than traditional data frames.
+Authors at R: c(
+    person("Hadley", "Wickham", , "hadley at rstudio.com", "aut"),
+    person("Romain", "Francois", , "romain at r-enthusiasts.com", "aut"),
+    person("Kirill", "Müller", , "krlmlr+r at mailbox.org", c("aut", "cre")),
+    person("RStudio", role = "cph")
+    )
+URL: https://github.com/hadley/tibble
+BugReports: https://github.com/hadley/tibble/issues
+Depends: R (>= 3.1.2)
+Imports: methods, assertthat, utils, lazyeval (>= 0.1.10), Rcpp
+Suggests: testthat, withr, knitr (>= 1.5.32), rmarkdown, nycflights13,
+        microbenchmark
+LinkingTo: Rcpp
+LazyData: yes
+License: MIT + file LICENSE
+RoxygenNote: 5.0.1
+VignetteBuilder: knitr
+NeedsCompilation: yes
+Packaged: 2016-08-26 11:42:44 UTC; muelleki
+Author: Hadley Wickham [aut],
+  Romain Francois [aut],
+  Kirill Müller [aut, cre],
+  RStudio [cph]
+Maintainer: Kirill Müller <krlmlr+r at mailbox.org>
+Repository: CRAN
+Date/Publication: 2016-08-26 21:50:28
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..46b1e49
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,2 @@
+YEAR: 2013-2016
+COPYRIGHT HOLDER: RStudio
diff --git a/MD5 b/MD5
new file mode 100644
index 0000000..8a52707
--- /dev/null
+++ b/MD5
@@ -0,0 +1,94 @@
+b2e9ae1b9352939da5c5fd2625b5d1a0 *DESCRIPTION
+b9b3d969565e7a0cae5fc295df89b8df *LICENSE
+44698edb7b450b59cc7185e36be83e85 *NAMESPACE
+96d757bfaa09b04e8f74fd1a52a7d466 *NEWS.md
+eb9619f7830ea8f3050299ae1b3d60e5 *R/RcppExports.R
+22673a507154c6aaff91ebef883ef4f5 *R/add.R
+2766f5518bf33bff07c6aeeaf67f8559 *R/all-equal.r
+ee288bac55cdb66c414ae04b6505be7a *R/check-names.R
+053be47b9adf56d4cca566ee0f84d677 *R/dataframe.R
+17f857477f0b4d27765ffa9f622e6a3a *R/enframe.R
+6eef80de53c4c474d74e4a3799d772b5 *R/glimpse.R
+93ceb5444587193dffc940a5cf514528 *R/has-name.R
+4519520146ba19ec0e365cae28a9eb92 *R/repair-names.R
+34e22b5a8d8caffad8f6847f86b23842 *R/rownames.R
+0f41126f668536900cef37ef232838f3 *R/tbl-df.r
+a64dfb358f5b5a7af5f9055d01af3e1e *R/tibble.R
+783e93f5ab08a3d365e2306c28352c48 *R/tribble.R
+abbbf29429acc59b04cb26835195f522 *R/type-sum.r
+a5ca35ad1819dcfaa6742f86a789c045 *R/utils-format.r
+0bda8fe6f887b1c24ab243553753e578 *R/utils.r
+b8a67878190a75c0e2410a01c3cf5e13 *README.md
+6d3e129b933b7c3596ab817585e5e7c0 *build/vignette.rds
+004d2bcbbbefd366635f6dbc32f6045d *inst/doc/formatting.R
+59535b317f2c66f3b8ad1406061f1b9b *inst/doc/formatting.Rmd
+6710ae71bcd615dd4c6d66c88b6fa951 *inst/doc/formatting.html
+b3661ba91509d77490523eec7c8752ee *inst/doc/tibble.R
+e6e4a4fefa97e6baa72268a5aaf1b09b *inst/doc/tibble.Rmd
+56c99c7b9a0e13f631d98eb447245e15 *inst/doc/tibble.html
+a9db417eedafc8f650a0060a80afdf24 *man/add_column.Rd
+b0377576b964cc0e1a118a19926b5454 *man/add_row.Rd
+ddc140d2b85ac02ecd4d6fa45ec60795 *man/all_equal.Rd
+775f766e6220494ffd9975270c758ae9 *man/as_tibble.Rd
+9fc2813fca4a54cdf9f9462f4e4161c1 *man/enframe.Rd
+e2b25f484fee0c8ec71add8f777e2bf8 *man/formatting.Rd
+b0974f5e23669cd9e4358c101ed1145a *man/glimpse.Rd
+45ee0ec6fa0f6c2d377b5cefa83a0049 *man/has_name.Rd
+4746e48b255539a8015e149fe745a84e *man/is.tibble.Rd
+407773a91a2714a9c5ba3741cff95103 *man/knit_print.trunc_mat.Rd
+442bd7f4be4f16275f30e2cd1ef5126d *man/obj_sum.Rd
+1f16ab048d6f018a58bebc7c7e6763f3 *man/repair_names.Rd
+d40ab396cf65dca644a42a6addba20aa *man/rownames.Rd
+bffd0d9278358ea709bd7bc0f8bda634 *man/tibble-package.Rd
+c1493b3d06b93f334a229a1de6f6e324 *man/tibble.Rd
+e56afd8ca208190010590f3cec63c7a7 *man/tribble.Rd
+b330a9146ed0494723fa830de0f9044f *src/RcppExports.cpp
+4f8ba78cba02ac2dd80ad056f3257cf8 *src/matrixToDataFrame.cpp
+934bcde5b57357bf4ec7ac5793041f17 *tests/testthat.R
+9f597b81fd89f044d1242ee86767bfe2 *tests/testthat/helper-data.R
+236aac070ae1970e0b086b328d25a656 *tests/testthat/helper-output.R
+897f112238d560f580b6a6c2602676c4 *tests/testthat/helper-unknown-rows.R
+c72ceac37b3a6f8ce601e7c692c0b757 *tests/testthat/output/glimpse/5.txt
+6b385238353522edd3bf4416de1dbf68 *tests/testthat/output/glimpse/all-35.txt
+e6cafb5ba57c19960362703f8364d5a2 *tests/testthat/output/glimpse/all-50.txt
+50b2e81888a4bfec784fce942345242b *tests/testthat/output/glimpse/all-70.txt
+82df862794444dc5d7a3eb4cdc83e981 *tests/testthat/output/glimpse/iris-70.txt
+0d74f615866191dd8527f360e3394ae8 *tests/testthat/output/glimpse/iris-empty-70.txt
+7a3f9a2883c851cf26f735fe6363e1fb *tests/testthat/output/glimpse/mtcars-70.txt
+d4332c0a89832a00336ab26bdd9444cc *tests/testthat/output/trunc_mat/POSIXlt-8-60.txt
+7fb8f60258181c9a4ee71576f0f2c7d0 *tests/testthat/output/trunc_mat/all--30.txt
+214e588f18aa610efdf81e7494eed72d *tests/testthat/output/trunc_mat/all--300.txt
+f576682fe5285723cbc15fe754be67ec *tests/testthat/output/trunc_mat/all-1-30-0.txt
+51ce95b0b64793299d2f0d5e68793999 *tests/testthat/output/trunc_mat/all-1-30-2.txt
+f0cde25ef1a1091a3e02567a7a665fcd *tests/testthat/output/trunc_mat/all-knit-120.txt
+8414d8080216f00a5c2d3ae7b061cb23 *tests/testthat/output/trunc_mat/all-knit-60.txt
+3a231a5e6301b027eff1508a16a36162 *tests/testthat/output/trunc_mat/iris--70.txt
+7a5a611b258bbb88f7fe800f0fff435a *tests/testthat/output/trunc_mat/iris-3-5.txt
+745bd57ce074fcd3738a3495120f5180 *tests/testthat/output/trunc_mat/iris-5-30.txt
+35ec2e9d790daa26072c26958d143a40 *tests/testthat/output/trunc_mat/iris_unk-10-70.txt
+d83fea704dc163b15a012b93b2e54662 *tests/testthat/output/trunc_mat/long-5-30.txt
+6a9c2f34c5cea6d9809470dede9cc1b0 *tests/testthat/output/trunc_mat/long_unk-5-30.txt
+f14405de2d72657b5cd2dcdcbeb0ad10 *tests/testthat/output/trunc_mat/mtcars-8-30.txt
+22f428f7006b28215d69ac7f7d51c5c2 *tests/testthat/output/trunc_mat/mtcars-knit-60.txt
+157b15602882d1b0c698ba564b42c8d8 *tests/testthat/output/trunc_mat/wide-8-60.txt
+9d2265837d2166c8f0dd40ae5b84d7fc *tests/testthat/output/trunc_mat/zero-cols_unk-5-30.txt
+2b45ff53f989aab58ba94b1d6496f8cf *tests/testthat/output/trunc_mat/zero-rows_unk-5-30.txt
+c9a877ac50cdc83efe505ee07eb94e42 *tests/testthat/output/trunc_mat/zero_cols-5-30.txt
+f4cbe49da6c2a6c795bb73ff7939c295 *tests/testthat/output/trunc_mat/zero_rows--30.txt
+fb8040e67e9a11cb4d0c41931085e2ac *tests/testthat/test-add.R
+fb97cb3387406ceb662e28d9f54969a6 *tests/testthat/test-data-frame.R
+f02fd3cfc8ea195e9daa1f98d47e3d69 *tests/testthat/test-enframe.R
+58f5b25559d0b761384152a082165a2e *tests/testthat/test-equality.R
+fc78843ed14552ade6a8aa7a5e6355ac *tests/testthat/test-glimpse.R
+f0df9cdc9aebd923cd9307ea78d88599 *tests/testthat/test-has-name.R
+80a22c8f001c0b7f4703b9b1eb01abb4 *tests/testthat/test-lst.R
+d9b7265ff4b99b4f015c52bf58d46846 *tests/testthat/test-matrix.R
+9aed81144588f240ef722377ea33184e *tests/testthat/test-nibble.R
+26d59e5cbbd6d85806a249dd09018d32 *tests/testthat/test-obj-sum.R
+cb4a7b816230647cc54be3b6a11e7deb *tests/testthat/test-options.R
+80907cf7a15f840a511fba9edbc39452 *tests/testthat/test-repair_names.R
+3a8690468bd9fab7cb48af157e194f26 *tests/testthat/test-rownames.R
+67255210bad12a8d337c5d1800ba9200 *tests/testthat/test-tbl-df.R
+970ed238e0bf2810520f72962e447f3b *tests/testthat/test-trunc-mat.R
+59535b317f2c66f3b8ad1406061f1b9b *vignettes/formatting.Rmd
+e6e4a4fefa97e6baa72268a5aaf1b09b *vignettes/tibble.Rmd
diff --git a/NAMESPACE b/NAMESPACE
new file mode 100644
index 0000000..9a1db1c
--- /dev/null
+++ b/NAMESPACE
@@ -0,0 +1,96 @@
+# Generated by roxygen2: do not edit by hand
+
+S3method("$",tbl_df)
+S3method("[",tbl_df)
+S3method("[[",tbl_df)
+S3method("row.names<-",tbl_df)
+S3method(all.equal,tbl_df)
+S3method(as.data.frame,tbl_df)
+S3method(as_data_frame,"NULL")
+S3method(as_data_frame,data.frame)
+S3method(as_data_frame,default)
+S3method(as_data_frame,list)
+S3method(as_data_frame,matrix)
+S3method(as_data_frame,table)
+S3method(as_data_frame,tbl_df)
+S3method(as_tibble,"NULL")
+S3method(as_tibble,data.frame)
+S3method(as_tibble,default)
+S3method(as_tibble,list)
+S3method(as_tibble,matrix)
+S3method(as_tibble,poly)
+S3method(as_tibble,table)
+S3method(as_tibble,tbl_df)
+S3method(check_names_before_after,character)
+S3method(check_names_before_after,default)
+S3method(check_names_df,character)
+S3method(check_names_df,default)
+S3method(check_names_df,logical)
+S3method(check_names_df,numeric)
+S3method(format_v,character)
+S3method(format_v,default)
+S3method(format_v,list)
+S3method(glimpse,data.frame)
+S3method(glimpse,default)
+S3method(glimpse,tbl)
+S3method(is_vector_s3,Date)
+S3method(is_vector_s3,POSIXct)
+S3method(is_vector_s3,data.frame)
+S3method(is_vector_s3,default)
+S3method(is_vector_s3,difftime)
+S3method(is_vector_s3,factor)
+S3method(is_vector_s3,ordered)
+S3method(obj_sum,POSIXlt)
+S3method(obj_sum,default)
+S3method(obj_sum,list)
+S3method(print,tbl_df)
+S3method(print,trunc_mat)
+S3method(quote_n,character)
+S3method(quote_n,default)
+S3method(tbl_sum,default)
+S3method(tbl_sum,grouped_df)
+S3method(tbl_sum,tbl_df)
+S3method(tbl_sum,tbl_sql)
+S3method(type_sum,Date)
+S3method(type_sum,POSIXt)
+S3method(type_sum,data.frame)
+S3method(type_sum,default)
+S3method(type_sum,difftime)
+S3method(type_sum,factor)
+S3method(type_sum,ordered)
+S3method(type_sum,tbl_df)
+export(add_column)
+export(add_row)
+export(as_data_frame)
+export(as_tibble)
+export(column_to_rownames)
+export(data_frame)
+export(data_frame_)
+export(enframe)
+export(frame_data)
+export(glimpse)
+export(has_name)
+export(has_rownames)
+export(is.tibble)
+export(is_tibble)
+export(is_vector_s3)
+export(knit_print.trunc_mat)
+export(lst)
+export(lst_)
+export(obj_sum)
+export(remove_rownames)
+export(repair_names)
+export(rownames_to_column)
+export(tbl_sum)
+export(tibble)
+export(tibble_)
+export(tribble)
+export(trunc_mat)
+export(type_sum)
+import(assertthat)
+importFrom(Rcpp,sourceCpp)
+importFrom(stats,setNames)
+importFrom(utils,head)
+importFrom(utils,str)
+importFrom(utils,tail)
+useDynLib(tibble)
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 0000000..5b99cc3
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,199 @@
+# tibble 1.2 (2016-08-26)
+
+## Bug fixes
+
+- The `tibble.width` option is used for `glimpse()` only if it is finite (#153, @kwstat).
+- New `as_tibble.poly()` to support conversion of a `poly` object to a tibble (#110).
+- `add_row()` now correctly handles existing columns of type `list` that are not updated (#148).
+- `all.equal()` doesn't throw an error anymore if one of the columns is named `na.last`, `decreasing` or `method` (#107, @BillDunlap).
+
+## Interface changes
+
+- New `add_column()`, analogously to `add_row()` (#99).
+- `print.tbl_df()` gains `n_extra` method and will have the same interface as `trunc_mat()` from now on.
+- `add_row()` and `add_column()` gain `.before` and `.after` arguments which indicate the row (by number) or column (by number or name) before or after which the new data are inserted. Updated or added columns cannot be named `.before` or `.after` (#99).
+- Rename `frame_data()` to `tribble()`, stands for "transposed tibble". The former is still available as alias (#132, #143).
+
+## Features
+
+- `add_row()` now can add multiple rows, with recycling (#142, @jennybc).
+- Use multiply character `×` instead of `x` when printing dimensions (#126). Output tests had to be disabled for this on Windows.
+- Back-tick non-semantic column names on output (#131).
+- Use `dttm` instead of `time` for `POSIXt` values (#133), which is now used for columns of the `difftime` class.
+- Better output for 0-row results when total number of rows is unknown (e.g., for SQL data sources).
+
+## Documentation
+
+- New object summary vignette that shows which methods to define for custom vector classes to be used as tibble columns (#151).
+- Added more examples for `print.tbl_df()`, now using data from `nycflights13` instead of `Lahman` (#121), with guidance to install `nycflights13` package if necessary (#152).
+- Minor changes in vignette (#115, @helix123).
+
+
+# tibble 1.1 (2016-07-01)
+
+Follow-up release.
+
+## Breaking changes
+
+- `tibble()` is no longer an alias for `frame_data()` (#82).
+- Remove `tbl_df()` (#57).
+- `$` returns `NULL` if column not found, without partial matching. A warning is given (#109).
+- `[[` returns `NULL` if column not found (#109).
+
+
+## Output
+
+- Reworked output: More concise summary (begins with hash `#` and contains more text (#95)), removed empty line, showing number of hidden rows and columns (#51). The trailing metadata also begins with hash `#` (#101). Presence of row names is indicated by a star in printed output (#72).
+- Format `NA` values in character columns as `<NA>`, like `print.data.frame()` does (#69).
+- The number of printed extra cols is now an option (#68, @lionel-).
+- Computation of column width properly handles wide (e.g., Chinese) characters, tests still fail on Windows (#100).
+- `glimpse()` shows nesting structure for lists and uses angle brackets for type (#98).
+- Tibbles with `POSIXlt` columns can be printed now, the text `<POSIXlt>` is shown as placeholder to encourage usage of `POSIXct` (#86).
+- `type_sum()` shows only topmost class for S3 objects.
+
+
+## Error reporting
+
+- Strict checking of integer and logical column indexes. For integers, passing a non-integer index or an out-of-bounds index raises an error. For logicals, only vectors of length 1 or `ncol` are supported. Passing a matrix or an array now raises an error in any case (#83).
+- Warn if setting non-`NULL` row names (#75).
+- Consistently surround variable names with single quotes in error messages.
+- Use "Unknown column 'x'" as error message if column not found, like base R (#94).
+- `stop()` and `warning()` are now always called with `call. = FALSE`.
+
+
+## Coercion
+
+- The `.Dim` attribute is silently stripped from columns that are 1d matrices (#84).
+- Converting a tibble without row names to a regular data frame does not add explicit row names.
+- `as_tibble.data.frame()` preserves attributes, and uses `as_tibble.list()` to calling overriden methods which may lead to endless recursion.
+
+
+## New features
+
+- New `has_name()` (#102).
+- Prefer `tibble()` and `as_tibble()` over `data_frame()` and `as_data_frame()` in code and documentation (#82).
+- New `is.tibble()` and `is_tibble()` (#79).
+- New `enframe()` that converts vectors to two-column tibbles (#31, #74).
+- `obj_sum()` and `type_sum()` show `"tibble"` instead of `"tbl_df"` for tibbles (#82).
+- `as_tibble.data.frame()` gains `validate` argument (as in `as_tibble.list()`), if `TRUE` the input is validated.
+- Implement `as_tibble.default()` (#71, hadley/dplyr#1752).
+- `has_rownames()` supports arguments that are not data frames.
+
+
+## Bug fixes
+
+- Two-dimensional indexing with `[[` works (#58, #63).
+- Subsetting with empty index (e.g., `x[]`) also removes row names.
+
+
+# Documentation
+
+- Document behavior of `as_tibble.tbl_df()` for subclasses (#60).
+- Document and test that subsetting removes row names.
+
+
+## Internal
+
+- Don't rely on `knitr` internals for testing (#78).
+- Fix compatibility with `knitr` 1.13 (#76).
+- Enhance `knit_print()` tests.
+- Provide default implementation for `tbl_sum.tbl_sql()` and `tbl_sum.tbl_grouped_df()` to allow `dplyr` release before a `tibble` release.
+- Explicit tests for `format_v()` (#98).
+- Test output for `NULL` value of `tbl_sum()`.
+- Test subsetting in all variants (#62).
+- Add missing test from dplyr.
+- Use new `expect_output_file()` from `testthat`.
+
+
+Version 1.0 (2016-03-21)
+===
+
+- Initial CRAN release
+
+- Extracted from `dplyr` 0.4.3
+
+- Exported functions:
+    - `tbl_df()`
+    - `as_data_frame()`
+    - `data_frame()`, `data_frame_()`
+    - `frame_data()`, `tibble()`
+    - `glimpse()`
+    - `trunc_mat()`, `knit_print.trunc_mat()`
+    - `type_sum()`
+    - New `lst()` and `lst_()` create lists in the same way that
+      `data_frame()` and `data_frame_()` create data frames (hadley/dplyr#1290).
+      `lst(NULL)` doesn't raise an error (#17, @jennybc), but always
+      uses deparsed expression as name (even for `NULL`).
+    - New `add_row()` makes it easy to add a new row to data frame
+      (hadley/dplyr#1021).
+    - New `rownames_to_column()` and `column_to_rownames()` (#11, @zhilongjia).
+    - New `has_rownames()` and `remove_rownames()` (#44).
+    - New `repair_names()` fixes missing and duplicate names (#10, #15,
+      @r2evans).
+    - New `is_vector_s3()`.
+
+- Features
+    - New `as_data_frame.table()` with argument `n` to control name of count
+      column (#22, #23).
+    - Use `tibble` prefix for options (#13, #36).
+    - `glimpse()` now (invisibly) returns its argument (hadley/dplyr#1570). It
+      is now a generic, the default method dispatches to `str()`
+      (hadley/dplyr#1325).  The default width is obtained from the
+      `tibble.width` option (#35, #56).
+    - `as_data_frame()` is now an S3 generic with methods for lists (the old
+      `as_data_frame()`), data frames (trivial), matrices (with efficient
+      C++ implementation) (hadley/dplyr#876), and `NULL` (returns a 0-row
+      0-column data frame) (#17, @jennybc).
+    - Non-scalar input to `frame_data()` and `tibble()` (including lists)
+      creates list-valued columns (#7). These functions return 0-row but n-col
+      data frame if no data.
+
+- Bug fixes
+    - `frame_data()` properly constructs rectangular tables (hadley/dplyr#1377,
+      @kevinushey).
+
+- Minor modifications
+    - Uses `setOldClass(c("tbl_df", "tbl", "data.frame"))` to help with S4
+      (hadley/dplyr#969).
+    - `tbl_df()` automatically generates column names (hadley/dplyr#1606).
+    - `tbl_df`s gain `$` and `[[` methods that are ~5x faster than the defaults,
+      never do partial matching (hadley/dplyr#1504), and throw an error if the
+      variable does not exist.  `[[.tbl_df()` falls back to regular subsetting
+      when used with anything other than a single string (#29).
+      `base::getElement()` now works with tibbles (#9).
+    - `all_equal()` allows to compare data frames ignoring row and column order,
+      and optionally ignoring minor differences in type (e.g. int vs. double)
+      (hadley/dplyr#821).  Used by `all.equal()` for tibbles.  (This package
+      contains a pure R implementation of `all_equal()`, the `dplyr` code has
+      identical behavior but is written in C++ and thus faster.)
+    - The internals of `data_frame()` and `as_data_frame()` have been aligned,
+      so `as_data_frame()` will now automatically recycle length-1 vectors.
+      Both functions give more informative error messages if you are attempting
+      to create an invalid data frame.  You can no longer create a data frame
+      with duplicated names (hadley/dplyr#820).  Both functions now check that
+      you don't have any `POSIXlt` columns, and tell you to use `POSIXct` if you
+      do (hadley/dplyr#813).  `data_frame(NULL)` raises error "must be a 1d
+      atomic vector or list".
+    - `trunc_mat()` and `print.tbl_df()` are considerably faster if you have
+      very wide data frames.  They will now also only list the first 100
+      additional variables not already on screen - control this with the new
+      `n_extra` parameter to `print()` (hadley/dplyr#1161).  The type of list
+      columns is printed correctly (hadley/dplyr#1379).  The `width` argument is
+      used also for 0-row or 0-column data frames (#18).
+    - When used in list-columns, S4 objects only print the class name rather
+      than the full class hierarchy (#33).
+    - Add test that `[.tbl_df()` does not change class (#41, @jennybc).  Improve
+      `[.tbl_df()` error message.
+
+- Documentation
+    - Update README, with edits (#52, @bhive01) and enhancements (#54,
+      @jennybc).
+    - `vignette("tibble")` describes the difference between tbl_dfs and
+      regular data frames (hadley/dplyr#1468).
+
+- Code quality
+    - Test using new-style Travis-CI and AppVeyor. Full test coverage (#24,
+      #53). Regression tests load known output from file (#49).
+    - Renamed `obj_type()` to `obj_sum()`, improvements, better integration with
+     `type_sum()`.
+    - Internal cleanup.
diff --git a/R/RcppExports.R b/R/RcppExports.R
new file mode 100644
index 0000000..423023d
--- /dev/null
+++ b/R/RcppExports.R
@@ -0,0 +1,7 @@
+# This file was generated by Rcpp::compileAttributes
+# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
+
+matrixToDataFrame <- function(x) {
+    .Call('tibble_matrixToDataFrame', PACKAGE = 'tibble', x)
+}
+
diff --git a/R/add.R b/R/add.R
new file mode 100644
index 0000000..1f3756b
--- /dev/null
+++ b/R/add.R
@@ -0,0 +1,153 @@
+#' Add rows to a data frame
+#'
+#' This is a convenient way to add one or more rows of data to an existing data
+#' frame. See \code{\link{tribble}} for an easy way to create an complete
+#' data frame row-by-row.
+#'
+#' @param .data Data frame to append to.
+#' @param ... Name-value pairs. If you don't supply the name of a variable,
+#'   it'll be given the value \code{NA}.
+#' @param .before,.after One-based row index where to add the new rows,
+#'   default: after last row
+#' @family addition
+#' @examples
+#' # add_row ---------------------------------
+#' df <- tibble(x = 1:3, y = 3:1)
+#'
+#' add_row(df, x = 4, y = 0)
+#'
+#' # You can specify where to add the new rows
+#' add_row(df, x = 4, y = 0, .before = 2)
+#'
+#' # You can supply vectors, to add multiple rows (this isn't
+#' # recommended because it's a bit hard to read)
+#' add_row(df, x = 4:5, y = 0:-1)
+#'
+#' # Absent variables get missing values
+#' add_row(df, x = 4)
+#'
+#' # You can't create new variables
+#' \dontrun{
+#' add_row(df, z = 10)
+#' }
+#' @export
+add_row <- function(.data, ..., .before = NULL, .after = NULL) {
+  df <- tibble(...)
+  attr(df, "row.names") <- .set_row_names(max(1L, nrow(df)))
+
+  extra_vars <- setdiff(names(df), names(.data))
+  if (length(extra_vars) > 0) {
+    stopc(
+      "This row would add new variables: ", format_n(extra_vars)
+    )
+  }
+
+  missing_vars <- setdiff(names(.data), names(df))
+  df[missing_vars] <- lapply(.data[missing_vars], na_value)
+  df <- df[names(.data)]
+
+  pos <- pos_from_before_after(.before, .after, nrow(.data))
+
+  if (pos <= 0L) {
+    out <- rbind(df, .data)
+  } else if (pos >= nrow(.data)) {
+    out <- rbind(.data, df)
+  } else {
+    indexes <- seq_len(pos)
+    out <- rbind(.data[indexes, ], df, .data[-indexes, ])
+  }
+
+  set_class(remove_rownames(out), class(.data))
+}
+
+na_value <- function(boilerplate) {
+  if (is.list(boilerplate))
+    list(NULL)
+  else
+    NA
+}
+
+#' Add columns to a data frame
+#'
+#' This is a convenient way to add one or more columns to an existing data
+#' frame.
+#'
+#' @param .data Data frame to append to.
+#' @param ... Name-value pairs, all values must have one element for each row
+#'   in the data frame, or be of length 1
+#' @param .before,.after One-based column index or column name where to add the
+#'   new columns, default: after last column
+#' @family addition
+#' @examples
+#' # add_row ---------------------------------
+#' df <- tibble(x = 1:3, y = 3:1)
+#'
+#' add_column(df, z = -1:1, w = 0)
+#'
+#' # You can't overwrite existing columns
+#' \dontrun{
+#' add_column(df, x = 4:6)
+#' }
+
+#' # You can't create new observations
+#' \dontrun{
+#' add_column(df, z = 1:5)
+#' }
+#' @export
+add_column <- function(.data, ..., .before = NULL, .after = NULL) {
+  df <- tibble(...)
+
+  if (ncol(df) == 0L) {
+    return(.data)
+  }
+
+  if (nrow(df) != nrow(.data)) {
+    stopc("Expected ", nrow(.data), " rows, got ", nrow(df))
+  }
+
+  extra_vars <- intersect(names(df), names(.data))
+  if (length(extra_vars) > 0) {
+    stopc(
+      "Columns already in data frame: ", format_n(extra_vars)
+    )
+  }
+
+  pos <- pos_from_before_after_names(.before, .after, colnames(.data))
+
+  if (pos <= 0L) {
+    out <- cbind(df, .data)
+  } else if (pos >= ncol(.data)) {
+    out <- cbind(.data, df)
+  } else {
+    indexes <- seq_len(pos)
+    out <- cbind(.data[indexes], df, .data[-indexes])
+  }
+
+  set_class(remove_rownames(out), class(.data))
+}
+
+
+# helpers -----------------------------------------------------------------
+
+pos_from_before_after_names <- function(before, after, names) {
+  before <- check_names_before_after(before, names)
+  after <- check_names_before_after(after, names)
+
+  pos_from_before_after(before, after, length(names))
+}
+
+pos_from_before_after <- function(before, after, len) {
+  if (is.null(before)) {
+    if (is.null(after)) {
+      len
+    } else {
+      after
+    }
+  } else {
+    if (is.null(after)) {
+      before - 1L
+    } else {
+      stopc("Can't specify both .before and .after")
+    }
+  }
+}
diff --git a/R/all-equal.r b/R/all-equal.r
new file mode 100644
index 0000000..9fc1ad9
--- /dev/null
+++ b/R/all-equal.r
@@ -0,0 +1,103 @@
+#' Flexible equality comparison for data frames.
+#'
+#' When comparing two \code{tbl_df} using \code{\link{all.equal}}, column and
+#' row order is ignored by default, and types are not coerced.  The \code{dplyr}
+#' package provides a much more efficient implementation for this functionality.
+#'
+#' @param target,current Two data frames to compare.
+#' @param ignore_col_order Should order of columns be ignored?
+#' @param ignore_row_order Should order of rows be ignored?
+#' @param convert Should similar classes be converted? Currently this will
+#'   convert factor to character and integer to double.
+#' @param ... Ignored. Needed for compatibility with \code{all.equal}.
+#' @return \code{TRUE} if equal, otherwise a character vector describing
+#'   the reasons why they're not equal. Use \code{\link{isTRUE}} if using the
+#'   result in an \code{if} expression.
+#' @examples
+#' scramble <- function(x) x[sample(nrow(x)), sample(ncol(x))]
+#' mtcars_df <- as_tibble(mtcars)
+#'
+#' # By default, ordering of rows and columns ignored
+#' all.equal(mtcars_df, scramble(mtcars_df))
+#'
+#' # But those can be overriden if desired
+#' all.equal(mtcars_df, scramble(mtcars_df), ignore_col_order = FALSE)
+#' all.equal(mtcars_df, scramble(mtcars_df), ignore_row_order = FALSE)
+#'
+#' # By default all.equal is sensitive to variable differences
+#' df1 <- tibble(x = "a")
+#' df2 <- tibble(x = factor("a"))
+#' all.equal(df1, df2)
+#' # But you can request to convert similar types
+#' all.equal(df1, df2, convert = TRUE)
+all_equal <- function(target, current, ignore_col_order = TRUE,
+                      ignore_row_order = TRUE, convert = FALSE, ...) {
+
+  if (!identical(class(target), class(current))) {
+    return(paste0("Different types: ",
+                  "x ", format_n(class(target)), ", ",
+                  "y ", format_n(class(current))))
+  }
+  if (nrow(target) != nrow(current)) {
+    return("Different number of rows")
+  }
+  extra_x <- setdiff(names(target), names(current))
+  if (length(extra_x) > 0L) {
+    return(paste0("Cols in x but not y: ", format_n(extra_x)))
+  }
+  extra_y <- setdiff(names(current), names(target))
+  if (length(extra_y) > 0L) {
+    return(paste0("Cols in y but not x: ", format_n(extra_y)))
+  }
+  if (!ignore_col_order && names(target) != names(current)) {
+    return("Column names same but in different order")
+  }
+
+  current <- as_tibble(remove_rownames(current))
+  target <- as_tibble(remove_rownames(target))
+
+  current <- current[names(target)]
+
+  types <- unlist(mapply(
+    function(x, y) {
+      if (!identical(class(x), class(y))) {
+        paste0("x ", class(x), ", y ", class(y))
+      }
+    },
+    target, current
+  ))
+
+  if (length(types) > 0L) {
+    types <- paste0("Incompatible type for column ", names(types), ": ", types)
+    if (convert) {
+      lapply(types, warningc)
+    } else {
+      return(types)
+    }
+  }
+
+  factor_levels <- unlist(mapply(
+    function(x, y) {
+      if (!identical(levels(x), levels(y))) {
+        TRUE
+      }
+    },
+    target, current
+  ))
+
+  if (length(factor_levels) > 0L) {
+    return(paste0("Factor levels not equal for column ", names(factor_levels)))
+  }
+
+  if (ignore_row_order) {
+    target <- target[do.call(order, unname(target)), ]
+    current <- current[do.call(order, unname(current)), ]
+  }
+
+  all.equal(as.data.frame(target), as.data.frame(current), ...)
+}
+
+#' @export
+#' @rdname all_equal
+#' @method all.equal tbl_df
+all.equal.tbl_df <- all_equal
diff --git a/R/check-names.R b/R/check-names.R
new file mode 100644
index 0000000..8fb9b82
--- /dev/null
+++ b/R/check-names.R
@@ -0,0 +1,85 @@
+# check_names_df ----------------------------------------------------------
+
+check_names_df <- function(j, ...) UseMethod("check_names_df")
+
+#' @export
+check_names_df.default <- function(j, ...) {
+  stopc("Unsupported index type: ", class(j)[[1L]])
+}
+
+#' @export
+check_names_df.character <- function(j, x) {
+  check_needs_no_dim(j)
+
+  pos <- safe_match(j, names(x))
+  if(any(is.na(pos))){
+    unknown_names <- j[is.na(pos)]
+    stopc("Unknown columns ", format_n(unknown_names))
+  }
+  pos
+}
+
+#' @export
+check_names_df.numeric <- function(j, x) {
+  check_needs_no_dim(j)
+
+  if (any(is.na(j))) {
+    stopc("NA column indexes not supported")
+  }
+
+  non_integer <- (j != trunc(j))
+  if (any(non_integer)) {
+    stopc("Invalid non-integer column indexes: ", format_n(j[non_integer]))
+  }
+  neg_too_small <- (j < -length(x))
+  if (any(neg_too_small)) {
+    stopc("Invalid negative column indexes: ", format_n(j[neg_too_small]))
+  }
+  pos_too_large <- (j > length(x))
+  if (any(pos_too_large)) {
+    stopc("Invalid column indexes: ", format_n(j[pos_too_large]))
+  }
+
+  seq_along(x)[j]
+}
+
+#' @export
+check_names_df.logical <- function(j, x) {
+  check_needs_no_dim(j)
+
+  if (!(length(j) %in% c(1L, length(x)))) {
+    stopc("Length of logical index vector must be 1 or ", length(x), ", got: ", length(j))
+  }
+  if (any(is.na(j))) {
+    stopc("NA column indexes not supported")
+  }
+  seq_along(x)[j]
+}
+
+check_needs_no_dim <- function(j) {
+  if (needs_dim(j)) {
+    stopc("Unsupported use of matrix or array for column indexing")
+  }
+}
+
+
+# check_names_before_after ------------------------------------------------
+
+check_names_before_after <- function(j, ...) UseMethod("check_names_before_after")
+
+#' @export
+check_names_before_after.default <- function(j, ...) {
+  j
+}
+
+#' @export
+check_names_before_after.character <- function(j, names) {
+  check_needs_no_dim(j)
+
+  pos <- safe_match(j, names)
+  if(any(is.na(pos))){
+    unknown_names <- j[is.na(pos)]
+    stopc("Unknown columns ", format_n(unknown_names))
+  }
+  pos
+}
diff --git a/R/dataframe.R b/R/dataframe.R
new file mode 100644
index 0000000..60aaa5c
--- /dev/null
+++ b/R/dataframe.R
@@ -0,0 +1,333 @@
+#' Build a data frame or list.
+#'
+#' \code{tibble} is a trimmed down version of \code{\link{data.frame}} that:
+#' \enumerate{
+#' \item Never coerces inputs (i.e. strings stay as strings!).
+#' \item Never adds \code{row.names}.
+#' \item Never munges column names.
+#' \item Only recycles length 1 inputs.
+#' \item Evaluates its arguments lazily and in order.
+#' \item Adds \code{tbl_df} class to output.
+#' \item Automatically adds column names.
+#' }
+#'
+#' \code{lst} is similar to \code{\link{list}}, but like \code{tibble}, it
+#' evaluates its arguments lazily and in order, and automatically adds names.
+#'
+#' \code{data_frame} is an alias to \code{tibble}.
+#'
+#' @param ... A set of name-value pairs. Arguments are evaluated sequentially,
+#'   so you can refer to previously created variables.
+#' @param xs  A list of unevaluated expressions created with \code{~},
+#'   \code{quote()}, or \code{\link[lazyeval]{lazy}}.
+#' @seealso \code{\link{as_tibble}} to turn an existing list into
+#'   a data frame.
+#' @export
+#' @examples
+#' a <- 1:5
+#' tibble(a, b = a * 2)
+#' tibble(a, b = a * 2, c = 1)
+#' tibble(x = runif(10), y = x * 2)
+#'
+#' lst(n = 5, x = runif(n))
+#'
+#' # tibble never coerces its inputs
+#' str(tibble(letters))
+#' str(tibble(x = list(diag(1), diag(2))))
+#'
+#' # or munges column names
+#' tibble(`a + b` = 1:5)
+#'
+#' # With the SE version, you give it a list of formulas/expressions
+#' tibble_(list(x = ~1:10, y = quote(x * 2)))
+#'
+#' # data frames can only contain 1d atomic vectors and lists
+#' # and can not contain POSIXlt
+#' \dontrun{
+#' tibble(x = tibble(1, 2, 3))
+#' tibble(y = strptime("2000/01/01", "%x"))
+#' }
+tibble <- function(...) {
+  as_tibble(lst(...))
+}
+
+#' @export
+#' @rdname tibble
+tibble_ <- function(xs) {
+  as_tibble(lst_(xs))
+}
+
+#' @export
+#' @rdname tibble
+data_frame <- tibble
+
+#' @export
+#' @rdname tibble
+data_frame_ <- tibble_
+
+#' @export
+#' @rdname tibble
+lst <- function(...) {
+  lst_(lazyeval::lazy_dots(...))
+}
+
+#' @export
+#' @rdname tibble
+lst_ <- function(xs) {
+  n <- length(xs)
+  if (n == 0) return(list())
+
+  # If named not supplied, used deparsed expression
+  col_names <- names2(xs)
+  missing_names <- col_names == ""
+  if (any(missing_names)) {
+    deparse2 <- function(x) paste(deparse(x$expr, 500L), collapse = "")
+    defaults <- vapply(xs[missing_names], deparse2, character(1),
+      USE.NAMES = FALSE)
+    col_names[missing_names] <- defaults
+  }
+
+  # Evaluate each column in turn
+  output <- vector("list", n)
+  names(output) <- character(n)
+
+  for (i in seq_len(n)) {
+    res <- lazyeval::lazy_eval(xs[[i]], output)
+    if (!is.null(res)) {
+      output[[i]] <-  res
+    }
+    names(output)[i] <- col_names[[i]]
+  }
+
+  output
+}
+
+
+#' Coerce lists and matrices to data frames.
+#'
+#' \code{as.data.frame} is effectively a thin wrapper around \code{data.frame},
+#' and hence is rather slow (because it calls \code{data.frame} on each element
+#' before \code{cbind}ing together). \code{as_tibble} is a new S3 generic
+#' with more efficient methods for matrices and data frames.
+#'
+#' This is an S3 generic. tibble includes methods for data frames (adds tbl_df
+#' classes), tibbles (returns unchanged input), lists, matrices, and tables.
+#' Other types are first coerced via \code{\link[base]{as.data.frame}} with
+#' \code{stringsAsFactors = FALSE}.
+#'
+#' \code{as_data_frame} is an alias.
+#'
+#' @param x A list. Each element of the list must have the same length.
+#' @param ... Other arguments passed on to individual methods.
+#' @param validate When \code{TRUE}, verifies that the input is a valid data
+#'   frame (i.e. all columns are named, and are 1d vectors or lists). You may
+#'   want to suppress this when you know that you already have a valid data
+#'   frame and you want to save some time.
+#' @export
+#' @examples
+#' l <- list(x = 1:500, y = runif(500), z = 500:1)
+#' df <- as_tibble(l)
+#'
+#' m <- matrix(rnorm(50), ncol = 5)
+#' colnames(m) <- c("a", "b", "c", "d", "e")
+#' df <- as_tibble(m)
+#'
+#' # as_tibble is considerably simpler than as.data.frame
+#' # making it more suitable for use when you have things that are
+#' # lists
+#' \dontrun{
+#' l2 <- replicate(26, sample(letters), simplify = FALSE)
+#' names(l2) <- letters
+#' microbenchmark::microbenchmark(
+#'   as_tibble(l2, validate = FALSE),
+#'   as_tibble(l2),
+#'   as.data.frame(l2)
+#' )
+#'
+#' m <- matrix(runif(26 * 100), ncol = 26)
+#' colnames(m) <- letters
+#' microbenchmark::microbenchmark(
+#'   as_tibble(m),
+#'   as.data.frame(m)
+#' )
+#' }
+as_tibble <- function(x, ...) {
+  UseMethod("as_tibble")
+}
+
+#' @export
+#' @rdname as_tibble
+as_tibble.tbl_df <- function(x, ...) {
+  x
+}
+
+#' @export
+#' @rdname as_tibble
+as_tibble.data.frame <- function(x, validate = TRUE, ...) {
+  list_to_tibble(x, validate, raw_rownames(x))
+}
+
+#' @export
+#' @rdname as_tibble
+as_tibble.list <- function(x, validate = TRUE, ...) {
+  if (length(x) == 0) {
+    list_to_tibble(repair_names(list()), validate = FALSE, .set_row_names(0L))
+  } else {
+    list_to_tibble(x, validate)
+  }
+}
+
+list_to_tibble <- function(x, validate, rownames = NULL) {
+  # this is to avoid any method dispatch that may happen when processing x
+  x <- unclass(x)
+
+  if (validate) {
+    x <- check_tibble(x)
+  }
+  x <- recycle_columns(x)
+
+  if (is.null(rownames)) {
+    rownames <- .set_row_names(NROW(x[[1L]]))
+  }
+
+  class(x) <- c("tbl_df", "tbl", "data.frame")
+  attr(x, "row.names") <- rownames
+
+  x
+}
+
+#' @export
+#' @rdname as_tibble
+as_tibble.matrix <- function(x, ...) {
+  matrixToDataFrame(x)
+}
+
+#' @export
+as_tibble.poly <- function(x, ...) {
+  as_tibble(unclass(x))
+}
+
+#' @export
+#' @param n Name for count column, default: \code{"n"}.
+#' @rdname as_tibble
+as_tibble.table <- function(x, n = "n", ...) {
+  as_tibble(as.data.frame(x, responseName = n, stringsAsFactors = FALSE))
+}
+
+#' @export
+#' @rdname as_tibble
+as_tibble.NULL <- function(x, ...) {
+  as_tibble(list())
+}
+
+#' @export
+#' @rdname as_tibble
+as_tibble.default <- function(x, ...) {
+  value <- x
+  as_tibble(as.data.frame(value, stringsAsFactors = FALSE, ...))
+}
+
+#' @export
+#' @rdname as_tibble
+as_data_frame <- function(x, ...) {
+  UseMethod("as_data_frame")
+}
+
+#' @export
+as_data_frame.tbl_df <- as_tibble.tbl_df
+
+#' @export
+as_data_frame.data.frame <- as_tibble.data.frame
+
+#' @export
+as_data_frame.list <- as_tibble.list
+
+#' @export
+as_data_frame.matrix <- as_tibble.matrix
+
+#' @export
+as_data_frame.table <- as_tibble.table
+
+#' @export
+as_data_frame.NULL <- as_tibble.NULL
+
+#' @export
+as_data_frame.default <- as_tibble.default
+
+#' Test if the object is a tibble.
+#'
+#' @param x An object
+#' @return \code{TRUE} if the object inherits from the \code{tbl_df} class.
+#' @export
+is.tibble <- function(x) {
+  "tbl_df" %in% class(x)
+}
+
+#' @rdname is.tibble
+#' @export
+is_tibble <- is.tibble
+
+
+# Validity checks --------------------------------------------------------------
+
+check_tibble <- function(x) {
+  # Names
+  names_x <- names2(x)
+  bad_name <- is.na(names_x) | names_x == ""
+  if (any(bad_name)) {
+    invalid_df("Each variable must be named", x, which(bad_name))
+  }
+
+  dups <- duplicated(names_x)
+  if (any(dups)) {
+    invalid_df("Each variable must have a unique name", x, dups)
+  }
+
+  # Types
+  is_1d <- vapply(x, is_1d, logical(1))
+  if (any(!is_1d)) {
+    invalid_df("Each variable must be a 1d atomic vector or list", x, !is_1d)
+  }
+
+  x[] <- lapply(x, strip_dim)
+
+  posixlt <- vapply(x, inherits, "POSIXlt", FUN.VALUE = logical(1))
+  if (any(posixlt)) {
+    invalid_df("Date/times must be stored as POSIXct, not POSIXlt", x, posixlt)
+  }
+
+  x
+}
+
+recycle_columns <- function(x) {
+  if (length(x) == 0) {
+    return(x)
+  }
+
+  # Validate column lengths
+  lengths <- vapply(x, NROW, integer(1))
+  max <- max(lengths)
+
+  bad_len <- lengths != 1L & lengths != max
+  if (any(bad_len)) {
+    invalid_df(paste0("Variables must be length 1 or ", max), x, bad_len)
+  }
+
+  short <- lengths == 1
+  if (max != 1L && any(short)) {
+    x[short] <- lapply(x[short], rep, max)
+  }
+
+  x
+}
+
+invalid_df <- function(problem, df, vars) {
+  if (is.logical(vars)) {
+    vars <- names(df)[vars]
+  }
+  stopc(
+    problem, ".\n",
+    "Problem variables: ", format_n(vars)
+  )
+}
+
diff --git a/R/enframe.R b/R/enframe.R
new file mode 100644
index 0000000..5872a7e
--- /dev/null
+++ b/R/enframe.R
@@ -0,0 +1,24 @@
+#' Converting atomic vectors to data frames
+#'
+#' A helper function that converts named atomic vectors or lists to two-column
+#' data frames.
+#' For unnamed vectors, the natural sequence is used as name column.
+#'
+#' @param x An atomic vector
+#' @param name,value Names of the columns that store the names and values
+#'
+#' @return A \code{\link{tibble}}
+#' @export
+#'
+#' @examples
+#' enframe(1:3)
+#' enframe(c(a = 5, b = 7))
+enframe <- function(x, name = "name", value = "value") {
+  if (is.null(names(x))) {
+    df <- tibble(seq_along(x), x)
+  } else {
+    df <- tibble(names(x), unname(x))
+  }
+  names(df) <- c(name, value)
+  df
+}
diff --git a/R/glimpse.R b/R/glimpse.R
new file mode 100644
index 0000000..82ef854
--- /dev/null
+++ b/R/glimpse.R
@@ -0,0 +1,99 @@
+#' Get a glimpse of your data.
+#'
+#' This is like a transposed version of print: columns run down the page,
+#' and data runs across. This makes it possible to see every column in
+#' a data frame. It's a little like \code{\link{str}} applied to a data frame
+#' but it tries to show you as much data as possible. (And it always shows
+#' the underlying data, even when applied to a remote data source.)
+#'
+#' @section S3 methods:
+#' \code{glimpse} is an S3 generic with a customised method for \code{tbl}s and
+#' \code{data.frames}, and a default method that calls \code{\link{str}}.
+#'
+#' @param x An object to glimpse at.
+#' @param width Width of output: defaults to the setting of the option
+#'   \code{tibble.width} (if finite) or the width of the console.
+#' @param ... Other arguments passed onto individual methods.
+#' @return x original x is (invisibly) returned, allowing \code{glimpse} to be
+#' used within a data pipe line.
+#' @export
+#' @examples
+#' glimpse(mtcars)
+#'
+#' if (!requireNamespace("nycflights13", quietly = TRUE))
+#'   stop("Please install the nycflights13 package to run the rest of this example")
+#'
+#' glimpse(nycflights13::flights)
+glimpse <- function(x, width = NULL, ...) {
+  UseMethod("glimpse")
+}
+
+#' @export
+glimpse.tbl <- function(x, width = NULL, ...) {
+  width <- tibble_glimpse_width(width)
+  stopifnot(is.finite(width))
+
+  cat("Observations: ", big_mark(nrow(x)), "\n", sep = "")
+  if (ncol(x) == 0) return(invisible())
+
+  cat("Variables: ", big_mark(ncol(x)), "\n", sep = "")
+
+  # this is an overestimate, but shouldn't be too expensive.
+  # every type needs at least three characters: "x, "
+  rows <- as.integer(width / 3)
+  df <- as.data.frame(head(x, rows))
+
+  var_types <- vapply(df, type_sum, character(1))
+  var_names <- paste0("$ ", format(names(df)), " <", var_types, "> ")
+
+  data_width <- width - nchar(var_names) - 2
+
+  formatted <- vapply(df, function(x) paste0(format_v(x), collapse = ", "),
+    character(1), USE.NAMES = FALSE)
+  truncated <- str_trunc(formatted, data_width)
+
+  cat(paste0(var_names, truncated, collapse = "\n"), "\n", sep = "")
+  invisible(x)
+}
+
+#' @export
+glimpse.data.frame <- glimpse.tbl
+
+#' @export
+#' @importFrom utils str
+glimpse.default <- function(x, width = NULL, max.level = 3, ...) {
+  str(x, width = tibble_width(width), max.level = max.level, ...)
+  invisible(x)
+}
+
+str_trunc <- function(x, max_width) {
+  width <- nchar(x)
+
+  for(i in seq_along(x)) {
+    if (width[i] <= max_width[i]) next
+
+    x[i] <- paste0(substr(x[i], 1, max_width[i] - 3), "...")
+  }
+
+  x
+}
+
+format_v <- function(x) UseMethod("format_v")
+
+#' @export
+format_v.default <- function(x) format(x, trim = TRUE, justify = "none")
+
+#' @export
+format_v.list <- function(x) {
+  x <- lapply(x, format_v)
+  atomic <- vapply(x, length, integer(1L)) == 1L
+  x <- vapply(x, function(x) paste(x, collapse = ", "), character(1L))
+  x[!atomic] <- paste0("<", x[!atomic], ">")
+  if (length(x) == 1L)
+    x
+  else
+    paste0("[", paste(x, collapse = ", "), "]")
+}
+
+#' @export
+format_v.character <- function(x) encodeString(x, quote = '"')
diff --git a/R/has-name.R b/R/has-name.R
new file mode 100644
index 0000000..60a17b4
--- /dev/null
+++ b/R/has-name.R
@@ -0,0 +1,18 @@
+#' Convenience function to check presence of a named element
+#'
+#' This function returns a logical value that indicates if a data frame or
+#' another named object contains an element with a specific name.
+#'
+#' Unnamed objects are treated as if all names are empty strings.
+#' \code{NA} input gives \code{FALSE} as output.
+#'
+#' @param x A data frame or another named object
+#' @param name Element name(s) to check
+#' @return A logical vector of the same length as \code{name}
+#' @examples
+#' has_name(iris, "Species")
+#' has_name(mtcars, "gears")
+#' @export
+has_name <- function(x, name) {
+  name %in% names2(x)
+}
diff --git a/R/repair-names.R b/R/repair-names.R
new file mode 100644
index 0000000..828ca68
--- /dev/null
+++ b/R/repair-names.R
@@ -0,0 +1,40 @@
+#' Repair object names.
+#'
+#' \code{repair_names} ensures its input has non-missing and
+#' unique names (duplicated names get a numeric suffix). Valid names are
+#' left as is.
+#'
+#' @param x A named vector.
+#' @param prefix A string, the prefix to use for new column names.
+#' @param sep A string inserted between the column name and de-duplicating
+#'    number.
+#' @return \code{x} with valid names.
+#' @export
+#' @examples
+#' repair_names(list(3, 4, 5)) # works for lists, too
+#' repair_names(mtcars) # a no-op
+#' tbl <- as_tibble(structure(list(3, 4, 5), class = "data.frame"),
+#'                  validate = FALSE)
+#' repair_names(tbl)
+repair_names <- function(x, prefix = "V", sep = "") {
+  if (length(x) == 0) {
+    names(x) <- character()
+    return(x)
+  }
+
+  new_names <- make_unique(names2(x), prefix = prefix, sep = sep)
+  setNames(x, new_names)
+}
+
+make_unique <- function(x, prefix = "V", sep = "") {
+  blank <- x == ""
+
+  # Ensure existing names are unique
+  x[!blank] <- make.unique(x[!blank], sep = sep)
+
+  # Replace blank names
+  new_vars <- setdiff(paste(prefix, seq_along(x), sep = sep), x)
+  x[blank] <- new_vars[seq_len(sum(blank))]
+
+  x
+}
diff --git a/R/rownames.R b/R/rownames.R
new file mode 100644
index 0000000..05fd7c9
--- /dev/null
+++ b/R/rownames.R
@@ -0,0 +1,94 @@
+#' Tools for working with row names
+#'
+#' While a tibble can have row names (e.g., when converting from a regular data
+#' frame), they are removed when subsetting with the \code{[} operator.
+#' A warning will be raised when attempting to assign non-\code{NULL} row names
+#' to a tibble.
+#' Generally, it is best to avoid row names, because they are basically a
+#' character column with different semantics to every other column. These
+#' functions allow to you detect if a data frame has row names
+#' (\code{has_rownames}), remove them (\code{remove_rownames}), or convert
+#' them back-and-forth between an explicit column (\code{rownames_to_column}
+#' and \code{column_to_rownames}).
+#'
+#' In the printed output, the presence of row names is indicated by a star just
+#' above the row numbers.
+#'
+#' @param df A data frame
+#' @param var Name of column to use for rownames.
+#' @examples
+#' has_rownames(mtcars)
+#' has_rownames(iris)
+#' has_rownames(remove_rownames(mtcars))
+#'
+#' head(rownames_to_column(mtcars))
+#'
+#' mtcars_tbl <- as_tibble(rownames_to_column(mtcars))
+#' mtcars_tbl
+#' column_to_rownames(as.data.frame(mtcars_tbl))
+#' @name rownames
+NULL
+
+
+#' @export
+#' @rdname rownames
+has_rownames <- function(df) {
+  .row_names_info(df) > 0L
+}
+
+#' @export
+#' @rdname rownames
+remove_rownames <- function(df) {
+  stopifnot(is.data.frame(df))
+  rownames(df) <- NULL
+  df
+}
+
+#' @export
+#' @rdname rownames
+rownames_to_column <- function(df, var = "rowname") {
+  stopifnot(is.data.frame(df))
+
+  if (has_name(df, var))
+    stopc("There is a column named ", var, " already!")
+
+  rn <- tibble(rownames(df))
+  names(rn) <- var
+
+  attribs <- attributes(df)
+
+  new_df <- c(rn, df)
+  attribs[["names"]] <- names(new_df)
+
+  attributes(new_df) <- attribs[names(attribs) != "row.names"]
+  attr(new_df, "row.names") <- .set_row_names(nrow(df))
+  new_df
+}
+
+#' @rdname rownames
+#' @export
+column_to_rownames <- function(df, var = "rowname") {
+  stopifnot(is.data.frame(df))
+
+  if (has_rownames(df))
+    stopc("This data frame already has row names.")
+
+  if (!has_name(df, var))
+    stopc("This data frame has no column named ", var, ".")
+
+  rownames(df) <- df[[var]]
+  df[[var]] <- NULL
+  df
+}
+
+#' @export
+`row.names<-.tbl_df` <- function(x, value) {
+  if (!is.null(value)) {
+    warningc("Setting row names on a tibble is deprecated.")
+  }
+  NextMethod()
+}
+
+raw_rownames <- function(x) {
+  .row_names_info(x, 0L) %||%  .set_row_names(.row_names_info(x, 2L))
+}
diff --git a/R/tbl-df.r b/R/tbl-df.r
new file mode 100644
index 0000000..62a4bf0
--- /dev/null
+++ b/R/tbl-df.r
@@ -0,0 +1,75 @@
+methods::setOldClass(c("tbl_df", "tbl", "data.frame"))
+
+# Standard data frame methods --------------------------------------------------
+
+#' @export
+as.data.frame.tbl_df <- function(x, row.names = NULL, optional = FALSE, ...) {
+  class(x) <- "data.frame"
+  x
+}
+
+#' @rdname formatting
+#' @export
+print.tbl_df <- function(x, ..., n = NULL, width = NULL, n_extra = NULL) {
+  print(trunc_mat(x, n = n, width = width, n_extra = n_extra))
+  invisible(x)
+}
+
+#' @export
+`[[.tbl_df` <- function(x, i, j, ..., exact = TRUE) {
+  if (missing(j))
+    colname <- i
+  else
+    colname <- j
+  if (!exact) {
+    warningc("exact ignored")
+  }
+
+  NextMethod()
+}
+
+#' @export
+`$.tbl_df` <- function(x, i) {
+  if (is.character(i) && !has_name(x, i)) {
+    warningc("Unknown column '", i, "'")
+  }
+  .subset2(x, i)
+}
+
+#' @export
+`[.tbl_df` <- function(x, i, j, drop = FALSE) {
+  if (drop) warningc("drop ignored")
+
+  nr <- nrow(x)
+
+  # Escape early if nargs() == 2L; ie, column subsetting
+  if (nargs() <= 2L) {
+    if (!missing(i)) {
+      i <- check_names_df(i, x)
+      result <- .subset(x, i)
+    } else {
+      result <- x
+    }
+    attr(result, "row.names") <- .set_row_names(nr)
+    return(as_tibble.data.frame(result, validate = FALSE))
+  }
+
+  # First, subset columns
+  if (!missing(j)) {
+    j <- check_names_df(j, x)
+    x <- .subset(x, j)
+  }
+
+  # Next, subset rows
+  if (!missing(i)) {
+    if (length(x) == 0) {
+      nr <- length(attr(x, "row.names")[i])
+    } else {
+      x <- lapply(x, `[`, i)
+      nr <- length(x[[1]])
+    }
+  }
+
+  attr(x, "row.names") <- .set_row_names(nr)
+  as_tibble.data.frame(x, validate = FALSE)
+}
diff --git a/R/tibble.R b/R/tibble.R
new file mode 100644
index 0000000..2071144
--- /dev/null
+++ b/R/tibble.R
@@ -0,0 +1,65 @@
+#' @useDynLib tibble
+#' @importFrom Rcpp sourceCpp
+#' @import assertthat
+#' @importFrom utils head tail
+#' @aliases NULL
+#' @details The S3 class \code{tbl_df} wraps a local data frame. The main
+#' advantage to using a \code{tbl_df} over a regular data frame is the printing:
+#' tbl objects only print a few rows and all the columns that fit on one screen,
+#' describing the rest of it as text.
+#'
+#' @section Methods:
+#'
+#' \code{tbl_df} implements four important base methods:
+#'
+#' \describe{
+#' \item{print}{By default only prints the first 10 rows (at most 20), and the
+#'   columns that fit on screen; see \code{\link{print.tbl_df}}}
+#' \item{\code{[}}{Never simplifies (drops), so always returns data.frame}
+#' \item{\code{[[}, \code{$}}{Calls \code{\link{.subset2}} directly,
+#'   so is considerably faster. Returns \code{NULL} if column does not exist,
+#'   \code{$} warns.}
+#' }
+#' @section Important functions:
+#' \code{\link{tibble}} and \code{\link{tribble}} for construction,
+#' \code{\link{as_tibble}} for coercion,
+#' and \code{\link{print.tbl_df}} and \code{\link{glimpse}} for display.
+"_PACKAGE"
+
+#' @name tibble-package
+#' @section Package options:
+#' Display options for \code{tbl_df}, used by \code{\link{trunc_mat}} and
+#' (indirectly) by \code{\link{print.tbl_df}}.
+#' \describe{
+(op.tibble <- list(
+  #' \item{\code{tibble.print_max}}{Row number threshold: Maximum number of rows
+  #'   printed. Set to \code{Inf} to always print all rows.  Default: 20.}
+  tibble.print_max = 20L,
+
+  #' \item{\code{tibble.print_min}}{Number of rows printed if row number
+  #'   threshold is exceeded. Default: 10.}
+  tibble.print_min = 10L,
+
+  #' \item{\code{tibble.width}}{Output width. Default: \code{NULL} (use
+  #'   \code{width} option).}
+  tibble.width = NULL,
+
+  #' \item{\code{tibble.max_extra_cols}}{Number of extra columns
+  #'   printed in reduced form. Default: 100.}
+  tibble.max_extra_cols = 100L
+  #' }
+))
+
+tibble_opt <- function(x) {
+  x_tibble <- paste0("tibble.", x)
+  res <- getOption(x_tibble)
+  if (!is.null(res))
+    return(res)
+
+  x_dplyr <- paste0("dplyr.", x)
+  res <- getOption(x_dplyr)
+  if (!is.null(res))
+    return(res)
+
+  op.tibble[[x_tibble]]
+}
diff --git a/R/tribble.R b/R/tribble.R
new file mode 100644
index 0000000..e3e3ea7
--- /dev/null
+++ b/R/tribble.R
@@ -0,0 +1,102 @@
+#' Row-wise tibble creation
+#'
+#' Create \code{\link{tibble}}s laying out the data in rows, rather than
+#' in columns. This is useful for small tables of data where readability is
+#' important.  Please see \link{tibble-package} for a general introduction.
+#'
+#' \code{frame_data()} is an older name for \code{tribble()}. It will eventually
+#' be phased out.
+#'
+#' @param ... Arguments specifying the structure of a \code{tibble}.
+#'   Variable names should be formulas, and may only appear before the data.
+#' @return A \code{\link{tibble}}.
+#' @export
+#' @examples
+#' tribble(
+#'   ~colA, ~colB,
+#'   "a",   1,
+#'   "b",   2,
+#'   "c",   3
+#' )
+#'
+#' # tribble will create a list column if the value in any cell is
+#' # not a scalar
+#' tribble(
+#'   ~x,  ~y,
+#'   "a", 1:3,
+#'   "b", 4:6
+#' )
+tribble <- function(...) {
+
+  dots <- list(...)
+
+  # Extract the names.
+  frame_names <- character()
+  i <- 1
+  while (TRUE) {
+    if (i > length(dots)) {
+      out <- rep(list(logical()), length(frame_names))
+      names(out) <- frame_names
+      return(as_tibble(out))
+    }
+
+    el <- dots[[i]]
+    if (!is.call(el))
+      break
+
+    if (!identical(el[[1]], as.name("~")))
+      break
+
+    if (length(el) != 2) {
+      stopc("expected a column name with a single argument; e.g. '~ name'")
+    }
+
+    candidate <- el[[2]]
+    if (!(is.symbol(candidate) || is.character(candidate))) {
+      stopc("expected a symbol or string denoting a column name")
+    }
+
+    frame_names <- c(frame_names, as.character(el[[2]]))
+
+    i <- i + 1
+  }
+
+  if (!length(frame_names)) {
+    stopc("no column names detected in 'tribble()' call")
+  }
+
+  frame_rest <- dots[i:length(dots)]
+  n_elements <- length(frame_rest)
+
+  # Figure out the associated number of rows and number of columns,
+  # and validate that the supplied formula produces a rectangular
+  # structure.
+  frame_ncol <- length(frame_names)
+  if (n_elements %% frame_ncol != 0) {
+    stopc(
+      sprintf(
+        "invalid 'tribble()' specification: had %s elements and %s columns",
+        n_elements,
+        frame_ncol
+      )
+    )
+  }
+
+  frame_mat <- matrix(frame_rest, ncol = frame_ncol, byrow = TRUE)
+  frame_col <- lapply(seq_len(ncol(frame_mat)), function(i) {
+    col <- frame_mat[, i]
+    if (any(vapply(col, needs_list_col, logical(1L)))) {
+      col
+    } else {
+      unlist(col)
+    }
+  })
+
+  # Create a tbl_df and return it
+  names(frame_col) <- frame_names
+  as_tibble(frame_col)
+}
+
+#' @export
+#' @rdname tribble
+frame_data <- tribble
diff --git a/R/type-sum.r b/R/type-sum.r
new file mode 100644
index 0000000..7016bdb
--- /dev/null
+++ b/R/type-sum.r
@@ -0,0 +1,134 @@
+#' Provide a succinct summary of an object
+#'
+#' @description
+#' \code{type_sum} gives a brief summary of object type. Objects that commonly
+#' occur in a data frame should return a string with four or less characters.
+#'
+#' \code{obj_sum} also includes the size of the object if \code{is_s3_vector}
+#' is \code{TRUE}.
+#'
+#' \code{tbl_sum} gives a brief textual description of a table-like object,
+#' which should include the dimensions, the data source, and possible grouping
+#' (for dplyr).  The default implementation forwards to \code{obj_sum}
+#'
+#' @param x an object to summarise. Generally only methods of atomic vectors
+#'   and variants have been implemented.
+#' @keywords internal
+#' @examples
+#' obj_sum(1:10)
+#' obj_sum(matrix(1:10))
+#' obj_sum(Sys.Date())
+#' obj_sum(Sys.time())
+#' obj_sum(mean)
+#' @export
+obj_sum <- function(x) UseMethod("obj_sum")
+
+#' @export
+obj_sum.default <- function(x) {
+  paste0(type_sum(x), size_sum(x))
+}
+
+#' @export
+obj_sum.list <- function(x) {
+  vapply(x, obj_sum.default, character(1L))
+}
+
+#' @export
+obj_sum.POSIXlt <- function(x) {
+  rep("POSIXlt", length(x))
+}
+
+#' @export
+#' @rdname obj_sum
+type_sum <- function(x) UseMethod("type_sum")
+
+#' @export
+type_sum.ordered <- function(x) "ord"
+#' @export
+type_sum.factor <- function(x) "fctr"
+#' @export
+type_sum.POSIXt <- function(x) "dttm"
+#' @export
+type_sum.difftime <- function(x) "time"
+#' @export
+type_sum.Date <- function(x) "date"
+#' @export
+type_sum.data.frame <- function(x) class(x)[[1]]
+#' @export
+type_sum.tbl_df <- function(x) "tibble"
+#' @export
+type_sum.default <- function(x) {
+  if (!is.object(x)) {
+    switch(typeof(x),
+      logical = "lgl",
+      integer = "int",
+      double = "dbl",
+      character = "chr",
+      complex = "cplx",
+      closure = "fun",
+      environment = "env",
+      typeof(x)
+    )
+  } else if (!isS4(x)) {
+    paste0("S3: ", class(x)[[1]])
+  } else {
+    paste0("S4: ", methods::is(x)[[1]])
+  }
+}
+
+#' @rdname obj_sum
+#' @export
+tbl_sum <- function(x) UseMethod("tbl_sum", x)
+
+#' @export
+tbl_sum.default <- function(x) obj_sum(x)
+
+#' @export
+tbl_sum.tbl_df <- function(x) {
+  paste0("A tibble: ", dim_desc(x))
+}
+
+# FIXME: This belongs in dplyr, but can only be added there once tibble has been
+# updated.
+# nocov start
+#' @export
+tbl_sum.grouped_df <- function(x) {
+  NULL
+}
+
+#' @export
+tbl_sum.tbl_sql <- function(x) {
+  NULL
+}
+# nocov end
+
+dim_desc <- function(x) {
+  dim <- dim(x) %||% length(x)
+  format_dim <- vapply(dim, big_mark, character(1))
+  format_dim[is.na(dim)] <- "??"
+  paste0(format_dim, collapse = " \u00d7 ")
+}
+
+size_sum <- function(x) {
+  if (!is_vector_s3(x)) return("")
+
+  paste0(" [", dim_desc(x), "]" )
+}
+
+#' @export
+#' @rdname obj_sum
+is_vector_s3 <- function(x) UseMethod("is_vector_s3")
+#' @export
+is_vector_s3.ordered <- function(x) TRUE
+#' @export
+is_vector_s3.factor <- function(x) TRUE
+#' @export
+is_vector_s3.Date <- function(x) TRUE
+#' @export
+is_vector_s3.POSIXct <- function(x) TRUE
+#' @export
+is_vector_s3.difftime <- function(x) TRUE
+#' @export
+is_vector_s3.data.frame <- function(x) TRUE
+#' @export
+is_vector_s3.default <- function(x) !is.object(x) && is_vector(x)
diff --git a/R/utils-format.r b/R/utils-format.r
new file mode 100644
index 0000000..2360ea5
--- /dev/null
+++ b/R/utils-format.r
@@ -0,0 +1,288 @@
+#' Tools for describing matrices
+#'
+#' @param x Object to show.
+#' @param n Number of rows to show. If \code{NULL}, the default, will print
+#'   all rows if less than option \code{tibble.print_max}. Otherwise, will
+#'   print \code{tibble.print_min} rows.
+#' @param width Width of text output to generate. This defaults to NULL, which
+#'   means use \code{getOption("tibble.width")} or (if also NULL)
+#'   \code{getOption("width")}; the latter displays only the columns that
+#'   fit on one screen. You can also set \code{options(tibble.width = Inf)} to
+#'   override this default and always print all columns.
+#' @param n_extra Number of extra columns to print abbreviated information for,
+#'   if the width is too small for the entire tibble. If \code{NULL}, the
+#'   default, will print information about at most \code{tibble.max_extra_cols}
+#'   extra columns.
+#' @seealso \link{tibble-package}
+#' @keywords internal
+#' @examples
+#' trunc_mat(mtcars)
+#'
+#' print(as_tibble(mtcars))
+#' print(as_tibble(mtcars), n = 1)
+#' print(as_tibble(mtcars), n = 3)
+#' print(as_tibble(mtcars), n = 100)
+#'
+#' if (!requireNamespace("nycflights13", quietly = TRUE))
+#'   stop("Please install the nycflights13 package to run the rest of this example")
+#'
+#' print(nycflights13::flights, n_extra = 2)
+#' print(nycflights13::flights, width = Inf)
+#' @name formatting
+NULL
+
+#' @export
+#' @rdname formatting
+#' @importFrom stats setNames
+trunc_mat <- function(x, n = NULL, width = NULL, n_extra = NULL) {
+  rows <- nrow(x)
+
+  if (is.null(n)) {
+    if (is.na(rows) || rows > tibble_opt("print_max")) {
+      n <- tibble_opt("print_min")
+    } else {
+      n <- rows
+    }
+  }
+  n_extra <- n_extra %||% tibble_opt("max_extra_cols")
+
+  df <- as.data.frame(head(x, n))
+  width <- tibble_width(width)
+
+  shrunk <- shrink_mat(df, width, rows, n, star = has_rownames(x))
+  trunc_info <- list(width = width, rows_total = rows, rows_min = nrow(df),
+                     n_extra = n_extra, summary = tbl_sum(x))
+
+  structure(c(shrunk, trunc_info), class = "trunc_mat")
+}
+
+#' @importFrom stats setNames
+shrink_mat <- function(df, width, rows, n, star) {
+  var_types <- vapply(df, type_sum, character(1))
+
+  if (ncol(df) == 0 || nrow(df) == 0) {
+    return(new_shrunk_mat(NULL, var_types))
+  }
+
+  df <- remove_rownames(df)
+  col_names <- tickit(colnames(df))
+  names(var_types) <- col_names
+
+  # Minimum width of each column is 5 "<int>", so we can make a quick first
+  # pass
+  max_cols <- floor(width / 5)
+  extra_wide <- (seq_along(df) > max_cols)
+  df[] <- df[!extra_wide]
+
+  # List columns need special treatment because format can't be trusted
+  classes <- paste0("<", vapply(df, type_sum, character(1)), ">")
+  is_list <- vapply(df, is.list, logical(1))
+  df[is_list] <- lapply(df[is_list], function(x) {
+    summary <- obj_sum(x)
+    paste0("<", summary, ">")
+  })
+
+  # Character columns need special treatment because of NA
+  is_character <- vapply(df, is.character, logical(1))
+  df[is_character] <- lapply(df[is_character], format_character)
+
+  mat <- format(df, justify = "left")
+  values <- c(format(rownames(mat))[[1]], unlist(mat[1, ]))
+  names <- c("", col_names)
+
+  # Column needs to be as wide as widest of name, values, and class
+  w <- pmax(
+    pmax(
+      nchar_width(encodeString(values)),
+      nchar_width(encodeString(names))
+    ),
+    nchar_width(encodeString(c("", classes)))
+  )
+  cumw <- cumsum(w + 1)
+
+  too_wide <- cumw[-1] > width
+  # Always display at least one column
+  if (all(too_wide)) {
+    too_wide[1] <- FALSE
+    df[[1]] <- substr(df[[1]], 1, width)
+  }
+  shrunk <- format(df[, !too_wide, drop = FALSE])
+
+  shrunk <- rbind(" " = classes, shrunk)
+  if (star)
+    rownames(shrunk)[[1]] <- "*"
+  colnames(shrunk) <- col_names[!too_wide]
+
+  if (is.na(rows))
+    needs_dots <- (nrow(df) >= n)
+  else
+    needs_dots <- (rows > n)
+
+  if (needs_dots) {
+    rows_missing <- rows - n
+  } else {
+    rows_missing <- 0L
+  }
+
+  extra_wide[seq_along(too_wide)] <- too_wide
+  new_shrunk_mat(shrunk, var_types[extra_wide], rows_missing)
+}
+
+new_shrunk_mat <- function(table, extra, rows_missing = NULL) {
+  list(table = table, extra = extra, rows_missing = rows_missing)
+}
+
+#' @export
+print.trunc_mat <- function(x, ...) {
+  print_summary(x)
+  print_table(x)
+  print_extra(x)
+  invisible(x)
+}
+
+print_summary <- function(x) {
+  summary <- format_summary(x)
+  if (length(summary) > 0) {
+    print_comment(summary, width = x$width)
+  }
+}
+
+print_table <- function(x) {
+  if (!is.null(x$table)) {
+    print(x$table)
+  }
+}
+
+print_extra <- function(x) {
+  extra <- format_extra(x)
+  if (length(extra) > 0) {
+    print_comment("... ", collapse(extra), width = x$width)
+  }
+}
+
+print_comment <- function(..., width) {
+  cat_line(wrap(..., prefix = "# ", width = min(width, getOption("width"))))
+}
+
+format_summary <- function(x) {
+  x$summary
+}
+
+format_extra <- function(x) {
+  extra_rows <- format_extra_rows(x)
+  extra_cols <- format_extra_cols(x)
+
+  extra <- c(extra_rows, extra_cols)
+  if (length(extra) >= 1) {
+    extra[[1]] <- paste0("with ", extra[[1]])
+    extra[-1] <- vapply(extra[-1], function(ex) paste0("and ", ex), character(1))
+  }
+  extra
+}
+
+format_extra_rows <- function(x) {
+  if (!is.null(x$table)) {
+    if (is.na(x$rows_missing)) {
+      "more rows"
+    } else if (x$rows_missing > 0) {
+      paste0(big_mark(x$rows_missing), " more rows")
+    }
+  } else if (is.na(x$rows_total) && x$rows_min > 0) {
+    paste0("at least ", x$rows_min, " rows total")
+  }
+}
+
+format_extra_cols <- function(x) {
+  if (length(x$extra) > 0) {
+    var_types <- paste0(names(x$extra), NBSP, "<", x$extra, ">")
+    if (x$n_extra > 0) {
+      if (x$n_extra < length(var_types)) {
+        var_types <- c(var_types[seq_len(x$n_extra)], "...")
+      }
+      vars <- paste0(": ", collapse(var_types))
+    } else {
+      vars <- ""
+    }
+    paste0(length(x$extra), " ",
+           if (!identical(x$rows_total, 0L) && x$rows_min > 0) "more ",
+           "variables", vars)
+  }
+}
+
+#' knit_print method for trunc mat
+#' @keywords internal
+#' @export
+knit_print.trunc_mat <- function(x, options) {
+  summary <- format_summary(x)
+
+  kable <- knitr::kable(x$table, row.names = FALSE)
+
+  extra <- format_extra(x)
+
+  if (length(extra) > 0) {
+    extra <- wrap("(", collapse(extra), ")", width = x$width)
+  } else {
+    extra <- "\n"
+  }
+
+  res <- paste(c('', '', summary, '', kable, '', extra), collapse = '\n')
+  knitr::asis_output(res, cacheable = TRUE)
+}
+
+NBSP <- "\U00A0"
+
+wrap <- function(..., indent = 0, prefix = "", width) {
+  x <- paste0(..., collapse = "")
+  wrapped <- strwrap(x, indent = indent, exdent = indent + 2,
+    width = max(width - nchar_width(prefix), 0))
+  wrapped <- paste0(prefix, wrapped)
+  wrapped <- gsub(NBSP, " ", wrapped)
+
+  paste0(wrapped, collapse = "\n")
+}
+
+
+
+format_character <- function(x) {
+  x[is.na(x)] <- "<NA>"
+  x
+}
+
+# function for the thousand separator,
+# returns "," unless it's used for the decimal point, in which case returns "."
+big_mark <- function(x, ...) {
+  mark <- if (identical(getOption("OutDec"), ",")) "." else ","
+  formatC(x, big.mark = mark, ...)
+}
+
+tibble_width <- function(width) {
+  if (!is.null(width))
+    return(width)
+
+  width <- tibble_opt("width")
+  if (!is.null(width))
+    return(width)
+
+  getOption("width")
+}
+
+tibble_glimpse_width <- function(width) {
+  if (!is.null(width))
+    return(width)
+
+  width <- tibble_opt("width")
+  if (!is.null(width) && is.finite(width))
+    return(width)
+
+  getOption("width")
+}
+
+format_n <- function(x) collapse(quote_n(x))
+
+quote_n <- function(x) UseMethod("quote_n")
+#' @export
+quote_n.default <- function(x) as.character(x)
+#' @export
+quote_n.character <- function(x) encodeString(x, quote = "'")
+
+collapse <- function(x) paste(x, collapse = ", ")
diff --git a/R/utils.r b/R/utils.r
new file mode 100644
index 0000000..d5589d3
--- /dev/null
+++ b/R/utils.r
@@ -0,0 +1,86 @@
+names2 <- function(x) {
+  xnames <- names(x)
+  if (is.null(xnames)) {
+    rep("", length(x))
+  } else {
+    ifelse(is.na(xnames), "", xnames)
+  }
+}
+
+"%||%" <- function(x, y) {
+  if(is.null(x)) y else x
+}
+
+# is.atomic() is TRUE for atomic vectors AND NULL!
+is_atomic <- function(x) {
+  is.atomic(x) && !is.null(x)
+}
+
+is_vector <- function(x) {
+  is_atomic(x) || is.list(x)
+}
+
+has_names <- function(x) {
+  !is.null(names(x))
+}
+
+has_dim <- function(x) {
+  length(dim(x)) > 0L || has_names(x)
+}
+
+needs_dim <- function(x) {
+  length(dim(x)) > 1L
+}
+
+set_class <- `class<-`
+
+is_1d <- function(x) {
+  # dimension check is for matrices and data.frames
+  is_vector(x) && !needs_dim(x)
+}
+
+strip_dim <- function(x) {
+  # Careful update only if necessary, to avoid copying which is checked by
+  # the "copying" test in dplyr
+  if (is_atomic(x) && has_dim(x))
+    dim(x) <- NULL
+  x
+}
+
+needs_list_col <- function(x) {
+  is.list(x) || length(x) != 1L
+}
+
+# Work around bug in R 3.3.0
+safe_match <- function(x, table) {
+  # nocov start
+  if (getRversion() == "3.3.0")
+    match(x, table, incomparables = character())
+  else
+    match(x, table)
+  # nocov end
+}
+
+stopc <- function(...) {
+  stop(..., call. = FALSE, domain = NA)
+}
+
+warningc <- function(...) {
+  warning(..., call. = FALSE, domain = NA)
+}
+
+nchar_width <- function(x) {
+  nchar(x, type = "width")
+}
+
+cat_line <- function(...) {
+  cat(..., "\n", sep = "")
+}
+
+is_syntactic <- function(x) make.names(x) == x
+
+tickit <- function(x) {
+  needs_ticks <- !is_syntactic(x)
+  x[needs_ticks] <- paste0("`", gsub("`", "\\\\`", x[needs_ticks]), "`")
+  x
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..551489b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,136 @@
+
+<!-- README.md is generated from README.Rmd. Please edit that file -->
+tibble
+======
+
+[![Build Status](https://travis-ci.org/hadley/tibble.svg?branch=master)](https://travis-ci.org/hadley/tibble) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/hadley/tibble?branch=master&svg=true)](https://ci.appveyor.com/project/hadley/tibble) [![Coverage Status](https://img.shields.io/codecov/c/github/hadley/tibble/master.svg)](https://codecov.io/github/hadley/tibble?branch=master) [![CRAN\_Status\_Badge](http://www.r-pkg.org/badges/version/tibble)](https:// [...]
+
+tibble implements a modern reimagining of the data.frame, keeping what time has proven to be effective, and throwing out what is not. It extracts these basic ideas out of [dplyr](https://cran.r-project.org/package=dplyr), which is now more clearly focused on data manipulation. tibble provides a lighter-weight package for the basic care and feeding of `tbl_df`'s, aka "tibble diffs" or just "tibbles". Tibbles are data.frames with nicer behavior around printing, subsetting, and factor handling.
+
+Creating tibbles
+----------------
+
+You can create a tibble from an existing object with `as_tibble()`:
+
+``` r
+library(tibble)
+as_tibble(iris)
+#> # A tibble: 150 × 5
+#>    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
+#>           <dbl>       <dbl>        <dbl>       <dbl>  <fctr>
+#> 1           5.1         3.5          1.4         0.2  setosa
+#> 2           4.9         3.0          1.4         0.2  setosa
+#> 3           4.7         3.2          1.3         0.2  setosa
+#> 4           4.6         3.1          1.5         0.2  setosa
+#> 5           5.0         3.6          1.4         0.2  setosa
+#> 6           5.4         3.9          1.7         0.4  setosa
+#> 7           4.6         3.4          1.4         0.3  setosa
+#> 8           5.0         3.4          1.5         0.2  setosa
+#> 9           4.4         2.9          1.4         0.2  setosa
+#> 10          4.9         3.1          1.5         0.1  setosa
+#> # ... with 140 more rows
+```
+
+This will work for reasonable inputs that are already data.frame, list, matrix, or table.
+
+You can also create a new tibble from vectors that represent the columns with `tibble()`:
+
+``` r
+tibble(x = 1:5, y = 1, z = x ^ 2 + y)
+#> # A tibble: 5 × 3
+#>       x     y     z
+#>   <int> <dbl> <dbl>
+#> 1     1     1     2
+#> 2     2     1     5
+#> 3     3     1    10
+#> 4     4     1    17
+#> 5     5     1    26
+```
+
+`tibble()` does much less than `data.frame()`: it never changes the type of the inputs (e.g. it never converts strings to factors!), it never changes the names of variables, and it never creates `row.names()`. You can read more about these features in the vignette, `vignette("tibble")`.
+
+You can define a tibble row-by-row with `tribble()`:
+
+``` r
+tribble(
+  ~x, ~y,  ~z,
+  "a", 2,  3.6,
+  "b", 1,  8.5
+)
+#> # A tibble: 2 × 3
+#>       x     y     z
+#>   <chr> <dbl> <dbl>
+#> 1     a     2   3.6
+#> 2     b     1   8.5
+```
+
+You can see why this variant of the data.frame is called a "tibble diff" from its class:
+
+``` r
+class(as_tibble(iris))
+#> [1] "tbl_df"     "tbl"        "data.frame"
+```
+
+Tibbles vs data frames
+----------------------
+
+There are two main differences in the usage of a data frame vs a tibble: printing, and subsetting.
+
+Tibbles have a refined print method that shows only the first 10 rows, and all the columns that fit on screen. This makes it much easier to work with large data. In addition to its name, each column reports its type, a nice feature borrowed from `str()`:
+
+``` r
+library(nycflights13)
+flights
+#> # A tibble: 336,776 × 19
+#>     year month   day dep_time sched_dep_time dep_delay arr_time
+#>    <int> <int> <int>    <int>          <int>     <dbl>    <int>
+#> 1   2013     1     1      517            515         2      830
+#> 2   2013     1     1      533            529         4      850
+#> 3   2013     1     1      542            540         2      923
+#> 4   2013     1     1      544            545        -1     1004
+#> 5   2013     1     1      554            600        -6      812
+#> 6   2013     1     1      554            558        -4      740
+#> 7   2013     1     1      555            600        -5      913
+#> 8   2013     1     1      557            600        -3      709
+#> 9   2013     1     1      557            600        -3      838
+#> 10  2013     1     1      558            600        -2      753
+#> # ... with 336,766 more rows, and 12 more variables: sched_arr_time <int>,
+#> #   arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
+#> #   origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
+#> #   minute <dbl>, time_hour <dttm>
+```
+
+Tibbles are strict about subsetting. If you try to access a variable that does not exist via `$`, you'll get a warning:
+
+``` r
+flights$yea
+#> Warning: Unknown column 'yea'
+#> NULL
+```
+
+Tibbles also clearly delineate `[` and `[[`: `[` always returns another tibble, `[[` always returns a vector. No more `drop = FALSE`!
+
+``` r
+class(iris[ , 1])
+#> [1] "numeric"
+class(iris[ , 1, drop = FALSE])
+#> [1] "data.frame"
+class(as_tibble(iris)[ , 1])
+#> [1] "tbl_df"     "tbl"        "data.frame"
+```
+
+Installation
+------------
+
+tibble is on CRAN, install using:
+
+``` r
+install.packages("tibble")
+```
+
+You can try out the dev version with:
+
+``` r
+# install.packages("devtools")
+devtools::install_github("hadley/tibble")
+```
diff --git a/build/vignette.rds b/build/vignette.rds
new file mode 100644
index 0000000..6306fda
Binary files /dev/null and b/build/vignette.rds differ
diff --git a/debian/README.test b/debian/README.test
deleted file mode 100644
index 90657cf..0000000
--- a/debian/README.test
+++ /dev/null
@@ -1,8 +0,0 @@
-Notes on how this package can be tested.
-────────────────────────────────────────
-
-This package can be tested by running the provided test:
-
-    sh run-unit-test
-
-in order to confirm its integrity.
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index 7577dac..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,17 +0,0 @@
-r-cran-tibble (1.2-1) unstable; urgency=medium
-
-  * New upstream version
-  * Fixed watch file
-  * Adjusted Test depends (thanks to Gordon Ball)
-  * Convert to dh-r
-  * Canonical homepage for CRAN
-  * d/watch: version=4
-  * fix autopkgtest
-
- -- Andreas Tille <tille at debian.org>  Thu, 10 Nov 2016 11:03:33 +0100
-
-r-cran-tibble (1.1-1) unstable; urgency=medium
-
-  * Initial release (Closes: #832005)
-
- -- Andreas Tille <tille at debian.org>  Thu, 21 Jul 2016 11:22:50 +0200
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index ec63514..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-9
diff --git a/debian/control b/debian/control
deleted file mode 100644
index e4e6dad..0000000
--- a/debian/control
+++ /dev/null
@@ -1,26 +0,0 @@
-Source: r-cran-tibble
-Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Andreas Tille <tille at debian.org>
-Section: gnu-r
-Priority: optional
-Build-Depends: debhelper (>= 9),
-               dh-r,
-               r-base-dev,
-               r-cran-assertthat,
-               r-cran-lazyeval,
-               r-cran-rcpp
-Standards-Version: 3.9.8
-Vcs-Browser: https://anonscm.debian.org/viewvc/debian-med/trunk/packages/R/r-cran-tibble/trunk/
-Vcs-Svn: svn://anonscm.debian.org/debian-med/trunk/packages/R/r-cran-tibble/trunk/
-Homepage: https://cran.r-project.org/package=tibble
-
-Package: r-cran-tibble
-Architecture: any
-Depends: ${misc:Depends},
-         ${shlibs:Depends},
-         ${R:Depends}
-Recommends: ${R:Recommends}
-Suggests: ${R:Suggests}
-Description: GNU R Simple Data Frames
- This GNU R package provides a 'tbl_df' class that offers better checking
- and printing capabilities than traditional data frames.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index addb810..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,32 +0,0 @@
-Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Contact: Kirill Müller <krlmlr+r at mailbox.org>
-Upstream-Name: tibble
-Source: https://cran.r-project.org/package=tibble
-
-Files: *
-Copyright: 2013-2016 Hadley Wickham, Romain Francois, Kirill Müller, RStudio 
-License: MIT
-
-Files: debian/*
-Copyright: 2016 Andreas Tille <tille at debian.org>
-License: MIT
-
-License: MIT
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
- .
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
- .
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/debian/docs b/debian/docs
deleted file mode 100644
index 6926e20..0000000
--- a/debian/docs
+++ /dev/null
@@ -1,4 +0,0 @@
-tests
-debian/README.test
-debian/tests/run-unit-test
-README.md
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index ae86733..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/make -f
-
-%:
-	dh $@ --buildsystem R
-
-override_dh_install:
-	dh_install
-	find debian -name LICENSE -delete
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 163aaf8..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/tests/control b/debian/tests/control
deleted file mode 100644
index 03ee351..0000000
--- a/debian/tests/control
+++ /dev/null
@@ -1,3 +0,0 @@
-Tests: run-unit-test
-Depends: @, r-cran-testthat, r-cran-data.table, r-cran-rsqlite, r-cran-knitr
-Restrictions: allow-stderr
diff --git a/debian/tests/run-unit-test b/debian/tests/run-unit-test
deleted file mode 100644
index 9d87e05..0000000
--- a/debian/tests/run-unit-test
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/sh -e
-
-oname=tibble
-pkg=r-cran-`echo $oname | tr [A-Z] [a-z]`
-
-if [ "$ADTTMP" = "" ] ; then
-  ADTTMP=`mktemp -d /tmp/${pkg}-test.XXXXXX`
-fi
-cd $ADTTMP
-cp -a /usr/share/doc/${pkg}/tests/* $ADTTMP
-find . -name "*.gz" -exec gunzip \{\} \;
-# There is no package r-cran-withr available yet
-for testfile in testthat/* ; do
-    if grep -q withr $testfile ; then
-        rm $testfile
-    fi
-done
-sed -i '/withr/d' testthat.R
-LC_ALL=C.UTF-8 R --no-save < testthat.R
-rm -fr $ADTTMP/*
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index f782ab4..0000000
--- a/debian/watch
+++ /dev/null
@@ -1,3 +0,0 @@
-version=4
-http://cran.r-project.org/src/contrib/tibble_([-0-9\.]*)\.tar\.(?:gz|bz2|xz)
-
diff --git a/inst/doc/formatting.R b/inst/doc/formatting.R
new file mode 100644
index 0000000..7f51f82
--- /dev/null
+++ b/inst/doc/formatting.R
@@ -0,0 +1,53 @@
+## ----setup, include = FALSE----------------------------------------------
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+options(tibble.print_min = 4L, tibble.print_max = 4L)
+library(tibble)
+
+## ---- eval = FALSE-------------------------------------------------------
+#  devtools::use_package("tibble")
+
+## ------------------------------------------------------------------------
+#' @export
+foo <- function(x) {
+  stopifnot(is.numeric(x))
+  structure(x, class = "foo")
+}
+
+## ------------------------------------------------------------------------
+type_sum(1)
+type_sum(1:10)
+type_sum(Sys.time())
+
+## ------------------------------------------------------------------------
+type_sum(foo(1:10))
+
+## ------------------------------------------------------------------------
+#' @export
+type_sum.foo <- function(x, ...) {
+  "foo"
+}
+
+type_sum(foo(1:10))
+
+## ------------------------------------------------------------------------
+obj_sum(1)
+obj_sum(1:10)
+obj_sum(Sys.time())
+obj_sum(list(1:5))
+obj_sum(list("a", "b", "c"))
+
+## ------------------------------------------------------------------------
+x <- as.POSIXlt(Sys.time() + c(0, 60, 3600)) 
+str(unclass(x))
+
+## ------------------------------------------------------------------------
+x
+length(x)
+str(x)
+
+## ------------------------------------------------------------------------
+#' @export
+obj_sum.POSIXlt <- function(x) {
+  rep("POSIXlt", length(x))
+}
+
diff --git a/inst/doc/formatting.Rmd b/inst/doc/formatting.Rmd
new file mode 100644
index 0000000..d74bc9c
--- /dev/null
+++ b/inst/doc/formatting.Rmd
@@ -0,0 +1,106 @@
+---
+title: "Formatting of column data"
+author: "Kirill Müller, Hadley Wickham"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+  %\VignetteIndexEntry{Formatting of column data}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\VignetteEncoding{UTF-8}
+---
+
+```{r setup, include = FALSE}
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+options(tibble.print_min = 4L, tibble.print_max = 4L)
+library(tibble)
+```
+
+The presentation of a column in a tibble is powered by two S3 generics:
+
+* `type_sum()` determines what goes into the column header.
+* `obj_sum()` is used when rendering list columns.
+
+If you have written an S3 or S4 class that can be used as a column, you can override these generics to make sure your data prints well in a tibble. To start, you must import the `tibble` package. Either add `tibble` to the `Imports:` section of your `DESCRIPTION`, or simply call:
+
+```{r, eval = FALSE}
+devtools::use_package("tibble")
+```
+
+This vignette assumes a package that implements an S3 class `"foo"` and uses
+`roxygen2` to create documentation and the `NAMESPACE` file:
+
+```{r}
+#' @export
+foo <- function(x) {
+  stopifnot(is.numeric(x))
+  structure(x, class = "foo")
+}
+```
+
+## `type_sum()`
+
+This method should return a length-1 character vector that can be used in a column header. Strive for an evocative abbreviation that's under 6 characters. 
+
+```{r}
+type_sum(1)
+type_sum(1:10)
+type_sum(Sys.time())
+```
+
+The default implementation works reasonably well for any kind of object,
+but the generated output may be too wide and waste precious space when displaying the tibble:
+
+```{r}
+type_sum(foo(1:10))
+```
+
+To avoid this for provide a method for `type_sum()`:
+
+```{r}
+#' @export
+type_sum.foo <- function(x, ...) {
+  "foo"
+}
+
+type_sum(foo(1:10))
+```
+
+## `obj_sum()`
+
+This method is primarily used for displaying list columns. A list column is a powerful way to attach hierarchical or unstructured data to an observation in a data frame. Implementations of `obj_sum()` are expected to return a character vector as long as the input, with brief description of the contents of each input element.
+
+Examples:
+
+```{r}
+obj_sum(1)
+obj_sum(1:10)
+obj_sum(Sys.time())
+obj_sum(list(1:5))
+obj_sum(list("a", "b", "c"))
+```
+
+The default implementation calls `type_sum()` and appends the size of the object in brackets. If your object is built on top of an atomic vector the default will be adequate. You, will, however, need to provide a method if your object is vectorised and built on top of a list.
+
+An example of an object of this type in base R `POSIXlt`: it is a list with 9 components.
+
+```{r}
+x <- as.POSIXlt(Sys.time() + c(0, 60, 3600)) 
+str(unclass(x))
+```
+
+But it pretends to be a vector with 3 elements:
+
+```{r}
+x
+length(x)
+str(x)
+```
+
+So we need to define a method that returns a character vector the same length as `x`:
+
+```{r}
+#' @export
+obj_sum.POSIXlt <- function(x) {
+  rep("POSIXlt", length(x))
+}
+```
diff --git a/inst/doc/formatting.html b/inst/doc/formatting.html
new file mode 100644
index 0000000..6938143
--- /dev/null
+++ b/inst/doc/formatting.html
@@ -0,0 +1,170 @@
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+
+<meta charset="utf-8">
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="pandoc" />
+
+<meta name="viewport" content="width=device-width, initial-scale=1">
+
+<meta name="author" content="Kirill Müller, Hadley Wickham" />
+
+<meta name="date" content="2016-08-26" />
+
+<title>Formatting of column data</title>
+
+
+
+<style type="text/css">code{white-space: pre;}</style>
+<style type="text/css">
+div.sourceCode { overflow-x: auto; }
+table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
+  margin: 0; padding: 0; vertical-align: baseline; border: none; }
+table.sourceCode { width: 100%; line-height: 100%; }
+td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
+td.sourceCode { padding-left: 5px; }
+code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
+code > span.dt { color: #902000; } /* DataType */
+code > span.dv { color: #40a070; } /* DecVal */
+code > span.bn { color: #40a070; } /* BaseN */
+code > span.fl { color: #40a070; } /* Float */
+code > span.ch { color: #4070a0; } /* Char */
+code > span.st { color: #4070a0; } /* String */
+code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
+code > span.ot { color: #007020; } /* Other */
+code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
+code > span.fu { color: #06287e; } /* Function */
+code > span.er { color: #ff0000; font-weight: bold; } /* Error */
+code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
+code > span.cn { color: #880000; } /* Constant */
+code > span.sc { color: #4070a0; } /* SpecialChar */
+code > span.vs { color: #4070a0; } /* VerbatimString */
+code > span.ss { color: #bb6688; } /* SpecialString */
+code > span.im { } /* Import */
+code > span.va { color: #19177c; } /* Variable */
+code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
+code > span.op { color: #666666; } /* Operator */
+code > span.bu { } /* BuiltIn */
+code > span.ex { } /* Extension */
+code > span.pp { color: #bc7a00; } /* Preprocessor */
+code > span.at { color: #7d9029; } /* Attribute */
+code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
+code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
+code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
+code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
+</style>
+
+
+
+<link href="data:text/css;charset=utf-8,body%20%7B%0Abackground%2Dcolor%3A%20%23fff%3B%0Amargin%3A%201em%20auto%3B%0Amax%2Dwidth%3A%20700px%3B%0Aoverflow%3A%20visible%3B%0Apadding%2Dleft%3A%202em%3B%0Apadding%2Dright%3A%202em%3B%0Afont%2Dfamily%3A%20%22Open%20Sans%22%2C%20%22Helvetica%20Neue%22%2C%20Helvetica%2C%20Arial%2C%20sans%2Dserif%3B%0Afont%2Dsize%3A%2014px%3B%0Aline%2Dheight%3A%201%2E35%3B%0A%7D%0A%23header%20%7B%0Atext%2Dalign%3A%20center%3B%0A%7D%0A%23TOC%20%7B%0Aclear%3A%20bot [...]
+
+</head>
+
+<body>
+
+
+
+
+<h1 class="title toc-ignore">Formatting of column data</h1>
+<h4 class="author"><em>Kirill Müller, Hadley Wickham</em></h4>
+<h4 class="date"><em>2016-08-26</em></h4>
+
+
+
+<p>The presentation of a column in a tibble is powered by two S3 generics:</p>
+<ul>
+<li><code>type_sum()</code> determines what goes into the column header.</li>
+<li><code>obj_sum()</code> is used when rendering list columns.</li>
+</ul>
+<p>If you have written an S3 or S4 class that can be used as a column, you can override these generics to make sure your data prints well in a tibble. To start, you must import the <code>tibble</code> package. Either add <code>tibble</code> to the <code>Imports:</code> section of your <code>DESCRIPTION</code>, or simply call:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">devtools::<span class="kw">use_package</span>(<span class="st">"tibble"</span>)</code></pre></div>
+<p>This vignette assumes a package that implements an S3 class <code>"foo"</code> and uses <code>roxygen2</code> to create documentation and the <code>NAMESPACE</code> file:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co">#' @export</span>
+foo <-<span class="st"> </span>function(x) {
+  <span class="kw">stopifnot</span>(<span class="kw">is.numeric</span>(x))
+  <span class="kw">structure</span>(x, <span class="dt">class =</span> <span class="st">"foo"</span>)
+}</code></pre></div>
+<div id="type_sum" class="section level2">
+<h2><code>type_sum()</code></h2>
+<p>This method should return a length-1 character vector that can be used in a column header. Strive for an evocative abbreviation that’s under 6 characters.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">type_sum</span>(<span class="dv">1</span>)
+<span class="co">#> [1] "dbl"</span>
+<span class="kw">type_sum</span>(<span class="dv">1</span>:<span class="dv">10</span>)
+<span class="co">#> [1] "int"</span>
+<span class="kw">type_sum</span>(<span class="kw">Sys.time</span>())
+<span class="co">#> [1] "dttm"</span></code></pre></div>
+<p>The default implementation works reasonably well for any kind of object, but the generated output may be too wide and waste precious space when displaying the tibble:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">type_sum</span>(<span class="kw">foo</span>(<span class="dv">1</span>:<span class="dv">10</span>))
+<span class="co">#> [1] "S3: foo"</span></code></pre></div>
+<p>To avoid this for provide a method for <code>type_sum()</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co">#' @export</span>
+type_sum.foo <-<span class="st"> </span>function(x, ...) {
+  <span class="st">"foo"</span>
+}
+
+<span class="kw">type_sum</span>(<span class="kw">foo</span>(<span class="dv">1</span>:<span class="dv">10</span>))
+<span class="co">#> [1] "foo"</span></code></pre></div>
+</div>
+<div id="obj_sum" class="section level2">
+<h2><code>obj_sum()</code></h2>
+<p>This method is primarily used for displaying list columns. A list column is a powerful way to attach hierarchical or unstructured data to an observation in a data frame. Implementations of <code>obj_sum()</code> are expected to return a character vector as long as the input, with brief description of the contents of each input element.</p>
+<p>Examples:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">obj_sum</span>(<span class="dv">1</span>)
+<span class="co">#> [1] "dbl [1]"</span>
+<span class="kw">obj_sum</span>(<span class="dv">1</span>:<span class="dv">10</span>)
+<span class="co">#> [1] "int [10]"</span>
+<span class="kw">obj_sum</span>(<span class="kw">Sys.time</span>())
+<span class="co">#> [1] "dttm [1]"</span>
+<span class="kw">obj_sum</span>(<span class="kw">list</span>(<span class="dv">1</span>:<span class="dv">5</span>))
+<span class="co">#> [1] "int [5]"</span>
+<span class="kw">obj_sum</span>(<span class="kw">list</span>(<span class="st">"a"</span>, <span class="st">"b"</span>, <span class="st">"c"</span>))
+<span class="co">#> [1] "chr [1]" "chr [1]" "chr [1]"</span></code></pre></div>
+<p>The default implementation calls <code>type_sum()</code> and appends the size of the object in brackets. If your object is built on top of an atomic vector the default will be adequate. You, will, however, need to provide a method if your object is vectorised and built on top of a list.</p>
+<p>An example of an object of this type in base R <code>POSIXlt</code>: it is a list with 9 components.</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">x <-<span class="st"> </span><span class="kw">as.POSIXlt</span>(<span class="kw">Sys.time</span>() +<span class="st"> </span><span class="kw">c</span>(<span class="dv">0</span>, <span class="dv">60</span>, <span class="dv">3600</span>)) 
+<span class="kw">str</span>(<span class="kw">unclass</span>(x))
+<span class="co">#> List of 11</span>
+<span class="co">#>  $ sec   : num [1:3] 39.8 39.8 39.8</span>
+<span class="co">#>  $ min   : int [1:3] 42 43 42</span>
+<span class="co">#>  $ hour  : int [1:3] 13 13 14</span>
+<span class="co">#>  $ mday  : int [1:3] 26 26 26</span>
+<span class="co">#>  $ mon   : int [1:3] 7 7 7</span>
+<span class="co">#>  $ year  : int [1:3] 116 116 116</span>
+<span class="co">#>  $ wday  : int [1:3] 5 5 5</span>
+<span class="co">#>  $ yday  : int [1:3] 238 238 238</span>
+<span class="co">#>  $ isdst : int [1:3] 1 1 1</span>
+<span class="co">#>  $ zone  : chr [1:3] "CEST" "CEST" "CEST"</span>
+<span class="co">#>  $ gmtoff: int [1:3] 7200 7200 7200</span>
+<span class="co">#>  - attr(*, "tzone")= chr [1:3] "" "CET" "CEST"</span></code></pre></div>
+<p>But it pretends to be a vector with 3 elements:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">x
+<span class="co">#> [1] "2016-08-26 13:42:39 CEST" "2016-08-26 13:43:39 CEST"</span>
+<span class="co">#> [3] "2016-08-26 14:42:39 CEST"</span>
+<span class="kw">length</span>(x)
+<span class="co">#> [1] 3</span>
+<span class="kw">str</span>(x)
+<span class="co">#>  POSIXlt[1:3], format: "2016-08-26 13:42:39" "2016-08-26 13:43:39" ...</span></code></pre></div>
+<p>So we need to define a method that returns a character vector the same length as <code>x</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="co">#' @export</span>
+obj_sum.POSIXlt <-<span class="st"> </span>function(x) {
+  <span class="kw">rep</span>(<span class="st">"POSIXlt"</span>, <span class="kw">length</span>(x))
+}</code></pre></div>
+</div>
+
+
+
+<!-- dynamically load mathjax for compatibility with self-contained -->
+<script>
+  (function () {
+    var script = document.createElement("script");
+    script.type = "text/javascript";
+    script.src  = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
+    document.getElementsByTagName("head")[0].appendChild(script);
+  })();
+</script>
+
+</body>
+</html>
diff --git a/inst/doc/tibble.R b/inst/doc/tibble.R
new file mode 100644
index 0000000..a6b3fee
--- /dev/null
+++ b/inst/doc/tibble.R
@@ -0,0 +1,50 @@
+## ---- echo = FALSE, message = FALSE--------------------------------------
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+options(tibble.print_min = 4L, tibble.print_max = 4L)
+library(tibble)
+
+## ------------------------------------------------------------------------
+tibble(x = letters)
+
+## ------------------------------------------------------------------------
+tibble(x = 1:3, y = list(1:5, 1:10, 1:20))
+
+## ------------------------------------------------------------------------
+names(data.frame(`crazy name` = 1))
+names(tibble(`crazy name` = 1))
+
+## ------------------------------------------------------------------------
+tibble(x = 1:5, y = x ^ 2)
+
+## ------------------------------------------------------------------------
+l <- replicate(26, sample(100), simplify = FALSE)
+names(l) <- letters
+
+microbenchmark::microbenchmark(
+  as_tibble(l),
+  as.data.frame(l)
+)
+
+## ------------------------------------------------------------------------
+tibble(x = 1:1000)
+
+## ------------------------------------------------------------------------
+df1 <- data.frame(x = 1:3, y = 3:1)
+class(df1[, 1:2])
+class(df1[, 1])
+
+df2 <- tibble(x = 1:3, y = 3:1)
+class(df2[, 1:2])
+class(df2[, 1])
+
+## ------------------------------------------------------------------------
+class(df2[[1]])
+class(df2$x)
+
+## ---- error = TRUE-------------------------------------------------------
+df <- data.frame(abc = 1)
+df$a
+
+df2 <- tibble(abc = 1)
+df2$a
+
diff --git a/inst/doc/tibble.Rmd b/inst/doc/tibble.Rmd
new file mode 100644
index 0000000..cfadfc5
--- /dev/null
+++ b/inst/doc/tibble.Rmd
@@ -0,0 +1,128 @@
+---
+title: "Tibbles"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+  %\VignetteIndexEntry{Tibbles}
+  %\VignetteEngine{knitr::rmarkdown}
+  \usepackage[utf8]{inputenc}
+---
+
+```{r, echo = FALSE, message = FALSE}
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+options(tibble.print_min = 4L, tibble.print_max = 4L)
+library(tibble)
+```
+
+Tibbles are a modern take on data frames. They keep the features that have stood the test of time, and drop the features that used to be convenient but are now frustrating (i.e. converting character vectors to factors).
+
+## Creating
+
+`tibble()` is a nice way to create data frames. It encapsulates best practices for data frames:
+
+  * It never changes an input's type (i.e., no more `stringsAsFactors = FALSE`!).
+    
+    ```{r}
+    tibble(x = letters)
+    ```
+    
+    This makes it easier to use with list-columns:
+    
+    ```{r}
+    tibble(x = 1:3, y = list(1:5, 1:10, 1:20))
+    ```
+    
+    List-columns are most commonly created by `do()`, but they can be useful to
+    create by hand.
+      
+  * It never adjusts the names of variables:
+  
+    ```{r}
+    names(data.frame(`crazy name` = 1))
+    names(tibble(`crazy name` = 1))
+    ```
+
+  * It evaluates its arguments lazily and sequentially:
+  
+    ```{r}
+    tibble(x = 1:5, y = x ^ 2)
+    ```
+
+  * It never uses `row.names()`. The whole point of tidy data is to 
+    store variables in a consistent way. So it never stores a variable as 
+    special attribute.
+  
+  * It only recycles vectors of length 1. This is because recycling vectors of greater lengths 
+    is a frequent source of bugs.
+
+## Coercion
+
+To complement `tibble()`, tibble provides `as_tibble()` to coerce objects into tibbles. Generally, `as_tibble()` methods are much simpler than `as.data.frame()` methods, and in fact, it's precisely what `as.data.frame()` does, but it's similar to `do.call(cbind, lapply(x, data.frame))` - i.e. it coerces each component to a data frame and then `cbinds()` them all together. 
+
+`as_tibble()` has been written with an eye for performance:
+
+```{r}
+l <- replicate(26, sample(100), simplify = FALSE)
+names(l) <- letters
+
+microbenchmark::microbenchmark(
+  as_tibble(l),
+  as.data.frame(l)
+)
+```
+
+The speed of `as.data.frame()` is not usually a bottleneck when used interactively, but can be a problem when combining thousands of messy inputs into one tidy data frame.
+
+## Tibbles vs data frames
+
+There are two key differences between tibbles and data frames: printing and subsetting.
+
+### Printing
+
+When you print a tibble, it only shows the first ten rows and all the columns that fit on one screen. It also prints an abbreviated description of the column type:
+    
+```{r}
+tibble(x = 1:1000)
+```
+
+You can control the default appearance with options:
+
+* `options(tibble.print_max = n, tibble.print_min = m)`: if there are more 
+  than `n` rows, print only the first `m` rows. Use 
+  `options(tibble.print_max = Inf)` to always show all rows.
+
+* `options(tibble.width = Inf)` will always print all columns, regardless
+   of the width of the screen.
+
+### Subsetting
+
+Tibbles are quite strict about subsetting. `[` always returns another tibble. Contrast this with a data frame: sometimes `[` returns a data frame and sometimes it just returns a vector:
+    
+```{r}
+df1 <- data.frame(x = 1:3, y = 3:1)
+class(df1[, 1:2])
+class(df1[, 1])
+
+df2 <- tibble(x = 1:3, y = 3:1)
+class(df2[, 1:2])
+class(df2[, 1])
+```
+
+To extract a single column use `[[` or `$`:
+
+```{r}
+class(df2[[1]])
+class(df2$x)
+```
+
+Tibbles are also stricter with `$`. Tibbles never do partial matching, and will throw a warning and return `NULL` if the column does not exist:
+
+```{r, error = TRUE}
+df <- data.frame(abc = 1)
+df$a
+
+df2 <- tibble(abc = 1)
+df2$a
+```
+
+Also, tibbles ignore the `drop` argument.
diff --git a/inst/doc/tibble.html b/inst/doc/tibble.html
new file mode 100644
index 0000000..32e3cf5
--- /dev/null
+++ b/inst/doc/tibble.html
@@ -0,0 +1,204 @@
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+
+<meta charset="utf-8">
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="pandoc" />
+
+<meta name="viewport" content="width=device-width, initial-scale=1">
+
+
+<meta name="date" content="2016-08-26" />
+
+<title>Tibbles</title>
+
+
+
+<style type="text/css">code{white-space: pre;}</style>
+<style type="text/css">
+div.sourceCode { overflow-x: auto; }
+table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
+  margin: 0; padding: 0; vertical-align: baseline; border: none; }
+table.sourceCode { width: 100%; line-height: 100%; }
+td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
+td.sourceCode { padding-left: 5px; }
+code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
+code > span.dt { color: #902000; } /* DataType */
+code > span.dv { color: #40a070; } /* DecVal */
+code > span.bn { color: #40a070; } /* BaseN */
+code > span.fl { color: #40a070; } /* Float */
+code > span.ch { color: #4070a0; } /* Char */
+code > span.st { color: #4070a0; } /* String */
+code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
+code > span.ot { color: #007020; } /* Other */
+code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
+code > span.fu { color: #06287e; } /* Function */
+code > span.er { color: #ff0000; font-weight: bold; } /* Error */
+code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
+code > span.cn { color: #880000; } /* Constant */
+code > span.sc { color: #4070a0; } /* SpecialChar */
+code > span.vs { color: #4070a0; } /* VerbatimString */
+code > span.ss { color: #bb6688; } /* SpecialString */
+code > span.im { } /* Import */
+code > span.va { color: #19177c; } /* Variable */
+code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
+code > span.op { color: #666666; } /* Operator */
+code > span.bu { } /* BuiltIn */
+code > span.ex { } /* Extension */
+code > span.pp { color: #bc7a00; } /* Preprocessor */
+code > span.at { color: #7d9029; } /* Attribute */
+code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
+code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
+code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
+code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
+</style>
+
+
+
+<link href="data:text/css;charset=utf-8,body%20%7B%0Abackground%2Dcolor%3A%20%23fff%3B%0Amargin%3A%201em%20auto%3B%0Amax%2Dwidth%3A%20700px%3B%0Aoverflow%3A%20visible%3B%0Apadding%2Dleft%3A%202em%3B%0Apadding%2Dright%3A%202em%3B%0Afont%2Dfamily%3A%20%22Open%20Sans%22%2C%20%22Helvetica%20Neue%22%2C%20Helvetica%2C%20Arial%2C%20sans%2Dserif%3B%0Afont%2Dsize%3A%2014px%3B%0Aline%2Dheight%3A%201%2E35%3B%0A%7D%0A%23header%20%7B%0Atext%2Dalign%3A%20center%3B%0A%7D%0A%23TOC%20%7B%0Aclear%3A%20bot [...]
+
+</head>
+
+<body>
+
+
+
+
+<h1 class="title toc-ignore">Tibbles</h1>
+<h4 class="date"><em>2016-08-26</em></h4>
+
+
+
+<p>Tibbles are a modern take on data frames. They keep the features that have stood the test of time, and drop the features that used to be convenient but are now frustrating (i.e. converting character vectors to factors).</p>
+<div id="creating" class="section level2">
+<h2>Creating</h2>
+<p><code>tibble()</code> is a nice way to create data frames. It encapsulates best practices for data frames:</p>
+<ul>
+<li><p>It never changes an input’s type (i.e., no more <code>stringsAsFactors = FALSE</code>!).</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">tibble</span>(<span class="dt">x =</span> letters)
+<span class="co">#> # A tibble: 26 × 1</span>
+<span class="co">#>       x</span>
+<span class="co">#>   <chr></span>
+<span class="co">#> 1     a</span>
+<span class="co">#> 2     b</span>
+<span class="co">#> 3     c</span>
+<span class="co">#> 4     d</span>
+<span class="co">#> # ... with 22 more rows</span></code></pre></div>
+<p>This makes it easier to use with list-columns:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">tibble</span>(<span class="dt">x =</span> <span class="dv">1</span>:<span class="dv">3</span>, <span class="dt">y =</span> <span class="kw">list</span>(<span class="dv">1</span>:<span class="dv">5</span>, <span class="dv">1</span>:<span class="dv">10</span>, <span class="dv">1</span>:<span class="dv">20</span>))
+<span class="co">#> # A tibble: 3 × 2</span>
+<span class="co">#>       x          y</span>
+<span class="co">#>   <int>     <list></span>
+<span class="co">#> 1     1  <int [5]></span>
+<span class="co">#> 2     2 <int [10]></span>
+<span class="co">#> 3     3 <int [20]></span></code></pre></div>
+<p>List-columns are most commonly created by <code>do()</code>, but they can be useful to create by hand.</p></li>
+<li><p>It never adjusts the names of variables:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">names</span>(<span class="kw">data.frame</span>(<span class="st">`</span><span class="dt">crazy name</span><span class="st">`</span> =<span class="st"> </span><span class="dv">1</span>))
+<span class="co">#> [1] "crazy.name"</span>
+<span class="kw">names</span>(<span class="kw">tibble</span>(<span class="st">`</span><span class="dt">crazy name</span><span class="st">`</span> =<span class="st"> </span><span class="dv">1</span>))
+<span class="co">#> [1] "crazy name"</span></code></pre></div></li>
+<li><p>It evaluates its arguments lazily and sequentially:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">tibble</span>(<span class="dt">x =</span> <span class="dv">1</span>:<span class="dv">5</span>, <span class="dt">y =</span> x ^<span class="st"> </span><span class="dv">2</span>)
+<span class="co">#> # A tibble: 5 × 2</span>
+<span class="co">#>       x     y</span>
+<span class="co">#>   <int> <dbl></span>
+<span class="co">#> 1     1     1</span>
+<span class="co">#> 2     2     4</span>
+<span class="co">#> 3     3     9</span>
+<span class="co">#> 4     4    16</span>
+<span class="co">#> # ... with 1 more rows</span></code></pre></div></li>
+<li><p>It never uses <code>row.names()</code>. The whole point of tidy data is to store variables in a consistent way. So it never stores a variable as special attribute.</p></li>
+<li><p>It only recycles vectors of length 1. This is because recycling vectors of greater lengths is a frequent source of bugs.</p></li>
+</ul>
+</div>
+<div id="coercion" class="section level2">
+<h2>Coercion</h2>
+<p>To complement <code>tibble()</code>, tibble provides <code>as_tibble()</code> to coerce objects into tibbles. Generally, <code>as_tibble()</code> methods are much simpler than <code>as.data.frame()</code> methods, and in fact, it’s precisely what <code>as.data.frame()</code> does, but it’s similar to <code>do.call(cbind, lapply(x, data.frame))</code> - i.e. it coerces each component to a data frame and then <code>cbinds()</code> them all together.</p>
+<p><code>as_tibble()</code> has been written with an eye for performance:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">l <-<span class="st"> </span><span class="kw">replicate</span>(<span class="dv">26</span>, <span class="kw">sample</span>(<span class="dv">100</span>), <span class="dt">simplify =</span> <span class="ot">FALSE</span>)
+<span class="kw">names</span>(l) <-<span class="st"> </span>letters
+
+microbenchmark::<span class="kw">microbenchmark</span>(
+  <span class="kw">as_tibble</span>(l),
+  <span class="kw">as.data.frame</span>(l)
+)
+<span class="co">#> Unit: microseconds</span>
+<span class="co">#>              expr      min        lq      mean   median        uq      max</span>
+<span class="co">#>      as_tibble(l)  520.511  555.2405  596.2015  580.798  612.1915 1116.805</span>
+<span class="co">#>  as.data.frame(l) 2932.765 3060.7465 3429.1367 3113.495 3253.2700 8814.272</span>
+<span class="co">#>  neval cld</span>
+<span class="co">#>    100  a </span>
+<span class="co">#>    100   b</span></code></pre></div>
+<p>The speed of <code>as.data.frame()</code> is not usually a bottleneck when used interactively, but can be a problem when combining thousands of messy inputs into one tidy data frame.</p>
+</div>
+<div id="tibbles-vs-data-frames" class="section level2">
+<h2>Tibbles vs data frames</h2>
+<p>There are two key differences between tibbles and data frames: printing and subsetting.</p>
+<div id="printing" class="section level3">
+<h3>Printing</h3>
+<p>When you print a tibble, it only shows the first ten rows and all the columns that fit on one screen. It also prints an abbreviated description of the column type:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">tibble</span>(<span class="dt">x =</span> <span class="dv">1</span>:<span class="dv">1000</span>)
+<span class="co">#> # A tibble: 1,000 × 1</span>
+<span class="co">#>       x</span>
+<span class="co">#>   <int></span>
+<span class="co">#> 1     1</span>
+<span class="co">#> 2     2</span>
+<span class="co">#> 3     3</span>
+<span class="co">#> 4     4</span>
+<span class="co">#> # ... with 996 more rows</span></code></pre></div>
+<p>You can control the default appearance with options:</p>
+<ul>
+<li><p><code>options(tibble.print_max = n, tibble.print_min = m)</code>: if there are more than <code>n</code> rows, print only the first <code>m</code> rows. Use <code>options(tibble.print_max = Inf)</code> to always show all rows.</p></li>
+<li><p><code>options(tibble.width = Inf)</code> will always print all columns, regardless of the width of the screen.</p></li>
+</ul>
+</div>
+<div id="subsetting" class="section level3">
+<h3>Subsetting</h3>
+<p>Tibbles are quite strict about subsetting. <code>[</code> always returns another tibble. Contrast this with a data frame: sometimes <code>[</code> returns a data frame and sometimes it just returns a vector:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">df1 <-<span class="st"> </span><span class="kw">data.frame</span>(<span class="dt">x =</span> <span class="dv">1</span>:<span class="dv">3</span>, <span class="dt">y =</span> <span class="dv">3</span>:<span class="dv">1</span>)
+<span class="kw">class</span>(df1[, <span class="dv">1</span>:<span class="dv">2</span>])
+<span class="co">#> [1] "data.frame"</span>
+<span class="kw">class</span>(df1[, <span class="dv">1</span>])
+<span class="co">#> [1] "integer"</span>
+
+df2 <-<span class="st"> </span><span class="kw">tibble</span>(<span class="dt">x =</span> <span class="dv">1</span>:<span class="dv">3</span>, <span class="dt">y =</span> <span class="dv">3</span>:<span class="dv">1</span>)
+<span class="kw">class</span>(df2[, <span class="dv">1</span>:<span class="dv">2</span>])
+<span class="co">#> [1] "tbl_df"     "tbl"        "data.frame"</span>
+<span class="kw">class</span>(df2[, <span class="dv">1</span>])
+<span class="co">#> [1] "tbl_df"     "tbl"        "data.frame"</span></code></pre></div>
+<p>To extract a single column use <code>[[</code> or <code>$</code>:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r"><span class="kw">class</span>(df2[[<span class="dv">1</span>]])
+<span class="co">#> [1] "integer"</span>
+<span class="kw">class</span>(df2$x)
+<span class="co">#> [1] "integer"</span></code></pre></div>
+<p>Tibbles are also stricter with <code>$</code>. Tibbles never do partial matching, and will throw a warning and return <code>NULL</code> if the column does not exist:</p>
+<div class="sourceCode"><pre class="sourceCode r"><code class="sourceCode r">df <-<span class="st"> </span><span class="kw">data.frame</span>(<span class="dt">abc =</span> <span class="dv">1</span>)
+df$a
+<span class="co">#> [1] 1</span>
+
+df2 <-<span class="st"> </span><span class="kw">tibble</span>(<span class="dt">abc =</span> <span class="dv">1</span>)
+df2$a
+<span class="co">#> Warning: Unknown column 'a'</span>
+<span class="co">#> NULL</span></code></pre></div>
+<p>Also, tibbles ignore the <code>drop</code> argument.</p>
+</div>
+</div>
+
+
+
+<!-- dynamically load mathjax for compatibility with self-contained -->
+<script>
+  (function () {
+    var script = document.createElement("script");
+    script.type = "text/javascript";
+    script.src  = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
+    document.getElementsByTagName("head")[0].appendChild(script);
+  })();
+</script>
+
+</body>
+</html>
diff --git a/man/add_column.Rd b/man/add_column.Rd
new file mode 100644
index 0000000..216e89b
--- /dev/null
+++ b/man/add_column.Rd
@@ -0,0 +1,40 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/add.R
+\name{add_column}
+\alias{add_column}
+\title{Add columns to a data frame}
+\usage{
+add_column(.data, ..., .before = NULL, .after = NULL)
+}
+\arguments{
+\item{.data}{Data frame to append to.}
+
+\item{...}{Name-value pairs, all values must have one element for each row
+in the data frame, or be of length 1}
+
+\item{.before, .after}{One-based column index or column name where to add the
+new columns, default: after last column}
+}
+\description{
+This is a convenient way to add one or more columns to an existing data
+frame.
+}
+\examples{
+# add_row ---------------------------------
+df <- tibble(x = 1:3, y = 3:1)
+
+add_column(df, z = -1:1, w = 0)
+
+# You can't overwrite existing columns
+\dontrun{
+add_column(df, x = 4:6)
+}
+# You can't create new observations
+\dontrun{
+add_column(df, z = 1:5)
+}
+}
+\seealso{
+Other addition: \code{\link{add_row}}
+}
+
diff --git a/man/add_row.Rd b/man/add_row.Rd
new file mode 100644
index 0000000..b86daf7
--- /dev/null
+++ b/man/add_row.Rd
@@ -0,0 +1,47 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/add.R
+\name{add_row}
+\alias{add_row}
+\title{Add rows to a data frame}
+\usage{
+add_row(.data, ..., .before = NULL, .after = NULL)
+}
+\arguments{
+\item{.data}{Data frame to append to.}
+
+\item{...}{Name-value pairs. If you don't supply the name of a variable,
+it'll be given the value \code{NA}.}
+
+\item{.before, .after}{One-based row index where to add the new rows,
+default: after last row}
+}
+\description{
+This is a convenient way to add one or more rows of data to an existing data
+frame. See \code{\link{tribble}} for an easy way to create an complete
+data frame row-by-row.
+}
+\examples{
+# add_row ---------------------------------
+df <- tibble(x = 1:3, y = 3:1)
+
+add_row(df, x = 4, y = 0)
+
+# You can specify where to add the new rows
+add_row(df, x = 4, y = 0, .before = 2)
+
+# You can supply vectors, to add multiple rows (this isn't
+# recommended because it's a bit hard to read)
+add_row(df, x = 4:5, y = 0:-1)
+
+# Absent variables get missing values
+add_row(df, x = 4)
+
+# You can't create new variables
+\dontrun{
+add_row(df, z = 10)
+}
+}
+\seealso{
+Other addition: \code{\link{add_column}}
+}
+
diff --git a/man/all_equal.Rd b/man/all_equal.Rd
new file mode 100644
index 0000000..ff135a8
--- /dev/null
+++ b/man/all_equal.Rd
@@ -0,0 +1,54 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/all-equal.r
+\name{all_equal}
+\alias{all.equal.tbl_df}
+\alias{all_equal}
+\title{Flexible equality comparison for data frames.}
+\usage{
+all_equal(target, current, ignore_col_order = TRUE, ignore_row_order = TRUE,
+  convert = FALSE, ...)
+
+\method{all.equal}{tbl_df}(target, current, ignore_col_order = TRUE,
+  ignore_row_order = TRUE, convert = FALSE, ...)
+}
+\arguments{
+\item{target, current}{Two data frames to compare.}
+
+\item{ignore_col_order}{Should order of columns be ignored?}
+
+\item{ignore_row_order}{Should order of rows be ignored?}
+
+\item{convert}{Should similar classes be converted? Currently this will
+convert factor to character and integer to double.}
+
+\item{...}{Ignored. Needed for compatibility with \code{all.equal}.}
+}
+\value{
+\code{TRUE} if equal, otherwise a character vector describing
+  the reasons why they're not equal. Use \code{\link{isTRUE}} if using the
+  result in an \code{if} expression.
+}
+\description{
+When comparing two \code{tbl_df} using \code{\link{all.equal}}, column and
+row order is ignored by default, and types are not coerced.  The \code{dplyr}
+package provides a much more efficient implementation for this functionality.
+}
+\examples{
+scramble <- function(x) x[sample(nrow(x)), sample(ncol(x))]
+mtcars_df <- as_tibble(mtcars)
+
+# By default, ordering of rows and columns ignored
+all.equal(mtcars_df, scramble(mtcars_df))
+
+# But those can be overriden if desired
+all.equal(mtcars_df, scramble(mtcars_df), ignore_col_order = FALSE)
+all.equal(mtcars_df, scramble(mtcars_df), ignore_row_order = FALSE)
+
+# By default all.equal is sensitive to variable differences
+df1 <- tibble(x = "a")
+df2 <- tibble(x = factor("a"))
+all.equal(df1, df2)
+# But you can request to convert similar types
+all.equal(df1, df2, convert = TRUE)
+}
+
diff --git a/man/as_tibble.Rd b/man/as_tibble.Rd
new file mode 100644
index 0000000..00ea35a
--- /dev/null
+++ b/man/as_tibble.Rd
@@ -0,0 +1,87 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/dataframe.R
+\name{as_tibble}
+\alias{as_data_frame}
+\alias{as_tibble}
+\alias{as_tibble.NULL}
+\alias{as_tibble.data.frame}
+\alias{as_tibble.default}
+\alias{as_tibble.list}
+\alias{as_tibble.matrix}
+\alias{as_tibble.table}
+\alias{as_tibble.tbl_df}
+\title{Coerce lists and matrices to data frames.}
+\usage{
+as_tibble(x, ...)
+
+\method{as_tibble}{tbl_df}(x, ...)
+
+\method{as_tibble}{data.frame}(x, validate = TRUE, ...)
+
+\method{as_tibble}{list}(x, validate = TRUE, ...)
+
+\method{as_tibble}{matrix}(x, ...)
+
+\method{as_tibble}{table}(x, n = "n", ...)
+
+\method{as_tibble}{NULL}(x, ...)
+
+\method{as_tibble}{default}(x, ...)
+
+as_data_frame(x, ...)
+}
+\arguments{
+\item{x}{A list. Each element of the list must have the same length.}
+
+\item{...}{Other arguments passed on to individual methods.}
+
+\item{validate}{When \code{TRUE}, verifies that the input is a valid data
+frame (i.e. all columns are named, and are 1d vectors or lists). You may
+want to suppress this when you know that you already have a valid data
+frame and you want to save some time.}
+
+\item{n}{Name for count column, default: \code{"n"}.}
+}
+\description{
+\code{as.data.frame} is effectively a thin wrapper around \code{data.frame},
+and hence is rather slow (because it calls \code{data.frame} on each element
+before \code{cbind}ing together). \code{as_tibble} is a new S3 generic
+with more efficient methods for matrices and data frames.
+}
+\details{
+This is an S3 generic. tibble includes methods for data frames (adds tbl_df
+classes), tibbles (returns unchanged input), lists, matrices, and tables.
+Other types are first coerced via \code{\link[base]{as.data.frame}} with
+\code{stringsAsFactors = FALSE}.
+
+\code{as_data_frame} is an alias.
+}
+\examples{
+l <- list(x = 1:500, y = runif(500), z = 500:1)
+df <- as_tibble(l)
+
+m <- matrix(rnorm(50), ncol = 5)
+colnames(m) <- c("a", "b", "c", "d", "e")
+df <- as_tibble(m)
+
+# as_tibble is considerably simpler than as.data.frame
+# making it more suitable for use when you have things that are
+# lists
+\dontrun{
+l2 <- replicate(26, sample(letters), simplify = FALSE)
+names(l2) <- letters
+microbenchmark::microbenchmark(
+  as_tibble(l2, validate = FALSE),
+  as_tibble(l2),
+  as.data.frame(l2)
+)
+
+m <- matrix(runif(26 * 100), ncol = 26)
+colnames(m) <- letters
+microbenchmark::microbenchmark(
+  as_tibble(m),
+  as.data.frame(m)
+)
+}
+}
+
diff --git a/man/enframe.Rd b/man/enframe.Rd
new file mode 100644
index 0000000..c127a39
--- /dev/null
+++ b/man/enframe.Rd
@@ -0,0 +1,26 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/enframe.R
+\name{enframe}
+\alias{enframe}
+\title{Converting atomic vectors to data frames}
+\usage{
+enframe(x, name = "name", value = "value")
+}
+\arguments{
+\item{x}{An atomic vector}
+
+\item{name, value}{Names of the columns that store the names and values}
+}
+\value{
+A \code{\link{tibble}}
+}
+\description{
+A helper function that converts named atomic vectors or lists to two-column
+data frames.
+For unnamed vectors, the natural sequence is used as name column.
+}
+\examples{
+enframe(1:3)
+enframe(c(a = 5, b = 7))
+}
+
diff --git a/man/formatting.Rd b/man/formatting.Rd
new file mode 100644
index 0000000..54317a3
--- /dev/null
+++ b/man/formatting.Rd
@@ -0,0 +1,52 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/tbl-df.r, R/utils-format.r
+\name{print.tbl_df}
+\alias{formatting}
+\alias{print.tbl_df}
+\alias{trunc_mat}
+\title{Tools for describing matrices}
+\usage{
+\method{print}{tbl_df}(x, ..., n = NULL, width = NULL, n_extra = NULL)
+
+trunc_mat(x, n = NULL, width = NULL, n_extra = NULL)
+}
+\arguments{
+\item{x}{Object to show.}
+
+\item{n}{Number of rows to show. If \code{NULL}, the default, will print
+all rows if less than option \code{tibble.print_max}. Otherwise, will
+print \code{tibble.print_min} rows.}
+
+\item{width}{Width of text output to generate. This defaults to NULL, which
+means use \code{getOption("tibble.width")} or (if also NULL)
+\code{getOption("width")}; the latter displays only the columns that
+fit on one screen. You can also set \code{options(tibble.width = Inf)} to
+override this default and always print all columns.}
+
+\item{n_extra}{Number of extra columns to print abbreviated information for,
+if the width is too small for the entire tibble. If \code{NULL}, the
+default, will print information about at most \code{tibble.max_extra_cols}
+extra columns.}
+}
+\description{
+Tools for describing matrices
+}
+\examples{
+trunc_mat(mtcars)
+
+print(as_tibble(mtcars))
+print(as_tibble(mtcars), n = 1)
+print(as_tibble(mtcars), n = 3)
+print(as_tibble(mtcars), n = 100)
+
+if (!requireNamespace("nycflights13", quietly = TRUE))
+  stop("Please install the nycflights13 package to run the rest of this example")
+
+print(nycflights13::flights, n_extra = 2)
+print(nycflights13::flights, width = Inf)
+}
+\seealso{
+\link{tibble-package}
+}
+\keyword{internal}
+
diff --git a/man/glimpse.Rd b/man/glimpse.Rd
new file mode 100644
index 0000000..cd224e4
--- /dev/null
+++ b/man/glimpse.Rd
@@ -0,0 +1,41 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/glimpse.R
+\name{glimpse}
+\alias{glimpse}
+\title{Get a glimpse of your data.}
+\usage{
+glimpse(x, width = NULL, ...)
+}
+\arguments{
+\item{x}{An object to glimpse at.}
+
+\item{width}{Width of output: defaults to the setting of the option
+\code{tibble.width} (if finite) or the width of the console.}
+
+\item{...}{Other arguments passed onto individual methods.}
+}
+\value{
+x original x is (invisibly) returned, allowing \code{glimpse} to be
+used within a data pipe line.
+}
+\description{
+This is like a transposed version of print: columns run down the page,
+and data runs across. This makes it possible to see every column in
+a data frame. It's a little like \code{\link{str}} applied to a data frame
+but it tries to show you as much data as possible. (And it always shows
+the underlying data, even when applied to a remote data source.)
+}
+\section{S3 methods}{
+
+\code{glimpse} is an S3 generic with a customised method for \code{tbl}s and
+\code{data.frames}, and a default method that calls \code{\link{str}}.
+}
+\examples{
+glimpse(mtcars)
+
+if (!requireNamespace("nycflights13", quietly = TRUE))
+  stop("Please install the nycflights13 package to run the rest of this example")
+
+glimpse(nycflights13::flights)
+}
+
diff --git a/man/has_name.Rd b/man/has_name.Rd
new file mode 100644
index 0000000..69112e1
--- /dev/null
+++ b/man/has_name.Rd
@@ -0,0 +1,29 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/has-name.R
+\name{has_name}
+\alias{has_name}
+\title{Convenience function to check presence of a named element}
+\usage{
+has_name(x, name)
+}
+\arguments{
+\item{x}{A data frame or another named object}
+
+\item{name}{Element name(s) to check}
+}
+\value{
+A logical vector of the same length as \code{name}
+}
+\description{
+This function returns a logical value that indicates if a data frame or
+another named object contains an element with a specific name.
+}
+\details{
+Unnamed objects are treated as if all names are empty strings.
+\code{NA} input gives \code{FALSE} as output.
+}
+\examples{
+has_name(iris, "Species")
+has_name(mtcars, "gears")
+}
+
diff --git a/man/is.tibble.Rd b/man/is.tibble.Rd
new file mode 100644
index 0000000..50938b6
--- /dev/null
+++ b/man/is.tibble.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/dataframe.R
+\name{is.tibble}
+\alias{is.tibble}
+\alias{is_tibble}
+\title{Test if the object is a tibble.}
+\usage{
+is.tibble(x)
+
+is_tibble(x)
+}
+\arguments{
+\item{x}{An object}
+}
+\value{
+\code{TRUE} if the object inherits from the \code{tbl_df} class.
+}
+\description{
+Test if the object is a tibble.
+}
+
diff --git a/man/knit_print.trunc_mat.Rd b/man/knit_print.trunc_mat.Rd
new file mode 100644
index 0000000..c8ee78c
--- /dev/null
+++ b/man/knit_print.trunc_mat.Rd
@@ -0,0 +1,13 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/utils-format.r
+\name{knit_print.trunc_mat}
+\alias{knit_print.trunc_mat}
+\title{knit_print method for trunc mat}
+\usage{
+knit_print.trunc_mat(x, options)
+}
+\description{
+knit_print method for trunc mat
+}
+\keyword{internal}
+
diff --git a/man/obj_sum.Rd b/man/obj_sum.Rd
new file mode 100644
index 0000000..131de59
--- /dev/null
+++ b/man/obj_sum.Rd
@@ -0,0 +1,41 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/type-sum.r
+\name{obj_sum}
+\alias{is_vector_s3}
+\alias{obj_sum}
+\alias{tbl_sum}
+\alias{type_sum}
+\title{Provide a succinct summary of an object}
+\usage{
+obj_sum(x)
+
+type_sum(x)
+
+tbl_sum(x)
+
+is_vector_s3(x)
+}
+\arguments{
+\item{x}{an object to summarise. Generally only methods of atomic vectors
+and variants have been implemented.}
+}
+\description{
+\code{type_sum} gives a brief summary of object type. Objects that commonly
+occur in a data frame should return a string with four or less characters.
+
+\code{obj_sum} also includes the size of the object if \code{is_s3_vector}
+is \code{TRUE}.
+
+\code{tbl_sum} gives a brief textual description of a table-like object,
+which should include the dimensions, the data source, and possible grouping
+(for dplyr).  The default implementation forwards to \code{obj_sum}
+}
+\examples{
+obj_sum(1:10)
+obj_sum(matrix(1:10))
+obj_sum(Sys.Date())
+obj_sum(Sys.time())
+obj_sum(mean)
+}
+\keyword{internal}
+
diff --git a/man/repair_names.Rd b/man/repair_names.Rd
new file mode 100644
index 0000000..6511168
--- /dev/null
+++ b/man/repair_names.Rd
@@ -0,0 +1,32 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/repair-names.R
+\name{repair_names}
+\alias{repair_names}
+\title{Repair object names.}
+\usage{
+repair_names(x, prefix = "V", sep = "")
+}
+\arguments{
+\item{x}{A named vector.}
+
+\item{prefix}{A string, the prefix to use for new column names.}
+
+\item{sep}{A string inserted between the column name and de-duplicating
+number.}
+}
+\value{
+\code{x} with valid names.
+}
+\description{
+\code{repair_names} ensures its input has non-missing and
+unique names (duplicated names get a numeric suffix). Valid names are
+left as is.
+}
+\examples{
+repair_names(list(3, 4, 5)) # works for lists, too
+repair_names(mtcars) # a no-op
+tbl <- as_tibble(structure(list(3, 4, 5), class = "data.frame"),
+                 validate = FALSE)
+repair_names(tbl)
+}
+
diff --git a/man/rownames.Rd b/man/rownames.Rd
new file mode 100644
index 0000000..c1a6804
--- /dev/null
+++ b/man/rownames.Rd
@@ -0,0 +1,51 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/rownames.R
+\name{rownames}
+\alias{column_to_rownames}
+\alias{has_rownames}
+\alias{remove_rownames}
+\alias{rownames}
+\alias{rownames_to_column}
+\title{Tools for working with row names}
+\usage{
+has_rownames(df)
+
+remove_rownames(df)
+
+rownames_to_column(df, var = "rowname")
+
+column_to_rownames(df, var = "rowname")
+}
+\arguments{
+\item{df}{A data frame}
+
+\item{var}{Name of column to use for rownames.}
+}
+\description{
+While a tibble can have row names (e.g., when converting from a regular data
+frame), they are removed when subsetting with the \code{[} operator.
+A warning will be raised when attempting to assign non-\code{NULL} row names
+to a tibble.
+Generally, it is best to avoid row names, because they are basically a
+character column with different semantics to every other column. These
+functions allow to you detect if a data frame has row names
+(\code{has_rownames}), remove them (\code{remove_rownames}), or convert
+them back-and-forth between an explicit column (\code{rownames_to_column}
+and \code{column_to_rownames}).
+}
+\details{
+In the printed output, the presence of row names is indicated by a star just
+above the row numbers.
+}
+\examples{
+has_rownames(mtcars)
+has_rownames(iris)
+has_rownames(remove_rownames(mtcars))
+
+head(rownames_to_column(mtcars))
+
+mtcars_tbl <- as_tibble(rownames_to_column(mtcars))
+mtcars_tbl
+column_to_rownames(as.data.frame(mtcars_tbl))
+}
+
diff --git a/man/tibble-package.Rd b/man/tibble-package.Rd
new file mode 100644
index 0000000..65c1a60
--- /dev/null
+++ b/man/tibble-package.Rd
@@ -0,0 +1,54 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/tibble.R
+\docType{package}
+\name{tibble-package}
+\alias{tibble-package}
+\title{Simple Data Frames}
+\description{
+Provides a 'tbl_df' class that offers better checking and
+printing capabilities than traditional data frames.
+}
+\details{
+The S3 class \code{tbl_df} wraps a local data frame. The main
+advantage to using a \code{tbl_df} over a regular data frame is the printing:
+tbl objects only print a few rows and all the columns that fit on one screen,
+describing the rest of it as text.
+}
+\section{Methods}{
+
+
+\code{tbl_df} implements four important base methods:
+
+\describe{
+\item{print}{By default only prints the first 10 rows (at most 20), and the
+  columns that fit on screen; see \code{\link{print.tbl_df}}}
+\item{\code{[}}{Never simplifies (drops), so always returns data.frame}
+\item{\code{[[}, \code{$}}{Calls \code{\link{.subset2}} directly,
+  so is considerably faster. Returns \code{NULL} if column does not exist,
+  \code{$} warns.}
+}
+}
+
+\section{Important functions}{
+
+\code{\link{tibble}} and \code{\link{tribble}} for construction,
+\code{\link{as_tibble}} for coercion,
+and \code{\link{print.tbl_df}} and \code{\link{glimpse}} for display.
+}
+
+\section{Package options}{
+
+Display options for \code{tbl_df}, used by \code{\link{trunc_mat}} and
+(indirectly) by \code{\link{print.tbl_df}}.
+\describe{
+\item{\code{tibble.print_max}}{Row number threshold: Maximum number of rows
+  printed. Set to \code{Inf} to always print all rows.  Default: 20.}
+\item{\code{tibble.print_min}}{Number of rows printed if row number
+  threshold is exceeded. Default: 10.}
+\item{\code{tibble.width}}{Output width. Default: \code{NULL} (use
+  \code{width} option).}
+\item{\code{tibble.max_extra_cols}}{Number of extra columns
+  printed in reduced form. Default: 100.}
+}
+}
+
diff --git a/man/tibble.Rd b/man/tibble.Rd
new file mode 100644
index 0000000..fc26185
--- /dev/null
+++ b/man/tibble.Rd
@@ -0,0 +1,78 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/dataframe.R
+\name{tibble}
+\alias{data_frame}
+\alias{data_frame_}
+\alias{lst}
+\alias{lst_}
+\alias{tibble}
+\alias{tibble_}
+\title{Build a data frame or list.}
+\usage{
+tibble(...)
+
+tibble_(xs)
+
+data_frame(...)
+
+data_frame_(xs)
+
+lst(...)
+
+lst_(xs)
+}
+\arguments{
+\item{...}{A set of name-value pairs. Arguments are evaluated sequentially,
+so you can refer to previously created variables.}
+
+\item{xs}{A list of unevaluated expressions created with \code{~},
+\code{quote()}, or \code{\link[lazyeval]{lazy}}.}
+}
+\description{
+\code{tibble} is a trimmed down version of \code{\link{data.frame}} that:
+\enumerate{
+\item Never coerces inputs (i.e. strings stay as strings!).
+\item Never adds \code{row.names}.
+\item Never munges column names.
+\item Only recycles length 1 inputs.
+\item Evaluates its arguments lazily and in order.
+\item Adds \code{tbl_df} class to output.
+\item Automatically adds column names.
+}
+}
+\details{
+\code{lst} is similar to \code{\link{list}}, but like \code{tibble}, it
+evaluates its arguments lazily and in order, and automatically adds names.
+
+\code{data_frame} is an alias to \code{tibble}.
+}
+\examples{
+a <- 1:5
+tibble(a, b = a * 2)
+tibble(a, b = a * 2, c = 1)
+tibble(x = runif(10), y = x * 2)
+
+lst(n = 5, x = runif(n))
+
+# tibble never coerces its inputs
+str(tibble(letters))
+str(tibble(x = list(diag(1), diag(2))))
+
+# or munges column names
+tibble(`a + b` = 1:5)
+
+# With the SE version, you give it a list of formulas/expressions
+tibble_(list(x = ~1:10, y = quote(x * 2)))
+
+# data frames can only contain 1d atomic vectors and lists
+# and can not contain POSIXlt
+\dontrun{
+tibble(x = tibble(1, 2, 3))
+tibble(y = strptime("2000/01/01", "\%x"))
+}
+}
+\seealso{
+\code{\link{as_tibble}} to turn an existing list into
+  a data frame.
+}
+
diff --git a/man/tribble.Rd b/man/tribble.Rd
new file mode 100644
index 0000000..67a4552
--- /dev/null
+++ b/man/tribble.Rd
@@ -0,0 +1,44 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/tribble.R
+\name{tribble}
+\alias{frame_data}
+\alias{tribble}
+\title{Row-wise tibble creation}
+\usage{
+tribble(...)
+
+frame_data(...)
+}
+\arguments{
+\item{...}{Arguments specifying the structure of a \code{tibble}.
+Variable names should be formulas, and may only appear before the data.}
+}
+\value{
+A \code{\link{tibble}}.
+}
+\description{
+Create \code{\link{tibble}}s laying out the data in rows, rather than
+in columns. This is useful for small tables of data where readability is
+important.  Please see \link{tibble-package} for a general introduction.
+}
+\details{
+\code{frame_data()} is an older name for \code{tribble()}. It will eventually
+be phased out.
+}
+\examples{
+tribble(
+  ~colA, ~colB,
+  "a",   1,
+  "b",   2,
+  "c",   3
+)
+
+# tribble will create a list column if the value in any cell is
+# not a scalar
+tribble(
+  ~x,  ~y,
+  "a", 1:3,
+  "b", 4:6
+)
+}
+
diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp
new file mode 100644
index 0000000..73400a7
--- /dev/null
+++ b/src/RcppExports.cpp
@@ -0,0 +1,18 @@
+// This file was generated by Rcpp::compileAttributes
+// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
+
+#include <Rcpp.h>
+
+using namespace Rcpp;
+
+// matrixToDataFrame
+List matrixToDataFrame(SEXP x);
+RcppExport SEXP tibble_matrixToDataFrame(SEXP xSEXP) {
+BEGIN_RCPP
+    Rcpp::RObject __result;
+    Rcpp::RNGScope __rngScope;
+    Rcpp::traits::input_parameter< SEXP >::type x(xSEXP);
+    __result = Rcpp::wrap(matrixToDataFrame(x));
+    return __result;
+END_RCPP
+}
diff --git a/src/matrixToDataFrame.cpp b/src/matrixToDataFrame.cpp
new file mode 100644
index 0000000..a74c34b
--- /dev/null
+++ b/src/matrixToDataFrame.cpp
@@ -0,0 +1,103 @@
+#include <Rcpp.h>
+using namespace Rcpp;
+
+inline SEXP pairlist_shallow_copy(SEXP p){
+  Shield<SEXP> attr( Rf_cons(CAR(p), R_NilValue) ) ;
+  SEXP q = attr ;
+  SET_TAG(q, TAG(p)) ;
+  p = CDR(p) ;
+  while( !Rf_isNull(p) ){
+    Shield<SEXP> s( Rf_cons(CAR(p), R_NilValue) ) ;
+    SETCDR(q, s) ;
+    q = CDR(q) ;
+    SET_TAG(q, TAG(p)) ;
+    p = CDR(p) ;
+  }
+  return attr ;
+}
+
+inline void copy_attributes(SEXP out, SEXP data){
+  SEXP att = ATTRIB(data) ;
+  if( !Rf_isNull(att) ){
+    SET_ATTRIB( out, pairlist_shallow_copy(ATTRIB(data)) ) ;
+  }
+  SET_OBJECT( out, OBJECT(data) );
+  if( IS_S4_OBJECT(data) ) SET_S4_OBJECT(out) ;
+}
+
+// same as copy_attributes but without names
+inline void copy_most_attributes(SEXP out, SEXP data){
+  copy_attributes(out,data) ;
+  Rf_setAttrib( out, R_NamesSymbol, R_NilValue ) ;
+}
+
+template <int RTYPE>
+IntegerVector get_dim( const Matrix<RTYPE>& x){
+  if (!x.hasAttribute("dim"))
+    stop("`x` is not a matrix");
+
+  IntegerVector dim = x.attr("dim");
+  if (dim.size() != 2)
+    stop("`x` is not a matrix");
+
+  return dim ;
+}
+
+template <int RTYPE>
+CharacterVector get_names( const Matrix<RTYPE>& x, int nc){
+  if( x.hasAttribute("dimnames") ){
+    List dimnames = x.attr("dimnames") ;
+    try {
+      CharacterVector res( dimnames[1] ) ;
+      return res ;
+    } catch(...){}
+  }
+
+  CharacterVector names( nc ) ;
+  for( int i=0; i<nc; i++){
+    names[i] = tfm::format( "V%d", (i+1) ) ;
+  }
+  return names ;
+}
+
+template <int RTYPE>
+List copy_columns( const Matrix<RTYPE>& m ){
+  int ncol = m.ncol(), nrow = m.nrow() ;
+
+  CharacterVector names = get_names(m, ncol) ;
+
+  List out(ncol) ;
+  for(int j=0; j<ncol; j++) {
+    typename Matrix<RTYPE>::ConstColumn column( m.column(j) ) ;
+    Vector<RTYPE> vec( column.begin(), column.end() )  ;
+    copy_most_attributes( vec, m ) ;
+    Rf_setAttrib( vec, R_DimSymbol, R_NilValue ) ;
+    out[j] = vec ;
+  }
+
+  out.attr("names") = names ;
+  out.attr("class") = CharacterVector::create("tbl_df", "tbl", "data.frame");
+  out.attr("row.names") = IntegerVector::create(NA_INTEGER, -nrow);
+
+  return out ;
+}
+
+
+// [[Rcpp::export]]
+List matrixToDataFrame(SEXP x) {
+  SEXPTYPE type = TYPEOF(x);
+
+  switch(type){
+    case LGLSXP:  return copy_columns<LGLSXP>(x) ;  break ;
+    case INTSXP:  return copy_columns<INTSXP>(x) ;  break ;
+    case REALSXP: return copy_columns<REALSXP>(x) ; break ;
+    case CPLXSXP: return copy_columns<CPLXSXP>(x) ; break ;
+    case STRSXP:  return copy_columns<STRSXP>(x) ;  break ;
+    case VECSXP:  return copy_columns<VECSXP>(x) ;  break ;
+    default:
+    break ;
+  }
+  stop( "data type not handled" ) ;
+  return List() ;
+
+}
diff --git a/tests/testthat.R b/tests/testthat.R
new file mode 100644
index 0000000..02ba4cd
--- /dev/null
+++ b/tests/testthat.R
@@ -0,0 +1,3 @@
+library("testthat")
+
+test_check("tibble")
diff --git a/tests/testthat/helper-data.R b/tests/testthat/helper-data.R
new file mode 100644
index 0000000..e0eb913
--- /dev/null
+++ b/tests/testthat/helper-data.R
@@ -0,0 +1,12 @@
+# A data frame with all major types
+df_all <- tibble(
+  a = c(1, 2.5, NA),
+  b = c(1:2, NA),
+  c = c(T, F, NA),
+  d = c("a", "b", NA),
+  e = factor(c("a", "b", NA)),
+  f = as.Date("2015-12-09") + c(1:2, NA),
+  g = as.POSIXct("2015-12-09 10:51:34 UTC") + c(1:2, NA),
+  h = as.list(c(1:2, NA)),
+  i = list(list(1, 2:3), list(4:6), list(NA))
+)
diff --git a/tests/testthat/helper-output.R b/tests/testthat/helper-output.R
new file mode 100644
index 0000000..93f1d4f
--- /dev/null
+++ b/tests/testthat/helper-output.R
@@ -0,0 +1,18 @@
+output_file <- function(filename) file.path("output", filename)
+
+expect_output_file_rel <- function(x, filename) {
+  expect_output_file(x, output_file(filename), update = TRUE)
+}
+
+expect_output_knit <- function(knit, filename, envir = parent.frame()) {
+  expect_asis_output_with_cacheable(substitute(knit), envir)
+  expect_output_file_rel(cat(knit), filename)
+}
+
+expect_asis_output_with_cacheable <- function(knit_call, envir) {
+  asis_output_args <- with_mock(
+    `knitr::asis_output` = function(...) list(...),
+    eval(knit_call, envir = envir)
+  )
+  expect_true(asis_output_args[["cacheable"]])
+}
diff --git a/tests/testthat/helper-unknown-rows.R b/tests/testthat/helper-unknown-rows.R
new file mode 100644
index 0000000..d383c00
--- /dev/null
+++ b/tests/testthat/helper-unknown-rows.R
@@ -0,0 +1,21 @@
+as_unknown_rows <- function(x) {
+  x <- as_tibble(x)
+  class(x) <- c("unknown_rows", class(x))
+  x
+}
+
+dim.unknown_rows <- function(x) {
+  c(NA_integer_, length(x))
+}
+
+registerS3method("dim", "unknown_rows", dim.unknown_rows)
+
+head.unknown_rows <- function(x, n) {
+  head(as.data.frame(x), n)
+}
+
+registerS3method("head", "unknown_rows", head.unknown_rows)
+
+tbl_sum.unknown_rows <- function(x) NULL
+
+registerS3method("tbl_sum", "unknown_rows", tbl_sum.unknown_rows)
diff --git a/tests/testthat/output/glimpse/5.txt b/tests/testthat/output/glimpse/5.txt
new file mode 100644
index 0000000..8b2b886
--- /dev/null
+++ b/tests/testthat/output/glimpse/5.txt
@@ -0,0 +1 @@
+ num 5
diff --git a/tests/testthat/output/glimpse/all-35.txt b/tests/testthat/output/glimpse/all-35.txt
new file mode 100644
index 0000000..7b96f95
--- /dev/null
+++ b/tests/testthat/output/glimpse/all-35.txt
@@ -0,0 +1,11 @@
+Observations: 3
+Variables: 9
+$ a <dbl> 1.0, 2.5, NA
+$ b <int> 1, 2, NA
+$ c <lgl> TRUE, FALSE, NA
+$ d <chr> "a", "b", NA
+$ e <fctr> a, b, NA
+$ f <date> 2015-12-10, 2015-12...
+$ g <dttm> 2015-12-09 10:51:35...
+$ h <list> [1, 2, NA]
+$ i <list> [[1, <2, 3>], <4, 5...
diff --git a/tests/testthat/output/glimpse/all-50.txt b/tests/testthat/output/glimpse/all-50.txt
new file mode 100644
index 0000000..9b17cc6
--- /dev/null
+++ b/tests/testthat/output/glimpse/all-50.txt
@@ -0,0 +1,11 @@
+Observations: 3
+Variables: 9
+$ a <dbl> 1.0, 2.5, NA
+$ b <int> 1, 2, NA
+$ c <lgl> TRUE, FALSE, NA
+$ d <chr> "a", "b", NA
+$ e <fctr> a, b, NA
+$ f <date> 2015-12-10, 2015-12-11, NA
+$ g <dttm> 2015-12-09 10:51:35, 2015-12-09 10...
+$ h <list> [1, 2, NA]
+$ i <list> [[1, <2, 3>], <4, 5, 6>, NA]
diff --git a/tests/testthat/output/glimpse/all-70.txt b/tests/testthat/output/glimpse/all-70.txt
new file mode 100644
index 0000000..2a50498
--- /dev/null
+++ b/tests/testthat/output/glimpse/all-70.txt
@@ -0,0 +1,11 @@
+Observations: 3
+Variables: 9
+$ a <dbl> 1.0, 2.5, NA
+$ b <int> 1, 2, NA
+$ c <lgl> TRUE, FALSE, NA
+$ d <chr> "a", "b", NA
+$ e <fctr> a, b, NA
+$ f <date> 2015-12-10, 2015-12-11, NA
+$ g <dttm> 2015-12-09 10:51:35, 2015-12-09 10:51:36, NA
+$ h <list> [1, 2, NA]
+$ i <list> [[1, <2, 3>], <4, 5, 6>, NA]
diff --git a/tests/testthat/output/glimpse/iris-70.txt b/tests/testthat/output/glimpse/iris-70.txt
new file mode 100644
index 0000000..2d05751
--- /dev/null
+++ b/tests/testthat/output/glimpse/iris-70.txt
@@ -0,0 +1,7 @@
+Observations: 150
+Variables: 5
+$ Sepal.Length <dbl> 5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0, 4.4,...
+$ Sepal.Width  <dbl> 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9,...
+$ Petal.Length <dbl> 1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4,...
+$ Petal.Width  <dbl> 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2,...
+$ Species      <fctr> setosa, setosa, setosa, setosa, setosa, set...
diff --git a/tests/testthat/output/glimpse/iris-empty-70.txt b/tests/testthat/output/glimpse/iris-empty-70.txt
new file mode 100644
index 0000000..55f1ceb
--- /dev/null
+++ b/tests/testthat/output/glimpse/iris-empty-70.txt
@@ -0,0 +1 @@
+Observations: 150
diff --git a/tests/testthat/output/glimpse/mtcars-70.txt b/tests/testthat/output/glimpse/mtcars-70.txt
new file mode 100644
index 0000000..9233329
--- /dev/null
+++ b/tests/testthat/output/glimpse/mtcars-70.txt
@@ -0,0 +1,13 @@
+Observations: 32
+Variables: 11
+$ mpg  <dbl> 21.0, 21.0, 22.8, 21.4, 18.7, 18.1, 14.3, 24.4, 22.8...
+$ cyl  <dbl> 6, 6, 4, 6, 8, 6, 8, 4, 4, 6, 6, 8, 8, 8, 8, 8, 8, 4...
+$ disp <dbl> 160.0, 160.0, 108.0, 258.0, 360.0, 225.0, 360.0, 146...
+$ hp   <dbl> 110, 110, 93, 110, 175, 105, 245, 62, 95, 123, 123, ...
+$ drat <dbl> 3.90, 3.90, 3.85, 3.08, 3.15, 2.76, 3.21, 3.69, 3.92...
+$ wt   <dbl> 2.620, 2.875, 2.320, 3.215, 3.440, 3.460, 3.570, 3.1...
+$ qsec <dbl> 16.46, 17.02, 18.61, 19.44, 17.02, 20.22, 15.84, 20....
+$ vs   <dbl> 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1...
+$ am   <dbl> 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1...
+$ gear <dbl> 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4...
+$ carb <dbl> 4, 4, 1, 1, 2, 1, 4, 2, 2, 4, 4, 3, 3, 3, 4, 4, 4, 1...
diff --git a/tests/testthat/output/trunc_mat/POSIXlt-8-60.txt b/tests/testthat/output/trunc_mat/POSIXlt-8-60.txt
new file mode 100644
index 0000000..bf6ae9a
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/POSIXlt-8-60.txt
@@ -0,0 +1,12 @@
+# A tibble: 12 × 2
+                    x         y
+               <dttm>    <dttm>
+1 2016-01-01 12:34:57 <POSIXlt>
+2 2016-01-01 12:34:58 <POSIXlt>
+3 2016-01-01 12:34:59 <POSIXlt>
+4 2016-01-01 12:35:00 <POSIXlt>
+5 2016-01-01 12:35:01 <POSIXlt>
+6 2016-01-01 12:35:02 <POSIXlt>
+7 2016-01-01 12:35:03 <POSIXlt>
+8 2016-01-01 12:35:04 <POSIXlt>
+# ... with 4 more rows
diff --git a/tests/testthat/output/trunc_mat/all--30.txt b/tests/testthat/output/trunc_mat/all--30.txt
new file mode 100644
index 0000000..b6a3cf9
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/all--30.txt
@@ -0,0 +1,10 @@
+# A tibble: 3 × 9
+      a     b     c     d
+  <dbl> <int> <lgl> <chr>
+1   1.0     1  TRUE     a
+2   2.5     2 FALSE     b
+3    NA    NA    NA  <NA>
+# ... with 5 more variables:
+#   e <fctr>, f <date>,
+#   g <dttm>, h <list>,
+#   i <list>
diff --git a/tests/testthat/output/trunc_mat/all--300.txt b/tests/testthat/output/trunc_mat/all--300.txt
new file mode 100644
index 0000000..b93357c
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/all--300.txt
@@ -0,0 +1,11 @@
+# A tibble: 3 × 9
+      a     b     c     d      e          f                   g         h
+  <dbl> <int> <lgl> <chr> <fctr>     <date>              <dttm>    <list>
+1   1.0     1  TRUE     a      a 2015-12-10 2015-12-09 10:51:35 <int [1]>
+2   2.5     2 FALSE     b      b 2015-12-11 2015-12-09 10:51:36 <int [1]>
+3    NA    NA    NA  <NA>     NA       <NA>                <NA> <int [1]>
+           i
+      <list>
+1 <list [2]>
+2 <list [1]>
+3 <list [1]>
diff --git a/tests/testthat/output/trunc_mat/all-1-30-0.txt b/tests/testthat/output/trunc_mat/all-1-30-0.txt
new file mode 100644
index 0000000..3c9ea8d
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/all-1-30-0.txt
@@ -0,0 +1,6 @@
+# A tibble: 3 × 9
+      a     b     c     d
+  <dbl> <int> <lgl> <chr>
+1     1     1  TRUE     a
+# ... with 2 more rows, and 5
+#   more variables
diff --git a/tests/testthat/output/trunc_mat/all-1-30-2.txt b/tests/testthat/output/trunc_mat/all-1-30-2.txt
new file mode 100644
index 0000000..2e19d85
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/all-1-30-2.txt
@@ -0,0 +1,7 @@
+# A tibble: 3 × 9
+      a     b     c     d
+  <dbl> <int> <lgl> <chr>
+1     1     1  TRUE     a
+# ... with 2 more rows, and 5
+#   more variables: e <fctr>,
+#   f <date>, ...
diff --git a/tests/testthat/output/trunc_mat/all-knit-120.txt b/tests/testthat/output/trunc_mat/all-knit-120.txt
new file mode 100644
index 0000000..d73c346
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/all-knit-120.txt
@@ -0,0 +1,12 @@
+
+
+A tibble: 3 × 9
+
+|a     |b     |c     |d     |e      |f          |g                   |h         |i          |
+|:-----|:-----|:-----|:-----|:------|:----------|:-------------------|:---------|:----------|
+|<dbl> |<int> |<lgl> |<chr> |<fctr> |<date>     |<dttm>              |<list>    |<list>     |
+|1.0   |1     |TRUE  |a     |a      |2015-12-10 |2015-12-09 10:51:35 |<int [1]> |<list [2]> |
+|2.5   |2     |FALSE |b     |b      |2015-12-11 |2015-12-09 10:51:36 |<int [1]> |<list [1]> |
+|NA    |NA    |NA    |<NA>  |NA     |NA         |NA                  |<int [1]> |<list [1]> |
+
+
diff --git a/tests/testthat/output/trunc_mat/all-knit-60.txt b/tests/testthat/output/trunc_mat/all-knit-60.txt
new file mode 100644
index 0000000..2846c03
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/all-knit-60.txt
@@ -0,0 +1,12 @@
+
+
+A tibble: 3 × 9
+
+|a     |b     |c     |d     |e      |f          |
+|:-----|:-----|:-----|:-----|:------|:----------|
+|<dbl> |<int> |<lgl> |<chr> |<fctr> |<date>     |
+|1.0   |1     |TRUE  |a     |a      |2015-12-10 |
+|2.5   |2     |FALSE |b     |b      |2015-12-11 |
+|NA    |NA    |NA    |<NA>  |NA     |NA         |
+
+(with 3 more variables: g <dttm>, h <list>, i <list>)
diff --git a/tests/testthat/output/trunc_mat/iris--70.txt b/tests/testthat/output/trunc_mat/iris--70.txt
new file mode 100644
index 0000000..95a5482
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/iris--70.txt
@@ -0,0 +1,14 @@
+# A tibble: 150 × 5
+   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
+          <dbl>       <dbl>        <dbl>       <dbl>  <fctr>
+1           5.1         3.5          1.4         0.2  setosa
+2           4.9         3.0          1.4         0.2  setosa
+3           4.7         3.2          1.3         0.2  setosa
+4           4.6         3.1          1.5         0.2  setosa
+5           5.0         3.6          1.4         0.2  setosa
+6           5.4         3.9          1.7         0.4  setosa
+7           4.6         3.4          1.4         0.3  setosa
+8           5.0         3.4          1.5         0.2  setosa
+9           4.4         2.9          1.4         0.2  setosa
+10          4.9         3.1          1.5         0.1  setosa
+# ... with 140 more rows
diff --git a/tests/testthat/output/trunc_mat/iris-3-5.txt b/tests/testthat/output/trunc_mat/iris-3-5.txt
new file mode 100644
index 0000000..d5ce8c0
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/iris-3-5.txt
@@ -0,0 +1,23 @@
+# A
+#   tibble:
+#   150
+#   ×
+#   5
+  Sepal.Length
+         <dbl>
+1          5.1
+2          4.9
+3          4.7
+# ...
+#   with
+#   147
+#   more
+#   rows,
+#   and
+#   4
+#   more
+#   variables:
+#   Sepal.Width <dbl>,
+#   Petal.Length <dbl>,
+#   Petal.Width <dbl>,
+#   Species <fctr>
diff --git a/tests/testthat/output/trunc_mat/iris-5-30.txt b/tests/testthat/output/trunc_mat/iris-5-30.txt
new file mode 100644
index 0000000..9109a43
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/iris-5-30.txt
@@ -0,0 +1,13 @@
+# A tibble: 150 × 5
+  Sepal.Length Sepal.Width
+         <dbl>       <dbl>
+1          5.1         3.5
+2          4.9         3.0
+3          4.7         3.2
+4          4.6         3.1
+5          5.0         3.6
+# ... with 145 more rows, and
+#   3 more variables:
+#   Petal.Length <dbl>,
+#   Petal.Width <dbl>,
+#   Species <fctr>
diff --git a/tests/testthat/output/trunc_mat/iris_unk-10-70.txt b/tests/testthat/output/trunc_mat/iris_unk-10-70.txt
new file mode 100644
index 0000000..1851eb9
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/iris_unk-10-70.txt
@@ -0,0 +1,13 @@
+   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
+          <dbl>       <dbl>        <dbl>       <dbl>  <fctr>
+1           5.1         3.5          1.4         0.2  setosa
+2           4.9         3.0          1.4         0.2  setosa
+3           4.7         3.2          1.3         0.2  setosa
+4           4.6         3.1          1.5         0.2  setosa
+5           5.0         3.6          1.4         0.2  setosa
+6           5.4         3.9          1.7         0.4  setosa
+7           4.6         3.4          1.4         0.3  setosa
+8           5.0         3.4          1.5         0.2  setosa
+9           4.4         2.9          1.4         0.2  setosa
+10          4.9         3.1          1.5         0.1  setosa
+# ... with more rows
diff --git a/tests/testthat/output/trunc_mat/long-5-30.txt b/tests/testthat/output/trunc_mat/long-5-30.txt
new file mode 100644
index 0000000..b21cc49
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/long-5-30.txt
@@ -0,0 +1,9 @@
+# A tibble: 10,000 × 1
+      a
+  <int>
+1     1
+2     2
+3     3
+4     4
+5     5
+# ... with 9,995 more rows
diff --git a/tests/testthat/output/trunc_mat/long_unk-5-30.txt b/tests/testthat/output/trunc_mat/long_unk-5-30.txt
new file mode 100644
index 0000000..8026111
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/long_unk-5-30.txt
@@ -0,0 +1,8 @@
+      a
+  <int>
+1     1
+2     2
+3     3
+4     4
+5     5
+# ... with more rows
diff --git a/tests/testthat/output/trunc_mat/mtcars-8-30.txt b/tests/testthat/output/trunc_mat/mtcars-8-30.txt
new file mode 100644
index 0000000..086f64c
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/mtcars-8-30.txt
@@ -0,0 +1,17 @@
+# A tibble: 32 × 11
+    mpg   cyl  disp    hp
+* <dbl> <dbl> <dbl> <dbl>
+1  21.0     6 160.0   110
+2  21.0     6 160.0   110
+3  22.8     4 108.0    93
+4  21.4     6 258.0   110
+5  18.7     8 360.0   175
+6  18.1     6 225.0   105
+7  14.3     8 360.0   245
+8  24.4     4 146.7    62
+# ... with 24 more rows, and
+#   7 more variables:
+#   drat <dbl>, wt <dbl>,
+#   qsec <dbl>, vs <dbl>,
+#   am <dbl>, gear <dbl>,
+#   carb <dbl>
diff --git a/tests/testthat/output/trunc_mat/mtcars-knit-60.txt b/tests/testthat/output/trunc_mat/mtcars-knit-60.txt
new file mode 100644
index 0000000..4ac2513
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/mtcars-knit-60.txt
@@ -0,0 +1,20 @@
+
+
+data.frame [32 × 11]
+
+|mpg   |cyl   |disp  |hp    |drat  |wt    |qsec  |vs    |am    |
+|:-----|:-----|:-----|:-----|:-----|:-----|:-----|:-----|:-----|
+|<dbl> |<dbl> |<dbl> |<dbl> |<dbl> |<dbl> |<dbl> |<dbl> |<dbl> |
+|21.0  |6     |160.0 |110   |3.90  |2.620 |16.46 |0     |1     |
+|21.0  |6     |160.0 |110   |3.90  |2.875 |17.02 |0     |1     |
+|22.8  |4     |108.0 |93    |3.85  |2.320 |18.61 |1     |1     |
+|21.4  |6     |258.0 |110   |3.08  |3.215 |19.44 |1     |0     |
+|18.7  |8     |360.0 |175   |3.15  |3.440 |17.02 |0     |0     |
+|18.1  |6     |225.0 |105   |2.76  |3.460 |20.22 |1     |0     |
+|14.3  |8     |360.0 |245   |3.21  |3.570 |15.84 |0     |0     |
+|24.4  |4     |146.7 |62    |3.69  |3.190 |20.00 |1     |0     |
+|22.8  |4     |140.8 |95    |3.92  |3.150 |22.90 |1     |0     |
+|19.2  |6     |167.6 |123   |3.92  |3.440 |18.30 |1     |0     |
+
+(with 22 more rows, and 2 more variables: gear <dbl>,
+  carb <dbl>)
diff --git a/tests/testthat/output/trunc_mat/wide-8-60.txt b/tests/testthat/output/trunc_mat/wide-8-60.txt
new file mode 100644
index 0000000..a7064f8
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/wide-8-60.txt
@@ -0,0 +1,6 @@
+# A tibble: 3 × 2
+  成交日期 合同录入日期
+     <int>        <int>
+1        1            4
+2        2            5
+3        3            6
diff --git a/tests/testthat/output/trunc_mat/zero-cols_unk-5-30.txt b/tests/testthat/output/trunc_mat/zero-cols_unk-5-30.txt
new file mode 100644
index 0000000..5e58436
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/zero-cols_unk-5-30.txt
@@ -0,0 +1,2 @@
+# ... with at least 5 rows
+#   total
diff --git a/tests/testthat/output/trunc_mat/zero-rows_unk-5-30.txt b/tests/testthat/output/trunc_mat/zero-rows_unk-5-30.txt
new file mode 100644
index 0000000..889cd83
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/zero-rows_unk-5-30.txt
@@ -0,0 +1,6 @@
+# ... with 5 variables:
+#   Sepal.Length <dbl>,
+#   Sepal.Width <dbl>,
+#   Petal.Length <dbl>,
+#   Petal.Width <dbl>,
+#   Species <fctr>
diff --git a/tests/testthat/output/trunc_mat/zero_cols-5-30.txt b/tests/testthat/output/trunc_mat/zero_cols-5-30.txt
new file mode 100644
index 0000000..c926475
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/zero_cols-5-30.txt
@@ -0,0 +1 @@
+# A tibble: 150 × 0
diff --git a/tests/testthat/output/trunc_mat/zero_rows--30.txt b/tests/testthat/output/trunc_mat/zero_rows--30.txt
new file mode 100644
index 0000000..bcd6586
--- /dev/null
+++ b/tests/testthat/output/trunc_mat/zero_rows--30.txt
@@ -0,0 +1,3 @@
+# A tibble: 0 × 2
+# ... with 2 variables:
+#   a <chr>, b <lgl>
diff --git a/tests/testthat/test-add.R b/tests/testthat/test-add.R
new file mode 100644
index 0000000..d3d6f82
--- /dev/null
+++ b/tests/testthat/test-add.R
@@ -0,0 +1,216 @@
+context("add")
+
+# add_row ---------------------------------------------------------------
+
+test_that("can add new row", {
+  df_all_new <- add_row(df_all, a = 4, b = 3L)
+  expect_identical(colnames(df_all_new), colnames(df_all))
+  expect_identical(nrow(df_all_new), nrow(df_all) + 1L)
+  expect_identical(df_all_new$a, c(df_all$a, 4))
+  expect_identical(df_all_new$b, c(df_all$b, 3L))
+  expect_identical(df_all_new$c, c(df_all$c, NA))
+})
+
+test_that("add_row() keeps class of object", {
+  iris_new <- add_row(iris, Species = "unknown")
+  expect_equal(class(iris), class(iris_new))
+
+  iris_new <- add_row(as_tibble(iris), Species = "unknown")
+  expect_equal(class(as_tibble(iris)), class(iris_new))
+})
+
+test_that("add_row() keeps class of object when adding in the middle", {
+  iris_new <- add_row(iris, Species = "unknown", .after = 10)
+  expect_equal(class(iris), class(iris_new))
+
+  iris_new <- add_row(as_tibble(iris), Species = "unknown")
+  expect_equal(class(as_tibble(iris)), class(iris_new))
+})
+
+test_that("add_row() keeps class of object when adding in the beginning", {
+  iris_new <- add_row(iris, Species = "unknown", .after = 0)
+  expect_equal(class(iris), class(iris_new))
+
+  iris_new <- add_row(as_tibble(iris), Species = "unknown")
+  expect_equal(class(as_tibble(iris)), class(iris_new))
+})
+
+test_that("adds empty row if no arguments", {
+  new_iris_row <- add_row(iris)[nrow(iris) + 1, , drop = TRUE]
+  expect_true(all(is.na(new_iris_row)))
+})
+
+test_that("error if adding row with unknown variables", {
+  expect_error(add_row(tibble(a = 3), xxyzy = "err"),
+               "would add new variables")
+})
+
+test_that("can add multiple rows", {
+  df <- tibble(a = 3L)
+  df_new <- add_row(df, a = 4:5)
+  expect_identical(nrow(df_new), nrow(df) + 2L)
+  expect_identical(df_new$a, 3:5)
+})
+
+test_that("can recycle when adding rows", {
+  iris_new <- add_row(iris, Sepal.Length = -1:-2, Species = "unknown")
+  expect_identical(nrow(iris_new), nrow(iris) + 2L)
+  expect_identical(iris_new$Sepal.Length, c(iris$Sepal.Length, -1:-2))
+  expect_identical(as.character(iris_new$Species),
+                   c(as.character(iris$Species), "unknown", "unknown"))
+})
+
+test_that("can add as first row via .before = 1", {
+  df <- tibble(a = 3L)
+  df_new <- add_row(df, a = 2L, .before = 1)
+  expect_identical(nrow(df_new), nrow(df) + 1L)
+  expect_identical(df_new$a, 2:3)
+})
+
+test_that("can add as first row via .after = 0", {
+  df <- tibble(a = 3L)
+  df_new <- add_row(df, a = 2L, .after = 0)
+  expect_identical(nrow(df_new), nrow(df) + 1L)
+  expect_identical(df_new$a, 2:3)
+})
+
+test_that("can add row inbetween", {
+  df <- tibble(a = 1:3)
+  df_new <- add_row(df, a = 4:5, .after = 2)
+  expect_identical(nrow(df_new), nrow(df) + 2L)
+  expect_identical(df_new$a, c(1:2, 4:5, 3L))
+})
+
+test_that("error if both .before and .after are given", {
+  df <- tibble(a = 1:3)
+  expect_error(add_row(df, a = 4:5, .after = 2, .before = 3))
+})
+
+test_that("missing row names stay missing when adding row", {
+  expect_false(has_rownames(iris))
+  expect_false(has_rownames(add_row(iris, Species = "unknown", .after = 0)))
+  expect_false(has_rownames(add_row(iris, Species = "unknown", .after = nrow(iris))))
+  expect_false(has_rownames(add_row(iris, Species = "unknown", .before = 10)))
+})
+
+test_that("adding to a list column adds a NULL value (#148)", {
+  expect_null(add_row(data_frame(a = as.list(1:3)))$a[[4]])
+  expect_null(add_row(data_frame(a = as.list(1:3)), .before = 1)$a[[1]])
+  expect_null(add_row(data_frame(a = as.list(1:3)), .after = 1)$a[[2]])
+  expect_null(add_row(data_frame(a = as.list(1:3), b = 1:3), b = 4:6)$a[[5]])
+})
+
+# add_column ------------------------------------------------------------
+
+test_that("can add new column", {
+  df_all_new <- add_column(df_all, j = 1:3, k = 3:1)
+  expect_identical(nrow(df_all_new), nrow(df_all))
+  expect_identical(df_all_new[seq_along(df_all)], df_all)
+  expect_identical(df_all_new$j, 1:3)
+  expect_identical(df_all_new$k, 3:1)
+})
+
+test_that("add_column() keeps class of object", {
+  iris_new <- add_column(iris, x = 1:150)
+  expect_equal(class(iris), class(iris_new))
+
+  iris_new <- add_column(as_tibble(iris), x = 1:150)
+  expect_equal(class(as_tibble(iris)), class(iris_new))
+})
+
+test_that("add_column() keeps class of object when adding in the middle", {
+  iris_new <- add_column(iris, x = 1:150, .after = 3)
+  expect_equal(class(iris), class(iris_new))
+
+  iris_new <- add_column(as_tibble(iris), x = 1:150)
+  expect_equal(class(as_tibble(iris)), class(iris_new))
+})
+
+test_that("add_column() keeps class of object when adding in the beginning", {
+  iris_new <- add_column(iris, x = 1:150, .after = 0)
+  expect_equal(class(iris), class(iris_new))
+
+  iris_new <- add_column(as_tibble(iris), x = 1:150)
+  expect_equal(class(as_tibble(iris)), class(iris_new))
+})
+
+test_that("add_column() keeps unchanged if no arguments", {
+  expect_identical(iris, add_column(iris))
+})
+
+test_that("error if adding existing columns", {
+  expect_error(add_column(tibble(a = 3), a = 5),
+               "Columns already in data frame")
+})
+
+test_that("error if adding wrong number of rows with add_column()", {
+  expect_error(add_column(tibble(a = 3), b = 4:5),
+               "Expected 1 rows, got 2")
+})
+
+test_that("can add multiple columns", {
+  df <- tibble(a = 1:3)
+  df_new <- add_column(df, b = 4:6, c = 3:1)
+  expect_identical(ncol(df_new), ncol(df) + 2L)
+  expect_identical(df_new$b, 4:6)
+  expect_identical(df_new$c, 3:1)
+})
+
+test_that("can recycle when adding columns", {
+  df <- tibble(a = 1:3)
+  df_new <- add_column(df, b = 4, c = 3:1)
+  expect_identical(ncol(df_new), ncol(df) + 2L)
+  expect_identical(df_new$b, rep(4, 3))
+  expect_identical(df_new$c, 3:1)
+})
+
+test_that("can add as first column via .before = 1", {
+  df <- tibble(a = 3L)
+  df_new <- add_column(df, b = 2L, .before = 1)
+  expect_identical(ncol(df_new), ncol(df) + 1L)
+  expect_identical(names(df_new), c("b", "a"))
+  expect_identical(df_new$b, 2L)
+})
+
+test_that("can add as first column via .after = 0", {
+  df <- tibble(a = 3L)
+  df_new <- add_column(df, b = 2L, .after = 0)
+  expect_identical(ncol(df_new), ncol(df) + 1L)
+  expect_identical(names(df_new), c("b", "a"))
+  expect_identical(df_new$b, 2L)
+})
+
+test_that("can add column inbetween", {
+  df <- tibble(a = 1:3, c = 4:6)
+  df_new <- add_column(df, b = -1:1, .after = 1)
+  expect_identical(ncol(df_new), ncol(df) + 1L)
+  expect_identical(names(df_new), c("a", "b", "c"))
+  expect_identical(df_new$b, -1:1)
+})
+
+test_that("can add column relative to named column", {
+  df <- tibble(a = 1:3, c = 4:6)
+  df_new <- add_column(df, b = -1:1, .before = "c")
+  expect_identical(ncol(df_new), ncol(df) + 1L)
+  expect_identical(names(df_new), c("a", "b", "c"))
+  expect_identical(df_new$b, -1:1)
+})
+
+test_that("error if both .before and .after are given", {
+  df <- tibble(a = 1:3)
+  expect_error(add_column(df, b = 4:6, .after = 2, .before = 3),
+               "Can't specify both [.]before and [.]after")
+})
+
+test_that("error if column named by .before or .after not found", {
+  df <- tibble(a = 1:3)
+  expect_error(add_column(df, b = 4:6, .after = "x"), "Unknown columns")
+  expect_error(add_column(df, b = 4:6, .before = "x"), "Unknown columns")
+})
+
+test_that("missing row names stay missing when adding column", {
+  expect_false(has_rownames(iris))
+  expect_false(has_rownames(add_column(iris, x = 1:150, .after = 0)))
+  expect_false(has_rownames(add_column(iris, x = 1:150, .after = ncol(iris))))
+  expect_false(has_rownames(add_column(iris, x = 1:150, .before = 2)))
+})
diff --git a/tests/testthat/test-data-frame.R b/tests/testthat/test-data-frame.R
new file mode 100644
index 0000000..f503e2b
--- /dev/null
+++ b/tests/testthat/test-data-frame.R
@@ -0,0 +1,210 @@
+context("tibble")
+
+test_that("tibble returns correct number of rows with all combinatinos", {
+
+  expect_equal(nrow(tibble(value = 1:10)), 10L)
+
+  expect_equal(nrow(tibble(value = 1:10, name = "recycle_me")), 10L)
+
+  expect_equal(nrow(tibble(name = "recycle_me", value = 1:10)), 10L)
+
+  expect_equal(nrow(tibble(name = "recycle_me", value = 1:10, value2 = 11:20)), 10L)
+
+  expect_equal(nrow(tibble(value = 1:10, name = "recycle_me", value2 = 11:20)), 10L)
+
+})
+
+test_that("can't make tibble containing data.frame or array", {
+  expect_error(tibble(mtcars), "must be a 1d atomic vector or list")
+  expect_error(tibble(diag(5)), "must be a 1d atomic vector or list")
+})
+
+test_that("dim attribute is stripped of 1D array (#84)", {
+  expect_null(dim(tibble(x = array(1:3))$x))
+})
+
+test_that("bogus columns raise an error", {
+  expect_error(as_tibble(list(1)), "named")
+  expect_error(tibble(a = NULL), "1d atomic vector or list")
+  expect_error(tibble(a = ~a), "1d atomic vector or list")
+  expect_error(tibble(a = new.env()), "1d atomic vector or list")
+  expect_error(tibble(a = quote(a)), "1d atomic vector or list")
+})
+
+test_that("length 1 vectors are recycled", {
+  expect_equal(nrow(tibble(x = 1:10)), 10)
+  expect_equal(nrow(tibble(x = 1:10, y = 1)), 10)
+  expect_error(
+    nrow(tibble(x = 1:10, y = 1:2)),
+    "Variables must be length 1 or 10"
+  )
+})
+
+test_that("missing names are imputed from call", {
+  x <- 1:10
+  df <- tibble(x, y = x)
+  expect_equal(names(df), c("x", "y"))
+})
+
+test_that("empty input makes 0 x 0 tbl_df", {
+  zero <- tibble()
+  expect_is(zero, "tbl_df")
+  expect_equal(dim(zero), c(0L, 0L))
+  expect_identical(attr(zero, "names"), character(0L))
+})
+
+test_that("SE version", {
+  expect_identical(tibble_(list(a = ~1:10)), tibble(a = 1:10))
+})
+
+test_that("names are stripped from vectors", {
+  foo <- tibble(x = c(y = 1, z = 2))
+  expect_equal(names(foo), "x")
+  expect_null(names(foo$x))
+})
+
+test_that("names in list columns are preserved", {
+  foo <- tibble(x = list(y = 1:3, z = 4:5))
+  expect_equal(names(foo), "x")
+  expect_equal(names(foo$x), c("y", "z"))
+})
+
+test_that("attributes are preserved", {
+  df <- structure(
+    data.frame( x = 1:10, g1 = rep(1:2, each = 5), g2 = rep(1:5, 2) ),
+    meta = "this is important"
+  )
+  res <- as_tibble(df)
+
+  expect_identical(attr(res, "meta"), attr(df, "meta"))
+})
+
+test_that("tibble aliases", {
+  expect_identical(data_frame, tibble)
+  expect_identical(data_frame_, tibble_)
+})
+
+
+# as_tibble -----------------------------------------------------------
+
+test_that("columns must be same length", {
+  l <- list(x = 1:2, y = 1:3)
+  expect_error(as_tibble(l), "must be length 1 or")
+})
+
+test_that("columns must be named", {
+  l1 <- list(1:10)
+  l2 <- list(x = 1, 2)
+
+  expect_error(as_tibble(l1), "must be named")
+  expect_error(as_tibble(l2), "must be named")
+})
+
+test_that("can't coerce list data.frame or array", {
+  expect_error(as_tibble(list(x = mtcars)), "must be a 1d atomic vector or list")
+  expect_error(as_tibble(list(x = diag(5))), "must be a 1d atomic vector or list")
+})
+
+test_that("empty list() makes 0 x 0 tbl_df", {
+  zero <- as_tibble(list())
+  expect_is(zero, "tbl_df")
+  expect_equal(dim(zero), c(0L, 0L))
+})
+
+
+test_that("NULL makes 0 x 0 tbl_df", {
+  nnnull <- as_tibble(NULL)
+  expect_is(nnnull, "tbl_df")
+  expect_equal(dim(nnnull), c(0L, 0L))
+})
+
+
+test_that("as_tibble.tbl_df() leaves classes unchanged (#60)", {
+  df <- tibble()
+  expect_equal(class(df),
+               c("tbl_df", "tbl", "data.frame"))
+  expect_equal(class(structure(df, class = c("my_df", class(df)))),
+               c("my_df", "tbl_df", "tbl", "data.frame"))
+})
+
+
+test_that("Can convert tables to data frame", {
+  mtcars_table <- xtabs(mtcars, formula = ~vs+am+cyl)
+
+  mtcars_tbl <- as_tibble(mtcars_table)
+  expect_equal(names(mtcars_tbl), c(names(dimnames(mtcars_table)), "n"))
+
+  mtcars_tbl <- as_tibble(mtcars_table, "Freq")
+  expect_equal(names(mtcars_tbl), c(names(dimnames(mtcars_table)), "Freq"))
+})
+
+
+test_that("Can convert atomic vectors to data frame", {
+  expect_equal(as_tibble(1:3), tibble(value = 1:3))
+  expect_equal(as_tibble(c(TRUE, FALSE, NA)), tibble(value = c(TRUE, FALSE, NA)))
+  expect_equal(as_tibble(1.5:3.5), tibble(value = 1.5:3.5))
+  expect_equal(as_tibble(letters), tibble(value = letters))
+})
+
+
+test_that("Can convert named atomic vectors to data frame", {
+  expect_equal(as_tibble(setNames(nm = 1:3)), tibble(value = 1:3))
+  expect_equal(as_tibble(setNames(nm = c(TRUE, FALSE, NA))), tibble(value = c(TRUE, FALSE, NA)))
+  expect_equal(as_tibble(setNames(nm = 1.5:3.5)), tibble(value = 1.5:3.5))
+  expect_equal(as_tibble(setNames(nm = letters)), tibble(value = letters))
+})
+
+
+test_that("as_tibble alias", {
+  expect_identical(as_data_frame(NULL), as_tibble(NULL))
+})
+
+
+# Validation --------------------------------------------------------------
+
+test_that("2d object isn't a valid column", {
+  expect_error(
+    check_tibble(list(x = mtcars)),
+    "Each variable must be a 1d atomic vector"
+  )
+})
+
+test_that("POSIXlt isn't a valid column", {
+  expect_error(
+    check_tibble(list(x = as.POSIXlt(Sys.time()))),
+    "Date/times must be stored as POSIXct"
+  )
+})
+
+test_that("NULL isn't a valid column", {
+  expect_error(
+    check_tibble(list(a = NULL)),
+    "Each variable must be a 1d atomic vector"
+  )
+})
+
+test_that("columns must be named (#1101)", {
+  l <- list(1:10, 1:10)
+
+  expect_error(
+    check_tibble(l),
+    "Each variable must be named"
+  )
+
+  expect_error(
+    check_tibble(setNames(l, c("x", ""))),
+    "Each variable must be named"
+  )
+
+  expect_error(
+    check_tibble(setNames(l, c("x", NA))),
+    "Each variable must be named"
+  )
+})
+
+test_that("names must be unique (#820)", {
+  expect_error(
+    check_tibble(list(x = 1, x = 2)),
+    "Each variable must have a unique name"
+  )
+})
diff --git a/tests/testthat/test-enframe.R b/tests/testthat/test-enframe.R
new file mode 100644
index 0000000..77f5e78
--- /dev/null
+++ b/tests/testthat/test-enframe.R
@@ -0,0 +1,22 @@
+context("enframe")
+
+test_that("can convert unnamed vector", {
+  expect_identical(enframe(3:1),
+                   tibble(name = 1:3, value = 3:1))
+})
+
+test_that("can convert named vector", {
+  expect_identical(enframe(c(a = 2, b = 1)),
+                   tibble(name = letters[1:2], value = as.numeric(2:1)))
+})
+
+test_that("can convert zero-length vector", {
+  expect_identical(enframe(logical()),
+                   tibble(name = integer(), value = logical()))
+})
+
+test_that("can use custom names", {
+  expect_identical(enframe(letters, name = "index", value = "letter"),
+                   tibble(index = seq_along(letters),
+                          letter = letters))
+})
diff --git a/tests/testthat/test-equality.R b/tests/testthat/test-equality.R
new file mode 100644
index 0000000..fa1d481
--- /dev/null
+++ b/tests/testthat/test-equality.R
@@ -0,0 +1,92 @@
+context("Equality")
+
+test_that("data frames equal to themselves", {
+  expect_true(all.equal(as_tibble(mtcars), as_tibble(mtcars)))
+  expect_true(all.equal(as_tibble(iris), as_tibble(iris)))
+  expect_true(all.equal(as_tibble(df_all), as_tibble(df_all)))
+})
+
+test_that("data frames equal to random permutations of themselves", {
+  scramble <- function(x){
+    x[sample(nrow(x)), sample(ncol(x)), drop = FALSE]
+  }
+
+  expect_equal(as_tibble(mtcars), as_tibble(scramble(mtcars)))
+  expect_equal(as_tibble(iris), as_tibble(scramble(iris)))
+  expect_equal(as_tibble(df_all), as_tibble(scramble(df_all)))
+})
+
+test_that("data frames not equal if missing row", {
+  expect_match(all.equal(as_tibble(mtcars), as_tibble(mtcars)[-1, ]), "Different number of rows")
+  expect_match(all.equal(as_tibble(iris), as_tibble(iris)[-1, ]),     "Different number of rows")
+  expect_match(all.equal(as_tibble(df_all), as_tibble(df_all)[-1, ]), "Different number of rows")
+})
+
+test_that("data frames not equal if missing col", {
+  expect_match(all.equal(as_tibble(mtcars), as_tibble(mtcars)[, -1]), "Cols in x but not y: 'mpg'")
+  expect_match(all.equal(as_tibble(mtcars)[, -1], as_tibble(mtcars)), "Cols in y but not x: 'mpg'")
+  expect_match(all.equal(as_tibble(iris), as_tibble(iris)[, -1]),     "Cols in x but not y: 'Sepal.Length'")
+  expect_match(all.equal(as_tibble(df_all), as_tibble(df_all)[, -1]), "Cols in x but not y: 'a'")
+  expect_match(all.equal(as_tibble(mtcars), rev(as_tibble(mtcars)),
+                         ignore_col_order = FALSE),
+               "Column names same but in different order")
+})
+
+test_that("factors equal only if levels equal", {
+  df1 <- data.frame(x = factor(c("a", "b")))
+  df2 <- data.frame(x = factor(c("a", "d")))
+  expect_match(all.equal(as_tibble(df1), as_tibble(df2)), "Factor levels not equal for column x" )
+})
+
+test_that("all.equal.data.frame handles data.frames with NULL names", {
+  x <- data.frame(LETTERS[1:3], rnorm(3))
+  names(x) <- NULL
+  expect_true(all.equal(x,x))
+})
+
+test_that( "data frame equality test with ignore_row_order=TRUE detects difference in number of rows. #1065", {
+  DF1 <- tibble(a = 1:4, b = letters[1:4])
+  DF2 <- tibble(a = c(1:4,4L), b = letters[c(1:4,4L)])
+  expect_false( isTRUE(all.equal(DF1, DF2, ignore_row_order=TRUE)))
+
+  DF1 <- tibble(a = c(1:4,2L), b = letters[c(1:4,2L)])
+  DF2 <- tibble(a = c(1:4,4L), b = letters[c(1:4,4L)])
+  expect_false(isTRUE(all.equal(DF1, DF2, ignore_row_order=TRUE)))
+
+})
+
+test_that("all.equal handles NA_character_ correctly. #1095", {
+  d1 <- tibble(x = c(NA_character_))
+  expect_true(all.equal(d1, d1))
+
+  d2 <- tibble( x = c(NA_character_, "foo", "bar" ) )
+  expect_true(all.equal(d2, d2))
+})
+
+test_that( "handle Date columns of different types, integer and numeric (#1204)", {
+  a <- data.frame(date = as.Date("2015-06-07"))
+  b <- data.frame(date = structure( as.integer(a$date), class = "Date" ) )
+  expect_true( all.equal(a, b) )
+})
+
+test_that("equality test fails when convert is FALSE and types don't match (#1484)", {
+  df1 <- tibble(x = "a")
+  df2 <- tibble(x = factor("a"))
+
+  expect_equal( all.equal(df1, df2, convert = FALSE), "Incompatible type for column x: x character, y factor" )
+  expect_warning( all.equal(df1, df2, convert = TRUE) )
+})
+
+test_that("equality handles data frames with 0 columns (#1506)", {
+  df0 <- tibble(x = numeric(0), y = character(0) )
+  expect_equal(df0, df0)
+})
+
+test_that("equality fails if types different", {
+  expect_equal(all.equal(as_tibble(iris), iris), "Different types: x 'tbl_df', 'tbl', 'data.frame', y 'data.frame'")
+})
+
+test_that("equality works for data frames with columns named like arguments to order() (#107)", {
+  U <- data_frame(method = c("old","new"), time = c(4.5, 2.3))
+  expect_equal(U, U)
+})
diff --git a/tests/testthat/test-glimpse.R b/tests/testthat/test-glimpse.R
new file mode 100644
index 0000000..1641859
--- /dev/null
+++ b/tests/testthat/test-glimpse.R
@@ -0,0 +1,66 @@
+context("Glimpse")
+
+test_that("format_v for values", {
+  expect_equal(format_v(1), "1")
+  expect_equal(format_v(1:3), c("1", "2", "3"))
+  expect_equal(format_v(NA), "NA")
+  expect_equal(format_v(TRUE), "TRUE")
+  expect_equal(format_v(logical()), character())
+})
+
+test_that("format_v for character", {
+  expect_equal(format_v("1"), paste0('"', "1", '"'))
+  expect_equal(format_v(letters), paste0('"', letters, '"'))
+  expect_equal(format_v(NA_character_), "NA")
+  expect_equal(format_v(character()), character())
+})
+
+test_that("format_v for list", {
+  expect_equal(format_v(list(1:3)), "<1, 2, 3>")
+  expect_equal(format_v(as.list(1:3)), "[1, 2, 3]")
+  expect_equal(format_v(list(1:3, 4)), "[<1, 2, 3>, 4]")
+  expect_equal(format_v(list(1:3, 4:5)), "[<1, 2, 3>, <4, 5>]")
+  expect_equal(format_v(list()), "[]")
+
+  skip("format_v corner cases")
+  expect_equal(format_v(list(list())), "[[]]")
+  expect_equal(format_v(list(character())), "[<>]")
+  expect_equal(format_v(list(1:3, list(4))), "[<1, 2, 3>, [4]]")
+  expect_equal(format_v(list(1:3, list(4:5))), "[<1, 2, 3>, [<4, 5>]]")
+})
+
+test_that("glimpse output matches known output", {
+  expect_output_file_rel(
+    glimpse(as_tibble(mtcars), width = 70L),
+    "glimpse/mtcars-70.txt")
+
+  expect_output_file_rel(
+    glimpse(as_tibble(iris), width = 70L),
+    "glimpse/iris-70.txt")
+
+  expect_output_file_rel(
+    glimpse(as_tibble(iris[integer()]), width = 70L),
+    "glimpse/iris-empty-70.txt")
+
+  expect_output_file_rel(
+    glimpse(as_tibble(df_all), width = 70L),
+    "glimpse/all-70.txt")
+
+  withr::with_options(
+    list(tibble.width = 50),
+    expect_output_file_rel(
+      glimpse(as_tibble(df_all)),
+      "glimpse/all-50.txt")
+  )
+
+  withr::with_options(
+    list(tibble.width = 35),
+    expect_output_file_rel(
+      glimpse(as_tibble(df_all)),
+      "glimpse/all-35.txt")
+  )
+
+  expect_output_file_rel(
+    glimpse(5),
+    "glimpse/5.txt")
+})
diff --git a/tests/testthat/test-has-name.R b/tests/testthat/test-has-name.R
new file mode 100644
index 0000000..549740f
--- /dev/null
+++ b/tests/testthat/test-has-name.R
@@ -0,0 +1,24 @@
+context("has-name")
+
+test_that("basic", {
+  expect_true(has_name(iris, "Species"))
+  expect_false(has_name(mtcars, "gears"))
+})
+
+test_that("other types", {
+  expect_true(has_name(list(a = 1), "a"))
+  expect_true(has_name(c(a = 1), "a"))
+})
+
+test_that("vectorized", {
+  expect_equal(has_name(list(a = 1), letters ), c(TRUE, rep(FALSE, 25)))
+})
+
+test_that("NA", {
+  expect_false(has_name(list(a = 1), NA))
+})
+
+test_that("unnamed", {
+  expect_false(has_name(1, "a"))
+  expect_true(has_name(1, ""))
+})
diff --git a/tests/testthat/test-lst.R b/tests/testthat/test-lst.R
new file mode 100644
index 0000000..43a4a77
--- /dev/null
+++ b/tests/testthat/test-lst.R
@@ -0,0 +1,13 @@
+context("lst")
+
+test_that("lst handles named and unnamed NULL arguments", {
+  expect_equivalent(lst(NULL), list("NULL" = NULL))
+  expect_identical(lst(a = NULL), list(a = NULL))
+  expect_identical(lst(NULL, b = NULL, 1:3),
+                   list("NULL" = NULL, b = NULL, "1:3" = 1:3))
+})
+
+test_that("lst handles internal references", {
+  expect_identical(lst(a = 1, b = a), list(a = 1, b = 1))
+  expect_identical(lst(a = NULL, b = a), list(a = NULL, b = NULL))
+})
diff --git a/tests/testthat/test-matrix.R b/tests/testthat/test-matrix.R
new file mode 100644
index 0000000..5b3a1a7
--- /dev/null
+++ b/tests/testthat/test-matrix.R
@@ -0,0 +1,70 @@
+context("matrix")
+
+test_that("correct rows and cols", {
+  x <- matrix(1:6, nrow = 2)
+  out <- as_tibble(x)
+
+  expect_equal(dim(out), c(2, 3))
+})
+
+test_that("preserves col names", {
+  x <- matrix(1:4, nrow = 2)
+  colnames(x) <- c("a", "b")
+
+  out <- as_tibble(x)
+  expect_equal(names(out), c("a", "b"))
+})
+
+test_that("creates col names", {
+  x <- matrix(1:4, nrow = 2)
+
+  out <- as_tibble(x)
+  expect_equal(names(out), c("V1", "V2"))
+})
+
+test_that("preserves attributes except dim and names", {
+  date <- Sys.Date() + 0:3
+  dim(date) <- c(2, 2)
+  colnames(date) <- c("a", "b")
+  attr(date, "special") <- 42
+
+  out <- as_tibble.matrix(date)
+  expect_null(attributes(out[[1]])$names)
+  expect_equal(attributes(out[[1]])$class, "Date")
+  expect_equal(attributes(out[[2]])$special, 42)
+})
+
+test_that("properly handles poly class (#110)", {
+  p <- poly(1:6, 3)
+  p_df <- as_tibble(p)
+
+  expect_equal(names(p_df), colnames(p))
+  expect_equal(class(p_df[[1L]]), class(p[,1]))
+})
+
+test_that("handles atomic vectors", {
+  x <- matrix(TRUE, nrow = 2)
+  out <- as_tibble(x)
+  expect_equal(out[[1]], c(TRUE, TRUE))
+
+  x <- matrix(1L, nrow = 2)
+  out <- as_tibble(x)
+  expect_equal(out[[1]], c(1L, 1L))
+
+  x <- matrix(1.5, nrow = 2)
+  out <- as_tibble(x)
+  expect_equal(out[[1]], c(1.5, 1.5))
+
+  x <- matrix("a", nrow = 2)
+  out <- as_tibble(x)
+  expect_equal(out[[1]], c("a", "a"))
+
+  x <- matrix(complex(real = 1, imag = 2), nrow = 2)
+  out <- as_tibble(x)
+  expect_equal(out[[1]], as.vector(x))
+})
+
+test_that("auto-assigning names", {
+  expect_identical(as_tibble(diag(3L)),
+                   as_tibble(as.data.frame(diag(3L))))
+})
diff --git a/tests/testthat/test-nibble.R b/tests/testthat/test-nibble.R
new file mode 100644
index 0000000..225720a
--- /dev/null
+++ b/tests/testthat/test-nibble.R
@@ -0,0 +1,106 @@
+context("tribble()")
+
+test_that("tribble() constructs 'tibble' as expected", {
+
+  result <- tribble(
+    ~colA, ~colB,
+    "a", 1,
+    "b", 2
+  )
+
+  compared <- tibble(colA = c("a", "b"), colB = c(1, 2))
+  expect_equal(result, compared)
+
+  ## wide
+  wide <- tribble(
+    ~colA, ~colB, ~colC, ~colD,
+    1, 2, 3, 4,
+    5, 6, 7, 8
+  )
+
+  wide_expectation <- tibble(
+    colA = c(1, 5),
+    colB = c(2, 6),
+    colC = c(3, 7),
+    colD = c(4, 8)
+  )
+
+  expect_equal(wide, wide_expectation)
+
+  ## long
+  long <- tribble(
+    ~colA, ~colB,
+    1, 6,
+    2, 7,
+    3, 8,
+    4, 9,
+    5, 10
+  )
+
+  long_expectation <- tibble(
+    colA = as.numeric(1:5),
+    colB = as.numeric(6:10)
+  )
+
+  expect_equal(long, long_expectation)
+
+})
+
+test_that("tribble() creates lists for non-atomic inputs (#7)", {
+  expect_identical(
+    tribble(~a, ~b, NA, "A", letters, LETTERS[-1L]),
+    tibble(a = list(NA, letters), b = list("A", LETTERS[-1L]))
+  )
+
+  expect_identical(
+    tribble(~a, ~b, NA, NULL, 1, 2),
+    tibble(a = c(NA, 1), b = list(NULL, 2))
+  )
+})
+
+test_that("tribble() errs appropriately on bad calls", {
+
+  # invalid colname syntax
+  expect_error(tribble(a~b), "single argument")
+
+  # invalid colname syntax
+  expect_error(tribble(~a + b), "symbol or string")
+
+  # tribble() must be passed colnames
+  expect_error(tribble(
+    "a", "b",
+    1, 2
+  ))
+
+  # tribble() must produce rectangular structure (no filling)
+  expect_error(tribble(
+    ~a, ~b, ~c,
+    1, 2,
+    3, 4, 5
+  ))
+
+})
+
+test_that("tribble can have list columns", {
+  df <- tribble(
+    ~x, ~y,
+    1,  list(a = 1),
+    2,  list(b = 2)
+  )
+  expect_equal(df$x, c(1, 2))
+  expect_equal(df$y, list(list(a = 1), list(b = 2)))
+})
+
+test_that("tribble creates n-col empty data frame", {
+  df <- tribble(~x, ~y)
+  expect_equal(names(df), c("x", "y"))
+})
+
+test_that("tribble recognizes quoted non-formula call", {
+  df <- tribble(
+    ~x, ~y,
+    quote(mean(1)), 1
+  )
+  expect_equal(df$x, list(quote(mean(1))))
+  expect_equal(df$y, 1)
+})
diff --git a/tests/testthat/test-obj-sum.R b/tests/testthat/test-obj-sum.R
new file mode 100644
index 0000000..1cfa6fd
--- /dev/null
+++ b/tests/testthat/test-obj-sum.R
@@ -0,0 +1,43 @@
+context("obj_sum")
+
+# obj_sum ----------------------------------------------------------------
+
+test_that("shows only first class name for S4", {
+  A <- methods::setClass("A")
+  expect_equal(obj_sum(A), "S4: classGeneratorFunction")
+})
+
+test_that("shows only first class name for S3", {
+  x <- structure(list(), class = c("a", "b", "c"))
+  expect_equal(obj_sum(x), "S3: a")
+})
+
+test_that("NULL handled specially", {
+  expect_equal(obj_sum(NULL), "NULL")
+})
+
+test_that("data frame and tibbles include rows and cols", {
+  skip_on_os("windows")
+
+  expect_equal(obj_sum(mtcars), "data.frame [32 × 11]")
+  expect_equal(obj_sum(as_tibble(mtcars)), "tibble [32 × 11]")
+})
+
+test_that("common data vectors treated as atomic", {
+  expect_equal(obj_sum(factor(1:3)), "fctr [3]")
+  expect_equal(obj_sum(ordered(1:3)), "ord [3]")
+  expect_equal(obj_sum(Sys.Date() + 1:3), "date [3]")
+  expect_equal(obj_sum(Sys.time() + 1:3), "dttm [3]")
+})
+
+test_that("difftime is shown as time", {
+  expect_equal(obj_sum(Sys.time() - Sys.time() + 1:3), "time [3]")
+})
+
+
+# type_sum ----------------------------------------------------------------
+
+test_that("less common objects get abbreviations", {
+  expect_equal(type_sum(environment()), "env")
+  expect_equal(type_sum(environment), "fun")
+})
diff --git a/tests/testthat/test-options.R b/tests/testthat/test-options.R
new file mode 100644
index 0000000..4ab4322
--- /dev/null
+++ b/tests/testthat/test-options.R
@@ -0,0 +1,22 @@
+context("options")
+
+test_that("tibble option takes preference", {
+  withr::with_options(
+    list(tibble.width = 10,
+         dplyr.width = 20),
+    expect_equal(tibble_opt("width"), 10))
+})
+
+test_that("dplyr option is used for compatibility", {
+  withr::with_options(
+    list(tibble.width = NULL,
+         dplyr.width = 20),
+    expect_equal(tibble_opt("width"), 20))
+})
+
+test_that("fallback to default option", {
+  withr::with_options(
+    list(tibble.width = NULL,
+         dplyr.width = NULL),
+    expect_equal(tibble_opt("width"), op.tibble[["tibble.width"]]))
+})
diff --git a/tests/testthat/test-repair_names.R b/tests/testthat/test-repair_names.R
new file mode 100644
index 0000000..49efdd2
--- /dev/null
+++ b/tests/testthat/test-repair_names.R
@@ -0,0 +1,42 @@
+context("repair_names")
+
+test_that("zero-length inputs given character names", {
+  out <- repair_names(character())
+  expect_equal(names(out), character())
+})
+
+test_that("unnamed input gives uniquely named output", {
+  out <- repair_names(1:3)
+  expect_equal(names(out), c("V1", "V2", "V3"))
+})
+
+# make_unique -------------------------------------------------------------
+
+test_that("duplicates are de-deduped", {
+  expect_equal(make_unique(c("x", "x")), c("x", "x1"))
+})
+
+test_that("blanks get prefix + numeric id", {
+  expect_equal(make_unique(c("", "")), c("V1", "V2"))
+})
+
+test_that("blanks skip existing names", {
+  expect_equal(make_unique(c("", "V1")), c("V2", "V1"))
+})
+
+test_that("blanks skip names created when de-duping", {
+  expect_equal(make_unique(c("", "V", "V")), c("V2", "V", "V1"))
+})
+
+# names2 ------------------------------------------------------------------
+
+test_that("names2 returns character vector even if names NULL", {
+  expect_equal(names2(1:3), rep("", 3))
+})
+
+test_that("names2 replaces missing value with blanks", {
+  x <- 1:3
+  names(x) <- c("a", "b", NA)
+
+  expect_equal(names2(x), c("a", "b", ""))
+})
diff --git a/tests/testthat/test-rownames.R b/tests/testthat/test-rownames.R
new file mode 100644
index 0000000..95c5534
--- /dev/null
+++ b/tests/testthat/test-rownames.R
@@ -0,0 +1,53 @@
+context("rownames")
+
+test_that("has_rownames and remove_rownames", {
+  expect_false(has_rownames(iris))
+  expect_true(has_rownames(mtcars))
+  expect_false(has_rownames(remove_rownames(mtcars)))
+  expect_false(has_rownames(remove_rownames(iris)))
+  expect_false(has_rownames(1:10))
+})
+
+test_that("setting row names on a tibble raises a warning", {
+  mtcars_tbl <- as_tibble(mtcars)
+  expect_warning(rownames(mtcars_tbl) <- rownames(mtcars), "deprecated")
+})
+
+test_that("rownames_to_column keeps the tbl classes (#882)", {
+  res <- rownames_to_column( mtcars, "Make&Model" )
+  expect_false(has_rownames(res))
+  expect_equal( class(res), class(mtcars) )
+  expect_error(rownames_to_column( mtcars, "wt"),
+               paste("There is a column named wt already!")  )
+  res1 <- rownames_to_column( as_tibble(mtcars), "Make&Model" )
+  expect_false(has_rownames(res1))
+  expect_equal( class(res1), class(as_tibble(mtcars)) )
+  expect_error(rownames_to_column( mtcars, "wt"),
+               paste("There is a column named wt already!")  )
+})
+
+test_that("column_to_rownames returns tbl", {
+  var <- "car"
+  mtcars1 <- as_tibble(mtcars)
+  expect_true(has_rownames(mtcars1))
+  res0 <- rownames_to_column(mtcars1, var)
+  expect_warning(res <- column_to_rownames(res0, var))
+  expect_true(has_rownames(res))
+  expect_equal(class(res), class(mtcars1))
+  expect_equal(rownames(res), rownames(mtcars1))
+  expect_equal(res, mtcars1)
+  expect_false(has_name(res, var))
+
+  mtcars1$num <- rev(seq_len(nrow(mtcars)))
+  res0 <- rownames_to_column(mtcars1)
+  expect_warning(res <- column_to_rownames(res0, var = "num"))
+  expect_true(has_rownames(res))
+  expect_equal(rownames(res), as.character(mtcars1$num))
+  expect_error(column_to_rownames(res), "This data frame already has row names.")
+  expect_error(column_to_rownames(rownames_to_column(mtcars1, var), "num2"),
+               paste("This data frame has no column named num2."))
+})
+
+test_that("converting to data frame does not add row names", {
+  expect_false(has_rownames(as.data.frame(as_tibble(iris))))
+})
diff --git a/tests/testthat/test-tbl-df.R b/tests/testthat/test-tbl-df.R
new file mode 100644
index 0000000..39c8542
--- /dev/null
+++ b/tests/testthat/test-tbl-df.R
@@ -0,0 +1,153 @@
+context("tbl_df")
+
+
+# [ -----------------------------------------------------------------------
+
+test_that("[ never drops", {
+  mtcars2 <- as_tibble(mtcars)
+  expect_is(mtcars2[, 1], "data.frame")
+  expect_is(mtcars2[, 1], "tbl_df")
+  expect_equal(mtcars2[, 1], mtcars2[1])
+})
+
+test_that("[ retains class", {
+  mtcars2 <- as_tibble(mtcars)
+  expect_identical(class(mtcars2), class(mtcars2[1:5, ]))
+  expect_identical(class(mtcars2), class(mtcars2[, 1:5]))
+  expect_identical(class(mtcars2), class(mtcars2[1:5, 1:5]))
+})
+
+test_that("[ and as_tibble commute", {
+  mtcars2 <- as_tibble(mtcars)
+  expect_identical(mtcars2, as_tibble(mtcars))
+  expect_identical(mtcars2[], remove_rownames(as_tibble(mtcars[])))
+  expect_identical(mtcars2[1:5, ], remove_rownames(as_tibble(mtcars[1:5, ])))
+  expect_identical(mtcars2[, 1:5], remove_rownames(as_tibble(mtcars[, 1:5])))
+  expect_identical(mtcars2[1:5, 1:5], remove_rownames(as_tibble(mtcars[1:5, 1:5])))
+  expect_identical(mtcars2[1:5], remove_rownames(as_tibble(mtcars[1:5])))
+})
+
+test_that("[ with 0 cols creates correct row names (#656)", {
+  zero_row <- as_tibble(iris)[, 0]
+  expect_is(zero_row, "tbl_df")
+  expect_equal(nrow(zero_row), 150)
+  expect_equal(ncol(zero_row), 0)
+
+  expect_identical(zero_row, as_tibble(iris)[0])
+})
+
+test_that("[.tbl_df is careful about names (#1245)",{
+  foo <- tibble(x = 1:10, y = 1:10)
+  expect_error(foo["z"], "Unknown columns 'z'", fixed = TRUE)
+  expect_error(foo[ c("x", "y", "z") ], "Unknown columns 'z'", fixed = TRUE)
+
+  expect_error(foo[, "z"], "Unknown columns 'z'", fixed = TRUE)
+  expect_error(foo[, c("x", "y", "z") ], "Unknown columns 'z'", fixed = TRUE)
+
+  expect_error(foo[as.matrix("x")], "matrix")
+  expect_error(foo[array("x", dim = c(1, 1, 1))], "array")
+})
+
+test_that("[.tbl_df is careful about column indexes (#83)",{
+  foo <- tibble(x = 1:10, y = 1:10, z = 1:10)
+  expect_identical(foo[1:3], foo)
+  expect_error(foo[0.5], "Invalid non-integer column indexes: 0.5", fixed = TRUE)
+  expect_error(foo[1:5], "Invalid column indexes: 4, 5", fixed = TRUE)
+  expect_error(foo[-1:1], "mixed with negative")
+  expect_error(foo[c(-1, 1)], "mixed with negative")
+  expect_error(foo[-4], "Invalid negative column indexes: -4", fixed = TRUE)
+  expect_error(foo[c(1:3, NA)], "NA column indexes not supported", fixed = TRUE)
+
+  expect_error(foo[as.matrix(1)], "matrix")
+  expect_error(foo[array(1, dim = c(1, 1, 1))], "array")
+})
+
+test_that("[.tbl_df is careful about column flags (#83)",{
+  foo <- tibble(x = 1:10, y = 1:10, z = 1:10)
+  expect_identical(foo[TRUE], foo)
+  expect_identical(foo[c(TRUE, TRUE, TRUE)], foo)
+  expect_identical(foo[FALSE], foo[integer()])
+  expect_identical(foo[c(FALSE, TRUE, FALSE)], foo[2])
+
+  expect_error(foo[c(TRUE, TRUE)], "Length of logical index vector must be 1 or 3, got: 2", fixed = TRUE)
+  expect_error(foo[c(TRUE, TRUE, FALSE, FALSE)], "Length of logical index vector must be 1 or 3, got: 4", fixed = TRUE)
+  expect_error(foo[c(TRUE, TRUE, NA)], "NA column indexes not supported", fixed = TRUE)
+
+  expect_error(foo[as.matrix(TRUE)], "matrix")
+  expect_error(foo[array(TRUE, dim = c(1, 1, 1))], "array")
+})
+
+test_that("[.tbl_df rejects unknown column indexes (#83)",{
+  foo <- tibble(x = 1:10, y = 1:10, z = 1:10)
+  expect_error(foo[list(1:3)], "Unsupported index type: list", fixed = TRUE)
+  expect_error(foo[as.list(1:3)], "Unsupported index type: list", fixed = TRUE)
+  expect_error(foo[factor(1:3)], "Unsupported index type: factor", fixed = TRUE)
+  expect_error(foo[Sys.Date()], "Unsupported index type: Date", fixed = TRUE)
+  expect_error(foo[Sys.time()], "Unsupported index type: POSIXct", fixed = TRUE)
+})
+
+test_that("[.tbl_df is no-op if args missing",{
+  expect_identical(df_all[], df_all)
+})
+
+test_that("[.tbl_df warns for drop argument",{
+  expect_warning(df_all[1, 2, drop = TRUE], "ignored")
+})
+
+
+# [[ ----------------------------------------------------------------------
+
+test_that("[[.tbl_df ignores exact argument",{
+  foo <- tibble(x = 1:10, y = 1:10)
+  expect_warning(foo[["x"]], NA)
+  expect_warning(foo[["x", exact = FALSE]], "ignored")
+  expect_identical(getElement(foo, "y"), 1:10)
+})
+
+test_that("can use recursive indexing with [[", {
+  foo <- tibble(x = list(y = 1:3, z = 4:5))
+  expect_equal(foo[[c(1, 1)]], 1:3)
+  expect_equal(foo[[c("x", "y")]], 1:3)
+})
+
+test_that("[[ returns NULL if name doesn't exist", {
+  df <- tibble(x = 1)
+  expect_null(df[["y"]])
+  expect_null(df[[1, "y"]])
+})
+
+test_that("can use two-dimensional indexing with [[", {
+  iris2 <- as_tibble(iris)
+  expect_equal(iris2[[1, 2]], iris[[1, 2]])
+  expect_equal(iris2[[2, 3]], iris[[2, 3]])
+})
+
+# $ -----------------------------------------------------------------------
+
+test_that("$ throws warning if name doesn't exist", {
+  df <- tibble(x = 1)
+  expect_warning(expect_null(df$y),
+                 "Unknown column 'y'")
+})
+
+test_that("$ doesn't do partial matching", {
+  df <- tibble(partial = 1)
+  expect_warning(expect_null(df$p),
+                 "Unknown column 'p'")
+  expect_warning(expect_null(df$part),
+                 "Unknown column 'part'")
+  expect_error(df$partial, NA)
+})
+
+# is.tibble ---------------------------------------------------------------
+
+test_that("is.tibble", {
+  expect_false(is.tibble(iris))
+  expect_true(is.tibble(as_tibble(iris)))
+  expect_false(is.tibble(NULL))
+  expect_false(is.tibble(0))
+})
+
+test_that("is_tibble", {
+  expect_identical(is.tibble, is_tibble)
+})
diff --git a/tests/testthat/test-trunc-mat.R b/tests/testthat/test-trunc-mat.R
new file mode 100644
index 0000000..5e972a3
--- /dev/null
+++ b/tests/testthat/test-trunc-mat.R
@@ -0,0 +1,125 @@
+context("Truncated matrix")
+
+test_that("interface of print() identical to trunc_mat()", {
+  print_arg_names <- names(formals(print.tbl_df))
+  print_arg_names_without_ellipsis <- setdiff(print_arg_names, "...")
+
+  trunc_mat_arg_names <- names(formals(trunc_mat))
+
+  expect_equal(print_arg_names_without_ellipsis, trunc_mat_arg_names)
+})
+
+test_that("print() returns output invisibly", {
+  expect_output(ret <- withVisible(print(as_tibble(mtcars))))
+  expect_false(ret$visible)
+  expect_identical(ret$value, as_tibble(mtcars))
+})
+
+test_that("trunc_mat output matches known output", {
+  skip_on_os("windows")
+
+  expect_output_file_rel(
+    print(as_tibble(mtcars), n = 8L, width = 30L),
+    "trunc_mat/mtcars-8-30.txt")
+
+  expect_output_file_rel(
+    print(as_tibble(iris), n = 5L, width = 30L),
+    "trunc_mat/iris-5-30.txt")
+
+  expect_output_file_rel(
+    print(as_tibble(iris), n = 3L, width = 5L),
+    "trunc_mat/iris-3-5.txt")
+
+  expect_output_file_rel(
+    print(as_tibble(iris), n = NULL, width = 70L),
+    "trunc_mat/iris--70.txt")
+
+  expect_output_file_rel(
+    print(as_unknown_rows(iris), n = 10, width = 70L),
+    "trunc_mat/iris_unk-10-70.txt")
+
+  expect_output_file_rel(
+    print(df_all, n = NULL, width = 30L),
+    "trunc_mat/all--30.txt")
+
+  expect_output_file_rel(
+    print(df_all, n = NULL, width = 300L),
+    "trunc_mat/all--300.txt")
+
+  expect_output_file_rel(
+    print(tibble(a = seq.int(10000)), n = 5L, width = 30L),
+    "trunc_mat/long-5-30.txt")
+
+  expect_output_file_rel(
+    print(tibble(a = character(), b = logical()), width = 30L),
+    "trunc_mat/zero_rows--30.txt")
+
+  expect_output_file_rel(
+    print(as_tibble(iris)[character()], n = 5L, width = 30L),
+    "trunc_mat/zero_cols-5-30.txt")
+
+  expect_output_file_rel(
+    print(as_unknown_rows(iris[integer(), ]), n = 5L, width = 30L),
+    "trunc_mat/zero-rows_unk-5-30.txt")
+
+  expect_output_file_rel(
+    print(as_unknown_rows(iris[, character()]), n = 5L, width = 30L),
+    "trunc_mat/zero-cols_unk-5-30.txt")
+
+  expect_output_file_rel(
+    print(as_unknown_rows(tibble(a = seq.int(10000))), n = 5L,
+          width = 30L),
+    "trunc_mat/long_unk-5-30.txt")
+
+  expect_output_file_rel(
+    print(trunc_mat(df_all, n = 1L, n_extra = 2L, width = 30L)),
+    "trunc_mat/all-1-30-2.txt")
+
+  expect_output_file_rel(
+    print(trunc_mat(df_all, n = 1L, n_extra = 0L, width = 30L)),
+    "trunc_mat/all-1-30-0.txt")
+
+  expect_output_knit(
+    knitr::knit_print(trunc_mat(df_all, width = 60L)),
+    "trunc_mat/all-knit-60.txt")
+
+  expect_output_knit(
+    knitr::knit_print(trunc_mat(df_all, width = 120L)),
+    "trunc_mat/all-knit-120.txt")
+
+  expect_output_knit(
+    knitr::knit_print(trunc_mat(mtcars, width = 60L)),
+    "trunc_mat/mtcars-knit-60.txt")
+})
+
+test_that("trunc_mat for POSIXlt columns (#86)", {
+  skip_on_os("windows")
+
+  df <- tibble(x = as.POSIXct("2016-01-01 12:34:56 GMT") + 1:12)
+  df$y <- as.POSIXlt(df$x)
+
+  expect_output_file_rel(
+    print(as_tibble(df), n = 8L, width = 60L),
+    "trunc_mat/POSIXlt-8-60.txt")
+})
+
+test_that("trunc_mat for wide-character columns (#100)", {
+  skip_on_os("windows")
+
+  x <- c("成交日期", "合同录入日期")
+  df <- setNames(tibble(1:3, 4:6), x)
+
+  expect_output_file_rel(
+    print(df, n = 8L, width = 60L),
+    "trunc_mat/wide-8-60.txt")
+})
+
+test_that("trunc_mat backticks non-syntactic names", {
+  tb <- tibble(
+    `:)` = "smile",
+    ` ` = "space"
+  )
+  narrow <- trunc_mat(tb, width = 5)
+  expect_equal(names(narrow$table), "`:)`")
+  expect_equal(names(narrow$extra), "` `")
+})
diff --git a/vignettes/formatting.Rmd b/vignettes/formatting.Rmd
new file mode 100644
index 0000000..d74bc9c
--- /dev/null
+++ b/vignettes/formatting.Rmd
@@ -0,0 +1,106 @@
+---
+title: "Formatting of column data"
+author: "Kirill Müller, Hadley Wickham"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+  %\VignetteIndexEntry{Formatting of column data}
+  %\VignetteEngine{knitr::rmarkdown}
+  %\VignetteEncoding{UTF-8}
+---
+
+```{r setup, include = FALSE}
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+options(tibble.print_min = 4L, tibble.print_max = 4L)
+library(tibble)
+```
+
+The presentation of a column in a tibble is powered by two S3 generics:
+
+* `type_sum()` determines what goes into the column header.
+* `obj_sum()` is used when rendering list columns.
+
+If you have written an S3 or S4 class that can be used as a column, you can override these generics to make sure your data prints well in a tibble. To start, you must import the `tibble` package. Either add `tibble` to the `Imports:` section of your `DESCRIPTION`, or simply call:
+
+```{r, eval = FALSE}
+devtools::use_package("tibble")
+```
+
+This vignette assumes a package that implements an S3 class `"foo"` and uses
+`roxygen2` to create documentation and the `NAMESPACE` file:
+
+```{r}
+#' @export
+foo <- function(x) {
+  stopifnot(is.numeric(x))
+  structure(x, class = "foo")
+}
+```
+
+## `type_sum()`
+
+This method should return a length-1 character vector that can be used in a column header. Strive for an evocative abbreviation that's under 6 characters. 
+
+```{r}
+type_sum(1)
+type_sum(1:10)
+type_sum(Sys.time())
+```
+
+The default implementation works reasonably well for any kind of object,
+but the generated output may be too wide and waste precious space when displaying the tibble:
+
+```{r}
+type_sum(foo(1:10))
+```
+
+To avoid this for provide a method for `type_sum()`:
+
+```{r}
+#' @export
+type_sum.foo <- function(x, ...) {
+  "foo"
+}
+
+type_sum(foo(1:10))
+```
+
+## `obj_sum()`
+
+This method is primarily used for displaying list columns. A list column is a powerful way to attach hierarchical or unstructured data to an observation in a data frame. Implementations of `obj_sum()` are expected to return a character vector as long as the input, with brief description of the contents of each input element.
+
+Examples:
+
+```{r}
+obj_sum(1)
+obj_sum(1:10)
+obj_sum(Sys.time())
+obj_sum(list(1:5))
+obj_sum(list("a", "b", "c"))
+```
+
+The default implementation calls `type_sum()` and appends the size of the object in brackets. If your object is built on top of an atomic vector the default will be adequate. You, will, however, need to provide a method if your object is vectorised and built on top of a list.
+
+An example of an object of this type in base R `POSIXlt`: it is a list with 9 components.
+
+```{r}
+x <- as.POSIXlt(Sys.time() + c(0, 60, 3600)) 
+str(unclass(x))
+```
+
+But it pretends to be a vector with 3 elements:
+
+```{r}
+x
+length(x)
+str(x)
+```
+
+So we need to define a method that returns a character vector the same length as `x`:
+
+```{r}
+#' @export
+obj_sum.POSIXlt <- function(x) {
+  rep("POSIXlt", length(x))
+}
+```
diff --git a/vignettes/tibble.Rmd b/vignettes/tibble.Rmd
new file mode 100644
index 0000000..cfadfc5
--- /dev/null
+++ b/vignettes/tibble.Rmd
@@ -0,0 +1,128 @@
+---
+title: "Tibbles"
+date: "`r Sys.Date()`"
+output: rmarkdown::html_vignette
+vignette: >
+  %\VignetteIndexEntry{Tibbles}
+  %\VignetteEngine{knitr::rmarkdown}
+  \usepackage[utf8]{inputenc}
+---
+
+```{r, echo = FALSE, message = FALSE}
+knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
+options(tibble.print_min = 4L, tibble.print_max = 4L)
+library(tibble)
+```
+
+Tibbles are a modern take on data frames. They keep the features that have stood the test of time, and drop the features that used to be convenient but are now frustrating (i.e. converting character vectors to factors).
+
+## Creating
+
+`tibble()` is a nice way to create data frames. It encapsulates best practices for data frames:
+
+  * It never changes an input's type (i.e., no more `stringsAsFactors = FALSE`!).
+    
+    ```{r}
+    tibble(x = letters)
+    ```
+    
+    This makes it easier to use with list-columns:
+    
+    ```{r}
+    tibble(x = 1:3, y = list(1:5, 1:10, 1:20))
+    ```
+    
+    List-columns are most commonly created by `do()`, but they can be useful to
+    create by hand.
+      
+  * It never adjusts the names of variables:
+  
+    ```{r}
+    names(data.frame(`crazy name` = 1))
+    names(tibble(`crazy name` = 1))
+    ```
+
+  * It evaluates its arguments lazily and sequentially:
+  
+    ```{r}
+    tibble(x = 1:5, y = x ^ 2)
+    ```
+
+  * It never uses `row.names()`. The whole point of tidy data is to 
+    store variables in a consistent way. So it never stores a variable as 
+    special attribute.
+  
+  * It only recycles vectors of length 1. This is because recycling vectors of greater lengths 
+    is a frequent source of bugs.
+
+## Coercion
+
+To complement `tibble()`, tibble provides `as_tibble()` to coerce objects into tibbles. Generally, `as_tibble()` methods are much simpler than `as.data.frame()` methods, and in fact, it's precisely what `as.data.frame()` does, but it's similar to `do.call(cbind, lapply(x, data.frame))` - i.e. it coerces each component to a data frame and then `cbinds()` them all together. 
+
+`as_tibble()` has been written with an eye for performance:
+
+```{r}
+l <- replicate(26, sample(100), simplify = FALSE)
+names(l) <- letters
+
+microbenchmark::microbenchmark(
+  as_tibble(l),
+  as.data.frame(l)
+)
+```
+
+The speed of `as.data.frame()` is not usually a bottleneck when used interactively, but can be a problem when combining thousands of messy inputs into one tidy data frame.
+
+## Tibbles vs data frames
+
+There are two key differences between tibbles and data frames: printing and subsetting.
+
+### Printing
+
+When you print a tibble, it only shows the first ten rows and all the columns that fit on one screen. It also prints an abbreviated description of the column type:
+    
+```{r}
+tibble(x = 1:1000)
+```
+
+You can control the default appearance with options:
+
+* `options(tibble.print_max = n, tibble.print_min = m)`: if there are more 
+  than `n` rows, print only the first `m` rows. Use 
+  `options(tibble.print_max = Inf)` to always show all rows.
+
+* `options(tibble.width = Inf)` will always print all columns, regardless
+   of the width of the screen.
+
+### Subsetting
+
+Tibbles are quite strict about subsetting. `[` always returns another tibble. Contrast this with a data frame: sometimes `[` returns a data frame and sometimes it just returns a vector:
+    
+```{r}
+df1 <- data.frame(x = 1:3, y = 3:1)
+class(df1[, 1:2])
+class(df1[, 1])
+
+df2 <- tibble(x = 1:3, y = 3:1)
+class(df2[, 1:2])
+class(df2[, 1])
+```
+
+To extract a single column use `[[` or `$`:
+
+```{r}
+class(df2[[1]])
+class(df2$x)
+```
+
+Tibbles are also stricter with `$`. Tibbles never do partial matching, and will throw a warning and return `NULL` if the column does not exist:
+
+```{r, error = TRUE}
+df <- data.frame(abc = 1)
+df$a
+
+df2 <- tibble(abc = 1)
+df2$a
+```
+
+Also, tibbles ignore the `drop` argument.

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/r-cran-tibble.git



More information about the debian-med-commit mailing list