Skip to content

Commit

Permalink
lots of fixes for model binding found from the integration with FubuMVC
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremydmiller committed Apr 6, 2012
1 parent f9bb024 commit a093716
Show file tree
Hide file tree
Showing 15 changed files with 127 additions and 86 deletions.
Expand Up @@ -199,7 +199,7 @@ public bool Matches(Type type)
throw new NotImplementedException();
}

public void Bind(Type type, object instance, IBindingContext context)
public void BindProperties(Type type, object instance, IBindingContext context)
{
throw new NotImplementedException();
}
Expand Down
Expand Up @@ -42,12 +42,15 @@ public void existing_collection_is_not_discarded()

var model = BindingScenario<AddressViewModel>.Build(x =>
{
x.Model.Localities = originalList;
x.Model = new AddressViewModel(){
Localities = originalList
};
x.Data("Localities[0]ZipCode", "84115");
});

model.Localities.Select(x => x.ZipCode).ShouldHaveTheSameElementsAs("previously_set_zipcode", "84115");
}

}
}
3 changes: 1 addition & 2 deletions src/FubuCore.Testing/Binding/ObjectResolverTester.cs
Expand Up @@ -46,7 +46,6 @@ public void should_throw_fubu_exception_2201()
matchingBinder = MockRepository.GenerateStub<IModelBinder>();
matchingBinder.Stub(x => x.Matches(_type)).Return(true);
matchingBinder.Stub(x => x.Bind(_type, null)).IgnoreArguments().Throw(new Exception("fake message"));
matchingBinder.Stub(x => x.Bind(_type, null, null)).IgnoreArguments().Throw(new Exception("fake message"));



Expand Down Expand Up @@ -109,7 +108,7 @@ public bool Matches(Type type)
return type == typeof (BinderTarget);
}

public void Bind(Type type, object instance, IBindingContext context)
public void BindProperties(Type type, object instance, IBindingContext context)
{
throw new NotImplementedException();
}
Expand Down
Expand Up @@ -67,14 +67,19 @@ public void captures_the_property_binder_and_converter_if_exists()
[Test]
public void captures_details_for_a_nested_object()
{
var report = BindingScenario<ClassThatNestsTarget>.For(x =>
var scenario = BindingScenario<ClassThatNestsTarget>.For(x =>
{
x.Data(@"
TargetName=Jeremy
TargetAge=38
TargetEyeColor=Blue
");
}).Report;
});


scenario.Model.Target.Name.ShouldEqual("Jeremy");

var report = scenario.Report;



Expand All @@ -85,7 +90,7 @@ public void captures_details_for_a_nested_object()
var value = age.Values.Single();
value.RawKey.ShouldEqual("TargetAge");
value.RawValue.ShouldEqual("38");
value.Source.ShouldEqual("Anonymous");
value.Source.ShouldEqual("Anonymous");
}

[Test]
Expand Down
8 changes: 7 additions & 1 deletion src/FubuCore.Testing/Binding/StandardModelBinderTester.cs
Expand Up @@ -205,10 +205,16 @@ public void populate_extra_values_in_dictionary_are_ignored()
[Test]
public void populate_should_not_change_property_values_not_found_in_the_dictionary()
{
var turkey = new Turkey()
{
Name = "Smith"
};

usingData(x =>
{
x.Model = turkey;
x.Data("Age", 9);
x.Model.Name = "Smith";
});

theResultingObject.Name.ShouldEqual("Smith");
Expand Down
3 changes: 2 additions & 1 deletion src/FubuCore/Binding/BindingContext.cs
Expand Up @@ -128,7 +128,8 @@ public void BindObject(IRequestData data, Type type, Action<object> continuation

public void BindProperties(object instance)
{
_resolver.Value.BindModel(instance.GetType(), instance, this);
// Start here in the morning. You have to use StandardModel
_resolver.Value.BindProperties(instance.GetType(), instance, this);
}

public static void AddNamingStrategy(Func<string, string> strategy)
Expand Down
8 changes: 8 additions & 0 deletions src/FubuCore/Binding/BindingRegistry.cs
Expand Up @@ -73,6 +73,14 @@ private StandardModelBinder buildStandardModelBinder()
return new StandardModelBinder(this, new TypeDescriptorCache());
}

public IPropertySetter PropertySetter
{
get
{
return _standardBinder.Value;
}
}

public void Add(IModelBinder binder)
{
_binders.Add(binder);
Expand Down
2 changes: 1 addition & 1 deletion src/FubuCore/Binding/FlatFileReader.cs
Expand Up @@ -49,7 +49,7 @@ private void readTargetFromLine(FlatFileRequest<T> request, FlatFileValues data,
data.ReadLine(line);

var target = request.Finder(new RequestData(new FlatValueSource(data)));
_resolver.BindModel(target, context);
_resolver.BindProperties(target, context);

request.Callback(target);
}
Expand Down
1 change: 0 additions & 1 deletion src/FubuCore/Binding/IModelBinder.cs
Expand Up @@ -5,7 +5,6 @@ namespace FubuCore.Binding
public interface IModelBinder
{
bool Matches(Type type);
void Bind(Type type, object instance, IBindingContext context);
object Bind(Type type, IBindingContext context);
}
}
4 changes: 2 additions & 2 deletions src/FubuCore/Binding/IObjectResolver.cs
Expand Up @@ -21,10 +21,10 @@ public interface IObjectResolver
[MarkedForTermination]
void TryBindModel(Type type, IBindingContext context, Action<BindResult> continuation);

BindResult BindModel<T>(T model, IBindingContext context);
void BindProperties<T>(T model, IBindingContext context);


BindResult BindModel(Type type, object model, IBindingContext context);
void BindProperties(Type type, object model, IBindingContext context);


void TryBindModel(Type type, IRequestData data, Action<BindResult> continuation);
Expand Down
109 changes: 58 additions & 51 deletions src/FubuCore/Binding/InMemory/BindingScenario.cs
Expand Up @@ -13,7 +13,6 @@ namespace FubuCore.Binding.InMemory
public class BindingScenario<T> where T : class, new()
{
private readonly StringWriter _writer = new StringWriter();
public InMemoryBindingHistory History{ get; private set;}

private BindingScenario(ScenarioDefinition definition)
{
Expand All @@ -32,6 +31,8 @@ private BindingScenario(ScenarioDefinition definition)
}
}

public InMemoryBindingHistory History { get; private set; }

public BindingReport Report { get; private set; }

public IList<ConvertProblem> Problems { get; private set; }
Expand All @@ -56,22 +57,43 @@ public static T Build(Action<ScenarioDefinition> configuration)
return For(configuration).Model;
}

#region Nested type: PropertyModelBinderStandin

[Description("Strictly a fake for the binding scenario")]
public class PropertyModelBinderStandin : IModelBinder
{
public bool Matches(Type type)
{
throw new NotImplementedException();
}

public void BindProperties(Type type, object instance, IBindingContext context)
{
throw new NotImplementedException();
}

public object Bind(Type type, IBindingContext context)
{
throw new NotImplementedException();
}
}

#endregion

#region Nested type: ScenarioDefinition

public class ScenarioDefinition
{
private readonly IList<Action<IBindingContext>> _actions = new List<Action<IBindingContext>>();
private readonly KeyValues _data = new KeyValues();
private readonly InMemoryBindingHistory _history = new InMemoryBindingHistory();
private readonly RecordingBindingLogger _logger;
private readonly BindingRegistry _registry = new BindingRegistry();
private readonly InMemoryServiceLocator _services = new InMemoryServiceLocator();
private IServiceLocator _customServices;
private readonly InMemoryBindingHistory _history = new InMemoryBindingHistory();
private readonly RecordingBindingLogger _logger;

public ScenarioDefinition()
{
Model = new T();

_logger = new RecordingBindingLogger(_history);

_services.Add<IObjectResolver>(new ObjectResolver(_services, _registry, _logger));
Expand Down Expand Up @@ -103,12 +125,22 @@ protected internal IEnumerable<Action<IBindingContext>> Actions
{
if (!_actions.Any())
{
if (Model != null)
{
return new Action<IBindingContext>[]{
context =>
new ObjectResolver(Services, Registry, new NulloBindingLogger()).BindProperties(
typeof (T), Model, context)
};
}

return new Action<IBindingContext>[]{
context =>
new ObjectResolver(Services, Registry, new NulloBindingLogger()).BindModel(Model,
context)
{
var resolver = new ObjectResolver(Services, Registry, _logger);
Model = (T) resolver.BindModel(typeof (T), context).Value;
}
};
;
}


Expand Down Expand Up @@ -137,13 +169,13 @@ public void Service<TService>(TService service)
}

/// <summary>
/// Allows you to force load key/value pairs in the format:
/// prop1=val1
/// ChildProp1=val
/// Prop2=val
/// Prop3=val
/// Allows you to force load key/value pairs in the format:
/// prop1=val1
/// ChildProp1=val
/// Prop2=val
/// Prop3=val
/// </summary>
/// <param name="text"></param>
/// <param name = "text"></param>
public void Data(string text)
{
_data.ReadData(text);
Expand All @@ -159,22 +191,6 @@ public void Data(Expression<Func<T, object>> property, object rawValue)
_data[property.ToAccessor().Name] = rawValue.ToString();
}

// TODO -- UT this
public void BindWith(IModelBinder binder)
{
_actions.Add(context =>
{
Logger.Chose(typeof(T), binder);
binder.Bind(typeof (T), Model, context);
Logger.FinishedModel();
});
}

public void BindWith<TBinder>() where TBinder : IModelBinder, new()
{
BindWith(new TBinder());
}

public void BindPropertyWith<TBinder>(Expression<Func<T, object>> property, string rawValue = null)
where TBinder : IPropertyBinder, new()
{
Expand All @@ -188,29 +204,20 @@ public void BindPropertyWith<TBinder>(Expression<Func<T, object>> property, stri
var prop = property.ToAccessor().InnerProperty;
_actions.Add(context =>
{
Logger.Chose(typeof(T), new PropertyModelBinderStandin());
StandardModelBinder.PopulatePropertyWithBinder(prop, context, binder);
Logger.FinishedModel();
});
}
}

[Description("Strictly a fake for the binding scenario")]
public class PropertyModelBinderStandin : IModelBinder
{
public bool Matches(Type type)
{
throw new NotImplementedException();
}
if (Model == null)
{
Model = new T();
}
public void Bind(Type type, object instance, IBindingContext context)
{
throw new NotImplementedException();
}
context.ForObject(Model, () =>
{
Logger.Chose(typeof(T), new PropertyModelBinderStandin());
StandardModelBinder.PopulatePropertyWithBinder(prop, context, binder);
Logger.FinishedModel();
});
public object Bind(Type type, IBindingContext context)
{
throw new NotImplementedException();
});
}
}

Expand Down
19 changes: 15 additions & 4 deletions src/FubuCore/Binding/InMemory/RecordingBindingLogger.cs
Expand Up @@ -56,12 +56,20 @@ public void Chose(Type modelType, IModelBinder binder)
// This acts as a de facto PushProperty
public void Chose(PropertyInfo property, IPropertyBinder binder)
{
currentReport.AddProperty(property, binder);
// Don't do anything if this has happened in the middle of a set properties operation
if (_models.Any())
{
currentReport.AddProperty(property, binder);
}

}

public void Chose(PropertyInfo property, ValueConverter converter)
{
currentReport.For(property).Chose(converter);
if (_models.Any())
{
currentReport.For(property).Chose(converter);
}
}

public void PushElement(Type elementType)
Expand All @@ -81,12 +89,15 @@ public void FinishedModel()

public void UsedValue(BindingValue value)
{
currentReport.LastProperty.Used(value);
if (_models.Any())
{
currentReport.LastProperty.Used(value);
}
}

private BindingReport startReport(Type modelType, IModelBinder binder)
{
if (_nextElement != null)
if (_nextElement != null && _models.Any())
{
return currentReport.LastProperty.AddElement(_nextElement, binder);
}
Expand Down

0 comments on commit a093716

Please sign in to comment.