Freitag, 7. September 2018

Using upcoming versions of Phoenix framework

How to create new projects using an upcoming version of the Phoenix Framework? Right now, 1.3 is the current stable version that is released and when using mix phx.new the project it creates will use 1.3.

The upcoming version 1.4 is certainly stable enough to play around with, but how to create a new project using it? Here are the steps:

➤ mix archive.uninstall phx_new
Are you sure you want to uninstall /Users/steffen/.mix/archives/phx_new? [Yn]

 git clone https://github.com/phoenixframework/phoenix
 cd phoenix/installer

➤ MIX_ENV=prod mix do archive.build, archive.install
Compiling 10 files (.ex)
Generated phx_new app
Generated archive "phx_new-1.4.0-dev.ez" with MIX_ENV=prod
Are you sure you want to install "phx_new-1.4.0-dev.ez"? [Yn]
* creating /Users/steffen/.mix/archives/phx_new-1.4.0-dev

Creating a new project with the development version of phoenix:

➤ mix phx.new asdf
➤ cat asdf/mix.exs
...
  defp deps do
    [
      {:phoenix, github: "phoenixframework/phoenix", override: true},
      {:phoenix_pubsub, "~> 1.1"},
      {:phoenix_ecto, "~> 3.2"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:cowboy, "~> 1.0"}
    ]
  end
...

Donnerstag, 26. Februar 2015

Using Mockito on Android

There are a few obstacles when trying to use Mockito on Android. However, they can be overcome easily: 0) Adding Mockito dependency to project:
androidTestCompile 'org.mockito:mockito-core:2.+'
1) Yields VerifyError exception:
java.lang.VerifyError: org/mockito/cglib/core/ReflectUtils
at org.mockito.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:167)
at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217)
at org.mockito.cglib.core.KeyFactory$Generator.create(KeyFactory.java:145)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:117)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:109)
at org.mockito.cglib.core.KeyFactory.create(KeyFactory.java:105)
at org.mockito.cglib.proxy.Enhancer.(Enhancer.java:70)
at org.mockito.internal.creation.cglib.ClassImposterizer.createProxyClass(ClassImposterizer.java:99)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:61)
at org.mockito.internal.creation.cglib.ClassImposterizer.imposterise(ClassImposterizer.java:53)
at org.mockito.internal.creation.cglib.CglibMockMaker.createMock(CglibMockMaker.java:24)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59)
at org.mockito.Mockito.mock(Mockito.java:1284)
at org.mockito.Mockito.mock(Mockito.java:1162)
at MyTest.java
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
at android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:192)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
Solution is adding the following dependency next to mockito:
    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
2) Yields another exception:
java.lang.IllegalArgumentException: dexcache == null (and no default could be found; consider setting the 'dexmaker.dexcache' system property)
at com.google.dexmaker.DexMaker.generateAndLoad(DexMaker.java:359)
at com.google.dexmaker.stock.ProxyBuilder.buildProxyClass(ProxyBuilder.java:258)
at com.google.dexmaker.mockito.DexmakerMockMaker.createMock(DexmakerMockMaker.java:55)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:33)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:59)
at org.mockito.Mockito.mock(Mockito.java:1284)
at org.mockito.Mockito.mock(Mockito.java:1162)
at MyTest.java
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
at android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:192)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
See here: http://code.google.com/p/dexmaker/issues/detail?id=2

Add this to your setUp() or beforeClass() methods in the test class:
    System.setProperty("dexmaker.dexcache", getInstrumentation().getTargetContext().getCacheDir().getPath());

Freitag, 21. November 2014

Couchbase: set document owner in sync function using requireUser

Couchbase's sync function is the central point where you can manage access rights of the documents in the database. In addition to the new and old document it used to receive a userContext as a third parameter that could be used to retrieve the currently logged in user name. This parameter was removed because a current user is not available in all the situations where the sync function gets invoked by Couchbase.

However, a common usecase is to set the currently logged in user as the owner of the document, for example to make sure only he can edit it. How do you achieve giving ownership of a document to the current user then? If you store the owner in doc.creator, then in your sync function, you can make sure the currently logged in user matches that by invoking requireUser(doc.creator);
After having verified doc.creator by this call, you can use it to set up your access model, for instance by setting the channel for the document.

Relevant post from the mailing list for more details.

Mittwoch, 3. September 2014

MacPorts and installing Ruby's mysql gem

MySQL 5.5 client and server is installed using MacPorts. When trying to install the mysql2-gem, it will fail as follows:
 $ sudo gem install mysql2 -v '0.3.16'
Building native extensions.  This could take a while...
ERROR:  Error installing mysql2:
 ERROR: Failed to build gem native extension.

    /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby extconf.rb
checking for ruby/thread.h... yes
checking for rb_thread_call_without_gvl() in ruby/thread.h... yes
checking for rb_thread_blocking_region()... yes
checking for rb_wait_for_single_fd()... yes
checking for rb_hash_dup()... yes
checking for rb_intern3()... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lm... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lz... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lsocket... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lnsl... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lmygcc... no
checking for mysql_query() in -lmysqlclient... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.
It seems like it has trouble locating the mysqlclient. A quick search in /opt discovered /opt/local/lib/mysql55/bin/mysql_config. mysql_config will help gem to find the mysql libraries:
 $ sudo gem install mysql2 -v '0.3.16' -- --with-mysql-config=/opt/local/lib/mysql55/bin/mysql_config
Building native extensions with: '--with-mysql-config=/opt/local/lib/mysql55/bin/mysql_config'
This could take a while...
Successfully installed mysql2-0.3.16
Parsing documentation for mysql2-0.3.16
unable to convert "\xCA" from ASCII-8BIT to UTF-8 for lib/mysql2/mysql2.bundle, skipping
Installing ri documentation for mysql2-0.3.16
1 gem installed
Voila.

Freitag, 9. Mai 2014

Disable horizontal scrolling for ViewPager

Depending on the state of your application it might become necessary to make your ViewPager not scroll horizontally when the user makes a swiping gesture. One use case for this is displaying multiple images in a ViewPager that can be zoomed into and you want the user only to be able to swipe to the next image when it is fully zoomed out. Swiping gestures that are carried out when having zoomed into the image should pan the image contents and not switch to the next one. There is an elegant way to achieve this behaviour by overriding canScroll() in ViewPager. Here's how to disable horizontal swiping temporarily:
public class GalleryViewPager extends ViewPager {
    ...
    @Override
    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        GalleryImage image = getImage();
        if(image.isZoomed()) {
            /* the child view of the ViewPager can scroll, so the ViewPager would not change page */
            return true;
        }
        else {
            /* let base class decide what to do */
            return super.canScroll(v, checkV, dx, x, y);
        }
    }
    ...
}
By returning true from canScroll() we indicate that the child view of the ViewPager itself can scroll, so the ViewPager will not change to the next page, effectively disabling horizontal scrolling for the ViewPager.

Dienstag, 25. Februar 2014

Attaching debugger when running Robolectric tests from Android Studio

The Gradle View (View → Tool Windows → Gradle) in Android Studio allows you to easily run your unit tests from within Android Studio. From the tree of tasks for your project, just select the task test and run it.
It's also possible to attach the debugger to that process executing your Robolectric tests by simply choosing Debug instead of Run. However, you might face this error:
Error running android-app:appname [test]: Unable to open debugger port : java.net.SocketException "Socket closed" 
In order for the debugger to attach you need to uncheck "Use in-process build" in the settings menu for Android Studio under Compiler → Gradle.

Freitag, 17. Januar 2014

Getting started with Robolectric and Gradle in Android Studio

Unit tests are an invaluable tool to ensure software behaves the way it is supposed to which you want to check as often as possible while developing. However, running tests on an Android emulator is slow. Robolectric is a unit test framework that enables you to run your Android unit tests as plain vanilla JUnit tests inside the JVM on your computer, which is much faster and less cumbersome than to run them inside the emulator.

New Android projects are built with Gradle

There is a plugin for Gradle called gradle-android-test-plugin that will run unit tests relying on Robolectric and compile the results in a test report. The following will describe how to change your build.gradle to set this up. First, pitfalls along the way will be shown, followed by a working build.gradle configuration. Finally, a few tweaks have to be made to integrate this properly with Android Studio.

Pitfalls and how to work around them

These are problems I encountered when trying to set up the gradle-android-test-plugin and how to circumvent them.

Gradle cannot determine dependencies

* What went wrong:
Could not determine the dependencies of task ':MyApp:testDefaultFlavorDebug'.
> A base directory must be specified in the task or via a method argument
Solution: Use 0.9.1-SNAPSHOT version of the gradle-android-test-plugin., it fixes this problem.

Robolectric: Resource path restrictions

java.lang.IllegalArgumentException: Resource path must end in "/res"
    at org.robolectric.res.PackageResourceLoader.<init>(PackageResourceLoader.java:20)
    at org.robolectric.res.PackageResourceLoader.<init>(PackageResourceLoader.java:11)
Solution: Use 2.3-SNAPSHOT version of Robolectric, it fixes this restriction on the path name.

Robolectric: No KitKat

java.lang.UnsupportedOperationException: Robolectric does not support API level 19, sorry!
Solution: As defined per SdkConfig.java, Roboelectric only supports SDKs 16 through 18. Change targetSdkVersion to 16, 17 or 18.

Summary of necessary changes to build.gradle

Your build.gradle has to be changed in quite a few places to set up gradle-android-test-plugin. Here's the condensed summary of theses changes:
buildscript {
    repositories {
        maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
    }
    dependencies {
        classpath 'com.squareup.gradle:gradle-android-test-plugin:0.9.1-SNAPSHOT'
    }
}
apply plugin: 'android-test'
repositories {
    maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
dependencies {
    testCompile 'junit:junit:4.10'
    testCompile 'org.robolectric:robolectric:2.3-SNAPSHOT'
    testCompile 'com.squareup:fest-android:1.0.+'
}

Write a first unit test

Now you can create a unit test. Create a file in src/test/com.myapp.MyTest.java:
package com.myapp;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;

import static org.junit.Assert.assertTrue;

@RunWith(RobolectricTestRunner.class)
public class MyTest {
    @Test
    public void testNumberOne() {
        assertTrue(false);
    }
}
Note that with Robolectric 2.3-SNAPSHOT you do not need RobolectricGradleTestRunner anymore, since it is integrated. When you now run ./gradlew test in your project, it should run that very unit test, which will -- by design -- fail:
com.myapp.MyTest > testNumberOne FAILED
    java.lang.AssertionError at MyTest.java:13

Integrating with Android Studio

So far, we have a working set up of Gradle, Robolectric and the gradle-android-test-plugin -- but only for the console. When we try to "Sync Project with Gradle Files" in Android Studio, the IDE will not pick up our newly created src/test folder and it will not provide content assist when writing code.
This is because Android Studio expects test code to be in src/instrumentationTest, but luckily we can change that in build.gradle:
android {
    sourceSets.instrumentTest.setRoot('src/test')
}
Android Studio also cannot compile the test code because it misses dependencies. This is because it expects these dependencies to be defined in the configuration named instrumentTestCompile, whereas currently, we are using the testCompile configuration only. We have to add the dependencies defined earlier to the configuration Android Studio uses. This can even be achieved without repeating them, but by creating a copy, like so:
dependencies {
    instrumentTestCompile configurations.testCompile.dependencies
}

Summary

This article described how to overcome the problems when adding the gradle-android-test-plugin to a project. It provided a configuration for Gradle to run a sample unit test leveraging the Roboelectric library. In the last step, it showed how to alter the configuration so that Android Studio would play along.