The code examples are in PHP but the question is language agnostic.
Situation
I'm trying to figure out the best way to separate a service layer into multiple well defined layers.
In the example below I'm uploading a base64 encoded user avatar and showing how it would go through the layers. I'm using the decorator pattern to simulate the layers.
Important:
The data passed into each layer is usually changed in some way before it is passed into the next layer which is exactly what I'm looking for. The one thing I don't like about this is that in order to update an avatar you must first talk to the ValidatedProfile object instead of say a Profile object. Something about it seems weird but I could always have a Profile object which delegates calls to the ValidatedProfile.
The Layers
Validation:
This is where you validate the data. As in the example below it is where you check the format of the $avatar string and ensure it is a valid image resource. During the validation process entity objects and resources are often created which are then passed to the next layer.
Verification:
Perform checks such as verifying if a supplied ID is real. As in the example below this is where I check if the supplied user ID is actually the real ID of a user.
Commander:
Where the action to be performed happens. By the time this layer is reached the data is thought to be fully validated and verified and no further checks need to be done on it. The commander delegates the action to other services(usually entity services) and can also call other services do more actions.
Entity:
This layer works with actions to be performed on an entity and/or its relations.
ValidatedProfile
class ValidatedProfile
{
private $verifiedProfile;
/**
* #param string $avatar Example: data:image/png;base64,AAAFBfj42Pj4
*/
public function updateAvatar($userId, $avatar)
{
$pattern = '/^data:image\/(png|jpeg|gif);base64,([a-zA-Z0-9=\+\/]+)$/';
if (!preg_match($pattern, $avatar, $matches)) {
// error
}
$type = $matches[1]; // Type of image
$data = $matches[2]; // Base64 encoded image data
$image = imagecreatefromstring(base64_decode($data));
// Check if the image is valid etc...
// Everything went okay
$this->verifiedProfile->updateAvatar($userId, $image);
}
}
VerifiedProfile
class VerifiedProfile
{
private $profileCommander;
public function updateAvatar($userId, $image)
{
$user = // get user from persistence
if ($user === null) {
// error
}
// User does exist
$this->profileCommander->updateAvatar($user, $image);
}
}
ProfileCommander
class ProfileCommander
{
private $userService;
public function updateAvatar($user, $image)
{
$this->userService->updateAvatar($user, $image);
// If any processes need to be run after an avatar is updated
// you can invoke them here.
}
UserService
class UserService
{
private $persistence;
public function updateAvatar($user, $image)
{
$fileName = // generate file name
// Save the image to disk.
$user->setAvatar($fileName);
$this->persistence->persist($user);
$this->persistence->flush($user);
}
}
You could then have a Profile class like the following:
class Profile
{
private $validatedProfile;
public function updateAvatar($userId, $avatar)
{
return $this->validatedProfile->updateAvatar($userId, $avatar);
}
}
That way you just talk to an instance of Profile rather than ValidatedProfile which makes more sense I think.
Are there better and more widely accepted ways to achieve what I'm trying to do here?
I think you have too many layers. Two major objects should be enough for an operation like this. You need to validate the avatar input and persist it somehow.
Since you need to validate the user id and it is tied to persistence somehow, you can delegate that to the UserService object.
interface UserService {
/**
* #param string $userId
* #param resource $imageResource
*/
public function updateAvatar($userId, $imageResource);
/**
* #param string $userId
* #return bool
*/
public function isValidId($userId);
}
This checking for a valid user id should be a part of request validation. I would not make it a separate step like verification. So UserAvatarInput can handle that (validation implementation is just an example), and a little wrapper method to persist it all.
class UserAvatarInput {
/**
* #var UserService
*/
private $userService;
/**
* #var string
*/
private $userId;
/**
* #var resource
*/
private $imageResource;
public function __construct(array $data, UserService $service) {
$this->userService = $service; //we need it for save method
$errorMessages = [];
if (!array_key_exists('image', $data)) {
$errorMessages['image'] = 'Mandatory field.';
} else {
//validate and create image and set error if not good
$this->imageResource = imagecreatefromstring($base64);
}
if (!array_key_exists('userId', $data)) {
$errorMessages['userId'] = 'Mandatory field.';
} else {
if ($this->userService->isValidId($data['userId'])) {
$this->userId = $data['userId'];
} else {
$errorMessages['userId'] = 'Invalid user id.';
}
}
if (!empty($errorMessages)) {
throw new InputException('Input Error', 0, null, $errorMessages);
}
}
public function save() {
$this->userService->updateAvatar($this->userId, $this->imageResource);
}
}
I used an exception object to pass validation messages around.
class InputException extends Exception {
private $inputErrors;
public function __construct($message, $code, $previous, $inputErrors) {
parent::__construct($message, $code, $previous);
$this->inputErrors = $inputErrors;
}
public function getErrors() {
return $this->inputErrors;
}
}
This is how a client would use it, for example:
class UserCtrl {
public function postAvatar() {
try {
$input = new AvatarInput($this->requestData(), new DbUserService());
$input->save();
} catch (InputException $exc) {
return new JsonResponse($exc->getErrors(), 403);
}
}
}
Related
I have a particular part in code where all I want to do is the below, but I am at a loss to write in a way that doesn't involve code repetition. Is there a way that I can declare a list of methods, which can be then applied to productFeatureValidationDto. My current approach is noob-ish.
public ValidateProductFeatureResponse validateProductFeatureAgainstAllCriteria(ProductFeatureValidationDto productFeatureValidationDto) throws
ApplicationException, ParseException {
ValidateProductFeatureResponse response;
response = this.validateProductFeatureA(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureB(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureA(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(MPResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureC(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(MPResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureD(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureE(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureF(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
return getResponseOnValidationSuccess(productFeatureValidationDto);
}
Thanks in advance.
if you can use spring framework.
at first you can define an interface like this.
public interface ValidateProduct{
ValidateProductFeatureResponse validate(ProductFeatureValidationDto dto);
}
Your specific verification class implements this interface and register to srpingcontext
public ValidateProductFeatureResponse validateProductFeatureAgainstAllCriteria(ProductFeatureValidationDto productFeatureValidationDto) throws
ApplicationException, ParseException {
ValidateProductFeatureResponse response;
Map<String, ValidateProduct> beansOfType = applicationContext.getBeansOfType(ValidateProduct.class);
for (ValidateProduct value : beansOfType.values()) {
response = value.validate(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
}
return getResponseOnValidationSuccess(productFeatureValidationDto);
}
I would suggest following approach (schematic):
/**
* List of validation functions
*/
private final static List<Function<ProductFeatureValidationDto, ProductFeatureValidationDto>> VALIDATIONS = new LinkedList<>();
/**
* Fill validations list
*/
static {
VALIDATIONS.add((source) -> {
// test for feature A
return source;
});
VALIDATIONS.add((source) -> {
// test for feature B
return source;
});
VALIDATIONS.add((source) -> {
// test for feature C
return source;
});
}
/**
* Predicate for failure determination
*/
private final Predicate<ProductFeatureValidationDto> IS_FAILURE = (dto) ->
dto.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name());
/**
* Validation method
*/
public ValidateProductFeatureResponse validateProductFeatureAgainstAllCriteria(
ProductFeatureValidationDto dto
) throws ApplicationException, ParseException {
// iterate over validation functions and invoke them on dto instance
// filter stream by failed validations
// stop on first match
Optional<ProductFeatureValidationDto> dtoOptional = VALIDATIONS.stream()
.map(action -> action.apply(dto))
.filter(IS_FAILURE)
.findFirst();
// apply fuilure / success maping depending on result
return dtoOptional.isPresent()
? getResponseOnValidationFailure(dto)
: getResponseOnValidationSuccess(dto);
}
I ended up composing my solution from the answers given by #Eiden and #Alexandra Dudkina. A big shoutout to them. Below is the crux of my whole solution.
So, I have two interfaces
IProductFeature : This is a functional interface which has only one method validate. Every constraint needs to implement this.
IProductValidationService: This is a contract specifying the core methods needed to be implemented. The method validateProductFeatureAgainstAllCriteria is part of the contract.
There is a config file where all the features have been imported and organised into lists as required for different kinds of products. This list has been kept in a map with the product type as key. So this is acting like a factory which is giving a list of constraint based on a given product type.
The concrete class implementing IProductValidationService gets the list from the config and then applies all the constraints in the list to the given dto.
This way, I have separated all the concerns into separate portions.
The practical advantages to this approach are:
You can write extensive test cases and documentation for individual features.
If in the future, there is a policy change in ProductFeatureB(for e.g.),all I have to do is create a new concrete class, call it ProductFeatureBV2 and change only config file. The policy changes can be documented as part of the class javadoc. This way without changing core validation method, I can deprecate ProductFeatureB. This makes the code extremely flexible.
Thanks a lot to the community for helping me getting this right. If there are further improvements to be made here, please suggest them.
I am using gson to produce json of a collection of objects in Java (Some objects have other collections too). This json will be used to populate the web page for users with different clearance levels. Therefore the detail which users can see differs. Web page only shows what it needs to show however if I use the same json for two different pages then html source code will have more data than it should have. Is there a way to inform gson which variables in which class should be added to the json? As far as I search I could not find an easy way. Either I will produce json myself or clear extra data from the json which gson produced.
I need to use same classes for different clearance levels and get different json.
You are trying to use Gson to generate multiple different JSON outputs of the same objects in the same JVM, which is going to be difficult, both in Gson and any good serialization library, because their express goal is essentially the opposite of what you're looking for.
The right thing to do would be to instead represent these different clearance levels with different classes, and simply serialize those different classes with Gson as normal. This way you separate the security model from the serialization, letting you safely pass this information around.
/**
* Core data class, contains all information the application needs.
* Should never be serialized for display to any end user, no matter their level.
*/
public class GlobalData {
private final String username;
private final String private_data;
private final String secure_data;
}
/** Interface for all data display operations */
public interface DisplayData {
/** Returns a JSON representation of the data to be displayed */
public String toJson();
}
/**
* Class for safe display to an untrusted user, only holds onto public
* data anyone should see.
*/
public class UserDisplayData implements DisplayData {
private final String username;
public UserDisplayData(GlobalData gd) {
username = gd.username;
}
public String toJson() {
return gson.toJson(this);
}
}
/**
* Class for safe display to a trusted user, holds private information but
* does not display secure content (passwords, credit cards, etc.) that even
* admins should not see.
*/
public class AdminDisplayData implements DisplayData {
private final String username;
private final String private_data;
public AdminDisplayData(GlobalData gd) {
username = gd.username;
private_data = gd.private_data;
}
public String toJson() {
// these could be different Gson instances, for instance
// admin might want to see nulls, while users might not.
return gson.toJson(this);
}
}
Now you can sanitize and serialize your data as two separate steps, and use type safety to ensure your GlobalData is never displayed.
public void getDisplayData(GlobalData gd, User user) {
if(user.isAdmin()) {
return new AdminDisplayData(gd);
} else {
return new UserDisplayData(gd);
}
}
public void showData(DisplayData data) {
String json = data.toJson();
// display json however you want
}
If you erroneously tried to call showData(gd) you'd get a clear compilation error that you've done something wrong, and it's a quick fix to get the correct result by calling showData(getDisplayData(gd, user)) which safely and clearly does exactly what you want.
you can add a Expose annotations like this on the filed you don't want:
#Expose(serialize = false, deserialize = false)
private String address;
some more information here:
https://sites.google.com/site/gson/gson-user-guide#TOC-Gson-s-Expose
Upon building an MVC framework in PHP I ran into a problem which could be solved easily using Java style generics. An abstract Controller class might look something like this:
abstract class Controller {
abstract public function addModel(Model $model);
There may be a case where a subclass of class Controller should only accept a subclass of Model. For example ExtendedController should only accept ReOrderableModel into the addModel method because it provides a reOrder() method that ExtendedController needs to have access to:
class ExtendedController extends Controller {
public function addModel(ReOrderableModel $model) {
In PHP the inherited method signature has to be exactly the same so the type hint cannot be changed to a different class, even if the class inherits the class type hinted in the superclass. In java I would simply do this:
abstract class Controller<T> {
abstract public addModel(T model);
class ExtendedController extends Controller<ReOrderableModel> {
public addModel(ReOrderableModel model) {
But there is no generics support in PHP. Is there any solution which would still adhere to OOP principles?
Edit
I am aware that PHP does not require type hinting at all but it is perhaps bad OOP. Firstly it is not obvious from the interface (the method signature) what kind of objects should be accepted. So if another developer wanted to use the method it should be obvious that objects of type X are required without them having to look through the implementation (method body) which is bad encapsulation and breaks the information hiding principle. Secondly because there's no type safety the method can accept any invalid variable which means manual type checking and exception throwing is needed all over the place!
It appears to work for me (though it does throw a Strict warning) with the following test case:
class PassMeIn
{
}
class PassMeInSubClass extends PassMeIn
{
}
class ClassProcessor
{
public function processClass (PassMeIn $class)
{
var_dump (get_class ($class));
}
}
class ClassProcessorSubClass extends ClassProcessor
{
public function processClass (PassMeInSubClass $class)
{
parent::processClass ($class);
}
}
$a = new PassMeIn;
$b = new PassMeInSubClass;
$c = new ClassProcessor;
$d = new ClassProcessorSubClass;
$c -> processClass ($a);
$c -> processClass ($b);
$d -> processClass ($b);
If the strict warning is something you really don't want, you can work around it like this.
class ClassProcessor
{
public function processClass (PassMeIn $class)
{
var_dump (get_class ($class));
}
}
class ClassProcessorSubClass extends ClassProcessor
{
public function processClass (PassMeIn $class)
{
if ($class instanceof PassMeInSubClass)
{
parent::processClass ($class);
}
else
{
throw new InvalidArgumentException;
}
}
}
$a = new PassMeIn;
$b = new PassMeInSubClass;
$c = new ClassProcessor;
$d = new ClassProcessorSubClass;
$c -> processClass ($a);
$c -> processClass ($b);
$d -> processClass ($b);
$d -> processClass ($a);
One thing you should bear in mind though, this is strictly not best practice in OOP terms. If a superclass can accept objects of a particular class as a method argument then all its subclasses should also be able of accepting objects of that class as well. Preventing subclasses from processing classes that the superclass can accept means you can't use the subclass in place of the superclass and be 100% confident that it will work in all cases. The relevant practice is known as the Liskov Substitution Principle and it states that, amongst other things, the type of method arguments can only get weaker in subclasses and the type of return values can only get stronger (input can only get more general, output can only get more specific).
It's a very frustrating issue, and I've brushed up against it plenty of times myself, so if ignoring it in a particular case is the best thing to do then I'd suggest that you ignore it. But don't make a habit of it or your code will start to develop all kinds of subtle interdependencies that will be a nightmare to debug (unit testing won't catch them because the individual units will behave as expected, it's the interaction between them where the issue lies). If you do ignore it, then comment the code to let others know about it and that it's a deliberate design choice.
Whatever the Java world invented need not be always right. I think I detected a violation of the Liskov substitution principle here, and PHP is right in complaining about it in E_STRICT mode:
Cite Wikipedia: "If S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program."
T is your Controller. S is your ExtendedController. You should be able to use the ExtendedController in every place where the Controller works without breaking anything. Changing the typehint on the addModel() method breaks things, because in every place that passed an object of type Model, the typehint will now prevent passing the same object if it isn't accidentally a ReOrderableModel.
How to escape this?
Your ExtendedController can leave the typehint as is and check afterwards whether he got an instance of ReOrderableModel or not. This circumvents the PHP complaints, but it still breaks things in terms of the Liskov substitution.
A better way is to create a new method addReOrderableModel() designed to inject ReOrderableModel objects into the ExtendedController. This method can have the typehint you need, and can internally just call addModel() to put the model in place where it is expected.
If you require an ExtendedController to be used instead of a Controller as parameter, you know that your method for adding ReOrderableModel is present and can be used. You explicitly declare that the Controller will not fit in this case. Every method that expects a Controller to be passed will not expect addReOrderableModel() to exist and never attempt to call it. Every method that expects ExtendedController has the right to call this method, because it must be there.
class ExtendedController extends Controller
{
public function addReOrderableModel(ReOrderableModel $model)
{
return $this->addModel($model);
}
}
My workaround is the following:
/**
* Generic list logic and an abstract type validator method.
*/
abstract class AbstractList {
protected $elements;
public function __construct() {
$this->elements = array();
}
public function add($element) {
$this->validateType($element);
$this->elements[] = $element;
}
public function get($index) {
if ($index >= sizeof($this->elements)) {
throw new OutOfBoundsException();
}
return $this->elements[$index];
}
public function size() {
return sizeof($this->elements);
}
public function remove($element) {
validateType($element);
for ($i = 0; $i < sizeof($this->elements); $i++) {
if ($this->elements[$i] == $element) {
unset($this->elements[$i]);
}
}
}
protected abstract function validateType($element);
}
/**
* Extends the abstract list with the type-specific validation
*/
class MyTypeList extends AbstractList {
protected function validateType($element) {
if (!($element instanceof MyType)) {
throw new InvalidArgumentException("Parameter must be MyType instance");
}
}
}
/**
* Just an example class as a subject to validation.
*/
class MyType {
// blahblahblah
}
function proofOfConcept(AbstractList $lst) {
$lst->add(new MyType());
$lst->add("wrong type"); // Should throw IAE
}
proofOfConcept(new MyTypeList());
Though this still differs from Java generics, it pretty much minimalizes the extra code needed for mimicking the behaviour.
Also, it is a bit more code than some examples given by others, but - at least to me - it seems to be more clean (and more simliar to the Java counterpart) than most of them.
I hope some of you will find it useful.
Any improvements over this design are welcome!
I did went through the same type of problem before. And I used something like this to tackle it.
Class Myclass {
$objectParent = "MyMainParent"; //Define the interface or abstract class or the main parent class here
public function method($classObject) {
if(!$classObject instanceof $this -> objectParent) { //check
throw new Exception("Invalid Class Identified");
}
// Carry on with the function
}
}
You can consider to switch to Hack and HHVM. It is developed by Facebook and full compatible to PHP. You can decide to use <?php or <?hh
It support that what you want:
http://docs.hhvm.com/manual/en/hack.generics.php
I know this is not PHP. But it is compatible with it, and also improves your performance dramatically.
You can do it dirtily by passing the type as a second argument of the constructor
<?php class Collection implements IteratorAggregate{
private $type;
private $container;
public function __construct(array $collection, $type='Object'){
$this->type = $type;
foreach($collection as $value){
if(!($value instanceof $this->type)){
throw new RuntimeException('bad type for your collection');
}
}
$this->container = new \ArrayObject($collection);
}
public function getIterator(){
return $this->container->getIterator();
}
}
To provide a high level of static code-analysis, strict typing and usability, i came up with this solution: https://gist.github.com/rickhub/aa6cb712990041480b11d5624a60b53b
/**
* Class GenericCollection
*/
class GenericCollection implements \IteratorAggregate, \ArrayAccess{
/**
* #var string
*/
private $type;
/**
* #var array
*/
private $items = [];
/**
* GenericCollection constructor.
*
* #param string $type
*/
public function __construct(string $type){
$this->type = $type;
}
/**
* #param $item
*
* #return bool
*/
protected function checkType($item): bool{
$type = $this->getType();
return $item instanceof $type;
}
/**
* #return string
*/
public function getType(): string{
return $this->type;
}
/**
* #param string $type
*
* #return bool
*/
public function isType(string $type): bool{
return $this->type === $type;
}
#region IteratorAggregate
/**
* #return \Traversable|$type
*/
public function getIterator(): \Traversable{
return new \ArrayIterator($this->items);
}
#endregion
#region ArrayAccess
/**
* #param mixed $offset
*
* #return bool
*/
public function offsetExists($offset){
return isset($this->items[$offset]);
}
/**
* #param mixed $offset
*
* #return mixed|null
*/
public function offsetGet($offset){
return isset($this->items[$offset]) ? $this->items[$offset] : null;
}
/**
* #param mixed $offset
* #param mixed $item
*/
public function offsetSet($offset, $item){
if(!$this->checkType($item)){
throw new \InvalidArgumentException('invalid type');
}
$offset !== null ? $this->items[$offset] = $item : $this->items[] = $item;
}
/**
* #param mixed $offset
*/
public function offsetUnset($offset){
unset($this->items[$offset]);
}
#endregion
}
/**
* Class Item
*/
class Item{
/**
* #var int
*/
public $id = null;
/**
* #var string
*/
public $data = null;
/**
* Item constructor.
*
* #param int $id
* #param string $data
*/
public function __construct(int $id, string $data){
$this->id = $id;
$this->data = $data;
}
}
/**
* Class ItemCollection
*/
class ItemCollection extends GenericCollection{
/**
* ItemCollection constructor.
*/
public function __construct(){
parent::__construct(Item::class);
}
/**
* #return \Traversable|Item[]
*/
public function getIterator(): \Traversable{
return parent::getIterator();
}
}
/**
* Class ExampleService
*/
class ExampleService{
/**
* #var ItemCollection
*/
private $items = null;
/**
* SomeService constructor.
*
* #param ItemCollection $items
*/
public function __construct(ItemCollection $items){
$this->items = $items;
}
/**
* #return void
*/
public function list(){
foreach($this->items as $item){
echo $item->data;
}
}
}
/**
* Usage
*/
$collection = new ItemCollection;
$collection[] = new Item(1, 'foo');
$collection[] = new Item(2, 'bar');
$collection[] = new Item(3, 'foobar');
$collection[] = 42; // InvalidArgumentException: invalid type
$service = new ExampleService($collection);
$service->list();
Even if something like this would feel so much better:
class ExampleService{
public function __construct(Collection<Item> $items){
// ..
}
}
Hope generics will get into PHP soon.
One alternative is the combination of splat operator + typed hint + private array:
<?php
class Student {
public string $name;
public function __construct(string $name){
$this->name = $name;
}
}
class Classe {
private $students = [];
public function add(Student ...$student){
array_merge($this->students, $student);
}
public function getAll(){
return $this->students;
}
}
$c = new Classe();
$c->add(new Student('John'), new Student('Mary'), new Student('Kate'));
Here's a really simple class:
static public class Bean1
{
final private String name;
final private Bean1 parent;
private int favoriteNumber;
public String getName() { return this.name; }
public Bean getParent() { return this.parent; }
public int getFavoriteNumber() { return this.favoriteNumber; }
public void setFavoriteNumber(int i) { this.favoriteNumber = i; }
}
What I would like to do is to bind some UI components to a BeanAdapter<Bean1> (see com.jgoodies.binding.beans.BeanAdapter) so that if the BeanAdapter points to Bean1 bean1, then I can display
bean1.name (blank if null)
bean1.parent.name (blank if null or if bean1.parent is null)
bean1.favoriteNumber
The fields name and favoriteNumber are easy, but I'm confused about how to display the parent name. It looks like BeanAdapter only lets me bind to properties which exist directly in Bean1. But this is poor modularity and it forces me to add getter/setter functions every time I want to bind to a new aspect of the bean.
What I would like to do is write a helper class which knows how to access a bean, and am confused how to get it to work properly with Bean1 and BeanAdapter.
I'm sorry if this question is not more clear, I don't know the vocabulary and am a little hazy on the concepts of binding.
The problem here is that binding works in both ways: from model to ui, and from ui to model.
In your case, how would you deal with someone entering information for the first time in a textfield that's binded to parent.name? Would you create a parent on the fly? Would you give an error?
If you know what to do in that situation (e.g. create a parent with that name), you could use a com.jgoodies.binding.value.AbstractConverter to convert a Bean1 to a String:
public class ParentNameConverter extends AbstractConverter {
/**
* Converts a value from the subject to the type or format used
* by this converter.
*
* #param subjectValue the subject's value
* #return the converted value in the type or format used by this converter
*/
public Object convertFromSubject(Object subjectValue) { ... }
/**
* Sets a new value on the subject, after converting to appropriate type
* or format
*
* #param newValue the ui component's value
*/
public void setValue(Object newValue) { ... }
}
You can use this converter the same way you use a normal ValueModel:
Bindings.bind(uifield,"value",
new ParentNameConverter(beanAdapter.getValueModel("parent")));
So I have this GWT code that handles RPC requests maintain states(ready, waiting, error etc).
And I would like to check if the class change its states correctly after each call, set response variables etc.
Now how should I proceed to test that without making actual requests to the server(that could run into errors in the server it self).
I think I could mock the request callback class somehow but it is invisible to the test.
I'm lost, help!
Sample of the code below(I'll post the whole thing later in case anyone wants).
public class RPCHandler
{
public RPCHandler(String method,String[] argumentsName,
String[][] argumentsValues)
{
this.method = method;
this.argumentsName = argumentsName;
this.argumentsValues = argumentsValues;
}
/**
* Method that creates a RPC request using JSON in a POST
*
*/
public void rpcRequest(){
if(currentState == HandlerState.WAITING_RESPONSE)return;
currentState = HandlerState.WAITING_RESPONSE;
// Append watch list stock symbols to query URL.
url = URL.encode(url);
url += "action=";
url += method;
// Send request to server and catch any errors.
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
String requestData = parseToJSON(argumentsName, argumentsValues);
try{
Request request = builder.sendRequest(requestData, new RequestCallback()
{
public void onError(Request request, Throwable exception)
{
setRPCException(new Exception("Error while saving. Action="+method));
setCurrentState(HandlerState.ON_ERROR);
}
//Few other error, response received hander methods after this point.
}
}
It looks like you're trying to mock out the actual transport so you should build a mock of the RequestBuilder class. In JMockit, you could write:
public class MockRequestBuilder
{
public void $init( int method, String url)
{
/* check values and/or store for later */
}
public Request sendRequest( String data, RequestCallback callback )
{
/* check values and/or store for later */
}
}
You'll need to fill in the details of the what you want the mock to do. Also, you can isolate the callback testing if you moved the callback to a named class instance inside of your outer class:
public class MyGWTClass
{
protected static class RpcCallback extends RequestCallback
{
public void onError(...) { ... }
}
}
By moving the callback object into a class and using a factory method, you can create tests that only check the callback.