calyx 0.11.3 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +49 -0
- data/README.md +61 -3
- data/examples/any_gradient.rb +60 -0
- data/examples/tiny_woodland_bot.rb +31 -0
- data/lib/calyx/grammar.rb +9 -6
- data/lib/calyx/production/choices.rb +2 -2
- data/lib/calyx/production/concat.rb +2 -2
- data/lib/calyx/production/expression.rb +2 -2
- data/lib/calyx/production/memo.rb +1 -1
- data/lib/calyx/production/non_terminal.rb +2 -2
- data/lib/calyx/production/terminal.rb +1 -1
- data/lib/calyx/production/weighted_choices.rb +4 -3
- data/lib/calyx/registry.rb +8 -7
- data/lib/calyx/version.rb +1 -1
- metadata +6 -4
- data/examples/tiny_woodland.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19046d53109e4dfe958d0e9e4fd3a3ff2d446109
|
4
|
+
data.tar.gz: 4a41b45d945bcb44ea7359db7398d0bdb6c366e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38527d602bf96335cef8c504612c0d4bfb1fbf17dd459420123ceeb2c088207921e0d68580ec1777cf822476ab4e3a935f8f2b02d2d7834642806a3593f4170c
|
7
|
+
data.tar.gz: e49e3d348caaec3290e6412317f31e29054f4dbecce4dbf35c264c2a952fc36c442e1adeb7af2d6444ca7429f82d59f1b2bf504f15dc6967641a383fa09ca18c
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributing to Calyx
|
2
|
+
|
3
|
+
Calyx is an open source project and contributions are welcome with the caveat that this is very much a personal creative project by Mark Rickerby, so keep that context in mind if you’re considering drastic code changes.
|
4
|
+
|
5
|
+
Changes that fix bugs and improve test coverage will generally be merged on-the-spot. Larger changes that introduce new features should harmonise with the vision, goals and style of the project. If in doubt, just ask in advance.
|
6
|
+
|
7
|
+
Should Calyx become more popular, these guidelines are likely to become more community-oriented, and I’ll look at handing project stewardship over to the community with a more formal project charter and structure.
|
8
|
+
|
9
|
+
## General Contributions
|
10
|
+
|
11
|
+
The best way to contribute is to use the Gem. Install it on a local project and try things out. Does it work? Does it do what you expect? Is there anything missing?
|
12
|
+
|
13
|
+
It’s really helpful to contribute to discussion on open issues, reporting bugs or suggest new features. Any feedback and criticism is received with gratitude.
|
14
|
+
|
15
|
+
## Code Contributions
|
16
|
+
|
17
|
+
### Submitting Changes
|
18
|
+
|
19
|
+
Changes to the source code and documentation should be submitted as a pull request on GitHub, corresponding to the following process:
|
20
|
+
|
21
|
+
- Fork the repo and make a new branch for your changes
|
22
|
+
- Submit your branch as a pull request against the master branch
|
23
|
+
|
24
|
+
If any aspects of your changes are ambiguous or unclear, use the pull request description to explain and provide further context (including code samples as necessary).
|
25
|
+
|
26
|
+
Please don’t bump the version as part of your changes (this happens separately).
|
27
|
+
|
28
|
+
### Pull Request Checklist
|
29
|
+
|
30
|
+
- Extraneous and trivial small commits should be squashed into larger descriptive commits
|
31
|
+
- Commits should include concise and clear messages using standard formatting conventions
|
32
|
+
- The test suite must be passing
|
33
|
+
- Newly introduced code branches should be covered by tests
|
34
|
+
-- Introduce new tests if existing tests don’t support your changes
|
35
|
+
- Changes to method signatures and class organisation should be annotated by doc comments
|
36
|
+
|
37
|
+
## Code of Conduct
|
38
|
+
|
39
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
40
|
+
|
41
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
42
|
+
|
43
|
+
Examples of unacceptable behaviour by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
44
|
+
|
45
|
+
The maintainer has the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct.
|
46
|
+
|
47
|
+
Instances of abusive, harassing, or otherwise unacceptable behaviour may be reported by opening an issue or contacting the maintainer.
|
48
|
+
|
49
|
+
*From the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/).*
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Calyx
|
2
2
|
|
3
3
|
[![Gem Version](http://img.shields.io/gem/v/calyx.svg)](https://rubygems.org/gems/calyx)
|
4
|
-
[![Build Status](https://
|
4
|
+
[![Build Status](https://img.shields.io/travis/maetl/calyx.svg)](https://travis-ci.org/maetl/calyx)
|
5
5
|
|
6
6
|
Calyx provides a simple API for generating text with declarative recursive grammars.
|
7
7
|
|
@@ -19,6 +19,60 @@ gem install calyx
|
|
19
19
|
gem 'calyx'
|
20
20
|
```
|
21
21
|
|
22
|
+
## Examples
|
23
|
+
|
24
|
+
The best way to get started quickly is to install the gem and run the examples locally.
|
25
|
+
|
26
|
+
## Any Gradient
|
27
|
+
|
28
|
+
Requires Roda and Rack to be available.
|
29
|
+
|
30
|
+
```
|
31
|
+
gem install roda
|
32
|
+
```
|
33
|
+
|
34
|
+
Demonstrates how to use Calyx to construct SVG graphics. **Any Gradient** generates a rectangle with a linear gradient of random colours.
|
35
|
+
|
36
|
+
Run as a web server and preview the output in a browser (`http://localhost:9292`):
|
37
|
+
|
38
|
+
```
|
39
|
+
ruby examples/any_gradient.rb
|
40
|
+
```
|
41
|
+
|
42
|
+
Or generate SVG files via a command line pipe:
|
43
|
+
|
44
|
+
```
|
45
|
+
ruby examples/any_gradient > gradient1.xml
|
46
|
+
```
|
47
|
+
|
48
|
+
## Tiny Woodland Bot
|
49
|
+
|
50
|
+
Requires the Twitter client gem and API access configured for a specific Twitter handle.
|
51
|
+
|
52
|
+
```
|
53
|
+
gem install twitter
|
54
|
+
```
|
55
|
+
|
56
|
+
Demonstrates how to use Calyx to make a minimal Twitter bot that periodically posts unique tweets. See [@tiny_woodland on Twitter](https://twitter.com/tiny_woodland) and the [writeup here](http://maetl.net/notes/storyboard/tiny-woodlands).
|
57
|
+
|
58
|
+
```
|
59
|
+
TWITTER_CONSUMER_KEY=XXX-XXX
|
60
|
+
TWITTER_CONSUMER_SECRET=XXX-XXX
|
61
|
+
TWITTER_ACCESS_TOKEN=XXX-XXX
|
62
|
+
TWITTER_CONSUMER_SECRET=XXX-XXX
|
63
|
+
ruby examples/tiny_woodland_bot.rb
|
64
|
+
```
|
65
|
+
|
66
|
+
## Faker
|
67
|
+
|
68
|
+
[Faker](https://github.com/stympy/faker) is a popular library for generating fake names and associated sample data like internet addresses, company names and locations.
|
69
|
+
|
70
|
+
This example demonstrates how to use Calyx to reproduce the same functionality using custom lists defined in a YAML configuration file.
|
71
|
+
|
72
|
+
```
|
73
|
+
ruby examples/faker.rb
|
74
|
+
```
|
75
|
+
|
22
76
|
## Usage
|
23
77
|
|
24
78
|
Require the library and inherit from `Calyx::Grammar` to construct a set of rules to generate a text.
|
@@ -231,11 +285,13 @@ Dot-notation is supported in template expressions, allowing you to call any avai
|
|
231
285
|
```ruby
|
232
286
|
class Greeting < Calyx::Grammar
|
233
287
|
start '{hello.capitalize} there.', 'Why, {hello} there.'
|
234
|
-
hello 'hello'
|
288
|
+
hello 'hello', 'hi'
|
235
289
|
end
|
236
290
|
|
237
291
|
# => "Hello there."
|
292
|
+
# => "Hi there."
|
238
293
|
# => "Why, hello there."
|
294
|
+
# => "Why, hi there."
|
239
295
|
```
|
240
296
|
|
241
297
|
You can also extend the grammar with custom modifiers that provide useful formatting functions.
|
@@ -251,11 +307,13 @@ class Greeting < Calyx::Grammar
|
|
251
307
|
end
|
252
308
|
|
253
309
|
start '{hello.shoutycaps} there.', 'Why, {hello} there.'
|
254
|
-
hello 'hello'
|
310
|
+
hello 'hello', 'hi'
|
255
311
|
end
|
256
312
|
|
257
313
|
# => "HELLO there."
|
314
|
+
# => "HI there."
|
258
315
|
# => "Why, HELLO there."
|
316
|
+
# => "Why, HI there."
|
259
317
|
```
|
260
318
|
|
261
319
|
### Mappings
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'roda'
|
2
|
+
require 'calyx'
|
3
|
+
|
4
|
+
class AnyGradient < Calyx::Grammar
|
5
|
+
html '<body style="margin:0; padding:0">{svg}</body>'
|
6
|
+
xml '<?xml version="1.0" encoding="utf-8"?>{svg}'
|
7
|
+
svg '
|
8
|
+
<svg width="100%"
|
9
|
+
height="100%"
|
10
|
+
version="1.1"
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
12
|
+
xmlns:xlink="http://www.w3.org/1999/xlink">
|
13
|
+
{defs}
|
14
|
+
{rect}
|
15
|
+
</svg>
|
16
|
+
'
|
17
|
+
defs '
|
18
|
+
<defs>
|
19
|
+
{linear_gradient}
|
20
|
+
</defs>
|
21
|
+
'
|
22
|
+
linear_gradient '
|
23
|
+
<linearGradient id="linearGradient-1">
|
24
|
+
{stop_fixed}
|
25
|
+
{stop_offset}
|
26
|
+
</linearGradient>
|
27
|
+
'
|
28
|
+
stop_fixed '<stop stop-color="{stop_color}" offset="0%"></stop>'
|
29
|
+
stop_offset '<stop stop-color="{stop_color}" offset="{offset}"></stop>'
|
30
|
+
stop_color '#{hex_num}{hex_num}{hex_num}'
|
31
|
+
hex_num '{hex_val}{hex_val}'
|
32
|
+
hex_val '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
33
|
+
offset '{tens}{ones}%'
|
34
|
+
tens 3,4,5,6
|
35
|
+
ones 1,2,3,4,5,6,7,8,9,0
|
36
|
+
rect '
|
37
|
+
<rect id="gradient"
|
38
|
+
stroke="none"
|
39
|
+
fill="url(#linearGradient-1)"
|
40
|
+
fill-rule="evenodd"
|
41
|
+
x="0"
|
42
|
+
y="0"
|
43
|
+
width="100%"
|
44
|
+
height="100%"></rect>
|
45
|
+
'
|
46
|
+
end
|
47
|
+
|
48
|
+
class Server < Roda
|
49
|
+
route do |r|
|
50
|
+
r.root do
|
51
|
+
AnyGradient.new.generate(:html)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if STDOUT.tty?
|
57
|
+
Rack::Server.start :app => Server
|
58
|
+
else
|
59
|
+
STDOUT.puts AnyGradient.new.generate(:xml)
|
60
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "calyx"
|
2
|
+
require "twitter"
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
tiny_woodland = Calyx::Grammar.new do
|
6
|
+
start :field
|
7
|
+
field (0..7).map { "{row}{br}" }.join
|
8
|
+
row (0..12).map { "{point}" }.join
|
9
|
+
point trees: 0.6, foliage: 0.35, flowers: 0.05
|
10
|
+
trees "🌲", "🌳"
|
11
|
+
foliage "🌿", "🌱"
|
12
|
+
flowers "🌷", "🌻", "🌼"
|
13
|
+
creatures "🐗", "🐻", "🐝", "🐦"
|
14
|
+
br "\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
twitter_client = Twitter::REST::Client.new do |config|
|
18
|
+
config.consumer_key = ENV["TWITTER_CONSUMER_KEY"]
|
19
|
+
config.consumer_secret = ENV["TWITTER_CONSUMER_SECRET"]
|
20
|
+
config.access_token = ENV["TWITTER_ACCESS_TOKEN"]
|
21
|
+
config.access_token_secret = ENV["TWITTER_CONSUMER_SECRET"]
|
22
|
+
end
|
23
|
+
|
24
|
+
logger = Logger.new(STDOUT)
|
25
|
+
|
26
|
+
begin
|
27
|
+
tweet = twitter_client.update(tiny_woodland.generate)
|
28
|
+
logger.info("Posted tweet: https://twitter.com/#{tweet.user.name}/status/#{tweet.id}")
|
29
|
+
rescue Exception => e
|
30
|
+
logger.error(e)
|
31
|
+
end
|
data/lib/calyx/grammar.rb
CHANGED
@@ -98,10 +98,13 @@ module Calyx
|
|
98
98
|
# Grammar rules can be constructed on the fly when the passed-in block is
|
99
99
|
# evaluated.
|
100
100
|
#
|
101
|
-
# @param [Number] seed
|
102
|
-
def initialize(seed=
|
103
|
-
|
104
|
-
|
101
|
+
# @param [Number, Random] seed
|
102
|
+
def initialize(seed=Random.new, &block)
|
103
|
+
if seed.is_a?(Numeric)
|
104
|
+
@rng = Random.new(seed)
|
105
|
+
else
|
106
|
+
@rng = seed
|
107
|
+
end
|
105
108
|
|
106
109
|
if block_given?
|
107
110
|
@registry = Registry.new
|
@@ -120,7 +123,7 @@ module Calyx
|
|
120
123
|
def generate(*args)
|
121
124
|
start_symbol, rules_map = map_default_args(*args)
|
122
125
|
|
123
|
-
@registry.evaluate(start_symbol, rules_map).flatten.reject do |obj|
|
126
|
+
@registry.evaluate(start_symbol, @rng, rules_map).flatten.reject do |obj|
|
124
127
|
obj.is_a?(Symbol)
|
125
128
|
end.join(''.freeze)
|
126
129
|
end
|
@@ -134,7 +137,7 @@ module Calyx
|
|
134
137
|
def evaluate(*args)
|
135
138
|
start_symbol, rules_map = map_default_args(*args)
|
136
139
|
|
137
|
-
@registry.evaluate(start_symbol, rules_map)
|
140
|
+
@registry.evaluate(start_symbol, @rng, rules_map)
|
138
141
|
end
|
139
142
|
|
140
143
|
private
|
@@ -35,8 +35,8 @@ module Calyx
|
|
35
35
|
# Evaluate the choice by randomly picking one of its possible options.
|
36
36
|
#
|
37
37
|
# @return [Array]
|
38
|
-
def evaluate
|
39
|
-
[:choice, @collection.sample.evaluate]
|
38
|
+
def evaluate(rng)
|
39
|
+
[:choice, @collection.sample(random: rng).evaluate(rng)]
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -18,8 +18,8 @@ module Calyx
|
|
18
18
|
# returning the transformed result.
|
19
19
|
#
|
20
20
|
# @return [Array]
|
21
|
-
def evaluate
|
22
|
-
terminal = @production.evaluate.flatten.reject { |o| o.is_a?(Symbol) }.join(''.freeze)
|
21
|
+
def evaluate(rng)
|
22
|
+
terminal = @production.evaluate(rng).flatten.reject { |o| o.is_a?(Symbol) }.join(''.freeze)
|
23
23
|
expression = @methods.reduce(terminal) do |value, method|
|
24
24
|
@registry.transform(method, value)
|
25
25
|
end
|
@@ -16,8 +16,8 @@ module Calyx
|
|
16
16
|
# Evaluate the non-terminal, using the registry to handle the expansion.
|
17
17
|
#
|
18
18
|
# @return [Array]
|
19
|
-
def evaluate
|
20
|
-
[@symbol, @registry.expand(@symbol).evaluate]
|
19
|
+
def evaluate(rng)
|
20
|
+
[@symbol, @registry.expand(@symbol).evaluate(rng)]
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -30,6 +30,7 @@ module Calyx
|
|
30
30
|
# Initialize a new choice with a list of child nodes.
|
31
31
|
#
|
32
32
|
# @param [Array] collection
|
33
|
+
# @param [Random] rng
|
33
34
|
def initialize(collection)
|
34
35
|
@collection = collection
|
35
36
|
end
|
@@ -41,12 +42,12 @@ module Calyx
|
|
41
42
|
# of code recommended in the Ruby standard library documentation.
|
42
43
|
#
|
43
44
|
# @return [Array]
|
44
|
-
def evaluate
|
45
|
+
def evaluate(rng)
|
45
46
|
choice = @collection.max_by do |_, weight|
|
46
|
-
rand ** (1.0 / weight)
|
47
|
+
rng.rand ** (1.0 / weight)
|
47
48
|
end.first
|
48
49
|
|
49
|
-
[:weighted_choice, choice.evaluate]
|
50
|
+
[:weighted_choice, choice.evaluate(rng)]
|
50
51
|
end
|
51
52
|
end
|
52
53
|
end
|
data/lib/calyx/registry.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Calyx
|
2
2
|
# Lookup table of all the available rules in the grammar.
|
3
3
|
class Registry
|
4
|
-
attr_reader :rules, :transforms, :modifiers
|
4
|
+
attr_reader :rng, :rules, :transforms, :modifiers
|
5
5
|
|
6
6
|
# Construct an empty registry.
|
7
7
|
def initialize
|
@@ -78,7 +78,7 @@ module Calyx
|
|
78
78
|
#
|
79
79
|
# @param [Symbol] symbol
|
80
80
|
def memoize_expansion(symbol)
|
81
|
-
memos[symbol] ||= expand(symbol).evaluate
|
81
|
+
memos[symbol] ||= expand(symbol).evaluate(@rng)
|
82
82
|
end
|
83
83
|
|
84
84
|
# Merges the given registry instance with the target registry.
|
@@ -99,8 +99,8 @@ module Calyx
|
|
99
99
|
# @param [Symbol] start_symbol
|
100
100
|
# @param [Hash] rules_map
|
101
101
|
# @return [Array]
|
102
|
-
def evaluate(start_symbol=:start, rules_map={})
|
103
|
-
reset_evaluation_context
|
102
|
+
def evaluate(start_symbol=:start, rng=Random.new, rules_map={})
|
103
|
+
reset_evaluation_context(rng)
|
104
104
|
|
105
105
|
rules_map.each do |key, value|
|
106
106
|
if rules.key?(key.to_sym)
|
@@ -117,7 +117,7 @@ module Calyx
|
|
117
117
|
expansion = expand(start_symbol)
|
118
118
|
|
119
119
|
if expansion.respond_to?(:evaluate)
|
120
|
-
[start_symbol, expansion.evaluate]
|
120
|
+
[start_symbol, expansion.evaluate(rng)]
|
121
121
|
else
|
122
122
|
raise Errors::MissingRule.new(start_symbol)
|
123
123
|
end
|
@@ -125,9 +125,10 @@ module Calyx
|
|
125
125
|
|
126
126
|
private
|
127
127
|
|
128
|
-
attr_reader :memos, :context
|
128
|
+
attr_reader :rng, :memos, :context
|
129
129
|
|
130
|
-
def reset_evaluation_context
|
130
|
+
def reset_evaluation_context(rng)
|
131
|
+
@rng = rng
|
131
132
|
@context = {}
|
132
133
|
@memos = {}
|
133
134
|
end
|
data/lib/calyx/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: calyx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Rickerby
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -62,13 +62,15 @@ extra_rdoc_files: []
|
|
62
62
|
files:
|
63
63
|
- ".gitignore"
|
64
64
|
- ".travis.yml"
|
65
|
+
- CONTRIBUTING.md
|
65
66
|
- Gemfile
|
66
67
|
- LICENSE
|
67
68
|
- README.md
|
68
69
|
- calyx.gemspec
|
70
|
+
- examples/any_gradient.rb
|
69
71
|
- examples/faker.rb
|
70
72
|
- examples/faker.yml
|
71
|
-
- examples/
|
73
|
+
- examples/tiny_woodland_bot.rb
|
72
74
|
- lib/calyx.rb
|
73
75
|
- lib/calyx/errors.rb
|
74
76
|
- lib/calyx/format.rb
|
@@ -103,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
105
|
version: '0'
|
104
106
|
requirements: []
|
105
107
|
rubyforge_project:
|
106
|
-
rubygems_version: 2.
|
108
|
+
rubygems_version: 2.5.1
|
107
109
|
signing_key:
|
108
110
|
specification_version: 4
|
109
111
|
summary: Generate text with declarative recursive grammars
|
data/examples/tiny_woodland.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
require "calyx"
|
2
|
-
|
3
|
-
tiny_woodland = Calyx::Grammar.new do
|
4
|
-
start :field
|
5
|
-
field (0..7).map { "{row}{br}" }.join
|
6
|
-
row (0..18).map { "{point}" }.join
|
7
|
-
point [:trees, 0.2], [:foliage, 0.25], [:flowers, 0.05], [:space, 0.5]
|
8
|
-
trees "🌲", "🌳"
|
9
|
-
foliage "🌿", "🌱"
|
10
|
-
flowers "🌷", "🌻", "🌼"
|
11
|
-
space " "
|
12
|
-
br "\n"
|
13
|
-
end
|
14
|
-
|
15
|
-
puts tiny_woodland.generate
|