text:
title: "Perfect is the Enemy of the Good"
---

How an Architecture Can Cripple Development

---

A couple of years ago, I was working on a side project with a few friends. We thought that it would be the next big thing. We put our collective best efforts into it; I worked long, hard hours fleshing out the scaffolding of the perfect architecture. Little did I know that my effort would doom the project to join the abyss of failed projects as quickly as it had begun.

## We had the best tools

![All the best iOS Swift tools]({{ site.url }}/assets/images/PerfectEnemyGood/toolbox.png){: .align-center}

Since this was *The Next Big Thing*™, we used everything at our disposal.

- RxSwift for reactive programming
- VIPER for our architecture pattern
- Swinject for dependency injection
- Cuckoo for mocking
- Flow Operations for managing navigation

The **Flow Operations** were a particularly interesting concept, inspired by the [Advanced NSOperations](https://developer.apple.com/videos/play/wwdc2015/226/) session from WWDC 2015. We used Flow Operations to manage navigation in the app. For instance, if you wanted to register a new user, you'd invoke a `RegisterFlowOperation`.

A FlowOperation was a subclass of Operation:

```swift
class FlowOperation: Operation
```

The [Operation](https://developer.apple.com/documentation/foundation/operation) class represents the code and data for a task of your choosing. It also handles concurrency and dependencies. So, our Flow Operations represented the task of flowing from one screen to another in an app.

Operations can be dependent on each other - for instance, the `EditProfileFlowOperation` is dependent on the `SignInFlowOperation`. If you've already signed in, you can edit your profile, but if you haven't, then you'll be directed to sign in if you invoke the `EditProfileFlowOperation`.

## How does it work?

There was a lot of hidden complexity in the Flow Operation system, and some trickiness that you wouldn't notice until you started using it. I'll briefly go over some of the code.

You could sell products in this app we were building. This is how you'd start the Sell Flow:

```swift
private func startSellFlow() {
  DispatchQueue.global().async { [navController = navController] in
    let flow = SellFlowOperation(navigationController: navController)
    flow.beginFlow()
    flow.waitUntilFinished()
  }
}
```

You create the flow operation, call `beginFlow` to navigate to the first screen, and then `waitUntilFinished` to prevent other flows from executing until this one is done.

But, don't forget to put it on a background queue, or the app will hang. Also, don't forget to call `waitUntilFinished`, or nothing interesting will happen at all. If another flow was already executing, yours wouldn't run until that one finished. I shudder when I think back on the hours of debugging issues like this. 💀

A flow can have one or more ViewControllers to display in succession. How? With a bunch of complicated RxSwift code, of course.

```swift
func present(viewControllers: Observable<UIViewController>,
             finishWhenDone: Bool = false) {
  viewControllers.observeOn(MainScheduler.instance)
    .subscribe { [weak self] event in
      if case .next(let viewController) = event {
        self?.pushViewController(viewController)
      } else if finishWhenDone {
        self?.finish()
      }
    }.addDisposableTo(disposeBag)
}
```

It subscribes to an RxSwift Observable of UIViewControllers that come from the Presenter portion of the VIPER architecture. When a new ViewController is received from the FlowAction, it's pushed onto the navigation stack. When there are no more ViewControllers, the FlowAction finishes (if `finishWhenDone` is true).

A Router object ➡  invokes the Flow Operation ➡  which gets View Controllers from a Presenter ➡  and then pushes them onto the navigation stack.

## Technically...

It's a great system. It's a complex, well-thought out architecture. Dependencies are managed elegantly and each part of the system has its own single responsibility.

Sounds good, right?

## In practice...

It's a mess. The architecture is extremely hard to use. You need to remember to:

- Invoke the FlowOperation on the background thread, or you'll cause a deadlock.
- Call `beginFlow` to start the flow, or nothing will happen.
- Supply ViewControllers from a Presenter or no new screens will display.
- Call `finish` when you're done to allow the next flow to start.
- Don't create dependency cycles between flows or the app will crash.

Complex architectures create complex problems. Building the right architecture for the right app is extremely important. I'm not advocating for spaghetti code. But when you build unnecessary complexity into an architecture like the one I just described, you're asking for trouble.

Developers follow the **path of least resistance**. A hard to use architecture causes a lot of resistance. When no one can figure out how to put a simple ViewController on the screen, they'll just opt for MVC instead. If you want your team using something other than Massive View Controllers, you need to provide something that's easy to use, or it won't be used at all.

With a hard-to-use system like the Flow Operations, it's probably better that they follow the path of least resistance and default to MVC anyways. A system with so many 'gotchas' makes it extremely easy to write buggy code. *Remember the background thread. Remember to avoid dependency cycles.* The more tricky bits you need to remember, the more likely you are to create bugs and spend more time in debugging than building actual features.

# A better approach

What could have changed in the Flow Operation architecture that might have led the project to better success?

### Easier

Your architecture shouldn't make life harder. If you want things to be hard, program in assembly. I'd much rather build my apps in a way that's easy to build now, and easy to maintain later.

### Enabler

The structure you have in place is not meant to be a barrier that keeps you from moving forward. Following VIPER or MVVM provides you with some scaffolding, but building something new shouldn't be a complicated process that's prone to errors. These architectural patterns are there to make development faster, in the long run.

When you have to keep a list of tricky problems in your head just to push a new ViewController onto the screen, then something's gone wrong.

### Don't make me think

How easy is the system to understand? How easy was it to write? Will you be able to comprehend things 6 months from now? What about a new developer on the team: will they be able to ramp up without too many headaches in the process?

This is why I've fallen out of love with frameworks like RxSwift. It makes code hard to understand, and harder for team members that are less versed in the library. I love the benefits of reactive programming, but I'd rather take a simpler approach such as using [property observers](https://medium.com/the-andela-way/property-observers-didset-and-willset-in-swift-4-c3730f26b1e9).

# Getting there

There are a few things that I like to keep in mind when building an architecture. This works for scaffolding a project, building a class interface, or choosing third-party frameworks.

### The right tool for the job

Complexity has its time and its place. Techniques that flourish in a large team of ten or more developers aren't necessarily the right approach for your side projects. A complicated architecture shouldn't be the starting point - add in the extra structure when your team grows and your needs evolve.

### Rule of Three

Preventing code duplication seems like the #1 rule of clean programming. While removing code duplication is a commendable goal, **don't do it** at the cost of creating an incorrect abstraction. When you're looking at duplicated code, do the two pieces in question share a similar purpose? Or is their similarity merely coincidental?

Code duplication is bad. Creating leaky and incorrect abstractions is worse. They will mutate your code base into an abstract mess that requires a lexicon to make sense of.

### YAGNI

You Ain't Gonna Need It. For every possible future scenario that you can think of, 90% of them won't happen. Don't spend your time preparing for the possibility that a project manager might have a change of heart six months down the line.

Rather write clean, flexible code that can adapt to change. Write code that's well tested and easy to change when the time comes. When a change of plans presents itself, you'll be ready. But if you try and prepare for every possible future outcome? YAGNI.

# The result

The unnecessary complication that I introduced to the side project mentioned earlier made development a pain. It was hard to build, and hard to use. The other developers on the project battled against the Flow Operations. RxSwift was a huge learning barrier for those that didn't know it. And, for a small app and team like ours, VIPER was most likely overkill.

If, instead, we followed the principles mentioned in this post, we could have built something that was easy to understand. The less you have to think when figuring out how to use it, the better. It should be easy to use. An enabler to your programming, not a barrier.

The system should be only as complex as it needs to be to solve the problem at hand. If the problem changes, the complexity of the system can change to follow suit.

# Please share your thoughts

Do you have any horror stories from failed projects? Do you have any thoughts to share on this advice, or some of your own? Please leave it in the comments below!


text:
package smbios

import (
	"fmt"
)

// Info contains the SMBIOS information.
type Info struct {
	Entry32 *Entry32
	Entry64 *Entry64
	Tables  []*Table
}

// String returns a summary of the SMBIOS version and number of tables.
func (i *Info) String() string {
	return fmt.Sprintf("SMBIOS %d.%d.%d (%d tables)", i.MajorVersion(), i.MinorVersion(), i.DocRev(), len(i.Tables))
}

// ParseInfo parses SMBIOS information from binary data.
func ParseInfo(entryData, tableData []byte) (*Info, error) {
	info := &Info{}
	var err error
	info.Entry32, info.Entry64, err = ParseEntry(entryData)
	if err != nil {
		return nil, fmt.Errorf("error parsing entry point structure: %v", err)
	}
	for len(tableData) > 0 {
		t, remainder, err := ParseTable(tableData)
		if err != nil && err != errEndOfTable {
			return nil, err
		}
		info.Tables = append(info.Tables, t)
		tableData = remainder
	}
	return info, nil
}

// MajorVersion return major version of the SMBIOS spec.
func (i *Info) MajorVersion() uint8 {
	if i.Entry64 != nil {
		return i.Entry64.SMBIOSMajorVersion
	}
	if i.Entry32 != nil {
		return i.Entry32.SMBIOSMajorVersion
	}
	return 0
}

// MinorVersion return minor version of the SMBIOS spec.
func (i *Info) MinorVersion() uint8 {
	if i.Entry64 != nil {
		return i.Entry64.SMBIOSMinorVersion
	}
	if i.Entry32 != nil {
		return i.Entry32.SMBIOSMinorVersion
	}
	return 0
}

// DocRev return document revision of the SMBIOS spec.
func (i *Info) DocRev() uint8 {
	if i.Entry64 != nil {
		return i.Entry64.SMBIOSDocRev
	}
	return 0
}

// GetTablesByType returns tables of specific type.
func (i *Info) GetTablesByType(tt TableType) []*Table {
	var res []*Table
	for _, t := range i.Tables {
		if t.Type == tt {
			res = append(res, t)
		}
	}
	return res
}

// GetBIOSInfo returns the Bios Info (type 0) table, if present.
func (i *Info) GetBIOSInfo() (*BIOSInfo, error) {
	bt := i.GetTablesByType(TableTypeBIOSInfo)
	if len(bt) == 0 {
		return nil, ErrTableNotFound
	}
	// There can only be one of these.
	return ParseBIOSInfo(bt[0])
}

// GetSystemInfo returns the System Info (type 1) table, if present.
func (i *Info) GetSystemInfo() (*SystemInfo, error) {
	bt := i.GetTablesByType(TableTypeSystemInfo)
	if len(bt) == 0 {
		return nil, ErrTableNotFound
	}
	// There can only be one of these.
	return ParseSystemInfo(bt[0])
}

// GetBaseboardInfo returns all the Baseboard Info (type 2) tables present.
func (i *Info) GetBaseboardInfo() ([]*BaseboardInfo, error) {
	var res []*BaseboardInfo
	for _, t := range i.GetTablesByType(TableTypeBaseboardInfo) {
		bi, err := ParseBaseboardInfo(t)
		if err != nil {
			return nil, err
		}
		res = append(res, bi)
	}
	return res, nil
}

// GetChassisInfo returns all the Chassis Info (type 3) tables present.
func (i *Info) GetChassisInfo() ([]*ChassisInfo, error) {
	var res []*ChassisInfo
	for _, t := range i.GetTablesByType(TableTypeChassisInfo) {
		ci, err := ParseChassisInfo(t)
		if err != nil {
			return nil, err
		}
		res = append(res, ci)
	}
	return res, nil
}

// GetProcessorInfo returns all the Processor Info (type 4) tables present.
func (i *Info) GetProcessorInfo() ([]*ProcessorInfo, error) {
	var res []*ProcessorInfo
	for _, t := range i.GetTablesByType(TableTypeProcessorInfo) {
		pi, err := ParseProcessorInfo(t)
		if err != nil {
			return nil, err
		}
		res = append(res, pi)
	}
	return res, nil
}

// GetCacheInfo returns all the Cache Info (type 7) tables present.
func (i *Info) GetCacheInfo() ([]*CacheInfo, error) {
	var res []*CacheInfo
	for _, t := range i.GetTablesByType(TableTypeCacheInfo) {
		ci, err := ParseCacheInfo(t)
		if err != nil {
			return nil, err
		}
		res = append(res, ci)
	}
	return res, nil
}

// GetMemoryDevices returns all the Memory Device (type 17) tables present.
func (i *Info) GetMemoryDevices() ([]*MemoryDevice, error) {
	var res []*MemoryDevice
	for _, t := range i.GetTablesByType(TableTypeMemoryDevice) {
		ci, err := NewMemoryDevice(t)
		if err != nil {
			return nil, err
		}
		res = append(res, ci)
	}
	return res, nil
}

// GetIPMIDeviceInfo returns all the IPMI Device Info (type 38) tables present.
func (i *Info) GetIPMIDeviceInfo() ([]*IPMIDeviceInfo, error) {
	var res []*IPMIDeviceInfo
	for _, t := range i.GetTablesByType(TableTypeIPMIDeviceInfo) {
		d, err := ParseIPMIDeviceInfo(t)
		if err != nil {
			return nil, err
		}
		res = append(res, d)
	}
	return res, nil
}

// GetTPMDevices returns all the TPM Device (type 43) tables present.
func (i *Info) GetTPMDevices() ([]*TPMDevice, error) {
	var res []*TPMDevice
	for _, t := range i.GetTablesByType(TableTypeTPMDevice) {
		d, err := NewTPMDevice(t)
		if err != nil {
			return nil, err
		}
		res = append(res, d)
	}
	return res, nil
}

redpajama_set_name: RedPajamaGithub


text:
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.affinity;

import java.util.List;

import org.apache.cloudstack.api.command.user.affinitygroup.CreateAffinityGroupCmd;

import com.cloud.uservm.UserVm;

public interface AffinityGroupService {

    /**
     * Creates an affinity/anti-affinity group for the given account/domain.
     *
     * @param accountName
     * @param projectId
     * @param domainId
     * @param affinityGroupName
     * @param affinityGroupType
     * @param description
     * @return AffinityGroup
     */
    AffinityGroup createAffinityGroup(String accountName, Long projectId, Long domainId, String affinityGroupName, String affinityGroupType, String description);

    AffinityGroup createAffinityGroup(CreateAffinityGroupCmd createAffinityGroupCmd);

    /**
     * Creates an affinity/anti-affinity group.
     *
     * @param affinityGroupId
     * @param accountName
     * @param domainId
     * @param affinityGroupName
     */
    boolean deleteAffinityGroup(Long affinityGroupId, String accountName, Long projectId, Long domainId, String affinityGroupName);

    /**
     * List group types available in deployment
     *
     * @return
     */
    List<String> listAffinityGroupTypes();

    AffinityGroup getAffinityGroup(Long groupId);

    UserVm updateVMAffinityGroups(Long vmId, List<Long> affinityGroupIds);

    boolean isAffinityGroupProcessorAvailable(String affinityGroupType);

    boolean isAdminControlledGroup(AffinityGroup group);

    boolean isAffinityGroupAvailableInDomain(long affinityGroupId, long domainId);

}


text:
package org.springframework.boot.actuate.autoconfigure.tracing.zipkin;

import zipkin2.Span;
import zipkin2.codec.BytesEncoder;
import zipkin2.codec.SpanBytesEncoder;
import zipkin2.reporter.Sender;

import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing;
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.BraveConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.OpenTelemetryConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.ReporterConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.SenderConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;

/**
 * {@link EnableAutoConfiguration Auto-configuration} for Zipkin.
 *
 * It uses imports on {@link ZipkinConfigurations} to guarantee the correct configuration
 * ordering.
 *
 * @author Moritz Halbritter
 * @since 3.0.0
 */
@AutoConfiguration(after = RestTemplateAutoConfiguration.class)
@ConditionalOnClass(Sender.class)
@Import({ SenderConfiguration.class, ReporterConfiguration.class, BraveConfiguration.class,
		OpenTelemetryConfiguration.class })
@ConditionalOnEnabledTracing
public class ZipkinAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean
	public BytesEncoder<Span> spanBytesEncoder() {
		return SpanBytesEncoder.JSON_V2;
	}

}


text:
const Lab = require('@hapi/lab');
const Hapi = require('@hapi/hapi');
const Sinon = require('sinon');
const Logger = require('test/fixtures/logger-plugin');
const ContactsCtrl = require('modules/authorization/controllers/contacts');
const ContactsService = require('modules/authorization/services/contacts');
const NSError = require('errors/nserror');

const { beforeEach, describe, expect, it } = (exports.lab = Lab.script());

describe('API Controller: contacts', () => {
    let server;

    const contacts = [
        { id: 1, email: 'admin@gmail.com', confirmed: true, signup_requests: 0 },
        { id: 2, email: 'test@gmail.com', confirmed: true, signup_requests: 0 },
        { id: 3, email: 'contact@gmail.com', confirmed: false, signup_requests: 0 },
        { id: 4, email: 'spammer@gmail.com', confirmed: false, signup_requests: 20 }
    ];

    beforeEach(async () => {
        // make server quiet, 500s are rethrown and logged by default..
        server = Hapi.server({ debug: { log: false, request: false } });
        await server.register(Logger);
    });

    it('performs user signup', async flags => {
        // cleanup
        flags.onCleanup = function() {
            ContactsService.signup.restore();
        };

        // setup
        const fakeEmail = 'test@test.com';
        Sinon.stub(ContactsService, 'signup').resolves();
        server.route({ method: 'POST', path: '/signup', handler: ContactsCtrl.signup });

        // exercise
        const response = await server.inject({
            method: 'POST',
            url: '/signup',
            payload: { email: fakeEmail }
        });

        // validate
        expect(ContactsService.signup.calledOnce).to.be.true();
        expect(response.statusCode).to.equal(200);
        expect(response.result.success).to.be.true();
        expect(response.result.message).to.equals('sign up');
    });

    it('handles user signup failure', async flags => {
        // cleanup
        flags.onCleanup = function() {
            ContactsService.signup.restore();
        };

        // setup
        Sinon.stub(ContactsService, 'signup').rejects();
        server.route({ method: 'POST', path: '/signup', handler: ContactsCtrl.signup });

        // exercise
        const response = await server.inject({
            method: 'POST',
            url: '/signup',
            payload: { email: '' }
        });

        // verify
        expect(ContactsService.signup.calledOnce).to.be.true();
        expect(response.statusCode).to.equals(500);
        expect(response.statusMessage).to.equal('Internal Server Error');
        expect(response.result.message).to.equal('An internal server error occurred');
    });

    it('lists available contacts', async flags => {
        // cleanup
        flags.onCleanup = function() {
            listStub.restore();
            countStub.restore();
        };

        // setup
        const listStub = Sinon.stub(ContactsService, 'list');
        const countStub = Sinon.stub(ContactsService, 'count');
        const toolkitStub = Sinon.stub()
            .withArgs(contacts, contacts.length)
            .returns(contacts);
        listStub.resolves(contacts);
        countStub.resolves(contacts.length);
        server.route({ method: 'GET', path: '/contact', handler: ContactsCtrl.list });
        server.decorate('toolkit', 'paginate', toolkitStub);

        // exercise
        const response = await server.inject({
            method: 'GET',
            url: '/contact'
        });

        // validate
        expect(listStub.calledOnce).to.be.true();
        expect(countStub.calledOnce).to.be.true();
        expect(toolkitStub.calledOnce).to.be.true();
        expect(response.statusCode).to.equal(200);
        expect(response.statusMessage).to.equal('OK');
        expect(response.result).to.equal(contacts);
    });

    it('lists available contacts with criteria', async flags => {
        // cleanup
        flags.onCleanup = function() {
            listStub.restore();
            countStub.restore();
        };

        // setup
        const fakeCriteria = { limit: '1' };
        const listStub = Sinon.stub(ContactsService, 'list');
        const countStub = Sinon.stub(ContactsService, 'count');
        const toolkitStub = Sinon.stub()
            .withArgs(contacts, contacts.length)
            .returns([contacts[0]]);
        listStub.withArgs(fakeCriteria).resolves(contacts[0]);
        countStub.withArgs(fakeCriteria).resolves(1);
        server.route({ method: 'GET', path: '/contact', handler: ContactsCtrl.list });
        server.decorate('toolkit', 'paginate', toolkitStub);

        // exercise
        const response = await server.inject({
            method: 'GET',
            url: '/contact?limit=1'
        });

        expect(listStub.calledOnce).to.be.true();
        expect(countStub.calledOnce).to.be.true();
        expect(toolkitStub.calledOnce).to.be.true();
        expect(response.statusCode).to.equal(200);
        expect(response.statusMessage).to.equal('OK');
        expect(response.result).to.equal([contacts[0]]);
    });

    it('handles server errors while listing contacts', async flags => {
        // cleanup
        flags.onCleanup = function() {
            listStub.restore();
            countStub.restore();
        };

        // setup
        const listStub = Sinon.stub(ContactsService, 'list');
        const countStub = Sinon.stub(ContactsService, 'count');
        listStub.rejects(NSError.RESOURCE_FETCH());
        countStub.resolves();
        server.route({ method: 'GET', path: '/contact', handler: ContactsCtrl.list });

        // exercise
        const response = await server.inject({
            method: 'GET',
            url: '/contact'
        });

        expect(listStub.calledOnce).to.be.true();
        expect(countStub.calledOnce).to.be.true();
        expect(response.statusCode).to.equal(500);
        expect(response.statusMessage).to.equal('Internal Server Error');
        expect(response.result.message).to.equal('An internal server error occurred');
    });

    it('gets a contact', async flags => {
        // cleanup
        flags.onCleanup = function() {
            findByIdStub.restore();
        };

        // setup
        const findByIdStub = Sinon.stub(ContactsService, 'findById');
        findByIdStub.withArgs(1).resolves(contacts[0]);
        server.route({ method: 'GET', path: '/contact/{id}', handler: ContactsCtrl.get });

        // exercise
        const response = await server.inject({
            method: 'GET',
            url: '/contact/1'
        });

        // validate
        expect(findByIdStub.calledOnce).to.be.true();
        expect(response.statusCode).to.equal(200);
        expect(response.statusMessage).to.equal('OK');
        expect(response.result).to.equal(contacts[0]);
    });

    it('handles get of a non existing contact', async flags => {
        // cleanup
        flags.onCleanup = function() {
            findByIdStub.restore();
        };

        // setup
        const findByIdStub = Sinon.stub(ContactsService, 'findById');
        findByIdStub.rejects(NSError.RESOURCE_NOT_FOUND());
        server.route({ method: 'GET', path: '/contact/{id}', handler: ContactsCtrl.get });

        // exercise
        const response = await server.inject({
            method: 'GET',
            url: '/contact/2'
        });

        // validate
        expect(findByIdStub.calledOnce).to.be.true();
        expect(response.statusCode).to.equals(404);
        expect(response.statusMessage).to.equals('Not Found');
        expect(response.result.message).to.equals(NSError.RESOURCE_NOT_FOUND().message);
    });

    it('handles server error while getting a user', async flags => {
        // cleanup
        flags.onCleanup = function() {
            findByIdStub.restore();
        };

        // setup
        const findByIdStub = Sinon.stub(ContactsService, 'findById');
        findByIdStub.rejects(NSError.RESOURCE_FETCH());
        server.route({ method: 'GET', path: '/contact/{id}', handler: ContactsCtrl.get });

        // exercise
        const response = await server.inject({
            method: 'GET',
            url: '/contact/2'
        });

        // validate
        expect(findByIdStub.calledOnce).to.be.true();
        expect(response.statusCode).to.equals(500);
        expect(response.statusMessage).to.equals('Internal Server Error');
        expect(response.result.message).to.equal('An internal server error occurred');
    });

    it('deletes an existing contact', async flags => {
        // cleanup
        flags.onCleanup = function() {
            deleteStub.restore();
        };

        // setup
        const deleteStub = Sinon.stub(ContactsService, 'delete');
        deleteStub.withArgs(2).resolves();
        server.route({ method: 'DELETE', path: '/contact/{id}', handler: ContactsCtrl.delete });

        // exercise
        const response = await server.inject({
            method: 'DELETE',
            url: '/contact/2'
        });

        // validate
        expect(deleteStub.calledOnce).to.be.true();
        expect(response.statusCode).to.equals(204);
        expect(response.statusMessage).to.equals('No Content');
    });

    it('handles deleting a contact that does not exist', async flags => {
        // cleanup
        flags.onCleanup = function() {
            deleteStub.restore();
        };

        // setup
        const deleteStub = Sinon.stub(ContactsService, 'delete');
        deleteStub.rejects(NSError.RESOURCE_NOT_FOUND());
        server.route({ method: 'DELETE', path: '/contact/2', handler: ContactsService.delete });

        // exercise
        const response = await server.inject({
            method: 'DELETE',
            url: '/contact/2'
        });

        // validate
        expect(deleteStub.calledOnce).to.be.true();
        expect(response.statusCode).to.equal(404);
        expect(response.statusMessage).to.equal('Not Found');
        expect(response.result.message).to.equal(NSError.RESOURCE_NOT_FOUND().message);
    });

    it('handles server errors while deleting a contact', async flags => {
        // cleanup
        flags.onCleanup = function() {
            deleteStub.restore();
        };

        // setup
        const deleteStub = Sinon.stub(ContactsService, 'delete');
        deleteStub.rejects(NSError.RESOURCE_FETCH());
        server.route({ method: 'DELETE', path: '/contact/2', handler: ContactsService.delete });

        // exercise
        const response = await server.inject({
            method: 'DELETE',
            url: '/contact/2'
        });

        // validate
        expect(deleteStub.calledOnce).to.be.true();
        expect(response.statusCode).to.equal(500);
        expect(response.statusMessage).to.equal('Internal Server Error');
        expect(response.result.message).to.equal('An internal server error occurred');
    });
});


text:
var gulp = require('gulp');
var runSequence = require('run-sequence');

gulp.task('build:dev', buildDevTask);

function buildDevTask() {
    runSequence(
        'clean',
        [
            'sass',
            'copy:html',
            'copy:resources'
        ],
        'watch'
    )
}


text:
@implementation SamuraiHtmlRenderObjectViewport

+ (instancetype)renderObjectWithDom:(SamuraiHtmlDomNode *)dom andStyle:(SamuraiHtmlStyle *)style
{
	SamuraiHtmlRenderObjectViewport * renderObject = [super renderObjectWithDom:dom andStyle:style];

	return renderObject;
}

#pragma mark -

- (id)init
{
	self = [super init];
	if ( self )
	{
	}
	return self;
}

- (void)dealloc
{
}

#pragma mark -

+ (Class)defaultViewClass
{
	return [HtmlViewport class];
}

#pragma mark -

- (UIView *)createViewWithIdentifier:(NSString *)identifier
{
	return [super createViewWithIdentifier:identifier];
}

#pragma mark -

- (BOOL)html_hasChildren
{
	return YES;
}

#pragma mark -

- (void)renderWillLoad
{
	[super renderWillLoad];
}

- (void)renderDidLoad
{
	[super renderDidLoad];
}

#pragma mark -

- (BOOL)layoutShouldWrapLine
{
	return [super layoutShouldWrapLine];
}

- (BOOL)layoutShouldWrapBefore
{
	return [super layoutShouldWrapBefore];
}

- (BOOL)layoutShouldWrapAfter
{
	return [super layoutShouldWrapAfter];
}

- (BOOL)layoutShouldAutoSizing
{
	return [super layoutShouldAutoSizing];
}

- (BOOL)layoutShouldCenteringInRow
{
	return [super layoutShouldCenteringInRow];
}

- (BOOL)layoutShouldCenteringInCol
{
	return [super layoutShouldCenteringInCol];
}

- (BOOL)layoutShouldLeftJustifiedInRow
{
	return [super layoutShouldLeftJustifiedInRow];
}

- (BOOL)layoutShouldRightJustifiedInRow
{
	return [super layoutShouldRightJustifiedInRow];
}

- (BOOL)layoutShouldPositioningChildren
{
	return [super layoutShouldPositioningChildren];
}

- (BOOL)layoutShouldArrangedInRow
{
	return [super layoutShouldArrangedInRow];
}

- (BOOL)layoutShouldArrangedInCol
{
	return [super layoutShouldArrangedInCol];
}

- (BOOL)layoutShouldArrangedReverse
{
	return [super layoutShouldArrangedReverse];
}

- (BOOL)layoutShouldHorizontalAlign
{
	return [super layoutShouldHorizontalAlign];
}

- (BOOL)layoutShouldHorizontalAlignLeft
{
	return [super layoutShouldHorizontalAlignLeft];
}

- (BOOL)layoutShouldHorizontalAlignRight
{
	return [super layoutShouldHorizontalAlignRight];
}

- (BOOL)layoutShouldHorizontalAlignCenter
{
	return [super layoutShouldHorizontalAlignCenter];
}

- (BOOL)layoutShouldVerticalAlign
{
	return [super layoutShouldVerticalAlign];
}

- (BOOL)layoutShouldVerticalAlignTop
{
	return [super layoutShouldVerticalAlignTop];
}

- (BOOL)layoutShouldVerticalAlignMiddle
{
	return [super layoutShouldVerticalAlignMiddle];
}

- (BOOL)layoutShouldVerticalAlignBottom
{
	return [super layoutShouldVerticalAlignBottom];
}

#pragma mark -

- (CGRect)layoutWithContext:(SamuraiHtmlLayoutContext *)context
			  parentContext:(SamuraiHtmlLayoutContext *)parentContext
{
	DEBUG_RENDERER_LAYOUT( self );

	htmlLayoutInit( context );

	if ( HtmlRenderDisplay_None != self.display )
	{
		htmlLayoutBegin( context );
		htmlLayoutResize( context, context->bounds );

		for ( SamuraiHtmlRenderObject * child in self.childs )
		{
			if ( HtmlRenderDisplay_None == child.display )
			{
				SamuraiHtmlLayoutContext childContext = {
					.style		= child.style,
					.bounds		= CGSizeZero,
					.origin		= CGPointZero,
					.collapse	= context->computedMargin
				};

				[child layoutWithContext:&childContext parentContext:NULL];
			}
			else
			{
				SamuraiHtmlLayoutContext childContext = {
					.style		= child.style,
					.bounds		= context->bounds,
					.origin		= CGPointZero,
					.collapse	= context->computedMargin
				};

				[child layoutWithContext:&childContext parentContext:NULL];
			}
		}

		htmlLayoutFinish( context );
	}

	self.lines		= 1;
	self.start		= CGPointMake( CGRectGetMinX( context->computedBounds ), CGRectGetMinY( context->computedBounds ) );
	self.end		= CGPointMake( CGRectGetMaxX( context->computedBounds ), CGRectGetMinY( context->computedBounds ) );

	self.inset		= context->computedInset;
	self.border		= context->computedBorder;
	self.margin		= context->computedMargin;
	self.padding	= context->computedPadding;
	self.bounds		= context->computedBounds;

	return self.bounds;
}

#pragma mark -

- (id)html_serialize
{
	if ( 0 == [self.childs count] )
	{
		return nil;
	}

	if ( 1 == [self.childs count] )
	{
		return [[self.childs firstObject] html_serialize];
	}
	else
	{
		NSMutableArray * array = [NSMutableArray array];

		for ( SamuraiRenderObject * child in self.childs )
		{
			[array addObject:[child html_serialize]];
		}

		return array;
	}
}

- (void)html_unserialize:(id)obj
{
	if ( 1 == [self.childs count] )
	{
		SamuraiRenderObject * childRender = [self.childs firstObject];

		if ( DomNodeType_Text == childRender.dom.type )
		{
			[childRender html_unserialize:obj];
		}
	}
	else
	{
		int __break = 0;
	}
}

- (void)zerolize
{
	for ( SamuraiRenderObject * child in self.childs )
	{
		[child html_zerolize];
	}
}

@end

// ----------------------------------
// Unit test
// ----------------------------------

#pragma mark -

#if __SAMURAI_TESTING__

TEST_CASE( WebCore, HtmlRenderViewport )

DESCRIBE( before )
{
}

DESCRIBE( after )
{
}

TEST_CASE_END

#endif	// #if __SAMURAI_TESTING__

#endif	// #if (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR)

#import "_pragma_pop.h"


text:
namespace BizHawk.Emulation.Cores.Components.M6502
{
	// Interface that has all the methods required by the MOS 6502X to talk to
	// the emulator core.
	// Should only be used as a generic type argument for the MOS 6502X, and
	// implementations should be structs where possible. This combination allows
	// the JITer to generate much faster code than calling a Func<> or Action<>.
	public interface IMOS6502XLink
	{
		byte ReadMemory(ushort address);
		byte DummyReadMemory(ushort address);
		byte PeekMemory(ushort address);
		void WriteMemory(ushort address, byte value);

		// This only calls when the first byte of an instruction is fetched.
		void OnExecFetch(ushort address);
	}
}


text:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

     <title>DrugBank(drugbank)</title>

     <link rel="shortcut icon" href="logo-circle.ico" />
     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
     <link rel="stylesheet" href="https://cdn.datatables.net/1.10.13/css/jquery.dataTables.min.css" crossorigin="anonymous">

     <link rel="stylesheet" href="style.css">

      <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">

      </button>

	(Re)usable Data Project

            Learn more

              Who we are
              What this is
              Criteria overview
              Our sources data

            Evaluation

              Criteria details
              License categories
              Curation fields (schema)
              Reading list

            Explore

            About

        <form class="form-inline my-2 my-lg-0"
	      action="https://google.com/search"
	      method="GET">
          <input class="form-control mr-sm-2"
		 placeholder="Site search"
		 type="text"
		 name="q">
          <input type="hidden"
		 name="sitesearch"
		 value="reusabledata.org">
          <button class="btn btn-outline-success my-2 my-sm-0" type="submit">🔍</button>
        </form>

DrugBank - drugbank

		 Grade

		 The grade for the resource as automatically
		 determined by the criteria
		 violations.

		 4

		 Description

		 A full description of the resource from the resource
		 itself, if possible.

		 The DrugBank database is a unique bioinformatics and cheminformatics resource that combines detailed drug data with comprehensive drug target information.

		 Last curated

		 (Optional) The ISO 8601 date of when the resource was
		 last curated.

		 2017-12-03

		 Location

		 URL for the resource.

		 https://www.drugbank.ca

		 Source type

		 (Optional) How the resource relates to the data it
		 contains.  Current allowable entries are: "unknown",
		 "repository", "source", "integrator", and "warehouse".

		 source

		 Curation status

		 Whether or not annotation is complete on this
		 resource.  Current allowable entries are: "complete",
		 "incomplete", and "nonpublic".

		 complete

		 Field

		 The area of research for the resource.

		 pharmacology

		 Type

		 The type of data the resource contains.

		 drug

		 Categories

		 (Optional) Tags to describe the resource and its data.

		 bioinformatics

		 cheminformatics

		 drugs

		 drug-protein interactions

		 targets

		 pathways

		 Access

		 (Optional) Links to the resource's data.

		 download

		 License

		  The license that is used by the resource. We use
		  SPDX where
		  we can or: "inconsistent", "public domain", "unlicensed",
		  "all rights reserved", or "custom".

		 CC-BY-NC-4.0

		 License type

		 The type of license that is being used. This will be
		 to define compatible data pools in the future; we
		 only use the grossest terms now.  If it is not known
		 "unknown" is used. Current possible values are:
		 "unknown", "unlicensed", "copyleft", "permissive",
		 "public domain", "copyright", "restrictive", or "private pool".

		 restrictive

		 License location

		 (Optional) The link to the resource license.

		 https://www.drugbank.ca/releases/latest

		 Focused curation

		 (Optional) Setting this flag to true indicates that
		 the licensing was combinatorially complicated enough
		 (as is the case in some commercial licenses) that the
		 curator chose to wear a single "hat" during the
		 process. From the site text: "While we try to cover
		 as much of the licensing possibilities of a data
		 resource that we can, in a few cases we may choose a
		 particular "hat" to wear while evaluating to prevent
		 a combinatorial explosion, which may also reduce the
		 clarity of our curations for the community. In these
		 cases, we may take on the role of a (1)
		 non-commercial (2) academic (3) group that is (4)
		 based in the US and trying to (5) create an
		 aggregating resource, noting that other entities may
		 have different results in the license commentary."

		 false

		 Issues

		 (Optional) Structured issues with the license.  For
		 every issue discovered with a resource, there should
		 be a corresponding item in the license-issues field
		 that marks the /exact/ violation, along with any
		 comments. This field can be used by resources as the
		 first step to improvement, as well as clarify any
		 surrounding circumstances.  Any issues or thoughts
		 about a resource that do not slot into one of the
		 criteria violations can go into the
		 license-commentary field.

		 Criteria D.1.1: The NC license allows for liberal non-commercial reuse.

		 Criteria E.1.1: The NC license allows for non-commercial reuse with other non-commercial interests.

		 Commentary

		 (Optional) Further commentary on the license,
		 possibly including the though process of the
		 curations and things like locations of additional
		 licenses.

		 • This seems to ba a standard NC/commercial licensing split, with DrugBank+ supporting commerical licensing for fees.

		 • To download, one must sign up for an account (https://www.drugbank.ca/public_users); possibly for tracking/analytics.

		 Controversial

		 (Optional) Marker noting that there was some extended
		 internal discussion or controversy about the
		 evaluation of the licensing terms. If this is marked
		 at "true", the controversy, or a link to a permanent
		 archive of the controversy, must be sufficiently
		 contained in the "license-commentary" to reconstruct
		 the issues.

		 false

		 Contacts

		 (Optional) Resource contact information, link, email,
		 or whatever is public.

		 support@drugbankplus.com

		 Grants

		 (Optional) Semi-structured list of supporting grants.

		 This project is supported by the Canadian Institutes of Health Research (award #111062), Alberta Innovates - Health Solutions, and by The Metabolomics Innovation Centre (TMIC), a nationally-funded research and core facility that supports a wide range of cutting-edge metabolomic studies. TMIC is funded by Genome Alberta, Genome British Columbia, and Genome Canada, a not-for-profit organization that is leading Canada\'s national genomics strategy with funding from the federal government. Maintenance, support, and commercial licensing is provided by OMx Personal Health Analytics, Inc. Designed by Educe Design & Innovation Inc.

	      All copyrightable materials on this site are ©
	      2019 the (Re)usable Data
	      Project under the
	      CC-BY
	      4.0 license.

	    The (Re)usable Data Project is funded by the National Center for
	    Advancing Translational Sciences
	    (NCATS) OT3
	    TR002019 as part of
	    the Biomedical
	    Data Translator project
	    and U24TR002306 as part of the CTSA Program National Center for Data to Health (CD2H).

	      The (Re)usable Data Project would like to acknowledge
	      the assistance of many more people than can be listed
	      here. Please visit the about
	      page for the full list.


text:
package com.billchan.maptest;

import android.content.Context;
import android.graphics.Color;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;

import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;

import org.w3c.dom.Document;

import java.util.ArrayList;

import android.location.LocationListener;
import android.view.View;
import android.widget.Button;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, LocationListener {

    LatLng myLatLng;
    LatLng shopLatLng;
    Boolean isDirectionDrawn = false;
    private GoogleMap mMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        Button showDirectionsButton = (Button) findViewById(R.id.resetRouteButton);
        showDirectionsButton.setOnClickListener(new Button.OnClickListener() {

            @Override
            public void onClick(View v) {
                new LongOperation().execute("");
            }
        });
    }

    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        // Add a marker in Sydney and move the camera
        double latitude = 48.144744;
        double longitude = 11.560171;
        shopLatLng = new LatLng(latitude, longitude);

        mMap.addMarker(new MarkerOptions()
                        .position(shopLatLng)
                        .title("Lou Lan Cha")
        );
        mMap.moveCamera(CameraUpdateFactory.newLatLng(shopLatLng));

        mMap.setMyLocationEnabled(true);

        moveToCurrentLocation(shopLatLng);

        LocationManager locationManager = (LocationManager)
                getSystemService(Context.LOCATION_SERVICE);
        Criteria criteria = new Criteria();

        Location location = locationManager.getLastKnownLocation(locationManager
                .getBestProvider(criteria, false));

        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
                2000, 1, this);

        try {

            myLatLng = new LatLng(location.getLatitude(), location.getLongitude());
        }
        catch (Exception e)
        {
            ///possible error:
            /// null location
        }

        new LongOperation().execute("");
    }

    private void moveToCurrentLocation(LatLng currentLocation)
    {
        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(currentLocation, 15));
        // Zoom in, animating the camera.
        mMap.animateCamera(CameraUpdateFactory.zoomIn());
        // Zoom out to zoom level 10, animating with a duration of 2 seconds.
        mMap.animateCamera(CameraUpdateFactory.zoomTo(15), 2000, null);

    }

    private void zoomToPoints() {
        try {

            LatLngBounds.Builder builder = new LatLngBounds.Builder();
            //        for (Marker marker : markers) {
            builder.include(myLatLng);
            builder.include(shopLatLng);
            //        }
            LatLngBounds bounds = builder.build();

            int padding = 50; // offset from edges of the map in pixels
            CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);

            mMap.animateCamera(cu);
        }
        catch (Exception e)
        {
            ///possible error:
            /// java.lang.NullPointerException: Attempt to invoke interface method 'org.w3c.dom.NodeList org.w3c.dom.Document.getElementsByTagName(java.lang.String)' on a null object reference
        }
    }

    @Override
    public void onLocationChanged(Location location) {

        double latitude = location.getLatitude();
        double longitude = location.getLongitude();

        myLatLng = new LatLng(latitude, longitude);

        zoomToPoints();

        if(!isDirectionDrawn) {

            new LongOperation().execute("");
        }
    }

    @Override
    public void onProviderDisabled(String provider) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        if(!isDirectionDrawn) {
            //            zoomToPoints();
            new LongOperation().execute("");
        }
    }

    private class LongOperation extends AsyncTask<String, Void, PolylineOptions> {

        private PolylineOptions getDirection() {
            try {
                GMapV2Direction md = new GMapV2Direction();

                Document doc = md.getDocument(myLatLng, shopLatLng,
                        GMapV2Direction.MODE_WALKING);

                ArrayList<LatLng> directionPoint = md.getDirection(doc);
                PolylineOptions rectLine = new PolylineOptions().width(9).color(
                        Color.GREEN);

                for (int i = 0; i < directionPoint.size(); i++) {
                    rectLine.add(directionPoint.get(i));
                }
                isDirectionDrawn = true;

                return rectLine;
            }
            catch (Exception e)
            {
                ///possible error:
                ///java.lang.IllegalStateException: Error using newLatLngBounds(LatLngBounds, int): Map size can't be 0. Most likely, layout has not yet occured for the map view.  Either wait until layout has occurred or use newLatLngBounds(LatLngBounds, int, int, int) which allows you to specify the map's dimensions.
                return null;
            }

        }

        @Override
        protected PolylineOptions doInBackground(String... params) {
            PolylineOptions polylineOptions = null;
            try {
                polylineOptions = getDirection();
            } catch (Exception e) {
                Thread.interrupted();
            }
            return polylineOptions;
        }

        @Override
        protected void onPostExecute(PolylineOptions result) {
            // might want to change "executed" for the returned string passed
            // into onPostExecute() but that is upto you

            mMap.clear();///TODO: clean the path only.

            mMap.addMarker(new MarkerOptions()
                            .position(shopLatLng)
                            .title("Lou Lan Cha")
            );

            mMap.addPolyline(result);
            zoomToPoints();
        }

        @Override
        protected void onPreExecute() {}

        @Override
        protected void onProgressUpdate(Void... values) {}
    }
}


text:
/**
 * @file DumpParse.h
 * @author biko
 * @date 2016-07-30
 *
 * DumpParse module public header.
 * Contains routines for accessing memory dump files.
 */
#pragma once

/** Headers *************************************************************/
#include <Windows.h>

/** Typedefs ************************************************************/

/**
 * Handle to an open dump file.
 */
DECLARE_HANDLE(HDUMP);
typedef HDUMP *PHDUMP;

/** Functions ***********************************************************/

/**
 * Opens a dump file.
 *
 * @param[in]	pwszPath	Path to the dump file.
 *							If not specified, the system crash dump
 *							will be opened (usually C:\Windows\MEMORY.DMP).
 * @param[in]	phDump		Will receive a handle to the dump file.
 *
 * @returns HRESULT
 */
HRESULT
DUMPPARSE_Open(
	_In_opt_	PCWSTR	pwszPath,
	_Out_		PHDUMP	phDump
);

/**
 * Closes a dump file.
 *
 * @param[in]	hDump	Dump file to close.
 */
VOID
DUMPPARSE_Close(
	_In_	HDUMP	hDump
);

/**
 * Reads tagged data from the dump file.
 *
 * @param[in]	hDump	Dump file to read from.
 * @param[in]	ptTag	Tag identifying the data to read.
 * @param[out]	ppvData	Will receive the read data.
 * @param[out]	pcbData	Will receive the read data's size, in bytes.
 *
 * @returns HRESULT
 *
 * @remark Free the returned buffer to the process heap.
 */
HRESULT
DUMPPARSE_ReadTagged(
	_In_									HDUMP	hDump,
	_In_									LPCGUID	ptTag,
	_Outptr_result_bytebuffer_(*pcbData)	PVOID *	ppvData,
	_Out_									PDWORD	pcbData
);


text:
ALeapMotionControllerActor::ALeapMotionControllerActor(const class FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// Create our battery collection volume
	LeapMotionControllerComponent = ObjectInitializer.CreateDefaultSubobject<ULeapMotionControllerComponent>(this, TEXT("RootLeapMotionControllerComponent"));
	RootComponent = LeapMotionControllerComponent;

	LeapMotionImageComponent = ObjectInitializer.CreateDefaultSubobject<ULeapMotionImageComponent>(this, TEXT("LeapMotionImageComponent"));
	LeapMotionImageComponent->bIsPaused = true;
	LeapMotionImageComponent->bIsVisible = false;
	LeapMotionImageComponent->DisplaySurfaceComponent->SetVisibility(false);

	PrimaryActorTick.bCanEverTick = true;
	PrimaryActorTick.bStartWithTickEnabled = true;
}

#if WITH_EDITOR
void ALeapMotionControllerActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);
	LeapMotionControllerComponent->PostEditChangeProperty(PropertyChangedEvent);
	LeapMotionImageComponent->PostEditChangeProperty(PropertyChangedEvent);
}
#endif


text:
Welcome to the Full Virtual Micromagnetics Simulation Environment!
------------------------------------------------------------------

This virtual machine contains a number of distributable software packages that
are often used by the micromagnetic community to simulate relevant
problems. These software can be downloaded from this machine; tarballs are
available in the home directory. Micromagnetic software includes (in no
particular order):

  | OOMMF 1.2a5 (The Object Oriented MicroMagnetic Framework): http://math.nist.gov/oommf/
  | Documentation: http://math.nist.gov/oommf/doc/

  | Nmag 0.2: http://nmag.soton.ac.uk/nmag/
  | Documentation: http://nmag.soton.ac.uk/nmag/current/manual/singlehtml/manual.html

  | Magpar 0.9 (Parallel Finite Element Micromagnetics Package): http://www.magpar.net/
  | Documentation: http://www.magpar.net/static/magpar-0.9/doc/html/

  | Fidimag 0.0.1 (Finite-difference Micromagnetic and Atomistic Package): http://computationalmodelling.github.io/fidimag/
  | Documentation: http://fidimag.readthedocs.org/en/latest/

For more information about these packages, see their respective websites and
examples available on the desktop, which walk the user through some examples
and tests where applicable. Users are also encouraged to install their own
simulation packages for use on this machine. To that end, a number of other
software packages have been provided:

  | FEniCS 1.5.0: http://fenicsproject.org/
  | The FEniCS project is a collection of free software tools with many features for solving differential equations in an automated and efficient manner.

  | gmsh 2.8.5: http://geuz.org/gmsh/
  | Netgen: http://sourceforge.net/projects/netgen-mesher/files/
  | gmsh and Netgen are 3D mesh generators often used for the finite element method in micromagnetic modelling.

Other lower-level tools are installed for convenience, such as paraview, grace,
gnuplot, the IPython (Jupyter) Notebook and more. This machine runs on 64-bit
Ubuntu with Xfce as a desktop environment.

For more information on Virtual Micromagnetics, visit
http://virtualmicromagnetics.org. Information about the environment that
created the base box that this machine is built with, such as the version
number, can be found in this `file
</virtualmicromagnetics_machine_characteristics.txt>`_. Please include this
information when reporting a suspected bug.

These tools are provided by the University of Southampton (Mark Vousden, Max
Albert, Hans Fangohr and others). Neither the University of Southampton nor the
authors assume any responsibility whatsoever for its use by other parties, and
makes no guarantees, expressed or implied, about its quality, reliability, or
any other characteristic. Please provide feedback about this machine to Mark
Vousden at mark.vousden@soton.ac.uk