ask_awesomely 0.1.2

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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +8 -0
  6. data/Gemfile +4 -0
  7. data/README.md +474 -0
  8. data/Rakefile +11 -0
  9. data/ask_awesomely.gemspec +27 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +7 -0
  12. data/lib/ask_awesomely.rb +31 -0
  13. data/lib/ask_awesomely/api_client.rb +56 -0
  14. data/lib/ask_awesomely/choice.rb +10 -0
  15. data/lib/ask_awesomely/configuration.rb +30 -0
  16. data/lib/ask_awesomely/dsl.rb +50 -0
  17. data/lib/ask_awesomely/embeddable.rb +30 -0
  18. data/lib/ask_awesomely/embeds/drawer.erb +3 -0
  19. data/lib/ask_awesomely/embeds/fullscreen.erb +28 -0
  20. data/lib/ask_awesomely/embeds/modal.erb +3 -0
  21. data/lib/ask_awesomely/embeds/widget.erb +3 -0
  22. data/lib/ask_awesomely/field.rb +18 -0
  23. data/lib/ask_awesomely/field/dropdown.rb +18 -0
  24. data/lib/ask_awesomely/field/email.rb +5 -0
  25. data/lib/ask_awesomely/field/field.rb +68 -0
  26. data/lib/ask_awesomely/field/legal.rb +4 -0
  27. data/lib/ask_awesomely/field/long_text.rb +9 -0
  28. data/lib/ask_awesomely/field/multiple_choice.rb +30 -0
  29. data/lib/ask_awesomely/field/number.rb +22 -0
  30. data/lib/ask_awesomely/field/opinion_scale.rb +40 -0
  31. data/lib/ask_awesomely/field/picture_choice.rb +34 -0
  32. data/lib/ask_awesomely/field/rating.rb +31 -0
  33. data/lib/ask_awesomely/field/short_text.rb +9 -0
  34. data/lib/ask_awesomely/field/statement.rb +21 -0
  35. data/lib/ask_awesomely/field/website.rb +4 -0
  36. data/lib/ask_awesomely/field/yes_no.rb +4 -0
  37. data/lib/ask_awesomely/picture.rb +41 -0
  38. data/lib/ask_awesomely/s3.rb +51 -0
  39. data/lib/ask_awesomely/typeform.rb +43 -0
  40. data/lib/ask_awesomely/version.rb +3 -0
  41. metadata +166 -0
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "lib" << "spec"
6
+ t.test_files = FileList["spec/**/*_spec.rb"]
7
+ t.verbose = true
8
+ end
9
+
10
+ task default: :test
11
+
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ask_awesomely/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ask_awesomely"
8
+ spec.version = AskAwesomely::VERSION
9
+ spec.authors = ["Lee Machin"]
10
+ spec.email = ["me@mrl.ee"]
11
+
12
+ spec.summary = "Create Typeforms on the fly, the Typeform way."
13
+ spec.homepage = "https://github.com/leemachin/ask_awesomely"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "aws-sdk", "~> 2.0"
21
+ spec.add_dependency "typhoeus", "~> 0.8"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.9"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "minitest", "~> 5.8"
26
+ spec.add_development_dependency "vcr", "~> 2.9"
27
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ask_awesomely"
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/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,31 @@
1
+ require "ask_awesomely/version"
2
+ require "ask_awesomely/configuration"
3
+ require "ask_awesomely/dsl"
4
+ require "ask_awesomely/field"
5
+ require "ask_awesomely/choice"
6
+ require "ask_awesomely/picture"
7
+ require "ask_awesomely/s3"
8
+ require "ask_awesomely/api_client"
9
+ require "ask_awesomely/typeform"
10
+ require "ask_awesomely/embeddable"
11
+
12
+ require "erb"
13
+ require "json"
14
+ require "aws-sdk"
15
+ require "typhoeus"
16
+
17
+ module AskAwesomely
18
+
19
+ ConfigurationError = Class.new(ArgumentError)
20
+ FieldTypeError = Class.new(ArgumentError)
21
+ EmbedTypeError = Class.new(ArgumentError)
22
+
23
+ def self.configuration
24
+ @configuration ||= Configuration.new
25
+ end
26
+
27
+ def self.configure(&block)
28
+ block.call(self.configuration)
29
+ end
30
+
31
+ end
@@ -0,0 +1,56 @@
1
+ module AskAwesomely
2
+ class ApiClient
3
+
4
+ USER_AGENT = "leemachin/ask_awesomely; (v#{AskAwesomely::VERSION})"
5
+ BASE_URL = "https://api.typeform.io/latest"
6
+ ENDPOINTS = {
7
+ root: "/",
8
+ create_typeform: "/forms"
9
+ }
10
+
11
+ def initialize
12
+ Typhoeus::Config.user_agent = USER_AGENT
13
+ end
14
+
15
+ def get_info
16
+ response = request.get(
17
+ url_for(:root),
18
+ headers: headers
19
+ )
20
+
21
+ JSON.parse(response.body)
22
+ end
23
+
24
+ def submit_typeform(typeform)
25
+ response = request.post(
26
+ url_for(:create_typeform),
27
+ headers: headers,
28
+ body: typeform.to_json
29
+ )
30
+
31
+ typeform.tap do |tf|
32
+ body = JSON.parse(response.body)
33
+ tf.update_with_api_response(body)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def request
40
+ Typhoeus::Request
41
+ end
42
+
43
+ def url_for(endpoint)
44
+ BASE_URL + ENDPOINTS[endpoint]
45
+ end
46
+
47
+ def headers(others = {})
48
+ others.merge({
49
+ "X-API-TOKEN" => AskAwesomely.configuration.typeform_api_key,
50
+ "Accept" => "application/json",
51
+ "Content-Type" => "application/json"
52
+ })
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,10 @@
1
+ module AskAwesomely
2
+ class Choice
3
+
4
+ def initialize(label:, picture: nil)
5
+ @label = label
6
+ @picture = picture && Picture.new(picture)
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ module AskAwesomely
2
+ class Configuration
3
+
4
+ DEFAULT_AWS_BUCKET = "typeform_io_images"
5
+ DEFAULT_AWS_REGION = "us-east-1"
6
+
7
+ attr_accessor :typeform_api_key,
8
+ :aws_access_key_id,
9
+ :aws_access_key_secret,
10
+ :aws_region,
11
+ :aws_bucket
12
+
13
+ def typeform_api_key
14
+ unless @typeform_api_key
15
+ raise ConfigurationError, "Typeform I/O API Key must be set before use, get one from <https://typeform.io>."
16
+ end
17
+
18
+ @typeform_api_key
19
+ end
20
+
21
+ def aws_region
22
+ @aws_region || DEFAULT_AWS_REGION
23
+ end
24
+
25
+ def aws_bucket
26
+ @aws_bucket || DEFAULT_AWS_BUCKET
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,50 @@
1
+ module AskAwesomely
2
+ module DSL
3
+
4
+ def self.included(recv)
5
+ recv.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def _api_client
10
+ @api_client = ApiClient.new
11
+ end
12
+
13
+ def _state
14
+ @state ||= OpenStruct.new(
15
+ title: "",
16
+ fields: []
17
+ )
18
+ end
19
+
20
+ def build(context = nil)
21
+ structure = new(context)
22
+ typeform = Typeform.new(structure)
23
+ _api_client.submit_typeform(typeform)
24
+ end
25
+
26
+ def title(title)
27
+ _state.title = title
28
+ end
29
+
30
+ def tags(*tags)
31
+ _state.tags = tags
32
+ end
33
+
34
+ def field(type, &block)
35
+ _state.fields << Field::Field.of_type(type, &block)
36
+ end
37
+ end
38
+
39
+ attr_reader :context, :json
40
+
41
+ private def initialize(context = nil)
42
+ @context = context
43
+ end
44
+
45
+ def to_json
46
+ self.class._state.to_h.to_json
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,30 @@
1
+ module AskAwesomely
2
+ class Embeddable
3
+
4
+ EMBED_TYPES = %i(
5
+ modal
6
+ drawer
7
+ widget
8
+ fullscreen
9
+ )
10
+
11
+ def initialize(embed_type)
12
+ unless EMBED_TYPES.include?(embed_type)
13
+ raise AskAwesomely::EmbedTypeError, "embed type must be one of: #{EMBED_TYPES.join(', ')}"
14
+ end
15
+
16
+ @embed_type = embed_type
17
+ end
18
+
19
+ def render(typeform)
20
+ ERB.new(template, 0, '>').result(typeform.get_binding)
21
+ end
22
+
23
+ private
24
+
25
+ def template
26
+ Pathname.new(__dir__).join("embeds", "#{@embed_type}.erb").read
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ <a class="typeform-share link" href="<%= public_url %>" data-mode="2" target="_blank">Launch me!</a>
2
+
3
+ <script>(function(){var qs,js,q,s,d=document,gi=d.getElementById,ce=d.createElement,gt=d.getElementsByTagName,id='typef_orm',b='https://s3-eu-west-1.amazonaws.com/share.typeform.com/';if(!gi.call(d,id)){js=ce.call(d,'script');js.id=id;js.src=b+'share.js';q=gt.call(d,'script')[0];q.parentNode.insertBefore(js,q)}})()</script>
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2
+ <html>
3
+ <head>
4
+ <!--Add the title of your typeform below-->
5
+ <title><%= title %></title>
6
+
7
+ <!--CSS styles that ensure your typeform takes up all the available screen space (DO NOT EDIT!)-->
8
+ <style type="text/css">
9
+ html{
10
+ margin: 0;
11
+ height: 100%;
12
+ overflow: hidden;
13
+ }
14
+ iframe{
15
+ position: absolute;
16
+ left:0;
17
+ right:0;
18
+ bottom:0;
19
+ top:0;
20
+ border:0;
21
+ }
22
+ </style>
23
+ </head>
24
+ <body>
25
+ <iframe id="typeform-full" width="100%" height="100%" frameborder="0" src="<%= public_url %>"></iframe>
26
+ <script type="text/javascript" src="https://s3-eu-west-1.amazonaws.com/share.typeform.com/embed.js"></script>
27
+ </body>
28
+ </html>
@@ -0,0 +1,3 @@
1
+ <a class="typeform-share link" href="<%= public_url %>" data-mode="1" target="_blank">Launch me!</a>
2
+
3
+ <script>(function(){var qs,js,q,s,d=document,gi=d.getElementById,ce=d.createElement,gt=d.getElementsByTagName,id='typef_orm',b='https://s3-eu-west-1.amazonaws.com/share.typeform.com/';if(!gi.call(d,id)){js=ce.call(d,'script');js.id=id;js.src=b+'share.js';q=gt.call(d,'script')[0];q.parentNode.insertBefore(js,q)}})()</script>
@@ -0,0 +1,3 @@
1
+ <div class="typeform-widget" data-url="<%= public_url %>" data-text="<%= title %>" style="width:100%;height:500px;"></div>
2
+
3
+ <script>(function(){var qs,js,q,s,d=document,gi=d.getElementById,ce=d.createElement,gt=d.getElementsByTagName,id='typef_orm',b='https://s3-eu-west-1.amazonaws.com/share.typeform.com/';if(!gi.call(d,id)){js=ce.call(d,'script');js.id=id;js.src=b+'widget.js';q=gt.call(d,'script')[0];q.parentNode.insertBefore(js,q)}})()</script>
@@ -0,0 +1,18 @@
1
+ module AskAwesomely
2
+ module Field
3
+ require "ask_awesomely/field/field"
4
+ require "ask_awesomely/field/short_text"
5
+ require "ask_awesomely/field/long_text"
6
+ require "ask_awesomely/field/multiple_choice"
7
+ require "ask_awesomely/field/picture_choice"
8
+ require "ask_awesomely/field/statement"
9
+ require "ask_awesomely/field/number"
10
+ require "ask_awesomely/field/dropdown"
11
+ require "ask_awesomely/field/yes_no"
12
+ require "ask_awesomely/field/rating"
13
+ require "ask_awesomely/field/opinion_scale"
14
+ require "ask_awesomely/field/email"
15
+ require "ask_awesomely/field/website"
16
+ require "ask_awesomely/field/legal"
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module AskAwesomely
2
+ class Field::Dropdown < Field::Field
3
+
4
+ def initialize(*)
5
+ super
6
+ @state.choices = []
7
+ end
8
+
9
+ def choice(label)
10
+ @state.choices << Choice.new(label: label)
11
+ end
12
+
13
+ def in_alphabetical_order
14
+ @state.alphabetical_order = true
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ module AskAwesomely
2
+ class Field::Email < Field::Field
3
+
4
+ end
5
+ end
@@ -0,0 +1,68 @@
1
+ module AskAwesomely
2
+ class Field::Field
3
+
4
+ VALID_FIELD_TYPES = %i(
5
+ short_text
6
+ long_text
7
+ multiple_choice
8
+ picture_choice
9
+ statement
10
+ dropdown
11
+ yes_no
12
+ number
13
+ rating
14
+ opinion_scale
15
+ email
16
+ website
17
+ legal
18
+ ).freeze
19
+
20
+ attr_reader :state
21
+
22
+ # todo: make this easier to maintain
23
+ def self.of_type(type, &block)
24
+ field = case type
25
+ when :short_text then Field::ShortText
26
+ when :long_text then Field::LongText
27
+ when :multiple_choice then Field::MultipleChoice
28
+ when :picture_choice then Field::PictureChoice
29
+ when :statement then Field::Statement
30
+ when :dropdown then Field::Dropdown
31
+ when :yes_no then Field::YesNo
32
+ when :number then Field::Number
33
+ when :rating then Field::Rating
34
+ when :opinion_scale then Field::OpinionScale
35
+ when :email then Field::Email
36
+ when :website then Field::Website
37
+ when :legal then Field::Legal
38
+ else
39
+ raise FieldTypeError, "This type of field does not exist, please use one of: #{VALID_FIELD_TYPES.join(", ")}"
40
+ end
41
+
42
+ field.new(type, &block)
43
+ end
44
+
45
+ # Allow init with properties common to *all* fields
46
+ def initialize(type, &block)
47
+ @state = OpenStruct.new(type: type)
48
+
49
+ self.instance_eval(&block) if block_given?
50
+ end
51
+
52
+ def ask(question)
53
+ @state.question = question
54
+ end
55
+
56
+ def required
57
+ @state.required = true
58
+ end
59
+
60
+ def tags(*tags)
61
+ @state.tags = tags
62
+ end
63
+
64
+ def to_json(*args)
65
+ @state.to_h.to_json(*args)
66
+ end
67
+ end
68
+ end