Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring. From Singleton to Static Factory #3

Closed
wants to merge 13 commits into from
Closed
21 changes: 21 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
language: android
jdk: oraclejdk7
env:
matrix:
- ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a

android:
components:
- build-tools-21.1.1
- android-21
- extra-android-m2repository

before_script:
# Create and start emulator
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
- emulator -avd test -no-skin -no-audio -no-window &
- android-wait-for-emulator
- adb shell input keyevent 82 &
- echo > local.properties

script: ./gradlew connectedAndroidTest
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Paper
[![Build Status](https://travis-ci.org/pilgr/Paper.svg?branch=master)](https://travis-ci.org/pilgr/Paper) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Paper-blue.svg?style=flat)](http://android-arsenal.com/details/1/2080)

Paper is a [fast](#benchmark-results) NoSQL data storage for Android that lets you save/restore Java objects by using efficient Kryo serialization and handling data structure changes automatically.

#### Add dependency
Expand All @@ -20,39 +22,39 @@ Save data object. Your custom classes must have no-arg constructor.
Paper creates separate data file for each key.

```java
Paper.put("city", "Lund"); // Primitive
Paper.put("task-queue", queue); // LinkedList
Paper.put("countries", countryCodeMap); // HashMap
Paper.book().write("city", "Lund"); // Primitive
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those changes to README shouldn't be visible till publish version 1.0. I'll create development branch. Then please move all your changes into development branch.

Paper.book().write("task-queue", queue); // LinkedList
Paper.book().write("countries", countryCodeMap); // HashMap
```

#### Read
Read data objects. Paper instantiates exactly the classes which has been used in saved data. The limited backward and forward compatibility is supported. See [Handle data class changes](#handle-data-structure-changes).

```java
String city = Paper.get("city");
LinkedList queue = Paper.get("task-queue");
HashMap countryCodeMap = Paper.get("countries");
String city = Paper.book().read("city");
LinkedList queue = Paper.book().read("task-queue");
HashMap countryCodeMap = Paper.book().read("countries");
```

Use default values if object doesn't exist in the storage.

```java
String city = Paper.get("city", "Kyiv");
LinkedList queue = Paper.get("task-queue", new LinkedList());
HashMap countryCodeMap = Paper.get("countries", new HashMap());
String city = Paper.book().read("city", "Kyiv");
LinkedList queue = Paper.book().read("task-queue", new LinkedList());
HashMap countryCodeMap = Paper.book().read("countries", new HashMap());
```

#### Delete
Delete data for one key.

```java
Paper.delete("countries");
Paper.book().delete("countries");
```

Completely clear Paper storage. Doesn't require to call init() before usage.

```java
Paper.clear(context);
Paper.book().clear(context);
```

#### Handle data structure changes
Expand Down Expand Up @@ -111,7 +113,7 @@ Paper is based on the following assumptions:
- Saved data on mobile are relatively small;
- Random file access on flash storage is very fast.

So each data object is saved in separate file and put/get operations write/read whole file.
So each data object is saved in separate file and write/read operations write/read whole file.

The [Kryo](https://github.com/EsotericSoftware/kryo) is used for object graph serialization and to provide data compatibility support.

Expand Down
6 changes: 3 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
compileSdkVersion 21
buildToolsVersion "21.1.2"

defaultConfig {
applicationId "paperdb.io.paperdb"
minSdkVersion 15
targetSdkVersion 22
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
Expand Down
6 changes: 3 additions & 3 deletions paperdb/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
apply plugin: 'com.android.library'

android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
compileSdkVersion 21
buildToolsVersion "21.1.2"

defaultConfig {
minSdkVersion 15
targetSdkVersion 22
targetSdkVersion 21
versionCode 1
versionName "1.0"

Expand Down
14 changes: 7 additions & 7 deletions paperdb/src/androidTest/java/io/paperdb/CompatibilityTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public class CompatibilityTest {

@Before
public void setUp() throws Exception {
Paper.clear(getTargetContext());
Paper.init(getTargetContext());
Paper.book().destroy();
}

@Test
Expand All @@ -34,10 +34,10 @@ public void testChangeClass()
testClass.timestamp = 123;

// Save original class. Only class name is changed to TestClassNew
Paper.put("test", testClass);
Paper.book().write("test", testClass);

// Read and instantiate a modified class TestClassNew based on saved data in TestClass
TestClassNew newTestClass = Paper.get("test");
TestClassNew newTestClass = Paper.book().read("test");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please separate structural changes and rename api changes in separate commits for better traceability and more easy review.

// Check original value is restored despite new default value in TestClassNew
assertThat(newTestClass.name).isEqualTo("original");
// Check default value for new added field
Expand All @@ -51,9 +51,9 @@ public void testNotCompatibleClassChanges() throws Exception {
TestClass testClass = getClassInstanceWithNewName(TestClass.class,
TestClassNotCompatible.class.getName());
testClass.timestamp = 123;
Paper.put("not-compatible", testClass);
Paper.book().write("not-compatible", testClass);

Paper.<TestClassNotCompatible>get("not-compatible");
Paper.book().<TestClassNotCompatible>read("not-compatible");
}

@Test
Expand All @@ -62,9 +62,9 @@ public void testTransientFields() throws Exception {
tc.timestamp = 123;
tc.transientField = "changed";

Paper.put("transient-class", tc);
Paper.book().write("transient-class", tc);

TestClassTransient readTc = Paper.get("transient-class");
TestClassTransient readTc = Paper.book().read("transient-class");
assertThat(readTc.timestamp).isEqualTo(123);
assertThat(readTc.transientField).isEqualTo("default");
}
Expand Down
24 changes: 12 additions & 12 deletions paperdb/src/androidTest/java/io/paperdb/DataTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,47 +23,47 @@
import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests List put/get API
* Tests List write/read API
*/
@RunWith(AndroidJUnit4.class)
public class DataTest {

@Before
public void setUp() throws Exception {
Paper.clear(getTargetContext());
Paper.init(getTargetContext());
Paper.book().destroy();
}

@Test
public void testPutEmptyList() throws Exception {
final List<Person> inserted = genPersonList(0);
Paper.put("persons", inserted);
assertThat(Paper.<List>get("persons")).isEmpty();
Paper.book().write("persons", inserted);
assertThat(Paper.book().<List>read("persons")).isEmpty();
}

@Test
public void testPutGetList() {
final List<Person> inserted = genPersonList(10000);
Paper.put("persons", inserted);
List<Person> persons = Paper.get("persons");
Paper.book().write("persons", inserted);
List<Person> persons = Paper.book().read("persons");
assertThat(persons).isEqualTo(inserted);
}

@Test
public void testPutMap() {
final Map<Integer, Person> inserted = genPersonMap(10000);
Paper.put("persons", inserted);
Paper.book().write("persons", inserted);

final Map<Integer, Person> personMap = Paper.get("persons");
final Map<Integer, Person> personMap = Paper.book().read("persons");
assertThat(personMap).isEqualTo(inserted);
}

@Test
public void testPutPOJO() {
final Person person = genPerson(1);
Paper.put("profile", person);
Paper.book().write("profile", person);

final Person savedPerson = Paper.get("profile");
final Person savedPerson = Paper.book().read("profile");
assertThat(savedPerson).isEqualTo(person);
assertThat(savedPerson).isNotSameAs(person);
}
Expand Down Expand Up @@ -134,8 +134,8 @@ public void testPutSynchronizedList() {
}

private Object testReadWriteWithoutClassCheck(Object originObj) {
Paper.put("obj", originObj);
Object readObj = Paper.get("obj");
Paper.book().write("obj", originObj);
Object readObj = Paper.book().read("obj");
assertThat(readObj).isEqualTo(originObj);
return readObj;
}
Expand Down
90 changes: 49 additions & 41 deletions paperdb/src/androidTest/java/io/paperdb/PaperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,110 +20,118 @@ public class PaperTest {

@Before
public void setUp() throws Exception {
Paper.clear(getTargetContext());
Paper.init(getTargetContext());
Paper.book().destroy();
}

@Test
public void testExist() throws Exception {
assertFalse(Paper.exist("persons"));
Paper.put("persons", TestDataGenerator.genPersonList(10));
assertTrue(Paper.exist("persons"));
assertFalse(Paper.book().exist("persons"));
Paper.book().write("persons", TestDataGenerator.genPersonList(10));
assertTrue(Paper.book().exist("persons"));
}

@Test
public void testDelete() throws Exception {
Paper.put("persons", TestDataGenerator.genPersonList(10));
assertTrue(Paper.exist("persons"));
Paper.delete("persons");
assertFalse(Paper.exist("persons"));
Paper.book().write("persons", TestDataGenerator.genPersonList(10));
assertTrue(Paper.book().exist("persons"));
Paper.book().delete("persons");
assertFalse(Paper.book().exist("persons"));
}

@Test
public void testDeleteNotExisted() throws Exception {
assertFalse(Paper.exist("persons"));
Paper.delete("persons");
assertFalse(Paper.book().exist("persons"));
Paper.book().delete("persons");
}

@Test
public void testClear() throws Exception {
Paper.put("persons", TestDataGenerator.genPersonList(10));
Paper.put("persons2", TestDataGenerator.genPersonList(20));
assertTrue(Paper.exist("persons"));
assertTrue(Paper.exist("persons2"));
Paper.book().write("persons", TestDataGenerator.genPersonList(10));
Paper.book().write("persons2", TestDataGenerator.genPersonList(20));
assertTrue(Paper.book().exist("persons"));
assertTrue(Paper.book().exist("persons2"));

Paper.clear(getTargetContext());
Paper.book().destroy();
// init() call is not required after clear()
assertFalse(Paper.exist("persons"));
assertFalse(Paper.exist("persons2"));
assertFalse(Paper.book().exist("persons"));
assertFalse(Paper.book().exist("persons2"));

// Should be possible to continue to use Paper after clear()
Paper.put("persons3", TestDataGenerator.genPersonList(30));
assertTrue(Paper.exist("persons3"));
assertThat(Paper.<List>get("persons3")).hasSize(30);
Paper.book().write("persons3", TestDataGenerator.genPersonList(30));
assertTrue(Paper.book().exist("persons3"));
assertThat(Paper.book().<List>read("persons3")).hasSize(30);
}

@Test
public void testPutGetNormal() {
Paper.put("city", "Lund");
String val = Paper.get("city", "default");
Paper.book().write("city", "Lund");
String val = Paper.book().read("city", "default");
assertThat(val).isEqualTo("Lund");
}

@Test
public void testPutGetNormalAfterReinit() {
Paper.put("city", "Lund");
String val = Paper.get("city", "default");
Paper.book().write("city", "Lund");
String val = Paper.book().read("city", "default");
Paper.init(getTargetContext());// Reinit Paper instance
assertThat(val).isEqualTo("Lund");
}

@Test
public void testGetNotExisted() {
String val = Paper.get("non-existed");
String val = Paper.book().read("non-existed");
assertThat(val).isNull();
}

@Test
public void testGetDefault() {
String val = Paper.get("non-existed", "default");
String val = Paper.book().read("non-existed", "default");
assertThat(val).isEqualTo("default");
}

@Test
public void testPutNull() {
Paper.put("city", "Lund");
String val = Paper.get("city");
Paper.book().write("city", "Lund");
String val = Paper.book().read("city");
assertThat(val).isEqualTo("Lund");

Paper.put("city", null);
String nullVal = Paper.get("city");
Paper.book().write("city", null);
String nullVal = Paper.book().read("city");
assertThat(nullVal).isNull();
}

@Test
public void testReplace() {
Paper.put("city", "Lund");
Paper.put("city", "Kyiv");
assertThat(Paper.get("city")).isEqualTo("Kyiv");
Paper.book().write("city", "Lund");
assertThat(Paper.book().read("city")).isEqualTo("Lund");
Paper.book().write("city", "Kyiv");
assertThat(Paper.book().read("city")).isEqualTo("Kyiv");
}

@Test
public void testValidKeyNames() {
Paper.put("city", "Lund");
assertThat(Paper.get("city")).isEqualTo("Lund");
Paper.book().write("city", "Lund");
assertThat(Paper.book().read("city")).isEqualTo("Lund");

Paper.put("city.dasd&%", "Lund");
assertThat(Paper.get("city.dasd&%")).isEqualTo("Lund");
Paper.book().write("city.dasd&%", "Lund");
assertThat(Paper.book().read("city.dasd&%")).isEqualTo("Lund");

Paper.put("city-ads", "Lund");
assertThat(Paper.get("city-ads")).isEqualTo("Lund");
Paper.book().write("city-ads", "Lund");
assertThat(Paper.book().read("city-ads")).isEqualTo("Lund");
}

@Test(expected=PaperDbException.class)
public void testInvalidKeyNameBackslash() {
Paper.put("city/ads", "Lund");
assertThat(Paper.get("city/ads")).isEqualTo("Lund");
Paper.book().write("city/ads", "Lund");
assertThat(Paper.book().read("city/ads")).isEqualTo("Lund");
}

@Test(expected=PaperDbException.class)
public void testBookname() {
Paper.book().write("city", "Lund");
assertThat(Paper.book().read("city")).isEqualTo("Lund");
//TODO replace "io.paperdb" with the reflection which will take value from the class
Paper.book("io.paperdb").write("city", "Lund");
}
}
Loading