bchavez / Bogus

:card_index: A simple fake data generator for C#, F#, and VB.NET. Based on and ported from the famed faker.js.
Other
8.79k stars 499 forks source link

Parse with extension #407

Open mathrb opened 2 years ago

mathrb commented 2 years ago

Hello and thanks for the product

Version Information

Software Version(s)
Bogus NuGet Package 34.0.1
.NET Core? 5.0
.NET Full Framework? N/A
Windows OS? N/A
Linux OS? Ubuntu 20.04.3 LTS
Visual Studio? VSCode 1.63.2

What locale are you using with Bogus?

fr

What's the problem?

I created an extension method for Bogus following the example here: https://github.com/bchavez/Bogus/blob/master/Examples/ExtendingBogus/ExtensionsForAddress.cs I'd like to parse an expression that uses this extension:

faker.Parse("{{address.downtownTorontoPostalCode}}");

Is it something doable? Or is there any other solution to extend a dataset that would allow me to use the parser?

Looking at the code, it does not seem to be possible using extension methods because of this: https://github.com/bchavez/Bogus/blob/59985ee11fb2a34b89d940d5da8b34617f6f9f6f/Source/Bogus/Tokenizer.cs#L92 DeclaringType cannot be a dataset from Bogus

bchavez commented 2 years ago

Hi @mathrb , unfortunately, with the current version, it is difficult to extend MustashMethods but it is possible if you really want to do it... but, again, it is not pretty, but works:

void Main()
{
   var existingMethods = Tokenizer.MustashMethods.Select(kv => kv.First())
                                  .ToList();

   var foodCandyMethod = new MustashMethod
   {
      Name = "FOOD.CANDY",
      Method = typeof(Food).GetMethod("Candy"),
      OptionalArgs = new object[0]
   };

   var foodDrinkMethod = new MustashMethod
   {
      Name = "FOOD.DRINK",
      Method = typeof(Food).GetMethod("Drink"),
      OptionalArgs = new object[0]
   };

   existingMethods.Add(foodDrinkMethod);
   existingMethods.Add(foodCandyMethod);

   var replacementLookup = existingMethods.ToLookup(mm => mm.Name);

   Tokenizer.MustashMethods = replacementLookup;

   var faker = new Faker();
   faker.ParseWithMyExtensions("My fav candy and drink is: {{food.candy}} and {{food.drink}}").Dump();
}

public static class ExtensionsForFood
{
   public static string ParseWithMyExtensions(this Faker faker, string str)
   {
      return Tokenizer.Parse(str, faker.Food(),
                                  faker.Address,
                                  faker.Company,
                                  faker.Date,
                                  faker.Finance,
                                  faker.Hacker,
                                  faker.Image,
                                  faker.Internet,
                                  faker.Lorem,
                                  faker.Name,
                                  faker.Phone,
                                  faker.System,
                                  faker.Commerce,
                                  faker.Database,
                                  faker.Random);
   }
   public static Food Food(this Faker faker)
   {
      return ContextHelper.GetOrSet(faker, () => new Food());
   }
}

public class Food : Bogus.DataSet
{
   private static readonly string[] Candies =
      {
            "Hard candy", "Taffy", "Chocolate bar", "Stick candy",
            "Jelly bean", "Mint", "Cotton candy", "Lollipop"
         };

   public string Candy()
   {
      return this.Random.ArrayElement(Candies);
   }

   private static readonly string[] Drinks = { "Soda", "Water", "Beer", "Wine", "Coffee", "Lemonade", "Milk" };
   public string Drink()
   {
      return this.Random.ArrayElement(Drinks);
   }
}

image

You'll have to do something similar for your program and unique situation. I'll leave this ticket open and hopefully at some point in the future I can make extendability in this area easier.

Notes for myself:

mathrb commented 2 years ago

Hello @bchavez Sorry for the little reaction (thumbs up), here is a more complete answer. Thanks it's working, as you said it's not really "integrated" but it's a valid workaround.

I do have one question though: why ContextHelper is part of the premium namespace?

Actually, this answers one use case: the dataset is external. How one would do to for exemple parse an extension method of the Address dataset? Does your example means that we can't, and therefore we need to create an external dataset?