I'm using the decorator pattern to implement caching for my Repositories as such:
IFooRepository()
IFooRepository FooRepository()
IFooRepository CachedFooRepository(IFooRepository fooRepository)
The Cached repository checks the cache for the requested object and if it doesn't exist, calls the FooRepository to retrieve and store it. I'm currently registering these types with StructureMap using the following method:
For<IFooRepository>().Use<CachedFooRepository()
.Ctor<IFooRepository>().Use<FooRepository>();
This works fine, but as the number of cached repositories grows, registering each one individually is becoming unwieldy and is error prone. Seeing as I have a common convention, I'm trying to scan my assembly using a custom IRegistrationConvention, but I can't seem to figure out how to pass the FooRepository to the constructor of CachedFooRepository in the void Process(Type type, Registry registry) function.
I've found examples to do something like:
Type interfaceType = type.GetInterface(type.Name.Replace("Cached", "I"));
registry.AddType(interfaceType, type);
or
Type interfaceType = type.GetInterface(type.Name.Replace("Cached", "I"));
registry.For(interfaceType).Use(type);
But neither method will allow me to chain the .Ctor. What am I missing? Any ideas?
Related
I am using boost test within a home-grown GUI, and want to access test results (e.g. the failure message and location when a test fails)
The unit_test::test_observer class provides the virtual method:
void assertion_result(boost::unit_test::assertion_result)
However, unit_test::assertion_result is just an enum indicating success or failure. From there, I cannot see how to access further information about the test result.
The framework also provides the class test_tools::assertion_result, which encapsulates an error message, but this only appears to be used for evaluating pre-conditions. (I would have expected this type to be the argument to unit_test::test_observer::assertion_result).
The log output classes appear to provide more information on test results. These are implemented as streams, which makes it non-trivial to extract test result data.
Does anyone know how I can access the information on test results - success/failure, the test code, the location, etc?
Adding an observer will not give you the level of details you need.
From this class you can add your own formatter using the add_formatter function. This will contain the details of what is happening and where, depending on the formatter log level.
I'm trying to create my RKEntityMapping outside of my UnitTest. The problem I have is it only works if I create it inside my test. For example, this works:
RKEntityMapping *accountListMapping = [RKEntityMapping mappingForEntityForName:#"CustomerListResponse" inManagedObjectStore:_sut.managedObjectStore];
[accountListMapping addAttributeMappingsFromDictionary:#{#"count": #"pageCount",
#"page": #"currentPage",
#"pages": #"pages"}];
While the following does now work. The all to accoutListMapping returns exactly what is shown above using the same managed object store:
RKEntityMapping *accountListMapping = [_sut accountListMapping];
When the RKEntityMapping is created in _sut I get this error:
<unknown>:0: error: -[SBAccountTests testAccountListFetch] : 0x9e9cd10: failed with error:
Error Domain=org.restkit.RestKit.ErrorDomain Code=1007 "Cannot perform a mapping operation
with a nil destination object." UserInfo=0x8c64490 {NSLocalizedDescription=Cannot perform
a mapping operation with a nil destination object.}
I'm assuming the nil destination object it is referring to is destinationObject:nil.
RKMappingTest *maptest = [RKMappingTest testForMapping:accountListMapping
sourceObject:_parsedJSON
destinationObject:nil];
Make sure that the file you have created has a target membership of both your main target, and your test target. You can find this by:
clicking on the .m file of your class
open the utilities toolbar (the one on the right)
in the target membership section tick both targets.
This is because if your class does not have target membership to your test target, the test target actually creates a copy of the class that you have created, meaning it has a different binary file to the main target. This leads to that class using the test's version of the RestKit binary, rather than the main projects RestKit. This will lead to the isKindOfClass method failing when it tries to see if the mapping you have passed is of type RKObjectMapping from the main project, because it is of type RKObjectMapping from the test projects version of RestKit, so your mapping doesn't get used, and you get your crash.
At least this is my understanding of how the LLVM compiler works. I'm new to iOS dev so please feel free to correct if I got something wrong.
This problem may also be caused by duplicated class definitions, when including RestKit components for multiple targets individually when using Cocoapods.
For more information on this have a look at this answer.
I used a category on the Mapped object for example
RestKitMappings+SomeClass
+ (RKObjectMapping*)responsemappings {
return mappings;
}
now this category has to be included in the test target as well otherwise the mapping will not be passed.
When you're running a test you aren't using the entire mapping infrastructure, so RestKit isn't going to create a destination object for you. It's only going to test the mapping itself. So you need to provide all three pieces of information to the test method or it can't work.
Is it possible to use JavaLoader to get objects returned by CF-called web services, and JavaLoader-loaded objects to be the same classpath context? I mean, without a lot of difficulty?
// get a web service
ws = createObject("webservice", local.lms.wsurl);
// user created by coldfusion
user = ws.GenerateUserObject();
/* user status created by java loader.
** this api provider requires that you move the stubs
** (generated when hitting the wsdl from CF for the first time)
** to the classpath.
** this is one of the stubs/classes that gets called from that.
*/
UserStatus = javaLoader.create("com.geolearning.geonext.webservices.Status");
// set user status: classpath context clash
user.setStatus(UserStatus.Active);
Error:
Detail: Either there are no methods with the specified method name and
argument types or the setStatus method is overloaded with argument
types that ColdFusion cannot decipher reliably. ColdFusion found 0
methods that match the provided arguments. If this is a Java object
and you verified that the method exists, use the javacast function to
reduce ambiguity.
Message: The setStatus method was not found.
MethodName setStatus
Even though the call, on the surface, matches a method signature on user--setStatus(com.geolearning.geonext.webservices.Status)--the class is on a different classpath context. That's why I get the error above.
Jamie and I worked on this off-line and came up with a creative solution :)
(Apologies for the long answer, but I thought a bit of an explanation was warranted for those who find class loaders as confusing as I do. If you are not interested in the "why" aspect, feel free to jump to the end).
Issue:
The problem is definitely due to multiple class loaders/paths. Apparently CF web services use a dynamic URLClassLoader (just like the JavaLoader). That is how it can load the generated web service classes on-the-fly, even though those classes are not in the core CF "class path".
(Based on my limited understanding...) Class loaders follow a hierarchy. When multiple class loaders are involved, they must observe certain rules or they will not play well together. One of the rules is that child class loaders can only "see" objects loaded by an ancestor (parent, grandparent, etcetera). They cannot see classes loaded by a sibling.
If you examine the object created by the JavaLoader, and the other by createObject, they are indeed siblings ie both children of the CF bootstrap class loader. So the one will not recognize objects loaded by the other, which would explain why the setStatus call failed.
Given that a child can see objects loaded by a parent, the obvious solution is to change how the objects are constructed. Structure the calls so that one of the class loaders ends up as a parent of the other. Curiously that turned out to be trickier than it sounded. I could not find a way to make that happen, despite trying a number of combinations (including using the switchThreadContextClassLoader method).
Solution:
Finally I had a crazy thought: do not load any jars. Just use the web service's loader as the parentClassLoader. It already has everything it needs in its own individual "class path":
// display class path of web service class loader
dynamicLoader = webService.getClass().getClassLoader();
dynamicClassPath = dynamicLoader.getURLS();
WriteDump("CLASS PATH: "& dynamicClassPath[1].toString() );
The JavaLoader will automatically delegate calls for classes it cannot find to parentClassLoader - and bingo - everything works. No more more class loader conflict.
webService = createObject("webservice", webserviceURL, webserviceArgs);
javaLoader = createObject("component", "javaloader.JavaLoader").init(
loadPaths = [] // nothing
, parentClassLoader=webService.getClass().getClassLoader()
);
user = webService.GenerateUserObject();
userStatus = javaLoader.create("com.geolearning.geonext.webservices.Status");
user.setStatus(userStatus.Active);
WriteDump(var=user.getStatus(), label="SUCCESS: user.getStatus()");
So, I'm experimenting with Webkit GTK DOM functions. It's pretty straightforward, except for one thing: there's a useful part of the API called the WebKitDOMTreeWalker which, I assume, lets you walk over each node in the DOM, just like the TreeWalker object in Javascript.
Now, in Javascript a TreeWalker is created by calling:
document.createTreeWalker(root, nodesToShow, filter, entityExpandBol)
So, in WebKit GTK, there is an obvious counterpart in the API - a function called webkit_dom_document_create_tree_walker. The function signature is:
WebKitDOMTreeWalker* webkit_dom_document_create_tree_walker(WebKitDOMDocument* self, WebKitDOMNode* root, gulong what_to_show, WebKitDOMNodeFilter* filter, gboolean expand_entity_references, GError **error);
So creating a TreeWalker with WebKit GTK seems pretty straightforward - except for one big problem: the fourth argument in the webkit_dom_document_create_tree_walker expects a filter object, that is, it wants an instance of WebKitDOMNodeFilter. Well, the Javascript function also takes a filter, but you can pass null if you don't want to use a filter. With the Webkit API, passing NULL doesn't work. If you call:
WebKitDOMTreeWalker* walker = webkit_dom_document_create_tree_walker(doc, root, SHOW_ALL, NULL, false, err)
You get the error message:
** (webkit:3367): CRITICAL : WebKitDOMTreeWalker* webkit_dom_document_create_tree_walker(WebKitDOMDocument*,
WebKitDOMNode*, gulong, WebKitDOMNodeFilter*, gboolean, GError):
assertion `filter' failed
So, the WebKit API won't accept a NULL pointer for the filter argument. Evidently you need to pass an instance of a WebKitDOMNodeFilter. Okay, again - this wouldn't be a problem either, except I've searched far and wide through the WebKit API, as well as Google, and I can't find anyway to create a WebKitDOMNodeFilter object! The header file for WebKitDOMNodeFilter.h doesn't expose any constructor for WebKitDOMNodeFilter. It seems like the API doesn't ever expose anyway to actually construct a WebKitDOMNodeFilter object at all.
Yet... the API exposes many functions (like webkit_dom_document_create_tree_walker, and webkit_dom_document_create_node_iterator) which require a WebKitDOMNodeFilter. So... is the API just incomplete right now? Or, is there some way to create a Filter object which I'm just not seeing?
Can you try casting your null to the WebKitDOMNodeFilter type by calling
WEBKIT_DOM_NODE_FILTER(null)?
I have been researching this, and cannot seem to find anything about it.
We work on CF8. When my coworker tried installing my latest code updates, he started seeing errors that the argument supplied to a function was not of the specified interface type. Worked fine for me. Same set up. Sometimes it works for him. Also have the problem on our dev server.
I have since been able to isolate and reproduce the problem locally.
Here is the set up.
I have 2 mappings on the server:
"webapp/" goes to c:\webroot\
"packages/" goes to c:\webroot\[domain]
Then I created an interface, call it ISubject and a component that implements it, called Person, and saved both under packages. Here is the declaration for Person:
cfcomponent implements="packages.ISubject"
Finally, there is a component, called SubjectMediator with a function, called setSubject, that wants an object of the ISubject interface type. Here is the argument declaration for setSubject:
cfargument name="subject_object" type="packages.ISubject"
To implement:
variables.person = createObject("component", "packages.Person").Init();
variables.subjectMediator = createObject("component", "packages.SubjectMediator ").Init();
variables.subjectMediator.setSubject(variables.person);
That last line throws the error that Person is not of type ISubject. If I do isInstanceOf() on Person against ISubject it validates fine.
So the reason this is happening? Dumping getMetaData(variables.person) shows me that the interface path is webapp.[domain].ISubject. And indeed, if I change the type attribute of the argument to use this path instead of packages.ISubject, all is fine again.
Coldfusion seems to be arbitrarily choosing which mapping to resolve the interface to, and then simply doing a string comparison for check the type argument?
Anyone had to contend with this? I need the webapp mapping, and I cannot change all references to "packages" to "webapp.[domain]." I also am not able in this instance to use an application-specific mapping for webapp. While any of these 3 options would circumvent the issue, I'm hoping someone has some insight...
The best I've got is to set argument type to "any" and then check isInstanceOf() inside the function... Seems like poor form.
Thanks,
Jen
Can you move the contents of the packages mapping to outside the webroot? This seems like the easiest way to fix it.