ask_awesomely 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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