expando 0.1.0
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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +8 -0
- data/README.md +2 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/bin/expando +82 -0
- data/bin/setup +7 -0
- data/circle.yml +8 -0
- data/expando.gemspec +31 -0
- data/lib/expando.rb +12 -0
- data/lib/expando/entity_updater.rb +60 -0
- data/lib/expando/expander.rb +59 -0
- data/lib/expando/intent_updater.rb +95 -0
- data/lib/expando/updater.rb +114 -0
- data/lib/expando/version.rb +3 -0
- metadata +192 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2d9e0e4d3a50bb75dea3ac215b6bedc116c5d21a
|
4
|
+
data.tar.gz: 2397629047792d35cc5171cc9808bb514e30157e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 816a5d9adc5f1a066b5385132276c892d192f15daf50d53b18040e4490b4d3f5ff595058a8d7115c71669af49c94b746c25004a0a67b9ec0f76be203205ca794
|
7
|
+
data.tar.gz: b7e3f739d3be12cad151a26da0106797c81a37d9e8e4d8967bcf06c0fac05642524b2adbd55331da29ed3993611ba22c21df2fe6621b5b40f229a2013380c14b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
expando
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.2.0
|
data/Gemfile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# gem 'api-ai-ruby', git: 'git@github.com:voxable-labs/api-ai-ruby.git'
|
4
|
+
# Uncomment the below when developing on api-ai-ruby locally
|
5
|
+
gem 'api-ai-ruby', :path => '../../api-ai-ruby'
|
6
|
+
|
7
|
+
# Specify your gem's dependencies in expando.gemspec
|
8
|
+
gemspec
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'expando'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start
|
data/bin/expando
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH << File.expand_path( File.dirname( __FILE__ ) + '/../lib' )
|
4
|
+
|
5
|
+
require 'expando'
|
6
|
+
require 'gli'
|
7
|
+
|
8
|
+
include GLI::App
|
9
|
+
|
10
|
+
version Expando::VERSION
|
11
|
+
|
12
|
+
program_desc 'A tool for managing files written in the Expando markup language.'
|
13
|
+
|
14
|
+
config_file File.join(ENV['HOME'],'.expando.rc.yaml')
|
15
|
+
|
16
|
+
flag :client_access_token, desc: 'Api.ai Client Access Token'
|
17
|
+
flag :developer_access_token, desc: 'Api.ai Developer Access Token'
|
18
|
+
flag :intents_path, desc: 'The path to the directory containing intent files'
|
19
|
+
flag :entities_path, desc: 'The path to the directory containing entity files'
|
20
|
+
|
21
|
+
# Make credentials globally accessible
|
22
|
+
pre do | global_options, command, options, args |
|
23
|
+
# TODO: Why are these being repeated in global_options?
|
24
|
+
global_options[:credentials] = {
|
25
|
+
client_access_token: global_options[:client_access_token],
|
26
|
+
developer_access_token: global_options[:developer_access_token]
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Update entities and/or intents'
|
31
|
+
long_desc <<-DESC
|
32
|
+
Update Expando's entities and intents on Api.ai based on the contents of the
|
33
|
+
files in the entities and intents directories, respectively.
|
34
|
+
DESC
|
35
|
+
command [ :update, :u ] do | c |
|
36
|
+
c.desc "Update Expando's entities"
|
37
|
+
c.long_desc <<-DESC
|
38
|
+
Updates the specified entities. The entity names can be specified after this
|
39
|
+
argument in a comma delimited list, and should match the file name of the
|
40
|
+
specific entity in the entities directory. If no entity name is specified,
|
41
|
+
all entities are updated.
|
42
|
+
DESC
|
43
|
+
c.arg 'entity, entity[, entity]*'
|
44
|
+
c.command :entities do | entities |
|
45
|
+
entities.action do | global_options, options, args |
|
46
|
+
# TODO: update all by default
|
47
|
+
args.each do | entity |
|
48
|
+
updater = Expando::EntityUpdater.new( entity,
|
49
|
+
client_keys: global_options[:credentials],
|
50
|
+
intents_path: global_options[:intents_path],
|
51
|
+
entities_path: global_options[:entities_path])
|
52
|
+
updater.update!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
c.desc "Update Api.ai intents"
|
58
|
+
c.long_desc <<-DESC
|
59
|
+
Updates the specified intents. The intent names can be specified after this
|
60
|
+
argument in a comma delimited list, and should match the file name of the
|
61
|
+
specific intent in the intents directory. If no intent name is specified,
|
62
|
+
all intents are updated.
|
63
|
+
DESC
|
64
|
+
c.arg 'intent, intent[, intent]*'
|
65
|
+
c.command :intents do | intents |
|
66
|
+
intents.action do | global_options, options, args |
|
67
|
+
# TODO: update all by default
|
68
|
+
args.each do | intent |
|
69
|
+
updater = Expando::IntentUpdater.new( intent,
|
70
|
+
client_keys: global_options[:credentials],
|
71
|
+
intents_path: global_options[:intents_path],
|
72
|
+
entities_path: global_options[:entities_path])
|
73
|
+
updater.update!
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# TODO: Default to both in sequence
|
79
|
+
#c.default_command :entities
|
80
|
+
end
|
81
|
+
|
82
|
+
exit run(ARGV)
|
data/bin/setup
ADDED
data/circle.yml
ADDED
data/expando.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'expando/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'expando'
|
8
|
+
spec.version = Expando::VERSION
|
9
|
+
spec.authors = ['Matt Buck']
|
10
|
+
spec.email = ['matt@voxable.io']
|
11
|
+
|
12
|
+
spec.summary = 'The Expando reference implementation.'
|
13
|
+
spec.description = 'A translation language for defining user utterance examples in conversational interfaces.'
|
14
|
+
spec.homepage = 'http://voxable.io'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = 'bin'
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.9'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
spec.add_development_dependency 'rspec', '~> 3.4.0'
|
24
|
+
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.2.2'
|
25
|
+
spec.add_development_dependency 'climate_control', '~> 0.0.3'
|
26
|
+
|
27
|
+
spec.add_runtime_dependency 'api-ai-ruby', '~> 1.1.0'
|
28
|
+
spec.add_runtime_dependency 'gli', '~> 2.13.4'
|
29
|
+
spec.add_runtime_dependency 'colorize', '~> 0.7.7'
|
30
|
+
spec.add_runtime_dependency 'awesome_print', '~> 1.6.1'
|
31
|
+
end
|
data/lib/expando.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'expando/version'
|
2
|
+
require 'expando/updater'
|
3
|
+
require 'expando/entity_updater'
|
4
|
+
require 'expando/intent_updater'
|
5
|
+
require 'expando/expander'
|
6
|
+
require 'api-ai-ruby'
|
7
|
+
require 'colorize'
|
8
|
+
require 'awesome_print'
|
9
|
+
|
10
|
+
module Expando
|
11
|
+
# Your code goes here...
|
12
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Expando
|
2
|
+
# Responsible for updating entity objects on Api.ai based on the contents of
|
3
|
+
# files in `/entities`.
|
4
|
+
class EntityUpdater < Updater
|
5
|
+
# !@attribute name
|
6
|
+
# @return [String] the name of the entity to be updated
|
7
|
+
attr_accessor :name
|
8
|
+
|
9
|
+
# !@attribute entities_path
|
10
|
+
# @return [String] the path to the directory containing the entities text files
|
11
|
+
attr_accessor :entities_path
|
12
|
+
|
13
|
+
# Initialize a new `EntityUpdater`.
|
14
|
+
#
|
15
|
+
# @see Updater#initialize
|
16
|
+
def initialize( * )
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
# Update the named entity on Api.ai.
|
21
|
+
#
|
22
|
+
# @return [Hash] if request successful. This is the response body.
|
23
|
+
# @return [ApiAiRuby::RequestError] if request is in error.
|
24
|
+
def update!
|
25
|
+
entity = [{ name: @name.to_s, entries: expanded_entries }]
|
26
|
+
|
27
|
+
response = @client.update_entities_request( entity )
|
28
|
+
|
29
|
+
handle_response( response, :entity )
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# @return [Array<String>] The expanded list of entries and their synonyms.
|
35
|
+
def expanded_entries
|
36
|
+
sorted_entries.inject( [] ) do | entries, ( entry_value, synonyms ) |
|
37
|
+
entries << { value: entry_value, synonyms: [ entry_value ] + synonyms }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Hash] Properly sorted entries. Each key is the entry's name, and
|
42
|
+
# the value is the list of synonyms for that entry.
|
43
|
+
def sorted_entries
|
44
|
+
sorted = Hash.new( [] )
|
45
|
+
|
46
|
+
expanded_entities.each do | line |
|
47
|
+
entry_value, *synonyms = *line.split(',').collect{ |s| s.strip }
|
48
|
+
sorted[ entry_value ] = sorted[ entry_value ] + synonyms
|
49
|
+
end
|
50
|
+
|
51
|
+
sorted
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Array<String>] The expanded list of entities.
|
55
|
+
def expanded_entities
|
56
|
+
entity_file_path = File.join( @entities_path, @name.to_s + '.txt')
|
57
|
+
Expander.expand! file_lines( entity_file_path )
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# The `Expander` module is responsible for taking an array of intents or utterances,
|
2
|
+
# and expanding any strings containing the following tokens:
|
3
|
+
#
|
4
|
+
# {I|we} heard you {love|hate} computers
|
5
|
+
#
|
6
|
+
# into the following Cartesian product:
|
7
|
+
#
|
8
|
+
# I heard you love computers
|
9
|
+
# I heard you hate computers
|
10
|
+
# we heard you love computers
|
11
|
+
# we heard you hate computers
|
12
|
+
#
|
13
|
+
# This greatly reduces the complexity of creating performant speech interfaces.
|
14
|
+
module Expando
|
15
|
+
module Expander
|
16
|
+
TOKEN_REGEX = /(?<!\\)\((.*?)\)/
|
17
|
+
|
18
|
+
module_function
|
19
|
+
|
20
|
+
# Generate a new `Expander`.
|
21
|
+
#
|
22
|
+
# @param [Array<String>] lines The text to scan and expand.
|
23
|
+
# @return [Array] The expanded text.
|
24
|
+
def expand!( lines )
|
25
|
+
expanded_lines = []
|
26
|
+
|
27
|
+
lines.each do |line|
|
28
|
+
tokens = line.scan TOKEN_REGEX
|
29
|
+
|
30
|
+
# Don't perform expansion if no tokens are present.
|
31
|
+
if tokens.empty?
|
32
|
+
expanded_lines << line
|
33
|
+
next
|
34
|
+
end
|
35
|
+
|
36
|
+
expanded_tokens = []
|
37
|
+
tokens.each_with_index do |token, index|
|
38
|
+
expanded_tokens[index] = token[0].split( '|' )
|
39
|
+
end
|
40
|
+
|
41
|
+
# Produce Cartesian product of all tokenized values.
|
42
|
+
token_product = expanded_tokens[ 0 ].product( *expanded_tokens[ 1..-1 ] )
|
43
|
+
|
44
|
+
# Generate new expanded lines.
|
45
|
+
token_product.each do |replacement_values|
|
46
|
+
expanded_line = line
|
47
|
+
|
48
|
+
replacement_values.each do |value|
|
49
|
+
expanded_line = expanded_line.sub( /\{(.*?)\}/, value )
|
50
|
+
end
|
51
|
+
|
52
|
+
expanded_lines << expanded_line
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
expanded_lines
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Expando
|
4
|
+
# Responsible for updating intent objects on Api.ai based on the contents of
|
5
|
+
# files in `/intents`.
|
6
|
+
class IntentUpdater < Updater
|
7
|
+
# !@attribute name
|
8
|
+
# @return [String] the name of the intent to be updated
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
# !@attribute intents_path
|
12
|
+
# @return [String] the path to the directory containing the intent source files
|
13
|
+
attr_accessor :intents_path
|
14
|
+
|
15
|
+
# Initialize a new `IntentUpdater`.
|
16
|
+
#
|
17
|
+
# @see Updater#initialize
|
18
|
+
def initialize( * )
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
# Update the named entity on Api.ai.
|
23
|
+
#
|
24
|
+
# @return [Hash] if request successful. This is the response body.
|
25
|
+
# @return [ApiAiRuby::RequestError] if request is in error.
|
26
|
+
def update!
|
27
|
+
response = @client.update_intent_request( intent_json )
|
28
|
+
|
29
|
+
handle_response( response, :intent )
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Construct a proper JSON object for updating the intent, based on its current
|
35
|
+
# state plus the expanded templates
|
36
|
+
#
|
37
|
+
# @return [Hash] The constructed JSON of for the intent.
|
38
|
+
def intent_json
|
39
|
+
@id = intent_id
|
40
|
+
|
41
|
+
log_message "Fetching latest version of #{ @name } intent"
|
42
|
+
json = @client.get_intent_request( @id )
|
43
|
+
|
44
|
+
json[ :userSays ] = expanded_utterances
|
45
|
+
|
46
|
+
# TODO: Make this a separate, tested method
|
47
|
+
responses = File.readlines File.join( @intents_path, '..', 'responses', @name.to_s + '.txt' )
|
48
|
+
responses = responses.collect { |response| response.chomp }
|
49
|
+
|
50
|
+
responsesJson = json[ :responses ]
|
51
|
+
responsesJson[ 0 ][ :speech ] = responses
|
52
|
+
json[:responses] = responsesJson
|
53
|
+
|
54
|
+
# Clean up portions of the JSON response that we don't need in the request
|
55
|
+
%w{templates state priority webhookUsed}.each { |key| json.delete( key.to_sym ) }
|
56
|
+
|
57
|
+
json
|
58
|
+
end
|
59
|
+
|
60
|
+
# Fetch the ID of the intent with this name on Api.ai.
|
61
|
+
#
|
62
|
+
# @return [String] The ID of the intent with this `@name` on Api.ai.
|
63
|
+
def intent_id
|
64
|
+
log_message "Fetching id of #{ @name } intent"
|
65
|
+
intents = @client.get_intents_request
|
66
|
+
|
67
|
+
matching_intent = intents.select { |intent| intent[ :name ] == @name.to_s }
|
68
|
+
|
69
|
+
if matching_intent.empty?
|
70
|
+
# TODO: Consult Exceptional Ruby for a better way to do this
|
71
|
+
raise "There is no intent named #{@name}"
|
72
|
+
else
|
73
|
+
return matching_intent.first[ :id ]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Array<String>] The expanded list of intent utterances.
|
78
|
+
def expanded_utterances
|
79
|
+
intent_utterance_file_path = File.join( @intents_path, @name.to_s + '.txt')
|
80
|
+
# TODO: Test
|
81
|
+
utterances = Expander.expand! file_lines( intent_utterance_file_path )
|
82
|
+
|
83
|
+
utterances.collect do |utterance|
|
84
|
+
{
|
85
|
+
data: [
|
86
|
+
text: utterance
|
87
|
+
],
|
88
|
+
# TODO: Make this an aption
|
89
|
+
isTemplate: false
|
90
|
+
}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Expando
|
2
|
+
class Updater
|
3
|
+
# The default location of intent source files
|
4
|
+
DEFAULT_INTENTS_PATH = File.join( Dir.getwd, '../../intents')
|
5
|
+
|
6
|
+
# The default location of entity source files
|
7
|
+
DEFAULT_ENTITIES_PATH = File.join( Dir.getwd, '../../entities' )
|
8
|
+
|
9
|
+
# Initialize a new `Updater`.
|
10
|
+
#
|
11
|
+
# @param [Symbol] name The name of the intent or entity to update. (default: `nil`)
|
12
|
+
# @param [String] entities_path The path to the directory containing the
|
13
|
+
# entities text files. (default: `'entities'`)
|
14
|
+
# @param [String] intents_path The path to the directory containing the
|
15
|
+
# intents source files. (default: 'intents')
|
16
|
+
# @param [Hash] client_keys A hash of Api.ai credentials.
|
17
|
+
# @option client_keys [String] :developer_access_token The Api.ai developer
|
18
|
+
# access token.
|
19
|
+
# @option client_keys [String] :client_access_token The Api.ai client access
|
20
|
+
# token.
|
21
|
+
# @return [Updater] The new `Updater`.
|
22
|
+
def initialize( name = nil, intents_path: DEFAULT_INTENTS_PATH, entities_path: DEFAULT_ENTITIES_PATH, client_keys: {})
|
23
|
+
@name = name
|
24
|
+
@intents_path = intents_path
|
25
|
+
@entities_path = entities_path
|
26
|
+
|
27
|
+
@client = ApiAiRuby::Client.new( credentials( client_keys ) )
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Generate a credentials hash for Api.ai from environment variables or passed
|
33
|
+
# arguments, whichever is provided.
|
34
|
+
#
|
35
|
+
# @param [Hash] client_keys A hash of Api.ai credentials.
|
36
|
+
# @option client_keys [String] :developer_access_token The Api.ai developer
|
37
|
+
# access token.
|
38
|
+
# @option client_keys [String] :client_access_token The Api.ai client access
|
39
|
+
# token.
|
40
|
+
# @return [Hash] The Api.ai client credentials.
|
41
|
+
def credentials(client_keys)
|
42
|
+
developer_access_token = ENV['API_AI_DEVELOPER_ACCESS_TOKEN'] || client_keys[:developer_access_token]
|
43
|
+
client_access_token = ENV['API_AI_CLIENT_ACCESS_TOKEN'] || client_keys[:client_access_token]
|
44
|
+
|
45
|
+
{
|
46
|
+
client_access_token: client_access_token,
|
47
|
+
developer_access_token: developer_access_token,
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Properly handle the response from Api.ai.
|
52
|
+
#
|
53
|
+
# @param [Hash] response The response from `ApiAiRuby::Client`.
|
54
|
+
# @param [Symbol] type Either `:intent` or `:entity`, depending on what is
|
55
|
+
# being updated.
|
56
|
+
# @return [void]
|
57
|
+
def handle_response( response, type )
|
58
|
+
begin
|
59
|
+
if successful?( response )
|
60
|
+
log_completion_message(type )
|
61
|
+
else
|
62
|
+
puts failed_update_message(type )
|
63
|
+
ap response
|
64
|
+
end
|
65
|
+
rescue Exception => e
|
66
|
+
puts e.message
|
67
|
+
puts e.backtrace.inspect
|
68
|
+
|
69
|
+
abort( failed_update_message )
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Determine if the query was successful.
|
74
|
+
#
|
75
|
+
# @param [Hash] response The raw response from Api.ai
|
76
|
+
# @return [Boolean] `true` if successful, `false` otherwise.
|
77
|
+
def successful?( response )
|
78
|
+
response && response[ :status ] && ( response[ :status ][ :code ] == 200 )
|
79
|
+
end
|
80
|
+
|
81
|
+
# Generate a failed entity update message.
|
82
|
+
#
|
83
|
+
# @param [Symbol] type The type of update (`:entity` or `:intent`).
|
84
|
+
# @return [String] The failed update message.
|
85
|
+
def failed_update_message( type )
|
86
|
+
'• '.colorize( :blue ) + "#{ @name } #{ type.to_s } update failed:".colorize(:red )
|
87
|
+
end
|
88
|
+
|
89
|
+
# Output a log message.
|
90
|
+
#
|
91
|
+
# @param [String] The message.
|
92
|
+
# @return [void]
|
93
|
+
def log_message( message )
|
94
|
+
puts '• '.colorize( :blue ) + message
|
95
|
+
end
|
96
|
+
|
97
|
+
# Output a successful update message.
|
98
|
+
#
|
99
|
+
# @param [Symbol] type The type of update (`:entity` or `:intent`).
|
100
|
+
# @return [void]
|
101
|
+
def log_completion_message( type )
|
102
|
+
puts "• ".colorize( :blue ) + "#{ @name } #{ type.to_s } successfully updated!".colorize( :green )
|
103
|
+
puts "\nExpando:".colorize( :magenta ) + " Api.ai agent updated."
|
104
|
+
end
|
105
|
+
|
106
|
+
# Read a file into an array of strings.
|
107
|
+
#
|
108
|
+
# @param [String] file_path The path to the file to convert.
|
109
|
+
# @return [Array<String>] An array of all of the lines in the file.
|
110
|
+
def file_lines( file_path )
|
111
|
+
File.read( file_path ).lines.collect{ |line| line.chomp }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
metadata
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: expando
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matt Buck
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.9'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.4.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.4.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec_junit_formatter
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.2.2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.2.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: climate_control
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.0.3
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.0.3
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: api-ai-ruby
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.1.0
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.1.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: gli
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 2.13.4
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.13.4
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: colorize
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.7.7
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.7.7
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: awesome_print
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.6.1
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.6.1
|
139
|
+
description: A translation language for defining user utterance examples in conversational
|
140
|
+
interfaces.
|
141
|
+
email:
|
142
|
+
- matt@voxable.io
|
143
|
+
executables:
|
144
|
+
- console
|
145
|
+
- expando
|
146
|
+
- setup
|
147
|
+
extensions: []
|
148
|
+
extra_rdoc_files: []
|
149
|
+
files:
|
150
|
+
- ".gitignore"
|
151
|
+
- ".rspec"
|
152
|
+
- ".ruby-gemset"
|
153
|
+
- ".ruby-version"
|
154
|
+
- Gemfile
|
155
|
+
- README.md
|
156
|
+
- Rakefile
|
157
|
+
- bin/console
|
158
|
+
- bin/expando
|
159
|
+
- bin/setup
|
160
|
+
- circle.yml
|
161
|
+
- expando.gemspec
|
162
|
+
- lib/expando.rb
|
163
|
+
- lib/expando/entity_updater.rb
|
164
|
+
- lib/expando/expander.rb
|
165
|
+
- lib/expando/intent_updater.rb
|
166
|
+
- lib/expando/updater.rb
|
167
|
+
- lib/expando/version.rb
|
168
|
+
homepage: http://voxable.io
|
169
|
+
licenses: []
|
170
|
+
metadata: {}
|
171
|
+
post_install_message:
|
172
|
+
rdoc_options: []
|
173
|
+
require_paths:
|
174
|
+
- lib
|
175
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
requirements: []
|
186
|
+
rubyforge_project:
|
187
|
+
rubygems_version: 2.4.6
|
188
|
+
signing_key:
|
189
|
+
specification_version: 4
|
190
|
+
summary: The Expando reference implementation.
|
191
|
+
test_files: []
|
192
|
+
has_rdoc:
|