render 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/Gemfile +1 -1
- data/lib/render.rb +12 -50
- data/lib/render/{array_attribute.rb → attributes/array_attribute.rb} +3 -4
- data/lib/render/attributes/attribute.rb +48 -0
- data/lib/render/{hash_attribute.rb → attributes/hash_attribute.rb} +2 -4
- data/lib/render/definition.rb +31 -0
- data/lib/render/extensions/dottable_hash.rb +82 -0
- data/lib/render/extensions/symbolizable_array.rb +26 -0
- data/lib/render/extensions/symbolizable_hash.rb +28 -0
- data/lib/render/generator.rb +58 -4
- data/lib/render/graph.rb +29 -34
- data/lib/render/schema.rb +12 -12
- data/lib/render/type.rb +63 -0
- data/lib/render/version.rb +1 -1
- data/rakefile.rb +3 -3
- data/readme.md +17 -38
- data/render.gemspec +5 -7
- data/spec/functional/{representation → render}/attribute_spec.rb +3 -4
- data/spec/functional/{representation → render}/graph_spec.rb +6 -6
- data/spec/functional/{representation → render}/nested_schemas_spec.rb +0 -0
- data/spec/functional/{representation → render}/schema_spec.rb +0 -0
- data/spec/integration/render/graph_spec.rb +119 -0
- data/spec/integration/render/nested_graph_spec.rb +67 -0
- data/spec/integration/render/schema_spec.rb +90 -0
- data/spec/support/helpers.rb +2 -1
- data/spec/{schemas → support/schemas}/film.json +1 -0
- data/spec/{schemas → support/schemas}/films.json +1 -0
- data/spec/unit/gemspec_spec.rb +8 -0
- data/spec/unit/{array_attribute_spec.rb → render/attributes/array_attribute_spec.rb} +1 -1
- data/spec/unit/render/{attribute_spec.rb → attributes/attribute_spec.rb} +0 -0
- data/spec/unit/render/{hash_attribute_spec.rb → attributes/hash_attribute_spec.rb} +3 -1
- data/spec/unit/render/definition_spec.rb +85 -0
- data/spec/unit/render/extensions/dottable_hash_spec.rb +148 -0
- data/spec/unit/render/extensions/symbolizable_array_spec.rb +20 -0
- data/spec/unit/render/generator_spec.rb +44 -22
- data/spec/unit/render/graph_spec.rb +18 -18
- data/spec/unit/render/schema_spec.rb +11 -16
- data/spec/unit/render/type_spec.rb +83 -0
- data/spec/unit/render_spec.rb +0 -139
- metadata +70 -60
- data/lib/extensions/boolean.rb +0 -2
- data/lib/extensions/enumerable.rb +0 -16
- data/lib/extensions/hash.rb +0 -39
- data/lib/render/attribute.rb +0 -59
- data/lib/render/dottable_hash.rb +0 -113
- data/spec/integration/nested_graph_spec.rb +0 -85
- data/spec/integration/single_graph_spec.rb +0 -76
- data/spec/unit/extensions/boolean_spec.rb +0 -7
- data/spec/unit/render/dottable_hash_spec.rb +0 -231
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OTJlZmIxMGY4MTViYTcxMTc1ZDQ1Y2NiMzQyOWIwOTJiNjkzODM1Yw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OGRkZWY2MWQ2Y2NlY2MxNGJmYThmNzIwMmY3MjAyMTI5NDEwYWVkNw==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NzdlN2JkNWU3MDdlOTUzYWUwY2RkZTYyOTFiN2U3ZGE5MTNiYTIwMzgzNmQ1
|
10
|
+
YmM3MzE3ZDYyODU3NTNjYTI2NjM0NjY3ZjJkNDA4ZDc5Nzg1YjY2ZGIzODgx
|
11
|
+
ZWQ2YjhkMDdkNzk5OTg0NTM5MDQ3YjA5YjkzYjZhNGRjMGEwYWQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZjMwNDIyNDdmZDg1ZGIyNDExMjYwODkzYjY2YTY3ZjllYWUzY2RkMmI4NjE0
|
14
|
+
NDQ0NTc2MzVhZjQzZTA0MGZhOTkzMzIxYzk4MTdlNTEwZTViYmM5MjVkNDgx
|
15
|
+
YzlhNDVjMWEwODkzMWJiMDZmZWQwNThhMGZhMjE3ODk5OTk4OTg=
|
data/Gemfile
CHANGED
data/lib/render.rb
CHANGED
@@ -1,68 +1,30 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
# - Add nested Graphs by interpreting/sending data they need
|
5
|
-
|
6
|
-
require "extensions/enumerable"
|
7
|
-
require "extensions/boolean"
|
8
|
-
require "extensions/hash"
|
1
|
+
require "uuid"
|
2
|
+
require "date"
|
3
|
+
require "logger"
|
9
4
|
|
10
5
|
require "render/version"
|
11
|
-
require "render/
|
6
|
+
require "render/errors"
|
7
|
+
require "render/extensions/dottable_hash"
|
8
|
+
require "render/type"
|
12
9
|
require "render/generator"
|
13
|
-
require "
|
14
|
-
require "
|
10
|
+
require "render/definition"
|
11
|
+
require "render/graph"
|
15
12
|
|
16
13
|
module Render
|
17
14
|
@live = true
|
18
|
-
@
|
19
|
-
@generators = []
|
20
|
-
@logger = ::Logger.new($stdout)
|
15
|
+
@logger = ::Logger.new("/dev/null")
|
21
16
|
@threading = true
|
22
17
|
|
23
18
|
class << self
|
24
|
-
attr_accessor :live,
|
25
|
-
:definitions,
|
26
|
-
:generators,
|
27
|
-
:logger,
|
28
|
-
:threading
|
19
|
+
attr_accessor :live, :logger, :threading
|
29
20
|
|
30
21
|
def threading?
|
31
22
|
threading == true
|
32
23
|
end
|
33
24
|
|
34
|
-
def
|
35
|
-
|
36
|
-
logger.info("Reading #{definition_file} definition")
|
37
|
-
definition_string = File.read(definition_file)
|
38
|
-
parsed_definition = JSON.parse(definition_string).recursive_symbolize_keys!
|
39
|
-
load_definition!(parsed_definition)
|
40
|
-
end
|
25
|
+
def live?
|
26
|
+
@live == true
|
41
27
|
end
|
42
28
|
|
43
|
-
def load_definition!(definition)
|
44
|
-
title = definition.fetch(:universal_title, definition.fetch(:title)).to_sym
|
45
|
-
self.definitions[title] = definition
|
46
|
-
end
|
47
|
-
|
48
|
-
def definition(title)
|
49
|
-
definitions.fetch(title.to_sym)
|
50
|
-
rescue KeyError => error
|
51
|
-
raise Errors::DefinitionNotFound.new(title)
|
52
|
-
end
|
53
|
-
|
54
|
-
# TODO better type parsing
|
55
|
-
def parse_type(type)
|
56
|
-
return type unless type.is_a?(String)
|
57
|
-
|
58
|
-
return UUID if type.match(/uuid/i)
|
59
|
-
return Boolean if type.match(/boolean/i)
|
60
|
-
return Float if type.match(/number/i)
|
61
|
-
return Time if type.match(/date.*time/i)
|
62
|
-
Object.const_get(type.capitalize)
|
63
|
-
rescue NameError => error
|
64
|
-
raise Errors::InvalidType.new(type)
|
65
|
-
end
|
66
29
|
end
|
67
|
-
|
68
30
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require "render/attribute"
|
1
|
+
require "render/attributes/attribute"
|
2
2
|
|
3
3
|
module Render
|
4
4
|
class ArrayAttribute < Attribute
|
@@ -10,10 +10,9 @@ module Render
|
|
10
10
|
super
|
11
11
|
|
12
12
|
self.name = options.fetch(:title, :render_array_attribute_untitled).to_sym
|
13
|
+
self.required = options.fetch(:required, nil)
|
13
14
|
options = options.fetch(:items)
|
14
|
-
|
15
|
-
self.format = Render.parse_type(options[:format]) rescue nil
|
16
|
-
self.enums = options[:enum]
|
15
|
+
process_type!(options)
|
17
16
|
|
18
17
|
if options.keys.include?(:properties)
|
19
18
|
self.schema = Schema.new(options)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "uuid"
|
2
|
+
|
3
|
+
module Render
|
4
|
+
class Attribute
|
5
|
+
SCHEMA_IDENTIFIERS = [:properties, :items].freeze
|
6
|
+
|
7
|
+
attr_accessor :name,
|
8
|
+
:type,
|
9
|
+
:schema,
|
10
|
+
:enums,
|
11
|
+
:format,
|
12
|
+
:required
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
Render.logger.debug("Initializing attribute #{options}")
|
16
|
+
self.required = false
|
17
|
+
end
|
18
|
+
|
19
|
+
def bias_type
|
20
|
+
format || type
|
21
|
+
end
|
22
|
+
|
23
|
+
def default_value
|
24
|
+
Render.live ? nil : faux_value
|
25
|
+
end
|
26
|
+
|
27
|
+
def nested_schema?(options = {})
|
28
|
+
options.any? { |name, value| SCHEMA_IDENTIFIERS.include?(name) }
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def process_type!(options)
|
34
|
+
self.type = Type.parse!(options[:type])
|
35
|
+
self.format = Type.parse(options[:format])
|
36
|
+
|
37
|
+
if (options[:enum])
|
38
|
+
self.enums = options[:enum]
|
39
|
+
self.format = Type::Enum
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def faux_value
|
44
|
+
Generator.trigger(bias_type, name, self)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require "render"
|
2
|
-
require "render/attribute"
|
2
|
+
require "render/attributes/attribute"
|
3
3
|
|
4
4
|
module Render
|
5
5
|
class HashAttribute < Attribute
|
@@ -8,9 +8,7 @@ module Render
|
|
8
8
|
|
9
9
|
self.name = options.keys.first
|
10
10
|
options = options[name]
|
11
|
-
|
12
|
-
self.format = Render.parse_type(options[:format]) rescue nil
|
13
|
-
self.enums = options[:enum]
|
11
|
+
process_type!(options)
|
14
12
|
|
15
13
|
initialize_schema!(options) if nested_schema?(options)
|
16
14
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Render
|
2
|
+
class Definition
|
3
|
+
@instances = {}
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :instances
|
7
|
+
|
8
|
+
def load_from_directory!(directory)
|
9
|
+
Dir.glob("#{directory}/**/*.json").each do |definition_file|
|
10
|
+
Render.logger.info("Reading #{definition_file} definition")
|
11
|
+
definition_string = File.read(definition_file)
|
12
|
+
json_definition = JSON.parse(definition_string)
|
13
|
+
parsed_definition = Extensions::DottableHash.new(json_definition).recursively_symbolize_keys!
|
14
|
+
load!(parsed_definition)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def load!(definition)
|
19
|
+
title = definition.fetch(:universal_title, definition.fetch(:title)).to_sym
|
20
|
+
self.instances[title] = definition
|
21
|
+
end
|
22
|
+
|
23
|
+
def find(title)
|
24
|
+
instances.fetch(title.to_sym)
|
25
|
+
rescue KeyError => error
|
26
|
+
raise Errors::DefinitionNotFound.new(title)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "render/extensions/symbolizable_hash"
|
2
|
+
require "render/extensions/symbolizable_array"
|
3
|
+
|
4
|
+
module Render
|
5
|
+
module Extensions
|
6
|
+
class DottableHash < SymbolizableHash
|
7
|
+
class << self
|
8
|
+
def new(element_to_hash = {})
|
9
|
+
symbolize_hash = SymbolizableHash.new.merge!(element_to_hash)
|
10
|
+
symbolize_hash.symbolize_keys!
|
11
|
+
hash = super().merge!(symbolize_hash)
|
12
|
+
|
13
|
+
hash.each do |key, value|
|
14
|
+
hash[key] = initialize_element(value)
|
15
|
+
end
|
16
|
+
|
17
|
+
hash
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize_element(value)
|
21
|
+
case value
|
22
|
+
when Hash
|
23
|
+
new(value)
|
24
|
+
when Array
|
25
|
+
values = value.collect do |v|
|
26
|
+
initialize_element(v)
|
27
|
+
end
|
28
|
+
SymbolizableArray.new(values)
|
29
|
+
else
|
30
|
+
value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def []=(key, value)
|
36
|
+
key = key.to_sym
|
37
|
+
value = self.class.initialize_element(value)
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
def [](key)
|
42
|
+
key = key.to_sym
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete(key)
|
47
|
+
key = key.to_sym
|
48
|
+
super
|
49
|
+
end
|
50
|
+
|
51
|
+
def has_key?(key)
|
52
|
+
super(key.to_sym)
|
53
|
+
end
|
54
|
+
|
55
|
+
def merge!(other_hash)
|
56
|
+
other_hash = SymbolizableHash.new().merge!(other_hash)
|
57
|
+
super(other_hash.recursively_symbolize_keys!)
|
58
|
+
end
|
59
|
+
|
60
|
+
def merge(other_hash)
|
61
|
+
other_hash = SymbolizableHash.new().merge!(other_hash)
|
62
|
+
super(other_hash.recursively_symbolize_keys!)
|
63
|
+
end
|
64
|
+
|
65
|
+
def method_missing(method, *arguments)
|
66
|
+
if method.match(/\=$/)
|
67
|
+
self[method.to_s.chop] = arguments.first
|
68
|
+
elsif has_key?(method)
|
69
|
+
self[method]
|
70
|
+
else
|
71
|
+
super
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def fetch(key, *args)
|
76
|
+
key = key.to_sym
|
77
|
+
super
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Render
|
2
|
+
module Extensions
|
3
|
+
class SymbolizableArray < Array
|
4
|
+
class << self
|
5
|
+
def new(array)
|
6
|
+
array.inject(super()) do |accumulator, item|
|
7
|
+
if item.is_a?(Array)
|
8
|
+
accumulator << new(item)
|
9
|
+
elsif item.is_a?(Hash)
|
10
|
+
accumulator << DottableHash.new(item)
|
11
|
+
else
|
12
|
+
accumulator << item
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def recursively_symbolize_keys!
|
19
|
+
each do |item|
|
20
|
+
item.recursively_symbolize_keys! if item.respond_to?(:recursively_symbolize_keys!)
|
21
|
+
end
|
22
|
+
self
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Render
|
2
|
+
module Extensions
|
3
|
+
class SymbolizableHash < Hash
|
4
|
+
def initialize
|
5
|
+
super()
|
6
|
+
end
|
7
|
+
|
8
|
+
def symbolize_keys!
|
9
|
+
keys.each do |key|
|
10
|
+
self[(key.to_sym rescue key) || key] = delete(key)
|
11
|
+
end
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def symbolize_keys
|
16
|
+
dup.symbolize_keys!
|
17
|
+
end
|
18
|
+
|
19
|
+
def recursively_symbolize_keys!
|
20
|
+
symbolize_keys!
|
21
|
+
values.each do |value|
|
22
|
+
value.recursively_symbolize_keys! if value.respond_to?(:recursively_symbolize_keys!)
|
23
|
+
end
|
24
|
+
self
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/render/generator.rb
CHANGED
@@ -1,14 +1,68 @@
|
|
1
|
+
# Generators make fake data in non-live mode.
|
2
|
+
# They are used for attributes of a specified type, and whose name matches its defined matcher.
|
3
|
+
|
4
|
+
require "uuid"
|
1
5
|
require "render/errors"
|
2
6
|
|
3
7
|
module Render
|
4
8
|
class Generator
|
9
|
+
@instances = []
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :instances
|
13
|
+
|
14
|
+
# When in non-live mode, e.g. testing, you can use custom generators to make fake data for a given attribute type
|
15
|
+
# and name-matcher.
|
16
|
+
# @param type [Class] Use generator exclusively for this type of data
|
17
|
+
# @param matcher [Regexp] Use generator for attribute whose name matches this
|
18
|
+
# @param algorithm [Proc, #call] Call this to generate a value
|
19
|
+
def create!(type, matcher, algorithm)
|
20
|
+
generator = new(type, matcher, algorithm)
|
21
|
+
instances.unshift(generator)
|
22
|
+
generator
|
23
|
+
end
|
24
|
+
|
25
|
+
def trigger(type, to_match, algorithm_argument = nil)
|
26
|
+
generator = find(type, to_match)
|
27
|
+
generator.trigger(algorithm_argument)
|
28
|
+
end
|
29
|
+
|
30
|
+
def find(type, to_match)
|
31
|
+
instances.detect do |generator|
|
32
|
+
(generator.type == type) && to_match.match(generator.matcher)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
5
37
|
attr_accessor :type, :matcher, :algorithm
|
6
38
|
|
7
|
-
def initialize(
|
8
|
-
|
9
|
-
|
10
|
-
|
39
|
+
def initialize(type, matcher, algorithm)
|
40
|
+
self.type = type
|
41
|
+
self.matcher = matcher
|
42
|
+
set_algorithm!(algorithm)
|
43
|
+
end
|
44
|
+
|
45
|
+
def trigger(algorithm_argument = nil)
|
46
|
+
algorithm[algorithm_argument]
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def set_algorithm!(algorithm)
|
52
|
+
if algorithm.respond_to?(:call)
|
53
|
+
self.algorithm = algorithm
|
54
|
+
else
|
55
|
+
raise Errors::Generator::MalformedAlgorithm.new(algorithm)
|
56
|
+
end
|
11
57
|
end
|
12
58
|
|
59
|
+
# Default set to ensure each type can generate fake data.
|
60
|
+
Generator.create!(String, /.*/, proc { |attribute| "#{attribute.name} (generated)" })
|
61
|
+
Generator.create!(Integer, /.*/, proc { rand(100) })
|
62
|
+
Generator.create!(Float, /.*/, proc { rand(0.1..99).round(2) })
|
63
|
+
Generator.create!(UUID, /.*/, proc { UUID.generate })
|
64
|
+
Generator.create!(Time, /.*/, proc { |attribute| time = Time.now; (attribute.type == String) ? time.to_s : time })
|
65
|
+
Generator.create!(Type::Boolean, /.*/, proc { [true, false].sample })
|
66
|
+
Generator.create!(Type::Enum, /.*/, proc { |attribute| attribute.enums.sample })
|
13
67
|
end
|
14
68
|
end
|