prontoforms 0.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/checks.yml +66 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +12 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile +6 -0
- data/README.md +17 -1
- data/Rakefile +5 -3
- data/bin/console +4 -10
- data/bin/setup +2 -2
- data/lib/prontoforms.rb +10 -1
- data/lib/prontoforms/client.rb +74 -12
- data/lib/prontoforms/document.rb +31 -0
- data/lib/prontoforms/form.rb +75 -0
- data/lib/prontoforms/form_iteration.rb +40 -0
- data/lib/prontoforms/form_space.rb +47 -0
- data/lib/prontoforms/form_submission.rb +109 -8
- data/lib/prontoforms/resource.rb +19 -7
- data/lib/prontoforms/resource_list.rb +28 -7
- data/lib/prontoforms/user.rb +34 -0
- data/lib/prontoforms/version.rb +3 -1
- data/prontoforms.gemspec +28 -15
- metadata +85 -10
- data/Gemfile.lock +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf9bb12142b5957e341f48804ce06b73f8842cdcbe8e2831e34823299ca73017
|
4
|
+
data.tar.gz: c3760f082616943fa75dd0fad8f4bf0554390d75bea741f448baead8ce97bdfc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a5da5f5afda87fd00f326deba55c97169aad8918347f308df002c2525420e92d8a22983199a2bf498afa0b8a55ea32be25549a5b20ce86fb0578123100733c8
|
7
|
+
data.tar.gz: 8f0312762f55f92b0a3d5383fef1cc1558b9e65a7ba8d7c2f770c63a97d0ef695976e542459b839bf76966f05daf776c4b8aff9dad5ca4dad690448ed64c31da
|
@@ -0,0 +1,66 @@
|
|
1
|
+
name: Checks
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [master]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
test:
|
9
|
+
name: Run automated tests
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
steps:
|
12
|
+
- name: Checkout repository
|
13
|
+
uses: actions/checkout@v2
|
14
|
+
|
15
|
+
- name: Set up ruby
|
16
|
+
uses: actions/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: 2.6
|
19
|
+
|
20
|
+
- name: Install dependencies
|
21
|
+
run: |
|
22
|
+
gem install bundler -v 2.1.4
|
23
|
+
bundle install
|
24
|
+
|
25
|
+
- name: Test
|
26
|
+
run: bundle exec rake
|
27
|
+
|
28
|
+
- name: Report test coverage
|
29
|
+
uses: paambaati/codeclimate-action@v2.7.4
|
30
|
+
env:
|
31
|
+
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
|
32
|
+
|
33
|
+
rubocop:
|
34
|
+
name: Rubocop
|
35
|
+
runs-on: ubuntu-latest
|
36
|
+
strategy:
|
37
|
+
fail-fast: false
|
38
|
+
|
39
|
+
steps:
|
40
|
+
- name: Checkout repository
|
41
|
+
uses: actions/checkout@v2
|
42
|
+
|
43
|
+
- name: Set up Ruby
|
44
|
+
uses: ruby/setup-ruby@v1
|
45
|
+
with:
|
46
|
+
ruby-version: 2.6
|
47
|
+
|
48
|
+
- name: Install Code Scanning integration
|
49
|
+
run: bundle add code-scanning-rubocop --version 0.3.0 --skip-install
|
50
|
+
|
51
|
+
- name: Install dependencies
|
52
|
+
run: |
|
53
|
+
gem install bundler -v 2.1.4
|
54
|
+
bundle install
|
55
|
+
|
56
|
+
- name: Rubocop run
|
57
|
+
run: |
|
58
|
+
bash -c "
|
59
|
+
bundle exec rubocop --require code_scanning --format CodeScanning::SarifFormatter -o rubocop.sarif
|
60
|
+
[[ $? -ne 2 ]]
|
61
|
+
"
|
62
|
+
|
63
|
+
- name: Upload Sarif output
|
64
|
+
uses: github/codeql-action/upload-sarif@v1
|
65
|
+
with:
|
66
|
+
sarif_file: rubocop.sarif
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
data/.travis.yml
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
---
|
2
|
+
os: linux
|
3
|
+
dist: xenial
|
2
4
|
language: ruby
|
3
5
|
cache: bundler
|
4
6
|
rvm:
|
5
7
|
- 2.6.0
|
6
8
|
before_install: gem install bundler -v 2.1.4
|
9
|
+
before_script:
|
10
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
11
|
+
- chmod +x ./cc-test-reporter
|
12
|
+
- ./cc-test-reporter before-build
|
13
|
+
script:
|
14
|
+
- bundle exec rspec
|
15
|
+
after_script:
|
16
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
# prontoforms changelog
|
2
|
+
|
3
|
+
## [Unreleased]
|
4
|
+
### Added
|
5
|
+
* Added Form resource
|
6
|
+
* Can retrieve forms by form space
|
7
|
+
|
8
|
+
## [v0.3.1] - 15 Sep 2020
|
9
|
+
### Removed
|
10
|
+
* Unused `InvalidHttpVerb` error class
|
11
|
+
|
12
|
+
## [v0.3.0] - 15 Sep 2020
|
13
|
+
### Added
|
14
|
+
* Autogenerated documentation via YARD.
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
* Errors now provide a message describing the issue
|
18
|
+
* `ProntoForms::Client` now raises error on invalid resource definition.
|
19
|
+
|
20
|
+
## [v0.2.0] - 26 Aug 2020
|
21
|
+
### Bugfixes
|
22
|
+
* Corrected pagination of resource lists
|
23
|
+
|
24
|
+
## [v0.1.0] - 25 Aug 2020
|
25
|
+
* Initial release
|
26
|
+
|
27
|
+
[Unreleased]: https://github.com/paulholden2/prontoforms/compare/v0.3.1...HEAD
|
28
|
+
[v0.1.0]: https://github.com/paulholden2/prontoforms/releases/tag/v0.1.0
|
29
|
+
[v0.2.0]: https://github.com/paulholden2/prontoforms/releases/tag/v0.2.0
|
30
|
+
[v0.3.0]: https://github.com/paulholden2/prontoforms/releases/tag/v0.3.0
|
31
|
+
[v0.3.1]: https://github.com/paulholden2/prontoforms/releases/tag/v0.3.1
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
# ProntoForms
|
2
2
|
|
3
|
+
[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fpaulholden2%2Fprontoforms%2Fbadge%3Fref%3Dmaster&style=flat)](https://actions-badge.atrox.dev/paulholden2/prontoforms/goto?ref=master) [![Gem Version](https://badge.fury.io/rb/prontoforms.svg)](https://badge.fury.io/rb/prontoforms) [![Inline docs](http://inch-ci.org/github/paulholden2/prontoforms.svg?branch=master)](http://inch-ci.org/github/paulholden2/prontoforms) [![Maintainability](https://api.codeclimate.com/v1/badges/e47cd40058313e1c1c38/maintainability)](https://codeclimate.com/github/paulholden2/prontoforms/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/e47cd40058313e1c1c38/test_coverage)](https://codeclimate.com/github/paulholden2/prontoforms/test_coverage)
|
4
|
+
|
3
5
|
A library for interacting with the ProntoForms REST API in Ruby applications.
|
4
6
|
|
7
|
+
Note: version 0.4.0 included a breaking change, and has been yanked from
|
8
|
+
RubyGems. If you are using it, please upgrade to 1.x or revert to 0.3.1.
|
9
|
+
|
5
10
|
## Installation
|
6
11
|
|
7
12
|
Add this line to your application's Gemfile:
|
@@ -20,7 +25,16 @@ Or install it yourself as:
|
|
20
25
|
|
21
26
|
## Usage
|
22
27
|
|
23
|
-
|
28
|
+
To get started, first [create a ProntoForms API key] in your ProntoForms
|
29
|
+
account. Then you can begin using the API client in your Ruby application:
|
30
|
+
|
31
|
+
```rb
|
32
|
+
client = ProntoForms::Client.new(api_key_id, api_key_secret)
|
33
|
+
client.form_spaces # Returns all FormSpaces in your account
|
34
|
+
```
|
35
|
+
|
36
|
+
Review [the documentation](https://rubydoc.info/github/paulholden2/prontoforms)
|
37
|
+
for more information on how to use this library to interact with ProntoForms.
|
24
38
|
|
25
39
|
## Development
|
26
40
|
|
@@ -37,3 +51,5 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/paulho
|
|
37
51
|
## License
|
38
52
|
|
39
53
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
54
|
+
|
55
|
+
[create a ProntoForms API key]: https://support.prontoforms.com/hc/en-us/articles/217496468-Setup-an-API-Application-on-ProntoForms#Create
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,14 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'prontoforms'
|
6
|
+
require 'irb'
|
5
7
|
|
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
8
|
IRB.start(__FILE__)
|
data/bin/setup
CHANGED
data/lib/prontoforms.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'prontoforms/version'
|
2
4
|
require 'prontoforms/client'
|
3
5
|
|
4
6
|
module ProntoForms
|
5
|
-
|
7
|
+
# Base error class.
|
8
|
+
class Error < StandardError
|
9
|
+
attr_reader :message
|
10
|
+
|
11
|
+
def initialize(message)
|
12
|
+
@message = message
|
13
|
+
end
|
14
|
+
end
|
6
15
|
end
|
data/lib/prontoforms/client.rb
CHANGED
@@ -1,40 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'faraday'
|
2
4
|
require 'json'
|
3
5
|
require 'prontoforms/resource_list'
|
4
6
|
require 'prontoforms/form_space'
|
5
7
|
require 'prontoforms/form_submission'
|
8
|
+
require 'prontoforms/user'
|
6
9
|
|
7
10
|
module ProntoForms
|
11
|
+
# Allows you to retrieve resources from ProntoForms and perform other
|
12
|
+
# functions with the API.
|
8
13
|
class Client
|
9
|
-
|
14
|
+
# @return [String] ProntoForms API key ID
|
15
|
+
attr_reader :api_key_id
|
16
|
+
# @return [String] ProntoForms API key secret
|
17
|
+
attr_reader :api_key_secret
|
10
18
|
|
19
|
+
# Create a client and use provided API credentials
|
20
|
+
# @param api_key_id Your ProntoForms REST API key
|
21
|
+
# @param api_key_secret Your ProntoForms REST API secret
|
11
22
|
def initialize(api_key_id, api_key_secret)
|
12
23
|
@api_key_id = api_key_id
|
13
24
|
@api_key_secret = api_key_secret
|
14
25
|
end
|
15
26
|
|
16
|
-
|
17
|
-
|
18
|
-
|
27
|
+
# Defines a resource that can be retrieved in a list
|
28
|
+
# @return [nil]
|
29
|
+
# @api private
|
30
|
+
# @!macro [attach] resource_list
|
31
|
+
# @method $1
|
32
|
+
# Retrieve a list of $2 resources
|
33
|
+
# @return [ResourceList] A ResourceList containing $2 results
|
34
|
+
def self.resource_list(method, resource, url = resource.resource_name)
|
19
35
|
define_method(method) do |query: {}|
|
20
|
-
res = connection.
|
36
|
+
res = connection.get do |req|
|
21
37
|
req.url url
|
22
38
|
query.each { |k, v| req.params[k] = v }
|
23
39
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
40
|
+
|
41
|
+
data = JSON.parse(res.body)
|
42
|
+
|
43
|
+
return nil if data.fetch('pageData').size.zero?
|
44
|
+
|
45
|
+
ResourceList.new(data, { 'p' => 0, 's' => 100 }.merge(query), method,
|
46
|
+
resource, self)
|
29
47
|
end
|
30
48
|
end
|
31
49
|
|
32
|
-
|
33
|
-
|
50
|
+
resource_list :form_spaces, FormSpace
|
51
|
+
resource_list :form_submissions, FormSubmission
|
52
|
+
|
53
|
+
# Retrieve a user by identifier
|
54
|
+
# @param id [String] The user identifier
|
55
|
+
# @return [User] A User object for the requested user
|
56
|
+
def user(id)
|
57
|
+
raise ArgumentError, 'id must be provided' if id.nil?
|
58
|
+
|
59
|
+
res = connection.get do |req|
|
60
|
+
req.url "users/#{id}"
|
61
|
+
end
|
62
|
+
|
63
|
+
User.new(JSON.parse(res.body), self)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Retrieve a form space by its identifier
|
67
|
+
# @param id [String] The form space identifier
|
68
|
+
# @return [FormSpace] A FormSpace object
|
69
|
+
def form_space(id)
|
70
|
+
raise ArgumentError, 'id must be provided' if id.nil?
|
71
|
+
|
72
|
+
res = connection.get do |req|
|
73
|
+
req.url "formspaces/#{id}"
|
74
|
+
end
|
75
|
+
|
76
|
+
FormSpace.new(JSON.parse(res.body), self)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Retrieve a form submission by identifier
|
80
|
+
# @param id [String] The form submission identifier
|
81
|
+
# @return [FormSubmission] A FormSubmission object
|
82
|
+
def form_submission(id)
|
83
|
+
return nil if id.nil?
|
84
|
+
|
85
|
+
res = connection.get do |req|
|
86
|
+
req.url "data/#{id}"
|
87
|
+
end
|
88
|
+
|
89
|
+
FormSubmission.new(JSON.parse(res.body), self)
|
90
|
+
end
|
34
91
|
|
92
|
+
# Create a connection that can be used to execute a request against the
|
93
|
+
# ProntoForms API.
|
94
|
+
# @return [Faraday::Connection]
|
95
|
+
# @api private
|
35
96
|
def connection
|
36
97
|
Faraday.new(url: 'https://api.prontoforms.com/api/1.1') do |conn|
|
37
98
|
conn.basic_auth(api_key_id, api_key_secret)
|
99
|
+
conn.use Faraday::Response::RaiseError
|
38
100
|
end
|
39
101
|
end
|
40
102
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'prontoforms/resource'
|
4
|
+
|
5
|
+
module ProntoForms
|
6
|
+
# A Document is a configuration that generates output data or files when
|
7
|
+
# attached to a form.
|
8
|
+
class Document < Resource
|
9
|
+
def self.resource_name
|
10
|
+
'documents'
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [String] The Document identifier
|
14
|
+
property :id, key: 'identifier'
|
15
|
+
# @return [String] Document type
|
16
|
+
property :type, key: 'type'
|
17
|
+
# @return [String] Document name
|
18
|
+
property :name, key: 'name'
|
19
|
+
# @return [String] Document descriptiojn
|
20
|
+
property :description, key: 'description'
|
21
|
+
# @return [String] Document form version
|
22
|
+
property :form_document_version, key: 'formDocumentVersion'
|
23
|
+
# @return [Boolean] Whether the document is standard (system generated)
|
24
|
+
property :standard, key: 'standard'
|
25
|
+
# @return [String] Whether the document auto-links to new forms
|
26
|
+
property :auto_link, key: 'autoLink'
|
27
|
+
|
28
|
+
alias standard? standard
|
29
|
+
alias auto_link? auto_link
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'prontoforms/resource'
|
4
|
+
require 'prontoforms/form_iteration'
|
5
|
+
|
6
|
+
module ProntoForms
|
7
|
+
# A form includes inputs, validations, logic, and other configuration that
|
8
|
+
# facilitates data capture for a specific purpose.
|
9
|
+
class Form < Resource
|
10
|
+
def self.resource_name
|
11
|
+
'forms'
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String] The Form identifier
|
15
|
+
property :id, key: 'identifier'
|
16
|
+
# @return [String] Form name
|
17
|
+
property :name, key: 'name'
|
18
|
+
# @return [String] Form description
|
19
|
+
property :description, key: 'description'
|
20
|
+
# @return [String] Form state
|
21
|
+
property :state, key: 'state'
|
22
|
+
|
23
|
+
# Get the Form's form space ID
|
24
|
+
# @return [String] Form space identifier
|
25
|
+
def form_space_id
|
26
|
+
parent.id
|
27
|
+
end
|
28
|
+
|
29
|
+
def active_version_id
|
30
|
+
full_data.dig('activeVersion', 'identifier')
|
31
|
+
end
|
32
|
+
|
33
|
+
def current_version
|
34
|
+
res = client.connection.get do |req|
|
35
|
+
req.url "#{url}/iterations/#{active_version_id}"
|
36
|
+
end
|
37
|
+
|
38
|
+
FormIteration.new(JSON.parse(res.body), client, self)
|
39
|
+
end
|
40
|
+
|
41
|
+
def iteration(id)
|
42
|
+
raise ArgumentError, 'id must be provided' if id.nil?
|
43
|
+
|
44
|
+
res = client.connection.get do |req|
|
45
|
+
req.url "#{url}/iterations/#{id}"
|
46
|
+
end
|
47
|
+
|
48
|
+
FormIteration.new(JSON.parse(res.body), client, self)
|
49
|
+
end
|
50
|
+
|
51
|
+
def iterations(query: {})
|
52
|
+
res = client.connection.get do |req|
|
53
|
+
req.url "#{url}/iterations"
|
54
|
+
end
|
55
|
+
|
56
|
+
ResourceList.new(JSON.parse(res.body), {
|
57
|
+
'p' => 0,
|
58
|
+
's' => 100
|
59
|
+
}.merge(query), :iterations, FormIteration, client, self)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def full_data
|
65
|
+
return @full_data unless @full_data.nil?
|
66
|
+
|
67
|
+
@full_data = client.form_space(form_space_id).form(id).data
|
68
|
+
@full_data
|
69
|
+
end
|
70
|
+
|
71
|
+
def url
|
72
|
+
"formspaces/#{form_space_id}/forms/#{id}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'prontoforms/resource'
|
4
|
+
|
5
|
+
module ProntoForms
|
6
|
+
# A form iteration is a form configuration, distinct from a form version
|
7
|
+
# in that version numbers increment when a form iteration is deployed.
|
8
|
+
class FormIteration < Resource
|
9
|
+
def self.resource_name
|
10
|
+
'iterations'
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [String] The form iteration identifier
|
14
|
+
property :id, key: 'identifier'
|
15
|
+
# @return [Integer] Version number
|
16
|
+
property :version, key: 'version'
|
17
|
+
# @return [String] Form iteration state
|
18
|
+
property :state, key: 'state'
|
19
|
+
# @return [String] Initiation method for the form iteration
|
20
|
+
property :initiation_method, key: 'initiationMethod'
|
21
|
+
# @return [String] Can dispatched forms of this iteration be declined
|
22
|
+
property :dispatched_declinable, key: 'dispatchDeclinable'
|
23
|
+
|
24
|
+
alias can_decline_dispatch? dispatched_declinable
|
25
|
+
|
26
|
+
# @return [Array] Array of document IDs attached to this iteration
|
27
|
+
def document_ids
|
28
|
+
full_data.fetch('documentIds')
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def full_data
|
34
|
+
return @full_data unless @full_data.nil?
|
35
|
+
|
36
|
+
@full_data = parent.iteration(id).data
|
37
|
+
@full_data
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,10 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'prontoforms/resource'
|
4
|
+
require 'prontoforms/form'
|
5
|
+
require 'prontoforms/document'
|
2
6
|
|
3
7
|
module ProntoForms
|
8
|
+
# Represents a form space resource in ProntoForms. Form spaces are the
|
9
|
+
# primary organizational unit for forms, data sources, destinations, and
|
10
|
+
# other resources.
|
4
11
|
class FormSpace < Resource
|
12
|
+
# @return [String] The FormSpace identifier
|
5
13
|
property :id, key: 'identifier'
|
14
|
+
# @return [String] The FormSpace name
|
6
15
|
property :name, key: 'name'
|
16
|
+
# @return [String] The address that error emails are sent to
|
7
17
|
property :problem_contact_email, key: 'problemContactEmail'
|
18
|
+
# @return [Boolean] Whether updates are automatically pushed to devices
|
8
19
|
property :push_updates_to_device, key: 'pushUpdatesToDevice'
|
20
|
+
|
21
|
+
# Get all documents in the form space
|
22
|
+
# @return [ResourceList] A ResourceList containing Document objects
|
23
|
+
def documents
|
24
|
+
res = client.connection.get do |req|
|
25
|
+
req.url "formspaces/#{id}/documents"
|
26
|
+
end
|
27
|
+
|
28
|
+
ResourceList.new(JSON.parse(res.body), {
|
29
|
+
'p' => 0,
|
30
|
+
's' => 100
|
31
|
+
}, :documents, Document, self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def form(form_id)
|
35
|
+
res = client.connection.get do |req|
|
36
|
+
req.url "formspaces/#{id}/forms/#{form_id}"
|
37
|
+
end
|
38
|
+
|
39
|
+
data = JSON.parse(res.body)
|
40
|
+
Form.new(data, client, self)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get all forms in the form space
|
44
|
+
# @return [ResourceList] A ResourceList containing Form objects
|
45
|
+
def forms(query: {})
|
46
|
+
res = client.connection.get do |req|
|
47
|
+
req.url "formspaces/#{id}/forms"
|
48
|
+
query.each { |k, v| req.params[k] = v }
|
49
|
+
end
|
50
|
+
|
51
|
+
ResourceList.new(JSON.parse(res.body), {
|
52
|
+
'p' => 0,
|
53
|
+
's' => 100
|
54
|
+
}.merge(query), :forms, Form, client, self)
|
55
|
+
end
|
9
56
|
end
|
10
57
|
end
|
@@ -1,36 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
require 'prontoforms/resource'
|
3
5
|
|
4
6
|
module ProntoForms
|
7
|
+
# A FormSubmission represents submitted form data in ProntoForms. It
|
8
|
+
# includes various metadata about the submission as well.
|
5
9
|
class FormSubmission < Resource
|
6
|
-
def self.resource_name
|
10
|
+
def self.resource_name
|
11
|
+
'data'
|
12
|
+
end
|
7
13
|
|
14
|
+
# @return [String] The FormSubmission identifier
|
8
15
|
property :id, key: 'identifier'
|
16
|
+
# @return [String] Submission reference number
|
9
17
|
property :reference_number, key: 'referenceNumber'
|
18
|
+
# @return [String] Submission state. One of: Complete, Processing,
|
19
|
+
# Dispatched
|
10
20
|
property :state, key: 'state'
|
21
|
+
# @return [String] Submission data state
|
11
22
|
property :data_state, key: 'dataState'
|
23
|
+
# @return [Boolean] Has the submission data been persisted on the server
|
12
24
|
property :data_persisted, key: 'dataPersisted'
|
25
|
+
# @return [String] The form's version identifier
|
13
26
|
property :form_version_id, key: 'formVersionId'
|
27
|
+
# @return [String] The form's identifier
|
14
28
|
property :form_id, key: 'formId'
|
29
|
+
# @return [String] Submitter's user identifier
|
15
30
|
property :user_id, key: 'userId'
|
31
|
+
# @return [String] Submitter's username
|
16
32
|
property :username, key: 'username'
|
17
|
-
# Aliases
|
18
|
-
property :data_persisted?, key: 'dataPersisted'
|
19
|
-
property :submitter_id, key: 'userId'
|
20
|
-
property :submitter_username, key: 'username'
|
21
33
|
|
34
|
+
alias data_persisted? data_persisted
|
35
|
+
alias submitter_id user_id
|
36
|
+
alias submitter_username username
|
37
|
+
|
38
|
+
# @return [DateTime] Timestamp the submission was received by the server
|
22
39
|
property :server_receive_date do
|
23
40
|
str = data.fetch('serverReceiveDate')
|
24
41
|
str.nil? ? nil : DateTime.strptime(str)
|
25
42
|
end
|
26
43
|
|
44
|
+
# Retrieve the pages containing the form questions and answers
|
45
|
+
# @return [Hash] Hash of questions and answers for the FormSubmission
|
27
46
|
def pages
|
47
|
+
document.fetch('pages')
|
48
|
+
end
|
49
|
+
|
50
|
+
# Retrieve the dispatching User, if the form was dispatched
|
51
|
+
# @return [User] The user that dispatched the form, or nil
|
52
|
+
def dispatcher
|
53
|
+
return nil unless dispatched?
|
54
|
+
|
55
|
+
client.user(document.dig('dispatcher', 'identifier'))
|
56
|
+
end
|
57
|
+
|
58
|
+
# Check if the form was dispatched
|
59
|
+
# @return [Boolean] True if the form was dispatched; false otherwise
|
60
|
+
def dispatched?
|
61
|
+
!document.dig('dispatcher', 'identifier').nil?
|
62
|
+
end
|
63
|
+
|
64
|
+
# Retrieve the form space for the form submission
|
65
|
+
# @return [FormSpace] Form space for the submission's form
|
66
|
+
def form_space
|
67
|
+
client.form_space(full_data.dig('form', 'formSpaceId'))
|
68
|
+
end
|
69
|
+
|
70
|
+
# Retrieve the form for the form submission
|
71
|
+
# @return [Form] Form for the submission
|
72
|
+
def form
|
73
|
+
form_space.form(full_data.dig('form', 'formId'))
|
74
|
+
end
|
75
|
+
|
76
|
+
# Retrieve the current version of the form
|
77
|
+
# @return [FormIteration] The form iteration
|
78
|
+
def form_version
|
79
|
+
form.current_version
|
80
|
+
end
|
81
|
+
|
82
|
+
# Retrieve all documents attached to this form submission
|
83
|
+
# @return [Array] Documents attached to the form submission
|
84
|
+
def documents(populate: false)
|
85
|
+
ids = form_version.document_ids
|
86
|
+
if populate
|
87
|
+
ids.map { |id| form.iteration(id) }
|
88
|
+
else
|
89
|
+
ids
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Download a specific document. The Document must have been attached to
|
94
|
+
# the form's current version at the time of submission.
|
95
|
+
# @return [IO] Data stream for the document
|
96
|
+
def download_document(document)
|
97
|
+
io = StringIO.new
|
98
|
+
client.connection.get do |req|
|
99
|
+
req.url "#{url}/documents/#{document.id}"
|
100
|
+
req.options.on_data = proc { |chunk| io << chunk }
|
101
|
+
end
|
102
|
+
io.rewind
|
103
|
+
io
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def url
|
109
|
+
"#{resource_name}/#{id}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def full_data
|
113
|
+
return @full_data unless @full_data.nil?
|
114
|
+
|
115
|
+
@full_data = client.form_submission(id).data
|
116
|
+
@full_data
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns additional data about the submission. Uses cached data,
|
120
|
+
# otherwise it loads and returns the data via #document!
|
121
|
+
def document
|
122
|
+
return @document unless @document.nil?
|
123
|
+
|
124
|
+
document!
|
125
|
+
end
|
126
|
+
|
127
|
+
# Force loads the submission document
|
128
|
+
def document!
|
28
129
|
res = client.connection.get do |req|
|
29
130
|
req.url "#{resource_name}/#{id}/document.json"
|
30
131
|
end
|
31
|
-
|
32
|
-
|
33
|
-
|
132
|
+
|
133
|
+
@document = JSON.parse(res.body)
|
134
|
+
@document
|
34
135
|
end
|
35
136
|
end
|
36
137
|
end
|
data/lib/prontoforms/resource.rb
CHANGED
@@ -1,19 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
|
3
5
|
module ProntoForms
|
6
|
+
# Base class for resource-related classes.
|
4
7
|
class Resource
|
5
|
-
|
8
|
+
# @return [Hash] Retrieve raw JSON data associated with this resource
|
9
|
+
attr_reader :data
|
10
|
+
# @return [Client] API client
|
11
|
+
attr_reader :client
|
12
|
+
# @return [Resource] Parent object (applicable to child resources)
|
13
|
+
attr_reader :parent
|
6
14
|
|
15
|
+
# Defines a property of the resource
|
16
|
+
# @return [nil]
|
17
|
+
# @api private
|
7
18
|
def self.property(name, key: nil, &block)
|
8
|
-
define_method(name)
|
19
|
+
define_method(name) do
|
9
20
|
if block_given?
|
10
21
|
instance_eval(&block)
|
11
22
|
elsif !key.nil?
|
12
|
-
|
13
|
-
|
14
|
-
nil
|
23
|
+
key = [key] unless key.is_a?(Array)
|
24
|
+
key.inject(data) { |obj, k| obj.fetch(k) }
|
15
25
|
end
|
16
|
-
|
26
|
+
end
|
17
27
|
end
|
18
28
|
|
19
29
|
def initialize(data, client, parent = nil)
|
@@ -22,11 +32,13 @@ module ProntoForms
|
|
22
32
|
@parent = parent
|
23
33
|
end
|
24
34
|
|
35
|
+
# The resource's identifier
|
25
36
|
def self.resource_name
|
26
|
-
name =
|
37
|
+
name = to_s.split('::').last
|
27
38
|
"#{name.downcase}s"
|
28
39
|
end
|
29
40
|
|
41
|
+
# The resource's identifier
|
30
42
|
def resource_name
|
31
43
|
self.class.resource_name
|
32
44
|
end
|
@@ -1,25 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'prontoforms/resource'
|
2
4
|
|
3
5
|
module ProntoForms
|
6
|
+
# A wrapper for retrieving paged resources.
|
4
7
|
class ResourceList < Resource
|
5
|
-
|
8
|
+
# @return [Hash] Query parameters for this resource list for e.g. filters
|
9
|
+
attr_reader :query
|
10
|
+
# @return [Symbol] Method to send to parent object (usually the client)
|
11
|
+
attr_reader :method
|
12
|
+
# @return [Class] Resource class
|
13
|
+
attr_reader :resource
|
14
|
+
# @return [Client] API client
|
15
|
+
attr_reader :client
|
16
|
+
# @return [Resource] Parent object (for child resources)
|
17
|
+
attr_reader :parent
|
6
18
|
|
7
|
-
|
19
|
+
# Initialize the resource list
|
20
|
+
# TODO: splat
|
21
|
+
# rubocop:disable Metrics/ParameterLists
|
22
|
+
def initialize(data, query, method, resource, client, parent = nil)
|
8
23
|
super(data, client)
|
9
|
-
@
|
10
|
-
@offset = offset
|
24
|
+
@query = query
|
11
25
|
@method = method
|
12
26
|
@resource = resource
|
27
|
+
@parent = parent
|
13
28
|
end
|
29
|
+
# rubocop:enable Metrics/ParameterLists
|
14
30
|
|
31
|
+
# Retrieve the next page of results, using the same number of items per
|
32
|
+
# page as the original request.
|
33
|
+
# @return [ResourceList] A ResourceList with the next set of results
|
15
34
|
def next
|
16
|
-
client.send(method,
|
35
|
+
client.send(method, query: query.merge({ 'p' => query['p'] + 1 }))
|
17
36
|
end
|
18
37
|
|
38
|
+
# Retrieve the result set
|
39
|
+
# @return [Array] Array of resource objects
|
19
40
|
def items
|
20
|
-
@data.fetch('pageData').map
|
41
|
+
@data.fetch('pageData').map do |item|
|
21
42
|
resource.new(item, client, parent)
|
22
|
-
|
43
|
+
end
|
23
44
|
end
|
24
45
|
end
|
25
46
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'prontoforms/resource'
|
4
|
+
|
5
|
+
module ProntoForms
|
6
|
+
# A ProntoForms user account.
|
7
|
+
class User < Resource
|
8
|
+
def self.resource_name
|
9
|
+
'users'
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [String] The User identifier
|
13
|
+
property :id, key: 'identifier'
|
14
|
+
# @return [String] The user's username
|
15
|
+
property :username, key: 'username'
|
16
|
+
# @return [String] The user's role
|
17
|
+
property :role, key: 'role'
|
18
|
+
# @return [String] The user's email address
|
19
|
+
property :email, key: 'email'
|
20
|
+
# @return [String] The user's first name
|
21
|
+
property :first_name, key: 'firstName'
|
22
|
+
# @return [String] The user's last name
|
23
|
+
property :last_name, key: 'lastName'
|
24
|
+
# @return [String] The user's preferred locale
|
25
|
+
property :locale, key: 'locale'
|
26
|
+
|
27
|
+
# Get a display name consisting of the first name followed by last name
|
28
|
+
# e.g. "John Doe"
|
29
|
+
# @return [String] The display name for the user
|
30
|
+
def display_name
|
31
|
+
"#{first_name} #{last_name}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/prontoforms/version.rb
CHANGED
data/prontoforms.gemspec
CHANGED
@@ -1,16 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'lib/prontoforms/version'
|
2
4
|
|
5
|
+
# rubocop:disable Layout/LineLength
|
3
6
|
Gem::Specification.new do |spec|
|
4
|
-
spec.name
|
5
|
-
spec.version
|
6
|
-
spec.authors
|
7
|
-
spec.email
|
7
|
+
spec.name = 'prontoforms'
|
8
|
+
spec.version = ProntoForms::VERSION
|
9
|
+
spec.authors = ['Paul Holden']
|
10
|
+
spec.email = ['paul@codelunker.com']
|
8
11
|
|
9
|
-
spec.summary
|
10
|
-
spec.description
|
11
|
-
spec.homepage
|
12
|
-
spec.license
|
13
|
-
spec.required_ruby_version = Gem::Requirement.new('>= 2.
|
12
|
+
spec.summary = 'A library for using the ProntoForms REST API in Ruby applications.'
|
13
|
+
spec.description = 'A library for using the ProntoForms REST API in Ruby applications.'
|
14
|
+
spec.homepage = 'https://github.com/paulholden2/prontoforms'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.6.0')
|
14
17
|
|
15
18
|
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
16
19
|
|
@@ -19,17 +22,27 @@ Gem::Specification.new do |spec|
|
|
19
22
|
spec.metadata['changelog_uri'] = 'https://github.com/paulholden2/prontoforms/blog/master/CHANGELOG.md'
|
20
23
|
|
21
24
|
# Specify which files should be added to the gem when it is released.
|
22
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added
|
23
|
-
|
24
|
-
|
25
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added
|
26
|
+
# into git.
|
27
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
28
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
29
|
+
f.match(%r{^(test|spec|features)/})
|
30
|
+
end
|
25
31
|
end
|
26
|
-
|
27
|
-
spec.
|
32
|
+
|
33
|
+
spec.bindir = 'exe'
|
34
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
35
|
spec.require_paths = ['lib']
|
29
36
|
|
30
37
|
spec.add_dependency 'faraday', '~> 1.0'
|
31
38
|
|
32
39
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
33
|
-
spec.add_development_dependency 'rake', '
|
40
|
+
spec.add_development_dependency 'rake', '>= 12.3.3'
|
34
41
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
42
|
+
spec.add_development_dependency 'rubocop', '~> 0.82.0'
|
43
|
+
spec.add_development_dependency 'sinatra', '~> 2.0'
|
44
|
+
spec.add_development_dependency 'sinatra-contrib', '~> 2.0'
|
45
|
+
spec.add_development_dependency 'webmock', '~> 3.6'
|
46
|
+
spec.add_development_dependency 'yard', '~> 0.9'
|
35
47
|
end
|
48
|
+
# rubocop:enable Layout/LineLength
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prontoforms
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Holden
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 12.3.3
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 12.3.3
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,19 +66,90 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.82.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.82.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sinatra
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sinatra-contrib
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: webmock
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.6'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.6'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: yard
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.9'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.9'
|
69
139
|
description: A library for using the ProntoForms REST API in Ruby applications.
|
70
140
|
email:
|
71
|
-
- paul@
|
141
|
+
- paul@codelunker.com
|
72
142
|
executables: []
|
73
143
|
extensions: []
|
74
144
|
extra_rdoc_files: []
|
75
145
|
files:
|
146
|
+
- ".github/workflows/checks.yml"
|
76
147
|
- ".gitignore"
|
77
148
|
- ".rspec"
|
149
|
+
- ".rubocop.yml"
|
78
150
|
- ".travis.yml"
|
79
151
|
- CHANGELOG.md
|
80
152
|
- Gemfile
|
81
|
-
- Gemfile.lock
|
82
153
|
- LICENSE.txt
|
83
154
|
- README.md
|
84
155
|
- Rakefile
|
@@ -86,10 +157,14 @@ files:
|
|
86
157
|
- bin/setup
|
87
158
|
- lib/prontoforms.rb
|
88
159
|
- lib/prontoforms/client.rb
|
160
|
+
- lib/prontoforms/document.rb
|
161
|
+
- lib/prontoforms/form.rb
|
162
|
+
- lib/prontoforms/form_iteration.rb
|
89
163
|
- lib/prontoforms/form_space.rb
|
90
164
|
- lib/prontoforms/form_submission.rb
|
91
165
|
- lib/prontoforms/resource.rb
|
92
166
|
- lib/prontoforms/resource_list.rb
|
167
|
+
- lib/prontoforms/user.rb
|
93
168
|
- lib/prontoforms/version.rb
|
94
169
|
- prontoforms.gemspec
|
95
170
|
homepage: https://github.com/paulholden2/prontoforms
|
@@ -108,14 +183,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
108
183
|
requirements:
|
109
184
|
- - ">="
|
110
185
|
- !ruby/object:Gem::Version
|
111
|
-
version: 2.
|
186
|
+
version: 2.6.0
|
112
187
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
188
|
requirements:
|
114
189
|
- - ">="
|
115
190
|
- !ruby/object:Gem::Version
|
116
191
|
version: '0'
|
117
192
|
requirements: []
|
118
|
-
rubygems_version: 3.
|
193
|
+
rubygems_version: 3.1.4
|
119
194
|
signing_key:
|
120
195
|
specification_version: 4
|
121
196
|
summary: A library for using the ProntoForms REST API in Ruby applications.
|
data/Gemfile.lock
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
prontoforms (0.1.0)
|
5
|
-
faraday (~> 1.0)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
diff-lcs (1.4.4)
|
11
|
-
faraday (1.0.1)
|
12
|
-
multipart-post (>= 1.2, < 3)
|
13
|
-
multipart-post (2.1.1)
|
14
|
-
rake (10.5.0)
|
15
|
-
rspec (3.9.0)
|
16
|
-
rspec-core (~> 3.9.0)
|
17
|
-
rspec-expectations (~> 3.9.0)
|
18
|
-
rspec-mocks (~> 3.9.0)
|
19
|
-
rspec-core (3.9.2)
|
20
|
-
rspec-support (~> 3.9.3)
|
21
|
-
rspec-expectations (3.9.2)
|
22
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
23
|
-
rspec-support (~> 3.9.0)
|
24
|
-
rspec-mocks (3.9.1)
|
25
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
26
|
-
rspec-support (~> 3.9.0)
|
27
|
-
rspec-support (3.9.3)
|
28
|
-
|
29
|
-
PLATFORMS
|
30
|
-
ruby
|
31
|
-
|
32
|
-
DEPENDENCIES
|
33
|
-
bundler (~> 2.0)
|
34
|
-
prontoforms!
|
35
|
-
rake (~> 10.0)
|
36
|
-
rspec (~> 3.0)
|
37
|
-
|
38
|
-
BUNDLED WITH
|
39
|
-
2.1.4
|