blinkboxbooks/genny

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# Genny [![Code Climate](https://codeclimate.com/github/blinkboxbooks/genny/badges/gpa.svg)](https://codeclimate.com/github/blinkboxbooks/genny) [![Test Coverage](https://codeclimate.com/github/blinkboxbooks/genny/badges/coverage.svg)](https://codeclimate.com/github/blinkboxbooks/genny)

Genny likes making things up. Unlike other faker libraries this one is based around generating whole data structures using JSONSchema as a basis.

This gem can be used without any additional gems. The following gems will be used if they can be loaded, but not having them installed/in your bundle will reduce functionality rather than prevent the library from working:

- [json-schema](https://rubygems.org/gems/json-schema) - for JSONSchema validation
- [faker](https://rubygems.org/gems/faker) - for better hinted strings

## Examples

### Extending core classes

Extending the core classes to have the `genny` class method is handy, but may not be desired. If you wish to use this style then you must initialise it:

```
Genny::String.genny
# =>  "pymliestqk" 
String.respond_to?(:genny)
# => false

Genny.extend_core
# => [NilClass, URI, Time, Date, Array, Float, String, Regexp, Integer, Hash]

String.respond_to?(:genny)
# => true
String.genny
# => "oniqpsxubm"
```

### Strings

```
Genny::String.genny
# => "fusmtzavyi"
Genny::String.genny(format: "ipv4")
# => "171.199.220.179"

# You can define your own formats
Genny::String.format("tla") do |opts|
  3.times.map { ('A'..'Z').to_a.sample }.join
end
Genny::String.genny(format: "tla")
# => "XSE"
```

### Objects

```
# Genny makes JSONSchema a first class... class. It acts like a hash but has a genny instance method.
# If the json-schema gem can be loaded then the new method will raise an error if it's an invalid schema.
js = JSONSchema.new(
  "type" => "object",
  "properties" => {
    "key" => {
      "type" => "string"
    }
  }
)
# => {"type"=>"object", "properties"=>{"key"=>{"type"=>"string"}}, "definitions"=>{}}
js.genny
# => {"key"=>"gcqhsanwzd"}
```

### Arrays

A generated array will always be empty unless a `:items` option has been defined. Any class in that array that responds to `genny` may be picked as the prototype for an element of the generated array.

```
Genny::Array.genny
# => []

Genny::Array.genny(items: [Genny::String, Genny::Integer])
# => ["mcztjgoriq", "nohfcavjyz", 739]

Genny::Array.genny(
  items: JSONSchema.new(
    "type" => "object",
    "properties" => {
      "key" => {
        "type" => "string"
      }
    }
  )
)
# => ["mcztjgoriq", "nohfcavjyz", 739]
```

#### Hinting

```
# Hinting for strings allows you to try and indicate what kind of string you'd like back.
Genny::String.genny(hint: "first name")
# => "Dean"
Genny::String.genny(hint: "name")
# => "Garrison O'Kon"

# And when an object value type is a string, the key will be used as a hint
JSONSchema.new(
  "type" => "object",
  "properties" => {
    "name" => {
      "type" => "string"
    }
  }
).genny
# => { "name" => "Everett Schmitt" }

# You can define your own hinters. Hints defined earlier will take precidence
Genny::String.hint do |opts|
  next unless opts[:hint].include?("answer")
  "42"
end
Genny::String.genny(hint: "the answer")
# => "42"
```

### Numbers

```
Genny::Integer.genny
# => 5
Genny::Integer.genny(maximum: 100)
# => 88
Genny::Integer.genny(minimum: 90, maximum: 100)
# => 92

Genny::Float.genny
# => 234.2934215006394
Genny::Float.genny(minimum: 90, maximum: 100)
# => 96.6691946757789 
```

### Dates & Times

```
Genny::Date.genny
# => #<Date: 1987-03-01 ((2446856j,0s,0n),+0s,2299161j)> 
Genny::Time.genny
# => 1988-11-23 05:48:04 UTC 

# These are also a format of string (for JSONSchema purposes)
Genny::String.genny(format: "date")
# => "1975-07-24"
Genny::String.genny(format: "date-time")
# => "1994-10-11T07:32:55Z"
```

### Boolean

There being no core `Boolean` class this must be called directly even if the core classes have been extended.

```
Genny::Boolean.genny
# => false
Genny::Boolean.genny
# => true
```

### URIs

```
Genny::URI.genny
# => #<URI::HTTP:0x007f8c1c164f90 URL:http://example.com/erichqlvjx>

# URIs are also a format of string (for JSONSchema purposes)
Genny::String.genny(format: "uri")
# => "http://example.com/itgxsewckm"
```

### Regular expressions

Generating a regular expression is a bit useless (it always returns `/.*/` which matches most strings) but there is also an instance `genny` method defined which will, when I implement it, make a best effort at creating a string which matches the given regular expression.

```
Genny::Regexp.genny
# => /.*/

/[a-f0-9]{2}/.extend(Genny::Regexp).genny
# => "a7"
Genny.extend_core
/[a-f0-9]{2}/.genny
# => "5c"

# You can also specify regular expressions as formats for strings
Genny::String.genny(format: "^979\\d{10}$")
# => "9790607263440"
```

**Please note**: Regular expression support is not guaranteed. I mean, I'm parsing regular expressions *with regular expressions* and that way madness lies.

If you're calling `genny` on a regular expression instance, be sure to rescue `NotImplementedError` exceptions, which will be thrown if the generated string doesn't match the Regexp.

If you're generating a string with a format that looks like a regular expression, the worst that will happen is a string will be returned as if no `format` had been specified. If you need a specific regular expression to work and don't feel like driving yourself insane, you can specfify a `format` with the regular expression you need and write your own code to generate it:

```
Genny::String.format("^(?!un)") do |opts|
  Genny::String.genny.sub(/^un/, '')
end

Genny::String.genny(format: "^(?!un)")
# => "doesntstartwithun"
```

## Contributing

Yes please! Pull requests would be lovely.

## Contact

I am [@jphastings](https://twitter.com/jphastings) and I work at [blinkbox Books](https://github.com/blinkboxbooks), do get in touch.

Also, let's have none of this gif/jif nonsense. Unless it sounds *ugly* in your language, you have just finished reading the documentation for **/jeni/** ;)