In ArchUnit, I can check that packages .should().beFreeOfCycles(). How can I specify exceptions to this rule for certain cycles?
E.g., given these packages and their dependencies:
A <-> B <-> C
How can I allow A <-> B, but still forbid A and B being part of any other cycle, e.g. B <-> C?
Freezing Arch Rules is always an option to allow for certain violations, but catch others.
Would that be feasible in your case?
Based on Manfred's answer, here is a solution that seems to work fine (implemented in Kotlin):
fun test() {
SlicesRuleDefinition.slices().matching("(com.mypackage.*..)")
.should()
// Using an extension method which allows us to specify allowed
// cycles succinctly:
.beFreeOfCyclesExcept("com.mypackage.a" to "com.mypackage.b")
.check(someClasses)
}
fun SlicesShould.beFreeOfCyclesExcept(
vararg allowed: Pair<String, String>
): ArchRule =
FreezingArchRule
// In case you are not familiar with Kotlin:
// We are in an extension method. 'this' will be substituted with
// 'SlicesRuleDefinition.slices().matching("(com.mypackage.*..)")
// .should()';
.freeze(this.beFreeOfCycles())
.persistIn(
// Using a custom ViolationStore instead of the default
// TextFileBasedViolationStore so we can configure the
// allowed violations in code instead of a text file:
object : ViolationStore {
override fun initialize(properties: Properties) {}
override fun contains(rule: ArchRule): Boolean = true
override fun save(rule: ArchRule, violations: List<String>) {
// Doing nothing here because we do not want ArchUnit
// to add any additional allowed violations.
}
override fun getViolations(rule: ArchRule): List<String> =
allowed
// ArchUnit records cycles in the form
// A -> B -> A. I.e., A -> B -> A and
// B -> A -> B are different violations.
// We add the reverse cycle to make sure
// both directions are allowed:
.flatMap { pair ->
listOf(pair, Pair(pair.second, pair.first))
}
// .distinct() is not necessary, but using it is
// cleaner because by adding the reverse cycles
// we may possibly have added duplicates:
.distinct()
.map { (sliceA, sliceB) ->
// This is a prefix of the format that
// ArchUnit uses:
"Cycle detected: Slice $sliceA -> \n" +
" Slice $sliceB -> \n" +
" Slice $sliceA\n"
}
}
)
// The lines that ArchUnit uses are very specific, including
// info about which methods etc. create the cycle. That is
// exactly what is desirable when establishing a baseline for
// legacy code. But we want to permanently allow certain
// cycles, regardless of which current or future code creates
// the cycle. Thus, we only compare the prefixes of violation
// lines:
.associateViolationLinesVia {
lineFromFirstViolation,
lineFromSecondViolation ->
lineFromFirstViolation.startsWith(lineFromSecondViolation)
}
Note that I have tested this only on a small project.
Related
I want to add a new key-value pair into Golang map from concurrent threads. Problem is that if there is a key present in the map we don't create new pair. From multithreaded perspective how to check the current condition and if key isn't present insert the key - value.
Is there any way to organize code to add key safely when first encountered?
The main problem is safely initializing mutex
Is there any way to organize code to add key safely when first encountered?
No. You need proper synchronisation.
I would recommend the combination of sync.Map to store the key-values and sync.Once inside of the value to perform the one-time initialization.
Here is an example:
type Value struct {
init sync.Once
someValue string
}
func (v *Value) Init() {
v.init.Do(func() {
// This function will only be executed one time
v.someValue = "initialized"
})
}
func main() {
var m sync.Map
v1, _ := m.LoadOrStore("key", &Value{})
v1.(*Value).Init() // init function is called
v2, _ := m.LoadOrStore("key", &Value{})
v2.(*Value).Init() // init function is not called
}
Consider this code:
var t: {a: Int} = {a:100, b:200};
It does not compile with error: { b : Int, a : Int } has extra field b
But this code compiles fine:
class Foo {
public var a: Int = 100;
public var b: Int = 200;
public function new() {}
}
...
var t: {a: Int} = new Foo();
Why is the first case forbidden?
What can go wrong if there are some extra fields? And if something can go wrong why they are allowed in second case.
This has previously been discussed in this issue, where Nicolas gives the following reasoning for the current behavior:
The idea is that constant structures are not allowed to be reduced. This allows for instance to check for the following:
function foo(o:{?x:Int,?y:Int}) {
}
var pt = { x: 0, yy : 1 }; // typo
foo(pt); // goes unnoticed
Also, it will gives error if you modify the signature of foo, for instance by removing a field.
However, the issue is still open and it looks like the behavior might be changed to allow this in the future.
I think this is answered here: https://groups.google.com/forum/#!topic/haxelang/KQO4eFUb-N0
Nicolas explained:
In your example both are considered constant values, and then an error
is printed because it has extra fields. That error was added in order
to enable code cleanup when you remove a field from a required
structure : it will tell you every place this field is still passed
(when passing a constant, which happen most of the time).
I agree the error is a bit misleading when making simple tests such as
your own, but in actual code it rarely occur.
As a step in switching from NLog to Serilog, I want to redirect the standard wiring underlying standard invocations of NLog's LogManager.GetLogger(name) to Bridge any code logging to NLog to forward immediately to the ambient Serilog Log.Logger - i.e. I want to just one piece of config that simply forwards the message, without buffering as Log4net.Appender.Serilog does for Log4net.
Can anyone concoct or point me to a canonical snippet that does this correctly and efficiently please? Requirements I can think of:
Maintain the level, i.e. nlog.Warn should be equivalent to serilog.Warning
It's ok for Serilog to generate the time anew
materializing the message in the appender - i.e., there's no need to maintain any properties associated with the 'message' (the LogEvent in Serilog terms)
no buffering
I don't need any other NLog Target to be used (i.e. mutating/deleting the message would be fine)
I think the best option is indeed a custom NLog target. Something like this: (C#)
using NLog;
using NLog.Targets;
using Serilog;
using Serilog.Events;
namespace MyNamespace
{
[Target("SerilogTarget")]
public sealed class SerilogTarget : TargetWithLayout
{
protected override void Write(LogEventInfo logEvent)
{
var log = Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, logEvent.LoggerName);
var logEventLevel = ConvertLevel(logEvent.Level);
if ((logEvent.Parameters?.Length ?? 0) == 0)
{
// NLog treats a single string as a verbatim string; Serilog treats it as a String.Format format and hence collapses doubled braces
// This is the most direct way to emit this without it being re-processed by Serilog (via #nblumhardt)
var template = new Serilog.Events.MessageTemplate(new[] { new Serilog.Parsing.TextToken(logEvent.FormattedMessage) });
log.Write(new Serilog.Events.LogEvent(DateTimeOffset.Now, logEventLevel, logEvent.Exception, template, Enumerable.Empty<Serilog.Events.LogEventProperty>()));
}
else
// Risk: tunneling an NLog format and assuming it will Just Work as a Serilog format
#pragma warning disable Serilog004 // Constant MessageTemplate verifier
log.Write(logEventLevel, logEvent.Exception, logEvent.Message, logEvent.Parameters);
#pragma warning restore Serilog004
}
static Serilog.Events.LogEventLevel ConvertLevel(LogLevel logEventLevel)
{
if (logEventLevel == LogLevel.Info)
return Serilog.Events.LogEventLevel.Information;
else if (logEventLevel == LogLevel.Trace)
return Serilog.Events.LogEventLevel.Verbose;
else if (logEventLevel == LogLevel.Debug)
return Serilog.Events.LogEventLevel.Debug;
else if (logEventLevel == LogLevel.Warn)
return Serilog.Events.LogEventLevel.Warning;
else if (logEventLevel == LogLevel.Error)
return Serilog.Events.LogEventLevel.Error;
return Serilog.Events.LogEventLevel.Fatal;
}
}
}
register it in your main() or app_start:
// Register so it can be used by config file parsing etc
Target.Register<MyNamespace.SerilogTarget>("SerilogTarget");
Before any logging takes place, the Target needs to be wired in so LogManager.GetLogger() can actually trigger a call to SerilogTarget.Write
public static void ReplaceAllNLogTargetsWithSingleSerilogForwarder()
{
// sic: blindly overwrite the forwarding rules every time
var target = new SerilogTarget();
var cfg = new NLog.Config.LoggingConfiguration();
cfg.AddTarget(nameof(SerilogTarget), target);
cfg.LoggingRules.Add(new NLog.Config.LoggingRule("*", LogLevel.Trace, target));
// NB assignment must happen last; rules get ingested upon assignment
LogManager.Configuration = cfg;
}
See also: https://github.com/nlog/nlog/wiki/How-to-write-a-custom-target
the optimal way to do this without inducing any avoidable perf impact etc.
This is the optimal way in NLog and has no performance impact on the NLog's site.
what does the TargetAttribute buy me ?
Well in this case you don't need it. The TargetAttribute is used when registering a full assembly, but because we register manually, it's not needed. I think it's best practice, but you could leave it out.
Also what does the Register buy me
This is indeed not needed when using programmatically config. But if you have XML config, you need the register.
I've the habit to write targets that works in all ways (register manually, register by assembly, config from code, config from XML). I could understand that could be confusing.
F# port:
module SerilogHelpers =
let private mapLevel = function
| x when x = NLog.LogLevel.Info -> LogEventLevel.Information
| x when x = NLog.LogLevel.Off || x = NLog.LogLevel.Trace -> LogEventLevel.Verbose
| x when x = NLog.LogLevel.Debug -> LogEventLevel.Debug
| x when x = NLog.LogLevel.Warn -> LogEventLevel.Warning
| x when x = NLog.LogLevel.Error -> LogEventLevel.Error
| _ -> LogEventLevel.Fatal
// via https://stackoverflow.com/a/49639001/11635
[<NLog.Targets.Target("SerilogTarget")>]
type SerilogTarget() =
inherit NLog.Targets.Target()
static member InitializeAsGlobalTarget() =
// sic: blindly overwrite the forwarding rules every time
// necessary as Azure Startup establishes a different config as a bootstrapping step
// see: LogModule.To.target("rollingFile", create, "*", LogLevel.Trace)
let cfg, target = NLog.Config.LoggingConfiguration(), SerilogTarget()
cfg.AddTarget("SerilogTarget", target)
cfg.LoggingRules.Add(NLog.Config.LoggingRule("*", NLog.LogLevel.Trace, target))
// NB assignment must happen last; rules get ingested upon assignment
NLog.LogManager.Configuration <- cfg
override __.Write(logEvent : NLog.LogEventInfo) =
let log = Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, logEvent.LoggerName)
match logEvent.Parameters with
| xs when isNull xs || xs.Length = 0 ->
// NLog treats a single string as a verbatim string; Serilog treats it as a String.Format format and hence collapses doubled braces
// This is the most direct way to emit this without it being re-processed by Serilog (via #nblumhardt)
let template = MessageTemplate [| Serilog.Parsing.TextToken(logEvent.FormattedMessage) |]
log.Write(new LogEvent(DateTimeOffset.Now, mapLevel logEvent.Level, logEvent.Exception, template, Seq.empty<LogEventProperty>))
| _ ->
// Risk: tunneling an NLog format and assuming it will Just Work as a Serilog format
log.Write(mapLevel logEvent.Level, logEvent.Exception, logEvent.Message, logEvent.Parameters)
I'm trying to apply ubiquitous language to my domain objects.
I want to convert a Data Transfer Object coming from a client into the domain object. The Aggregate's Constructor only accepts the required fields, and the rest of parameters should be passed using aggregate's API even when the Aggregate is being created(by say CreateAggregate command).
But the DTO to Aggregate mapping code becomes a bit messy:
if(DTO.RegistrantType == 0){
registrantType = RegistrantType.Person()
}
elseif(DTO.RegistrantType == 1){
registrantType = RegistrantType.Company()
}
//.....
//.....
var aggregate = new Aggregate(
title,
weight,
registrantType,
route,
callNumber,
)
//look at this one:
if(DTO.connectionType == 0){
aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}
//..........
//..........
One thing I should mention is that this problem doesn't seem a domain specific problem.
How can I reduce these If-Else statements without letting my domain internals leakage, and with being sure that the aggregate(not a mapping tool) doesn't accept values that can invalide it's business rules, and with having the ubiquitous language applied?
Please don't tell me I can use AoutoMapper to do the trick. Please read the last part carefully.'
Thank you.
A typical answer would be to convert the DTO (which is effectively a message) into a Command, where the command has all of the arguments expressed as domain specific value types.
void doX(DTO dto) {
Command command = toCommand(dto)
doX(command)
}
void doX(Command command) {
// ...
aggregate.Route(command.connectionType)
}
It's fairly common for the toCommand logic use something like a Builder pattern to improve the readability of the code.
if(DTO.connectionType == 0){
aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}
In cases like this one, the strategy pattern can help
ConnectionTypeFactory f = getConnectionFactory(DTO.connectionType)
ConnectionType connectionType = f.create(DTO)
Once that you recognize that ConnectionTypeFactory is a thing, you can think about building lookup tables to choose the right one.
Map<ConnectionType, ConnectionTypeFactory> lookup = /* ... */
ConnectionTypeFactory f = lookup(DTO.connectionType);
if (null == f) {
f = defaultConnectionFactory;
}
So why don't you use more inheritance
for example
class CompanyRegistration : Registration {
}
class PersonRegistraiton : Registration {
}
then you can use inheritance instead of your if/else scenario's
public class Aggregate {
public Aggregate (CompanyRegistration) {
registantType = RegistrantType.Company();
}
public Aggregate (PersonRegistration p) {
registrantType = RegistrantType.Person();
}
}
you can apply simmilar logic for say a setRoute method or any other large if/else situations.
Also, i know you don't want to hear it, you can write your own mapper (inside the aggegate) that maps and validates it's business logic
for example this idea comes from fluentmapper
var mapper = new FluentMapper.ThatMaps<Aggregate>().From<DTO>()
.ThatSets(x => x.title).When(x => x != null).From(x => x.title)
It isn't too hard to write your own mapper that allow this kind of rules and validates your properties. And i think it will improve readability
I used the below to see how dart calls methods passed in to other methods to see what context the passed in method would/can be called under.
void main() {
var one = new IDable(1);
var two = new IDable(2);
print('one ${caller(one.getMyId)}'); //one 1
print('two ${caller(two.getMyId)}'); //two 2
print('one ${callerJustForThree(one.getMyId)}'); //NoSuchMethod Exception
}
class IDable{
int id;
IDable(this.id);
int getMyId(){
return id;
}
}
caller(fn){
return fn();
}
callerJustForThree(fn){
var three = new IDable(3);
three.fn();
}
So how does caller manager to call its argument fn without a context i.e. one.fn(), and why does callerJustForThree fail to call a passed in fn on an object which has that function defined for it?
In Dart there is a difference between an instance-method, declared as part of a class, and other functions (like closures and static functions).
Instance methods are the only ones (except for constructors) that can access this. Conceptually they are part of the class description and not the object. That is, when you do a method call o.foo() Dart first extracts the class-type of o. Then it searches for foo in the class description (recursively going through the super classes, if necessary). Finally it applies the found method with this set to o.
In addition to being able to invoke methods on objects (o.foo()) it is also possible to get a bound closure: o.foo (without the parenthesis for the invocation). However, and this is crucial, this form is just syntactic sugar for (<args>) => o.foo(<args>). That is, this just creates a fresh closure that captures o and redirects calls to it to the instance method.
This whole setup has several important consequences:
You can tear off instance methods and get a bound closure. The result of o.foo is automatically bound to o. No need to bind it yourself (but also no way to bind it to a different instance). This is way, in your example, one.getMyId works. You are actually getting the following closure: () => one.getMyId() instead.
It is not possible to add or remove methods to objects. You would need to change the class description and this is something that is (intentionally) not supported.
var f = o.foo; implies that you get a fresh closure all the time. This means that you cannot use this bound closure as a key in a hashtable. For example, register(o.foo) followed by unregister(o.foo) will most likely not work, because each o.foo will be different. You can easily see this by trying print(o.foo == o.foo).
You cannot transfer methods from one object to another. However you try to access instance methods, they will always be bound.
Looking at your examples:
print('one ${caller(one.getMyId)}'); //one 1
print('two ${caller(two.getMyId)}'); //two 2
print('one ${callerJustForThree(one.getMyId)}'); //NoSuchMethod Exception
These lines are equivalent to:
print('one ${caller(() => one.getMyId())}');
print('two ${caller(() => two.getMyId())}');
print('one ${callerJustForThree(() => one.getMyId())}';
Inside callerJustForThree:
callerJustForThree(fn){
var three = new IDable(3);
three.fn();
}
The given argument fn is completely ignored. When doing three.fn() in the last line Dart will find the class description of three (which is IDable) and then search for fn in it. Since it doesn't find one it will call the noSuchMethod fallback. The fn argument is ignored.
If you want to call an instance member depending on some argument you could rewrite the last example as follows:
main() {
...
callerJustForThree((o) => o.getMyId());
}
callerJustForThree(invokeIDableMember){
var three = new IDable(3);
invokeIDableMember(three);
}
I'll try to explain, which is not necessarily a strength of mine. If something I wrote isn't understandable, feel free to give me a shout.
Think of methods as normal objects, like every other variable, too.
When you call caller(one.getMyId), you aren't really passing a reference to the method of the class definition - you pass the method "object" specific for instance one.
In callerJustForThree, you pass the same method "object" of instance one. But you don't call it. Instead of calling the object fn in the scope if your method, you are calling the object fn of the instance three, which doesn't exist, because you didn't define it in the class.
Consider this code, using normal variables:
void main() {
var one = new IDable(1);
var two = new IDable(2);
caller(one.id);
caller(two.id);
callerJustForThree(one.id);
}
class IDable{
int id;
IDable(this.id);
}
caller(param){
print(param);
}
callerJustForThree(param){
var three = new IDable(3);
print(three.id); // This works
print(param); // This works, too
print(three.param); // But why should this work?
}
It's exactly the same concept. Think of your callbacks as normal variables, and everything makes sense. At least I hope so, if I explained it good enough.