riddler 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +105 -6
- data/lib/riddler.rb +52 -3
- data/lib/riddler/configuration.rb +11 -0
- data/lib/riddler/context.rb +40 -2
- data/lib/riddler/context_builder.rb +35 -0
- data/lib/riddler/context_builders/faraday_builder.rb +23 -0
- data/lib/riddler/context_builders/user_agent.rb +34 -0
- data/lib/riddler/context_director.rb +67 -0
- data/lib/riddler/drops/hash_drop.rb +29 -0
- data/lib/riddler/element.rb +15 -14
- data/lib/riddler/elements/heading.rb +0 -1
- data/lib/riddler/elements/image.rb +21 -0
- data/lib/riddler/elements/link.rb +30 -0
- data/lib/riddler/elements/text.rb +17 -0
- data/lib/riddler/elements/variant.rb +21 -0
- data/lib/riddler/includeable.rb +19 -0
- data/lib/riddler/protobuf/content_definition.proto +51 -0
- data/lib/riddler/protobuf/content_definition_pb.rb +33 -0
- data/lib/riddler/protobuf/content_management.proto +35 -0
- data/lib/riddler/protobuf/content_management_pb.rb +43 -0
- data/lib/riddler/protobuf/content_management_services_pb.rb +27 -0
- data/lib/riddler/protobuf/slug.proto +61 -0
- data/lib/riddler/protobuf/slug_pb.rb +52 -0
- data/lib/riddler/step.rb +16 -16
- data/lib/riddler/steps/content.rb +5 -5
- data/lib/riddler/steps/variant.rb +23 -0
- data/lib/riddler/test_generator.rb +73 -0
- data/lib/riddler/use_cases.rb +7 -0
- data/lib/riddler/use_cases/admin_preview_step.rb +32 -0
- data/lib/riddler/use_cases/complete_interaction.rb +20 -0
- data/lib/riddler/use_cases/dismiss_interaction.rb +20 -0
- data/lib/riddler/use_cases/preview_context.rb +28 -0
- data/lib/riddler/use_cases/preview_step.rb +16 -5
- data/lib/riddler/use_cases/show_content_definition.rb +42 -0
- data/lib/riddler/use_cases/show_slug.rb +138 -0
- data/lib/riddler/version.rb +1 -1
- data/riddler.gemspec +12 -8
- metadata +100 -10
- data/.gitignore +0 -8
- data/.travis.yml +0 -7
- data/Gemfile +0 -6
- data/Gemfile.lock +0 -58
- data/Rakefile +0 -10
- data/bin/console +0 -14
- data/bin/setup +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a56c1db8c7d926edb5786eb578adf7189a1b70acebcc62f7ee7e67f615f67e7f
|
4
|
+
data.tar.gz: 64b7ca5382dbee480718022ff190839d8e2bf3f6e85538dbba47214c6a95945f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0b87cca825ae1dbc3b27d226abe773b2299c5491c35a6afd8523adf4b612f5a6b84c0c61cc77493b86a10f310d5208fb954ef1568138287415f26768ede8311
|
7
|
+
data.tar.gz: 945736aaf732241402a00acb5e805ae1f1403adc67ef2d8868203b004591f6b65e0ff0a952b967764a6e95b885152f2256511fcea05db0fb11d5ee68596a6e79
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -2,12 +2,114 @@
|
|
2
2
|
|
3
3
|
Riddler is a dynamic content and workflow engine.
|
4
4
|
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
### Basic Example (using Liquid)
|
8
|
+
|
9
|
+
Riddler combines a ContentDefinition with a Context to render the output (using
|
10
|
+
Liquid - https://shopify.github.io/liquid/)
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
require "riddler"
|
14
|
+
|
15
|
+
content_definition = {
|
16
|
+
"id"=>"el_text",
|
17
|
+
"name"=>"text",
|
18
|
+
"content_type"=>"element",
|
19
|
+
"type"=>"text",
|
20
|
+
"text"=>"Hello {{ params.name }}!"
|
21
|
+
}
|
22
|
+
|
23
|
+
Riddler.render content_definition
|
24
|
+
|
25
|
+
# {:content_type=>"element", :type=>"text", :id=>"el_text", :name=>"text", :text=>"Hello !"}
|
26
|
+
|
27
|
+
Riddler.render content_definition, params: {name: "World"}
|
28
|
+
|
29
|
+
# {:content_type=>"element", :type=>"text", :id=>"el_text", :name=>"text", :text=>"Hello World!"}
|
30
|
+
```
|
31
|
+
|
32
|
+
### Predicate Example (using Predicator)
|
33
|
+
|
34
|
+
Pieces of content can define if they should be included or not. Here we use the
|
35
|
+
`include_predicate` to specify that it should only be included if `params.name = 'foo'`.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
require "riddler"
|
39
|
+
|
40
|
+
content_definition = {
|
41
|
+
"id"=>"el_text",
|
42
|
+
"name"=>"text",
|
43
|
+
"content_type"=>"element",
|
44
|
+
"type"=>"text",
|
45
|
+
"text"=>"Hello {{ params.name }}!",
|
46
|
+
"include_predicate" => "params.name = 'foo'"
|
47
|
+
}
|
48
|
+
|
49
|
+
Riddler.render content_definition
|
50
|
+
|
51
|
+
# nil
|
52
|
+
|
53
|
+
Riddler.render content_definition, params: {name: "foo"}
|
54
|
+
|
55
|
+
# {:content_type=>"element", :type=>"text", :id=>"el_text", :name=>"text", :text=>"Hello foo!"}
|
56
|
+
```
|
57
|
+
|
58
|
+
|
59
|
+
### PokeAPI ContextBuilder example
|
60
|
+
|
61
|
+
One way Riddler can be extended is by adding new ContextBuilders. Let's add the ability to look up a Pokemon at https://pokeapi.co/
|
62
|
+
|
63
|
+
We will then pass in a `pokemon_id` as a param
|
64
|
+
|
65
|
+
This also shows Liquid filters - capitalizing the name
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
require "riddler"
|
69
|
+
require "net/http"
|
70
|
+
|
71
|
+
class PokemonContextBuilder < ::Riddler::ContextBuilder
|
72
|
+
# Does the current context have the data available for this builder to function
|
73
|
+
def data_available?
|
74
|
+
context.params.pokemon_id
|
75
|
+
end
|
76
|
+
|
77
|
+
# Extract IDs from the context (params, headers, JWTs, etc) and store
|
78
|
+
# them in context.ids
|
79
|
+
def extract_ids
|
80
|
+
add_id :pokemon_id, context.params.pokemon_id
|
81
|
+
end
|
82
|
+
|
83
|
+
def process
|
84
|
+
uri = URI "https://pokeapi.co/api/v2/pokemon/#{pokemon_id}/"
|
85
|
+
response_string = Net::HTTP.get uri
|
86
|
+
response = JSON.parse response_string
|
87
|
+
|
88
|
+
context.assign "pokemon", response
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
Riddler.configure { |c| c.context_builders << PokemonContextBuilder }
|
93
|
+
|
94
|
+
content_definition = {
|
95
|
+
"id"=>"el_text",
|
96
|
+
"name"=>"text",
|
97
|
+
"content_type"=>"element",
|
98
|
+
"type"=>"text",
|
99
|
+
"text"=>"Hello {{ pokemon.name | capitalize }}!"
|
100
|
+
}
|
101
|
+
|
102
|
+
Riddler.render content_definition, params: {pokemon_id: "1"}
|
103
|
+
|
104
|
+
# {:content_type=>"element", :type=>"text", :id=>"el_text", :name=>"text", :text=>"Hello Bulbasaur!"}
|
105
|
+
```
|
106
|
+
|
5
107
|
## Installation
|
6
108
|
|
7
109
|
Add this line to your application's Gemfile:
|
8
110
|
|
9
111
|
```ruby
|
10
|
-
gem
|
112
|
+
gem "riddler"
|
11
113
|
```
|
12
114
|
|
13
115
|
And then execute:
|
@@ -18,9 +120,6 @@ Or install it yourself as:
|
|
18
120
|
|
19
121
|
$ gem install riddler
|
20
122
|
|
21
|
-
## Usage
|
22
|
-
|
23
|
-
TODO: Write usage instructions here
|
24
123
|
|
25
124
|
## Development
|
26
125
|
|
@@ -30,7 +129,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
30
129
|
|
31
130
|
## Contributing
|
32
131
|
|
33
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
132
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/riddler/riddler. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
34
133
|
|
35
134
|
## License
|
36
135
|
|
@@ -38,4 +137,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
38
137
|
|
39
138
|
## Code of Conduct
|
40
139
|
|
41
|
-
Everyone interacting in the Riddler project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
140
|
+
Everyone interacting in the Riddler project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/riddler/riddler/blob/master/CODE_OF_CONDUCT.md).
|
data/lib/riddler.rb
CHANGED
@@ -1,19 +1,68 @@
|
|
1
1
|
require "riddler/version"
|
2
2
|
|
3
|
+
require "faraday"
|
4
|
+
require "faraday_middleware"
|
3
5
|
require "liquid"
|
6
|
+
require "outlog"
|
7
|
+
require "predicator"
|
8
|
+
require "ulid"
|
4
9
|
|
10
|
+
require "riddler/includeable"
|
11
|
+
|
12
|
+
require "riddler/drops/hash_drop"
|
13
|
+
|
14
|
+
require "riddler/configuration"
|
15
|
+
|
16
|
+
require "riddler/context_builder"
|
17
|
+
require "riddler/context_builders/faraday_builder"
|
18
|
+
require "riddler/context_director"
|
5
19
|
require "riddler/context"
|
6
20
|
|
7
21
|
require "riddler/element"
|
8
|
-
require "riddler/elements/copy"
|
9
22
|
require "riddler/elements/heading"
|
23
|
+
require "riddler/elements/image"
|
24
|
+
require "riddler/elements/link"
|
25
|
+
require "riddler/elements/text"
|
26
|
+
require "riddler/elements/variant"
|
10
27
|
|
11
28
|
require "riddler/step"
|
12
29
|
require "riddler/steps/content"
|
30
|
+
require "riddler/steps/variant"
|
13
31
|
|
14
|
-
require "riddler/use_cases
|
32
|
+
require "riddler/use_cases"
|
15
33
|
|
16
34
|
module Riddler
|
17
35
|
class Error < StandardError; end
|
18
|
-
|
36
|
+
|
37
|
+
def self.configure
|
38
|
+
yield configuration
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.configuration
|
42
|
+
@configuration ||= ::Riddler::Configuration.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.config; configuration; end
|
46
|
+
|
47
|
+
def self.logger
|
48
|
+
@logger ||= ::Outlog.logger
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.render content_definition, context={}
|
52
|
+
unless context.kind_of? ::Riddler::Context
|
53
|
+
director = ::Riddler::ContextDirector.new context
|
54
|
+
context = director.context
|
55
|
+
end
|
56
|
+
|
57
|
+
case content_definition["content_type"]
|
58
|
+
when "element"
|
59
|
+
content = ::Riddler::Element.for content_definition, context
|
60
|
+
when "step"
|
61
|
+
content = ::Riddler::Step.for content_definition, context
|
62
|
+
end
|
63
|
+
|
64
|
+
return nil unless content.include?
|
65
|
+
|
66
|
+
content.to_hash
|
67
|
+
end
|
19
68
|
end
|
data/lib/riddler/context.rb
CHANGED
@@ -5,20 +5,58 @@ module Riddler
|
|
5
5
|
|
6
6
|
def initialize input = nil
|
7
7
|
input ||= {}
|
8
|
-
@variables = {
|
8
|
+
@variables = {
|
9
|
+
"ids" => {}
|
10
|
+
}
|
9
11
|
input.each do |key, value|
|
12
|
+
next if value.nil?
|
10
13
|
assign key, value
|
11
14
|
end
|
12
15
|
end
|
13
16
|
|
14
17
|
def assign name, value
|
15
|
-
variables[name.to_s] = value
|
18
|
+
variables[name.to_s] = drop_for value
|
19
|
+
end
|
20
|
+
|
21
|
+
def variable name
|
22
|
+
variables[name.to_s]
|
16
23
|
end
|
17
24
|
|
18
25
|
def render string
|
19
26
|
template = ::Liquid::Template.parse string
|
20
27
|
template.render variables
|
21
28
|
end
|
29
|
+
|
30
|
+
def add_id name, value
|
31
|
+
variables["ids"][name.to_s] = value
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_liquid
|
35
|
+
Liquid::Context.new [variables]
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_hash
|
39
|
+
hash_array = variables.map do |key, value|
|
40
|
+
[key, value.to_hash]
|
41
|
+
end
|
42
|
+
Hash[hash_array]
|
43
|
+
end
|
44
|
+
|
45
|
+
def method_missing method_name, *_args
|
46
|
+
return super unless variables.key? method_name.to_s
|
47
|
+
variable method_name
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def drop_for data
|
53
|
+
case data
|
54
|
+
when ::Liquid::Drop
|
55
|
+
data
|
56
|
+
else
|
57
|
+
::Riddler::Drops::HashDrop.new data
|
58
|
+
end
|
59
|
+
end
|
22
60
|
end
|
23
61
|
|
24
62
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Riddler
|
2
|
+
class ContextBuilder
|
3
|
+
attr_reader :context
|
4
|
+
|
5
|
+
def initialize context
|
6
|
+
@context = context
|
7
|
+
end
|
8
|
+
|
9
|
+
# Does the current context have the data available for this builder
|
10
|
+
# to function
|
11
|
+
def data_available?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
# Extract IDs from the context (params, headers, JWTs, etc) and store
|
16
|
+
# them in context.ids
|
17
|
+
def extract_ids
|
18
|
+
# no-op
|
19
|
+
end
|
20
|
+
|
21
|
+
# Inspect context for identifiers or data.
|
22
|
+
# Add any additional relevant information to the context
|
23
|
+
def process
|
24
|
+
# no-op
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# Adds the ID to the context and defines a method for the ID
|
30
|
+
def add_id name, value
|
31
|
+
context.add_id name, value
|
32
|
+
define_singleton_method(name) { value }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Riddler
|
2
|
+
module ContextBuilders
|
3
|
+
class FaradayBuilder < ::Riddler::ContextBuilder
|
4
|
+
def self.base_uri
|
5
|
+
raise "The Faraday builder must define a class .base_uri method"
|
6
|
+
end
|
7
|
+
|
8
|
+
def connection
|
9
|
+
@connection ||= build_connection
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def build_connection
|
15
|
+
Faraday.new url: self.class.base_uri do |conn|
|
16
|
+
conn.response :json, :content_type => /\bjson$/
|
17
|
+
conn.request :url_encoded
|
18
|
+
conn.adapter Faraday.default_adapter
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "browser/aliases"
|
2
|
+
Browser::Base.include Browser::Aliases
|
3
|
+
|
4
|
+
module Riddler
|
5
|
+
module ContextBuilders
|
6
|
+
|
7
|
+
class UserAgent < ::Riddler::ContextBuilder
|
8
|
+
# Look for the user_agent header and build a drop for that
|
9
|
+
def process
|
10
|
+
drop = UserAgentDrop.new context.headers.user_agent
|
11
|
+
context.assign "user_agent", drop
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class UserAgentDrop < ::Liquid::Drop
|
16
|
+
attr_reader :user_agent, :browser
|
17
|
+
|
18
|
+
def initialize user_agent
|
19
|
+
@user_agent = user_agent
|
20
|
+
@browser = Browser.new user_agent
|
21
|
+
end
|
22
|
+
|
23
|
+
def liquid_method_missing method
|
24
|
+
browser.public_send method
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing method, *_args
|
28
|
+
return super unless browser.respond_to? method
|
29
|
+
liquid_method_missing method
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Riddler
|
2
|
+
|
3
|
+
class ContextDirector
|
4
|
+
attr_reader :params, :headers
|
5
|
+
|
6
|
+
def initialize options={}
|
7
|
+
options = {} if options.nil?
|
8
|
+
@params = options[:params] || options["params"]
|
9
|
+
@headers = options[:headers] || options["headers"]
|
10
|
+
@ctx = ::Riddler::Context.new params: params, headers: headers
|
11
|
+
@ids_extracted = false
|
12
|
+
@builders_applied = false
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return the context with only IDs extracted
|
16
|
+
def simple_context
|
17
|
+
extract_ids
|
18
|
+
@ctx
|
19
|
+
end
|
20
|
+
|
21
|
+
# Create a new context and use registered builders to fill it in
|
22
|
+
def context
|
23
|
+
extract_ids
|
24
|
+
apply_builders
|
25
|
+
@ctx
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def extract_ids
|
31
|
+
return if @ids_extracted
|
32
|
+
|
33
|
+
builders.each do |builder|
|
34
|
+
unless builder.data_available?
|
35
|
+
::Riddler.logger.debug "data not available for builder", context_builder: builder.class.name
|
36
|
+
next
|
37
|
+
end
|
38
|
+
::Riddler.logger.debug "extracting ids", context_builder: builder.class.name
|
39
|
+
builder.extract_ids
|
40
|
+
end
|
41
|
+
|
42
|
+
@ids_extracted = true
|
43
|
+
end
|
44
|
+
|
45
|
+
def apply_builders
|
46
|
+
return if @builders_applied
|
47
|
+
|
48
|
+
builders.each do |builder|
|
49
|
+
unless builder.data_available?
|
50
|
+
::Riddler.logger.debug "data not available for builder", context_builder: builder.class.name
|
51
|
+
next
|
52
|
+
end
|
53
|
+
::Riddler.logger.debug "processing context builder", context_builder: builder.class.name
|
54
|
+
builder.process
|
55
|
+
end
|
56
|
+
|
57
|
+
@builders_applied = true
|
58
|
+
end
|
59
|
+
|
60
|
+
def builders
|
61
|
+
@builders ||= ::Riddler.configuration.context_builders.map do |builder_class|
|
62
|
+
builder_class.new @ctx
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|