mobile_workflow 0.7.6 → 0.7.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +83 -0
- data/app/controllers/concerns/mobile_workflow/s3_storable.rb +7 -5
- data/app/controllers/mobile_workflow/sns_notifications_controller.rb +14 -37
- data/app/jobs/mobile_workflow/add_attachment_job.rb +30 -0
- data/app/models/concerns/mobile_workflow/attachable.rb +32 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/form.rb +70 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/list.rb +15 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/map.rb +11 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/pie_chart.rb +11 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/question.rb +25 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/stack.rb +75 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/styled_content/grid.rb +30 -0
- data/app/models/concerns/mobile_workflow/displayable/steps/styled_content/stack.rb +40 -0
- data/app/models/concerns/mobile_workflow/displayable.rb +17 -146
- data/lib/generators/mobile_workflow/install/install_generator.rb +12 -0
- data/lib/generators/mobile_workflow/install/templates/app/models/application_record.rb +1 -0
- data/lib/generators/mobile_workflow/install/templates/deserializer.rb.erb +13 -0
- data/lib/generators/mobile_workflow/install/templates/deserializer_spec.rb.erb +29 -0
- data/lib/generators/mobile_workflow/install/templates/user.rb.erb +4 -0
- data/lib/generators/mobile_workflow/model_generator.rb +9 -2
- data/lib/generators/mobile_workflow/templates/controller.rb.erb +4 -2
- data/lib/generators/mobile_workflow/templates/model.rb.erb +17 -2
- data/lib/mobile_workflow/displayable.rb +9 -0
- data/lib/mobile_workflow/engine.rb +7 -5
- data/lib/mobile_workflow/version.rb +1 -1
- data/lib/mobile_workflow.rb +1 -0
- metadata +49 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf9a3d0ae2752b171aea1043b7cc57a8425b80ecf086bb226faadffb936fd5ee
|
4
|
+
data.tar.gz: 1d0c4bf77cbbb66844a58a4d94bfb5532f4706a4a5fa2ae02f01e1e0f72e4939
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3c1748bdbe2b84415e434632080968cc02fb4e6e7b5a248bf8d979868b74714ce89c0d04165207d3b67008e45b3f08e4b84f67124a128f971627ad28611591f
|
7
|
+
data.tar.gz: 90329d6e78a89c601fa6bb5cc62b4d856b9ba264e56d6bfe83b7e1afc5ea5d1849355ccf1beb272d2474c90b5b5ec2bca191903db9dbe465fa5141b52053dadf
|
data/README.md
CHANGED
@@ -26,6 +26,89 @@ Or install it yourself as:
|
|
26
26
|
$ gem install mobile_workflow
|
27
27
|
```
|
28
28
|
|
29
|
+
## Upgrade to version 0.7.7 or higher
|
30
|
+
The following utility methods have changed their parameters:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
# app/models/concerns/mobile_workflow/displayable/steps/styled_content/grid.rb#20
|
34
|
+
def mw_grid_item(id: self.id, text:, detail_text: nil, preview_url: nil)
|
35
|
+
raise 'Missing id' if id.nil?
|
36
|
+
raise 'Missing text' if text.nil?
|
37
|
+
|
38
|
+
{ id: id, text: text, type: :item, detailText: detail_text, imageURL: preview_url }.compact
|
39
|
+
end
|
40
|
+
|
41
|
+
# app/models/concerns/mobile_workflow/displayable/steps/styled_content/stack.rb#20
|
42
|
+
def mw_stack_list_item(id:, text:, detail_text: nil, preview_url: nil)
|
43
|
+
raise 'Missing id' if id.nil?
|
44
|
+
raise 'Missing text' if text.nil?
|
45
|
+
|
46
|
+
{ id: id.to_s, text: text, detailText: detail_text, type: :listItem, imageURL: preview_url }.compact
|
47
|
+
end
|
48
|
+
|
49
|
+
# app/models/concerns/mobile_workflow/displayable/steps/list.rb#5
|
50
|
+
def mw_list_item(id: self.id, text:, detail_text: nil, sf_symbol_name: nil, material_icon_name: nil, preview_url: nil)
|
51
|
+
{ id: id, text: text, detailText: detail_text, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name, imageURL: preview_url }.compact
|
52
|
+
end
|
53
|
+
|
54
|
+
# app/models/concerns/mobile_workflow/displayable/steps/stack.rb#26
|
55
|
+
def mw_display_video(preview_url:, attachment_url:)
|
56
|
+
{type: :video, previewURL: preview_url, url: attachment_url}
|
57
|
+
end
|
58
|
+
|
59
|
+
# app/models/concerns/mobile_workflow/displayable/steps/stack.rb#11
|
60
|
+
def mw_display_image(preview_url:, attachment_url:, content_mode: :scale_aspect_fill)
|
61
|
+
validate_content_mode!(content_mode)
|
62
|
+
|
63
|
+
{type: :image, contentMode: camelcase_converter(content_mode.to_s, first_letter: :lower), previewURL: preview_url, url: attachment_url}
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
All URLs MUST now be explicitly sent as arguments to the above methods, which means they must be previously set. If not, the methods will not work.
|
68
|
+
|
69
|
+
In order to support projects using `ActiveStorage`, there is a new model concern `MobileWorkflow::Attachable` that provides a few helpers. This is what you can do to upgrade if you use ActiveStorage (otherwise the helpers must be manually created):
|
70
|
+
|
71
|
+
1. Include the concern in the `ApplicationRecord` class, together with `MobileWorkflow::Displayable`:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
class ApplicationRecord < ActiveRecord::Base
|
75
|
+
include MobileWorkflow::Attachable
|
76
|
+
include MobileWorkflow::Displayable
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
2. Once included, the following helpers will be available, so use them to generate the intended URLs:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
def preview_url(attachment, options: { resize_to_fill: [200, 200] })
|
84
|
+
return nil unless attachment.attached?
|
85
|
+
|
86
|
+
if attachment.image?
|
87
|
+
rails_representation_url(attachment.variant(options), host: heroku_attachment_host)
|
88
|
+
elsif attachment.previewable?
|
89
|
+
rails_representation_url(attachment.preview(options), host: heroku_attachment_host)
|
90
|
+
else
|
91
|
+
return nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def attachment_url(attachment)
|
96
|
+
return nil unless attachment.attached?
|
97
|
+
|
98
|
+
rails_blob_url(attachment, host: heroku_attachment_host)
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
Example of use:
|
103
|
+
```ruby
|
104
|
+
# Old method call
|
105
|
+
mw_list_item(text: 'John Doe', detail_text: 'Company Name', image_attachment: <ActiveStorage::Attached::One>, image_url: 'https://test.org/preview')
|
106
|
+
|
107
|
+
# New method call
|
108
|
+
preview_url = preview_url(<ActiveStorage::Attached::One>, options: { resize_to_fill: [200, 200] }) || 'https://test.org/preview'
|
109
|
+
mw_list_item(text: 'John Doe', detail_text: 'Company Name', preview_url: preview_url)
|
110
|
+
```
|
111
|
+
|
29
112
|
## Contributing
|
30
113
|
Contribution directions go here.
|
31
114
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'aws-sdk-s3'
|
2
|
+
|
1
3
|
module MobileWorkflow
|
2
4
|
module S3Storable
|
3
5
|
if Object.const_defined?("Aws::S3")
|
@@ -10,10 +12,11 @@ module MobileWorkflow
|
|
10
12
|
identifier = binary[:identifier]
|
11
13
|
object_attribute = identifier.split(".")[0] # ensure extension doesnt get added here
|
12
14
|
extension = binary[:mimetype].split('/')[1] # i.e. image/jpg --> jpg, video/mp4 --> mp4
|
13
|
-
|
15
|
+
metadata = binary[:metadata]
|
16
|
+
|
14
17
|
{
|
15
18
|
identifier: identifier,
|
16
|
-
url: presigned_url("#{object.class.name.underscore}/#{object.id}/#{object_attribute}/#{s3_object_uuid}.#{extension}"),
|
19
|
+
url: presigned_url("#{object.class.name.underscore}/#{object.id}/#{object_attribute}/#{s3_object_uuid}.#{extension}", metadata: metadata),
|
17
20
|
method: "PUT"
|
18
21
|
}
|
19
22
|
end
|
@@ -24,8 +27,8 @@ module MobileWorkflow
|
|
24
27
|
SecureRandom.uuid
|
25
28
|
end
|
26
29
|
|
27
|
-
def presigned_url(key)
|
28
|
-
presigner.presigned_url(:put_object, bucket: ENV['AWS_BUCKET_NAME'], key: key, metadata:
|
30
|
+
def presigned_url(key, metadata: nil)
|
31
|
+
presigner.presigned_url(:put_object, bucket: ENV['AWS_BUCKET_NAME'], key: key, metadata: metadata)
|
29
32
|
end
|
30
33
|
|
31
34
|
def presigner
|
@@ -35,7 +38,6 @@ module MobileWorkflow
|
|
35
38
|
def s3_client
|
36
39
|
Aws::S3::Client.new(region: ENV['AWS_REGION'], access_key_id: ENV['AWS_ACCESS_ID'], secret_access_key: ENV['AWS_SECRET_KEY'])
|
37
40
|
end
|
38
|
-
|
39
41
|
end
|
40
42
|
end
|
41
43
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'aws-sdk-sns'
|
2
|
+
|
1
3
|
module MobileWorkflow
|
2
4
|
class SnsNotificationsController < ActionController::API
|
3
5
|
if Object.const_defined?("Aws::S3") && Object.const_defined?("Aws::SNS")
|
@@ -10,26 +12,16 @@ module MobileWorkflow
|
|
10
12
|
when 'SubscriptionConfirmation'
|
11
13
|
confirm_subscription ? (head :ok) : (head :bad_request)
|
12
14
|
else
|
13
|
-
|
15
|
+
AddAttachmentJob.perform_now(object, object_key, attribute_name)
|
16
|
+
head :ok
|
14
17
|
end
|
18
|
+
rescue NameError => e
|
19
|
+
Rails.logger.warn "Error attaching object: #{e.message}"
|
20
|
+
rescue ActiveRecord::RecordNotFound
|
21
|
+
head :not_found
|
15
22
|
end
|
16
23
|
|
17
24
|
private
|
18
|
-
def add_attachment
|
19
|
-
begin
|
20
|
-
@object = find_object
|
21
|
-
@object.send("#{attribute_name}=",active_record_blob)
|
22
|
-
if @object.save
|
23
|
-
head :ok
|
24
|
-
else
|
25
|
-
Rails.logger.warn "Error saving object: #{@object} #{object.errors.full_messages}"
|
26
|
-
head :unprocessable_entity
|
27
|
-
end
|
28
|
-
rescue NameError => e
|
29
|
-
Rails.logger.warn "Error attaching object: #{e.message}"
|
30
|
-
head :unprocessable_entity
|
31
|
-
end
|
32
|
-
end
|
33
25
|
|
34
26
|
def verify_request_authenticity
|
35
27
|
head :unauthorized if raw_post.blank?
|
@@ -37,22 +29,11 @@ module MobileWorkflow
|
|
37
29
|
#head :unauthorized if raw_post.blank? || !message_verifier.authentic?(raw_post) # Not working
|
38
30
|
end
|
39
31
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
def checksum_base64(object_key, s3_object)
|
47
|
-
path = Tempfile.new(object_key).path
|
48
|
-
s3_object.download_file(path)
|
49
|
-
file = File.new(path)
|
50
|
-
Digest::MD5.file(file).base64digest
|
51
|
-
end
|
52
|
-
|
53
|
-
def find_object
|
54
|
-
object_class_name, object_id = key_identifiers
|
55
|
-
object_class_name.camelcase.constantize.find(object_id.to_i)
|
32
|
+
def object
|
33
|
+
@object ||= begin
|
34
|
+
object_class_name, object_id = key_identifiers
|
35
|
+
object_class_name.camelcase.constantize.find(object_id.to_i)
|
36
|
+
end
|
56
37
|
end
|
57
38
|
|
58
39
|
def attribute_name
|
@@ -91,11 +72,7 @@ module MobileWorkflow
|
|
91
72
|
Rails.logger.warn(e.message)
|
92
73
|
return false
|
93
74
|
end
|
94
|
-
|
95
|
-
def s3_bucket
|
96
|
-
Aws::S3::Resource.new(region: ENV['AWS_REGION'], access_key_id: ENV['AWS_ACCESS_ID'], secret_access_key: ENV['AWS_SECRET_KEY']).bucket(ENV['AWS_BUCKET_NAME'])
|
97
|
-
end
|
98
|
-
|
75
|
+
|
99
76
|
def sns_client
|
100
77
|
Aws::SNS::Client.new(region: ENV['AWS_REGION'], access_key_id: ENV['AWS_ACCESS_ID'], secret_access_key: ENV['AWS_SECRET_KEY'])
|
101
78
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MobileWorkflow
|
4
|
+
class AddAttachmentJob < ApplicationJob
|
5
|
+
def perform(object, object_key, attribute_name)
|
6
|
+
object.send("#{attribute_name}=", active_record_blob_from_s3(object_key))
|
7
|
+
Rails.logger.warn "Error saving object: #{object} #{object.errors.full_messages}" unless object.save
|
8
|
+
rescue NoMethodError => e
|
9
|
+
Rails.logger.warn "Error attaching object: #{e.message}"
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def active_record_blob_from_s3(object_key)
|
15
|
+
# etag cannot be used as the MD5 checksum when doing multi-part uploads
|
16
|
+
s3_object = s3_bucket.object(object_key)
|
17
|
+
base64_digest = hex_to_base64_digest(s3_object.etag.delete('"'))
|
18
|
+
ActiveStorage::Blob.create! key: s3_object.key, filename: s3_object.key, byte_size: s3_object.size,
|
19
|
+
checksum: base64_digest, content_type: s3_object.content_type
|
20
|
+
end
|
21
|
+
|
22
|
+
def s3_bucket
|
23
|
+
Aws::S3::Resource.new(region: ENV['AWS_REGION'], access_key_id: ENV['AWS_ACCESS_ID'], secret_access_key: ENV['AWS_SECRET_KEY']).bucket(ENV['AWS_BUCKET_NAME'])
|
24
|
+
end
|
25
|
+
|
26
|
+
def hex_to_base64_digest(hexdigest)
|
27
|
+
[[hexdigest].pack('H*')].pack('m0')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module MobileWorkflow
|
2
|
+
module Attachable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
include Rails.application.routes.url_helpers
|
5
|
+
|
6
|
+
def preview_url(attachment, options: { resize_to_fill: [200, 200] })
|
7
|
+
return nil unless attachment.attached?
|
8
|
+
|
9
|
+
if attachment.image?
|
10
|
+
rails_representation_url(attachment.variant(options), host: heroku_attachment_host)
|
11
|
+
elsif attachment.previewable?
|
12
|
+
rails_representation_url(attachment.preview(options), host: heroku_attachment_host)
|
13
|
+
else
|
14
|
+
return nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def attachment_url(attachment)
|
19
|
+
return nil unless attachment.attached?
|
20
|
+
|
21
|
+
rails_blob_url(attachment, host: heroku_attachment_host)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def heroku_attachment_host
|
27
|
+
# TODO: MBS - move this to a configuration property
|
28
|
+
app_name = Rails.env.test? ? 'test-app' : ENV.fetch('HEROKU_APP_NAME')
|
29
|
+
"https://#{app_name}.herokuapp.com"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module MobileWorkflow
|
2
|
+
module Displayable
|
3
|
+
module Steps
|
4
|
+
module Form
|
5
|
+
def mw_form_section(label:, identifier:)
|
6
|
+
raise 'Missing label' if label.nil?
|
7
|
+
raise 'Missing identifier' if identifier.nil?
|
8
|
+
|
9
|
+
{ item_type: :section, label: label, identifier: identifier }
|
10
|
+
end
|
11
|
+
|
12
|
+
def mw_form_multiple_selection(label:, identifier:, multiple_selection_options:, selection_type: :single, optional: false, show_other_option: false)
|
13
|
+
raise 'Missing label' if label.nil?
|
14
|
+
raise 'Missing identifier' if identifier.nil?
|
15
|
+
raise 'Missing multiple selection options' if multiple_selection_options.nil?
|
16
|
+
|
17
|
+
{ item_type: :multiple_selection, label: label, identifier: identifier, multiple_selection_options: multiple_selection_options, selection_type: selection_type, optional: optional, show_other_option: show_other_option }
|
18
|
+
end
|
19
|
+
|
20
|
+
def mw_form_multiple_selection_options(text:, hint: nil, is_pre_selected: false)
|
21
|
+
raise 'Missing text' if text.nil?
|
22
|
+
|
23
|
+
{ text: text, hint: hint, isPreSelected: is_pre_selected }
|
24
|
+
end
|
25
|
+
|
26
|
+
def mw_form_number(label:, identifier:, placeholder: nil, optional: false, symbol_position: :leading, default_text_answer: nil, hint: nil)
|
27
|
+
raise 'Missing label' if label.nil?
|
28
|
+
raise 'Missing identifier' if identifier.nil?
|
29
|
+
|
30
|
+
{ item_type: :number, number_type: :number, label: label, identifier: identifier, placeholder: placeholder, optional: optional, symbol_position: symbol_position, default_text_answer: default_text_answer, hint: hint }
|
31
|
+
end
|
32
|
+
|
33
|
+
def mw_form_text(label:, identifier:, placeholder: nil, optional: false, multiline: false, default_text_answer: nil)
|
34
|
+
raise 'Missing label' if label.nil?
|
35
|
+
raise 'Missing identifier' if identifier.nil?
|
36
|
+
|
37
|
+
{ item_type: :text, label: label, identifier: identifier, placeholder: placeholder, optional: optional, multiline: multiline, default_text_answer: default_text_answer }
|
38
|
+
end
|
39
|
+
|
40
|
+
def mw_form_date(label:, identifier:, optional: false, default_date_time_answer: nil)
|
41
|
+
raise 'Missing label' if label.nil?
|
42
|
+
raise 'Missing identifier' if identifier.nil?
|
43
|
+
|
44
|
+
{ item_type: :date, date_type: :calendar, label: label, identifier: identifier, optional: optional, default_date_time_answer: default_date_time_answer }
|
45
|
+
end
|
46
|
+
|
47
|
+
def mw_form_time(label:, identifier:, optional: false, default_date_time_answer: nil)
|
48
|
+
raise 'Missing label' if label.nil?
|
49
|
+
raise 'Missing identifier' if identifier.nil?
|
50
|
+
|
51
|
+
{ item_type: :time, label: label, identifier: identifier, optional: optional, default_date_time_answer: default_date_time_answer }
|
52
|
+
end
|
53
|
+
|
54
|
+
def mw_form_email(label:, identifier:, placeholder: nil, optional: false, default_text_answer: nil)
|
55
|
+
raise 'Missing label' if label.nil?
|
56
|
+
raise 'Missing identifier' if identifier.nil?
|
57
|
+
|
58
|
+
{ item_type: :email, label: label, identifier: identifier, placeholder: placeholder, optional: optional, default_text_answer: default_text_answer }
|
59
|
+
end
|
60
|
+
|
61
|
+
def mw_form_password(label:, identifier:, placeholder: nil, optional: false, default_text_answer: nil, hint: nil)
|
62
|
+
raise 'Missing label' if label.nil?
|
63
|
+
raise 'Missing identifier' if identifier.nil?
|
64
|
+
|
65
|
+
{ item_type: :secure, label: label, identifier: identifier, placeholder: placeholder, optional: optional, default_text_answer: default_text_answer, hint: hint }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module MobileWorkflow
|
2
|
+
module Displayable
|
3
|
+
module Steps
|
4
|
+
module List
|
5
|
+
def mw_list_item(id: self.id, text:, detail_text: nil, sf_symbol_name: nil, material_icon_name: nil, preview_url: nil)
|
6
|
+
{ id: id, text: text, detailText: detail_text, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name, imageURL: preview_url }.compact
|
7
|
+
end
|
8
|
+
|
9
|
+
def mw_list_search_suggestion(id: self.id, text:, section_name:, sf_symbol_name: nil)
|
10
|
+
{id: id.to_s, text: text, sectionName: section_name, sfSymbolName: sf_symbol_name}.compact
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module MobileWorkflow
|
2
|
+
module Displayable
|
3
|
+
module Steps
|
4
|
+
module Map
|
5
|
+
def mw_map_item(id: self.id, text:, detail_text: nil, latitude:, longitude:)
|
6
|
+
{id: id.to_s, text: text, detailText: detail_text, latitude: latitude, longitude: longitude}.compact
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module MobileWorkflow
|
2
|
+
module Displayable
|
3
|
+
module Steps
|
4
|
+
module Question
|
5
|
+
QUESTION_STYLES = [:single_choice, :multiple_choice]
|
6
|
+
|
7
|
+
def mw_text_choice_question(question:, style:, text_choices:)
|
8
|
+
raise 'Missing question' if question.empty?
|
9
|
+
raise 'Text Choices should be a hash' unless text_choices.is_a?(Hash)
|
10
|
+
|
11
|
+
validate_question_style!(style)
|
12
|
+
|
13
|
+
text_choices_a = text_choices.map{|k, v| {_class: "ORKTextChoice", exclusive: false, text: k, value: v} }.to_a
|
14
|
+
{ question: question, answerFormat: { _class: "ORKTextChoiceAnswerFormat", style: camelcase_converter(style.to_s, first_letter: :lower), textChoices: text_choices_a}}
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def validate_question_style!(style)
|
20
|
+
raise 'Unknown style' unless QUESTION_STYLES.include?(style)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module MobileWorkflow
|
2
|
+
module Displayable
|
3
|
+
module Steps
|
4
|
+
module Stack
|
5
|
+
CONTENT_MODE_OPTIONS = [:scale_aspect_fill, :scale_aspect_fit]
|
6
|
+
|
7
|
+
def mw_display_text(text:, label: nil)
|
8
|
+
{type: :text, label: label, text: text.to_s}.compact
|
9
|
+
end
|
10
|
+
|
11
|
+
def mw_display_image(preview_url:, attachment_url:, content_mode: :scale_aspect_fill)
|
12
|
+
validate_content_mode!(content_mode)
|
13
|
+
|
14
|
+
{type: :image, contentMode: camelcase_converter(content_mode.to_s, first_letter: :lower), previewURL: preview_url, url: attachment_url}
|
15
|
+
end
|
16
|
+
|
17
|
+
def mw_display_unsplash_image(image_url)
|
18
|
+
if image_url.start_with? "https://unsplash.com/photos"
|
19
|
+
unsplash_id = image_url.split('/').last
|
20
|
+
image_url = "https://source.unsplash.com/#{unsplash_id}/800x600"
|
21
|
+
end
|
22
|
+
|
23
|
+
{type: :image, previewURL: image_url, url: image_url}.compact
|
24
|
+
end
|
25
|
+
|
26
|
+
def mw_display_video(preview_url:, attachment_url:)
|
27
|
+
{type: :video, previewURL: preview_url, url: attachment_url}
|
28
|
+
end
|
29
|
+
|
30
|
+
def mw_display_button(label:, style: :primary, on_success: :forward, sf_symbol_name: nil, material_icon_name: nil)
|
31
|
+
validate_on_success!(on_success)
|
32
|
+
validate_button_style!(style)
|
33
|
+
|
34
|
+
{type: :button, label: label, style: style, onSuccess: on_success, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
def mw_display_delete_button(url:, label: "Delete", method: :delete, style: :danger, on_success: :backward)
|
38
|
+
validate_on_success!(on_success)
|
39
|
+
validate_button_style!(style)
|
40
|
+
|
41
|
+
{type: :button, label: label, url: url, method: method, style: style, onSuccess: on_success, sfSymbolName: 'trash', materialIconName: 'delete'}.compact
|
42
|
+
end
|
43
|
+
|
44
|
+
def mw_display_url_button(url:, label:, method: :put, style: :primary, confirm_title: nil, confirm_text: nil, on_success: :reload, sf_symbol_name: nil, material_icon_name: nil)
|
45
|
+
validate_on_success!(on_success)
|
46
|
+
validate_button_style!(style)
|
47
|
+
|
48
|
+
{type: :button, label: label, url: url, method: method, style: style, confirmTitle: confirm_title, confirmText: confirm_text, onSuccess: on_success, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
|
49
|
+
end
|
50
|
+
alias_method :mw_display_button_for_url, :mw_display_url_button
|
51
|
+
|
52
|
+
def mw_display_system_url_button(label:, apple_system_url: nil, android_deep_link: nil, style: :primary, sf_symbol_name: nil, material_icon_name: nil)
|
53
|
+
validate_button_style!(style)
|
54
|
+
raise 'Invalid android_deep_link' if android_deep_link && !android_deep_link.start_with?('http')
|
55
|
+
|
56
|
+
{type: :button, label: label, appleSystemURL: apple_system_url, androidDeepLink: android_deep_link, style: style, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
|
57
|
+
end
|
58
|
+
alias_method :mw_display_button_for_system_url, :mw_display_system_url_button
|
59
|
+
|
60
|
+
def mw_display_modal_workflow_button(label:, modal_workflow_name:, style: :primary, on_success: :none, sf_symbol_name: nil, material_icon_name: nil)
|
61
|
+
validate_on_success!(on_success)
|
62
|
+
validate_button_style!(style)
|
63
|
+
|
64
|
+
{type: :button, label: label, modalWorkflow: modal_workflow_name, style: style, onSuccess: on_success, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
|
65
|
+
end
|
66
|
+
alias_method :mw_display_button_for_modal_workflow, :mw_display_modal_workflow_button
|
67
|
+
|
68
|
+
private
|
69
|
+
def validate_content_mode!(on_success)
|
70
|
+
raise 'Unknown content_mode' unless CONTENT_MODE_OPTIONS.include?(on_success)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module MobileWorkflow
|
2
|
+
module Displayable
|
3
|
+
module Steps
|
4
|
+
module StyledContent
|
5
|
+
module Grid
|
6
|
+
def mw_grid_large_section(id:, text:)
|
7
|
+
raise 'Missing id' if id.nil?
|
8
|
+
raise 'Missing text' if text.nil?
|
9
|
+
|
10
|
+
{ id: id, text: text, type: :largeSection }
|
11
|
+
end
|
12
|
+
|
13
|
+
def mw_grid_small_section(id:, text:)
|
14
|
+
raise 'Missing id' if id.nil?
|
15
|
+
raise 'Missing text' if text.nil?
|
16
|
+
|
17
|
+
{ id: id, text: text, type: :smallSection }
|
18
|
+
end
|
19
|
+
|
20
|
+
def mw_grid_item(id: self.id, text:, detail_text: nil, preview_url: nil)
|
21
|
+
raise 'Missing id' if id.nil?
|
22
|
+
raise 'Missing text' if text.nil?
|
23
|
+
|
24
|
+
{ id: id, text: text, type: :item, detailText: detail_text, imageURL: preview_url }.compact
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module MobileWorkflow
|
2
|
+
module Displayable
|
3
|
+
module Steps
|
4
|
+
module StyledContent
|
5
|
+
module Stack
|
6
|
+
def mw_stack_title(id:, title:)
|
7
|
+
raise 'Missing id' if id.nil?
|
8
|
+
raise 'Missing title' if title.nil?
|
9
|
+
|
10
|
+
{ id: id, title: title, type: :title }
|
11
|
+
end
|
12
|
+
|
13
|
+
def mw_stack_text(id:, text:)
|
14
|
+
raise 'Missing id' if id.nil?
|
15
|
+
raise 'Missing text' if text.nil?
|
16
|
+
|
17
|
+
{ id: id, text: text, type: :text }
|
18
|
+
end
|
19
|
+
|
20
|
+
def mw_stack_list_item(id:, text:, detail_text: nil, preview_url: nil)
|
21
|
+
raise 'Missing id' if id.nil?
|
22
|
+
raise 'Missing text' if text.nil?
|
23
|
+
|
24
|
+
{ id: id.to_s, text: text, detailText: detail_text, type: :listItem, imageURL: preview_url }.compact
|
25
|
+
end
|
26
|
+
|
27
|
+
def mw_stack_button(id:, label:, url: nil, method: :nil, on_success: :none, style: :primary, modal_workflow_name: nil, link_url: nil, sf_symbol_name: nil, apple_system_url: nil, android_deep_link: nil, confirm_title: nil, confirm_text: nil)
|
28
|
+
raise 'Missing id' if id.nil?
|
29
|
+
raise 'Missing label' if label.nil?
|
30
|
+
|
31
|
+
validate_on_success!(on_success)
|
32
|
+
validate_button_style!(style)
|
33
|
+
|
34
|
+
{ id: id, type: :button, label: label, url: url, method: method, onSuccess: on_success, style: style, modalWorkflow: modal_workflow_name, linkURL: link_url, sfSymbolName: sf_symbol_name, appleSystemURL: apple_system_url, androidDeepLink: android_deep_link, confirmTitle: confirm_title, confirmText: confirm_text }.compact
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,163 +1,34 @@
|
|
1
1
|
module MobileWorkflow
|
2
2
|
module Displayable
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
ON_SUCCESS_OPTIONS = [:none, :reload, :backward, :forward]
|
7
|
-
BUTTON_STYLES = [:primary, :outline, :danger]
|
8
|
-
CONTENT_MODE_OPTIONS = [:scale_aspect_fill, :scale_aspect_fit]
|
9
|
-
QUESTION_STYLES = [:single_choice, :multiple_choice]
|
10
|
-
|
11
|
-
def mw_list_item(id: self.id, text:, detail_text: nil, sf_symbol_name: nil, material_icon_name: nil, image_attachment: nil)
|
12
|
-
mw_list_item = {id: id, text: text, detailText: detail_text, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}
|
13
|
-
mw_list_item[:imageURL] = preview_url(image_attachment, options: { resize_to_fill: [200, 200] }) if image_attachment
|
14
|
-
mw_list_item.compact
|
15
|
-
end
|
16
|
-
|
17
|
-
def mw_list_search_suggestion(id: self.id, text:, section_name:, sf_symbol_name: nil)
|
18
|
-
{id: id.to_s, text: text, sectionName: section_name, sfSymbolName: sf_symbol_name}.compact
|
19
|
-
end
|
20
|
-
|
21
|
-
def mw_map_item(id: self.id, text:, detail_text: nil, latitude:, longitude:)
|
22
|
-
{id: id.to_s, text: text, detailText: detail_text, latitude: latitude, longitude: longitude}.compact
|
23
|
-
end
|
24
|
-
|
25
|
-
def mw_pie_chart_item(id: self.id, label:, value:)
|
26
|
-
{id: id, label: label, value: value}.compact
|
27
|
-
end
|
28
|
-
|
29
|
-
def mw_display_text(text:, label: nil)
|
30
|
-
{type: :text, label: label, text: text.to_s}.compact
|
31
|
-
end
|
32
|
-
|
33
|
-
def mw_display_image(attachment, content_mode: :scale_aspect_fill, options: { resize_to_fill: [1200, 600] })
|
34
|
-
validate_content_mode!(content_mode)
|
35
|
-
|
36
|
-
{type: :image, contentMode: content_mode.to_s.camelize(:lower), previewURL: preview_url(attachment, options: options), url: attachment_url(attachment)}
|
37
|
-
end
|
38
|
-
|
39
|
-
def mw_display_unsplash_image(image_url)
|
40
|
-
if image_url.start_with? "https://unsplash.com/photos"
|
41
|
-
unsplash_id = image_url.split('/').last
|
42
|
-
image_url = "https://source.unsplash.com/#{unsplash_id}/800x600"
|
43
|
-
end
|
44
|
-
|
45
|
-
{type: :image, previewURL: image_url, url: image_url}.compact
|
46
|
-
end
|
47
|
-
|
48
|
-
def mw_display_video(attachment, preview_options: { resize_to_fill: [600, 1200] })
|
49
|
-
{type: :video, previewURL: preview_url(attachment, options: preview_options), url: attachment_url(attachment)}
|
50
|
-
end
|
51
|
-
|
52
|
-
def mw_display_button(label:, style: :primary, on_success: :forward, sf_symbol_name: nil, material_icon_name: nil)
|
53
|
-
validate_on_success!(on_success)
|
54
|
-
validate_button_style!(style)
|
55
|
-
|
56
|
-
{type: :button, label: label, style: style, onSuccess: on_success, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
|
57
|
-
end
|
58
|
-
|
59
|
-
def mw_display_delete_button(url:, label: "Delete", method: :delete, style: :danger, on_success: :backward)
|
60
|
-
validate_on_success!(on_success)
|
61
|
-
validate_button_style!(style)
|
62
|
-
|
63
|
-
{type: :button, label: label, url: url, method: method, style: style, onSuccess: on_success, sfSymbolName: 'trash', materialIconName: 'delete'}.compact
|
64
|
-
end
|
65
|
-
|
66
|
-
def mw_display_url_button(label:, url:, method: :put, style: :primary, confirm_title: nil, confirm_text: nil, on_success: :reload, sf_symbol_name: nil, material_icon_name: nil)
|
67
|
-
validate_on_success!(on_success)
|
68
|
-
validate_button_style!(style)
|
69
|
-
|
70
|
-
{type: :button, label: label, url: url, method: method, style: style, confirmTitle: confirm_title, confirmText: confirm_text, onSuccess: on_success, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
|
71
|
-
end
|
72
|
-
alias_method :mw_display_button_for_url, :mw_display_url_button
|
73
|
-
|
74
|
-
def mw_display_system_url_button(label:, apple_system_url: nil, android_deep_link: nil, style: :primary, sf_symbol_name: nil, material_icon_name: nil)
|
75
|
-
validate_button_style!(style)
|
76
|
-
raise 'Invalid android_deep_link' if android_deep_link && !android_deep_link.start_with?('http')
|
77
|
-
|
78
|
-
{type: :button, label: label, appleSystemURL: apple_system_url, androidDeepLink: android_deep_link, style: style, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
|
79
|
-
end
|
80
|
-
alias_method :mw_display_button_for_system_url, :mw_display_system_url_button
|
81
|
-
|
82
|
-
def mw_display_modal_workflow_button(label:, modal_workflow_name:, style: :primary, on_success: :none, sf_symbol_name: nil, material_icon_name: nil)
|
83
|
-
validate_on_success!(on_success)
|
84
|
-
validate_button_style!(style)
|
85
|
-
|
86
|
-
{type: :button, label: label, modalWorkflow: modal_workflow_name, style: style, onSuccess: on_success, sfSymbolName: sf_symbol_name, materialIconName: material_icon_name}.compact
|
87
|
-
end
|
88
|
-
alias_method :mw_display_button_for_modal_workflow, :mw_display_modal_workflow_button
|
89
|
-
|
90
|
-
def mw_text_choice_question(question:, style:, text_choices:)
|
91
|
-
raise 'Missing question' if question.blank?
|
92
|
-
raise 'Text Choices should be a hash' unless text_choices.is_a?(Hash)
|
93
|
-
validate_question_style!(style)
|
94
|
-
|
95
|
-
text_choices_a = text_choices.map{|k, v| {_class: "ORKTextChoice", exclusive: false, text: k, value: v} }.to_a
|
96
|
-
{ question: question, answerFormat: { _class: "ORKTextChoiceAnswerFormat", style: style.to_s.camelize(:lower), textChoices: text_choices_a}}
|
97
|
-
end
|
98
|
-
|
99
|
-
def mw_grid_large_section(id:, text:)
|
100
|
-
raise 'Missing id' if id.nil?
|
101
|
-
raise 'Missing text' if text.nil?
|
102
|
-
|
103
|
-
{ id: id, text: text, type: :largeSection }
|
104
|
-
end
|
105
|
-
|
106
|
-
def mw_grid_small_section(id:, text:)
|
107
|
-
raise 'Missing id' if id.nil?
|
108
|
-
raise 'Missing text' if text.nil?
|
109
|
-
|
110
|
-
{ id: id, text: text, type: :smallSection }
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(Steps::Form)
|
111
5
|
end
|
6
|
+
|
7
|
+
include Steps::List
|
8
|
+
include Steps::Map
|
9
|
+
include Steps::PieChart
|
10
|
+
include Steps::Question
|
11
|
+
include Steps::Stack
|
12
|
+
include Steps::StyledContent::Grid
|
13
|
+
include Steps::StyledContent::Stack
|
14
|
+
|
15
|
+
BUTTON_STYLES = [:primary, :outline, :danger, :textOnly]
|
16
|
+
ON_SUCCESS_OPTIONS = [:none, :reload, :backward, :forward]
|
112
17
|
|
113
|
-
def mw_grid_item(id: self.id, text:, image_attachment: nil, options: { resize_to_fill: [1560, 877.5] })
|
114
|
-
raise 'Missing id' if id.nil?
|
115
|
-
raise 'Missing text' if text.nil?
|
116
|
-
|
117
|
-
item = { id: id, text: text, type: :item }
|
118
|
-
item[:imageURL] = preview_url(image_attachment, options: options) if image_attachment
|
119
|
-
item
|
120
|
-
end
|
121
|
-
|
122
18
|
private
|
123
19
|
def validate_on_success!(on_success)
|
124
20
|
raise 'Unknown on_success action' unless ON_SUCCESS_OPTIONS.include?(on_success)
|
125
21
|
end
|
126
22
|
|
127
|
-
def validate_content_mode!(on_success)
|
128
|
-
raise 'Unknown content_mode' unless CONTENT_MODE_OPTIONS.include?(on_success)
|
129
|
-
end
|
130
|
-
|
131
23
|
def validate_button_style!(style)
|
132
24
|
raise 'Unknown style' unless BUTTON_STYLES.include?(style)
|
133
25
|
end
|
134
|
-
|
135
|
-
def validate_question_style!(style)
|
136
|
-
raise 'Unknown style' unless QUESTION_STYLES.include?(style)
|
137
|
-
end
|
138
|
-
|
139
|
-
def preview_url(attachment, options:)
|
140
|
-
return nil unless attachment.attached?
|
141
|
-
|
142
|
-
if attachment.image?
|
143
|
-
Rails.application.routes.url_helpers.rails_representation_url(attachment.variant(options), host: heroku_attachment_host)
|
144
|
-
elsif attachment.previewable?
|
145
|
-
Rails.application.routes.url_helpers.rails_representation_url(attachment.preview(options), host: heroku_attachment_host)
|
146
|
-
else
|
147
|
-
return nil
|
148
|
-
end
|
149
|
-
end
|
150
26
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
Rails.application.routes.url_helpers.rails_blob_url(attachment, host: heroku_attachment_host)
|
155
|
-
end
|
27
|
+
def camelcase_converter(string, first_letter: :upper)
|
28
|
+
string = string.split("_").map(&:capitalize).join
|
29
|
+
return string unless first_letter == :lower
|
156
30
|
|
157
|
-
|
158
|
-
# TODO: MBS - move this to a configuration property
|
159
|
-
app_name = Rails.env.test? ? 'test-app' : ENV.fetch('HEROKU_APP_NAME')
|
160
|
-
"https://#{app_name}.herokuapp.com"
|
31
|
+
string[0].downcase + string[1..-1]
|
161
32
|
end
|
162
33
|
end
|
163
34
|
end
|
@@ -67,6 +67,18 @@ module MobileWorkflow
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
+
def generate_deserializers
|
71
|
+
say "Generating deserializers"
|
72
|
+
|
73
|
+
model_name_to_properties.each_pair do |model_name, properties|
|
74
|
+
@deserializer_class = model_name
|
75
|
+
@deserializer_properties = properties.split(' ').map { |attribute| attribute.split(':').first }
|
76
|
+
|
77
|
+
template("deserializer.rb.erb", "app/services/#{model_name}_deserializer.rb")
|
78
|
+
template("deserializer_spec.rb.erb", "spec/services/#{model_name}_deserializer_spec.rb")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
70
82
|
def generate_controllers_and_routes
|
71
83
|
say "Generating controllers"
|
72
84
|
controller_name_to_actions = open_api_spec.controller_name_to_actions
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class <%= @deserializer_class.capitalize %>Deserializer
|
2
|
+
def self.parse(params)
|
3
|
+
<% @deserializer_properties.each do |attribute| -%>
|
4
|
+
<%= attribute %> = params.dig(:payload, :<%= attribute %>)
|
5
|
+
<% end -%>
|
6
|
+
|
7
|
+
{ <%= @deserializer_class %>: {
|
8
|
+
<% @deserializer_properties.each do |attribute| -%>
|
9
|
+
<%= attribute %>: <%= attribute %>,
|
10
|
+
<% end -%>
|
11
|
+
}.compact }
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_helper'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
RSpec.describe <%= @deserializer_class.capitalize %>Deserializer do
|
7
|
+
let(:params) { {} }
|
8
|
+
|
9
|
+
describe 'ok' do
|
10
|
+
let(:params) { { payload: payload } }
|
11
|
+
let(:parsed_params) { { <%= @deserializer_class %>: <%= @deserializer_class %>_params } }
|
12
|
+
let(:payload) {
|
13
|
+
{
|
14
|
+
<% @deserializer_properties.each do |attribute| -%>
|
15
|
+
<%= attribute %>: 'string',
|
16
|
+
<% end -%>
|
17
|
+
}
|
18
|
+
}
|
19
|
+
let(:<%= @deserializer_class %>_params) {
|
20
|
+
{
|
21
|
+
<% @deserializer_properties.each do |attribute| -%>
|
22
|
+
<%= attribute %>: 'string',
|
23
|
+
<% end -%>
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
it { expect(described_class.parse(params)).to eq parsed_params }
|
28
|
+
end
|
29
|
+
end
|
@@ -1,4 +1,8 @@
|
|
1
1
|
class User < ApplicationRecord
|
2
2
|
has_secure_password
|
3
|
+
|
4
|
+
has_many :access_grants, class_name: 'Doorkeeper::AccessGrant', foreign_key: :resource_owner_id, dependent: :destroy
|
5
|
+
has_many :access_tokens, class_name: 'Doorkeeper::AccessToken', foreign_key: :resource_owner_id, dependent: :destroy
|
6
|
+
|
3
7
|
validates :email, presence: true, uniqueness: true
|
4
8
|
end
|
@@ -5,11 +5,18 @@ module MobileWorkflow
|
|
5
5
|
|
6
6
|
class ModelGenerator < ActiveRecord::Generators::ModelGenerator
|
7
7
|
source_root File.join(File.dirname(ActiveRecord::Generators::ModelGenerator.instance_method(:create_migration_file).source_location.first), "templates")
|
8
|
-
|
8
|
+
|
9
|
+
class_option :doorkeeper_oauth, type: :boolean, default: false
|
10
|
+
|
9
11
|
def create_model_file
|
10
12
|
template File.join(File.dirname(__FILE__), "templates", "model.rb.erb"), File.join('app/models', class_path, "#{file_name}.rb")
|
11
13
|
end
|
12
|
-
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def doorkeeper_oauth?
|
18
|
+
options[:doorkeeper_oauth]
|
19
|
+
end
|
13
20
|
end
|
14
21
|
end
|
15
22
|
end
|
@@ -41,11 +41,13 @@ class <%= controller_class_name %>Controller < ApiController
|
|
41
41
|
# passport_id = params.dig(:payload, :choose_passport, :selected, :id)
|
42
42
|
|
43
43
|
Rails.logger.debug "Pre-rewrite params: #{params}"
|
44
|
-
|
44
|
+
|
45
|
+
parsed_params = <%= controller_class_name.singularize %>Deserializer.parse(params)
|
46
|
+
params.merge!(parsed_params)
|
45
47
|
end
|
46
48
|
|
47
49
|
def <%= singular_table_name.underscore %>_params
|
48
|
-
params.require(
|
50
|
+
params.require(:<%= singular_table_name %>).permit(<%= permitted_params %>)
|
49
51
|
end
|
50
52
|
<% end -%>
|
51
53
|
end
|
@@ -8,18 +8,33 @@ class <%= class_name %> < <%= parent_class_name.classify %>
|
|
8
8
|
<% end -%>
|
9
9
|
<% attributes.select(&:attachments?).each do |attribute| -%>
|
10
10
|
has_many_attached :<%= attribute.name %>
|
11
|
+
<% end -%>
|
12
|
+
<% if class_name == 'User' -%>
|
13
|
+
<% if doorkeeper_oauth? -%>
|
14
|
+
has_many :access_grants, class_name: 'Doorkeeper::AccessGrant', foreign_key: :resource_owner_id, dependent: :destroy
|
15
|
+
has_many :access_tokens, class_name: 'Doorkeeper::AccessToken', foreign_key: :resource_owner_id, dependent: :destroy
|
16
|
+
<% end -%>
|
17
|
+
<% attributes.each do |attribute| -%>
|
18
|
+
<% if attribute.name == 'email' -%>
|
19
|
+
validates :email, presence: true, uniqueness: { case_sensitive: false }
|
20
|
+
before_validation :downcase_email, if: :email_changed?
|
21
|
+
|
22
|
+
def downcase_email
|
23
|
+
self.email = email.downcase
|
24
|
+
end
|
25
|
+
<% end -%>
|
26
|
+
<% end -%>
|
11
27
|
<% end -%>
|
12
28
|
|
13
29
|
def list_item_as_json
|
14
30
|
mw_list_item(text: <%= attributes.first.name %>)
|
15
31
|
end
|
16
|
-
|
32
|
+
|
17
33
|
def display_as_json
|
18
34
|
[
|
19
35
|
mw_display_text(label: 'ID', text: id.to_s),
|
20
36
|
mw_display_text(label: 'Text', text: <%= attributes.first.name %>)
|
21
37
|
]
|
22
38
|
end
|
23
|
-
|
24
39
|
end
|
25
40
|
<% end -%>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require_relative '../../app/models/concerns/mobile_workflow/displayable/steps/styled_content/grid.rb'
|
2
|
+
require_relative '../../app/models/concerns/mobile_workflow/displayable/steps/styled_content/stack.rb'
|
3
|
+
require_relative '../../app/models/concerns/mobile_workflow/displayable/steps/form.rb'
|
4
|
+
require_relative '../../app/models/concerns/mobile_workflow/displayable/steps/list.rb'
|
5
|
+
require_relative '../../app/models/concerns/mobile_workflow/displayable/steps/map.rb'
|
6
|
+
require_relative '../../app/models/concerns/mobile_workflow/displayable/steps/pie_chart.rb'
|
7
|
+
require_relative '../../app/models/concerns/mobile_workflow/displayable/steps/question.rb'
|
8
|
+
require_relative '../../app/models/concerns/mobile_workflow/displayable/steps/stack.rb'
|
9
|
+
require_relative '../../app/models/concerns/mobile_workflow/displayable.rb'
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module MobileWorkflow
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
if defined?(Rails)
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
isolate_namespace MobileWorkflow
|
5
|
+
|
6
|
+
config.generators do |g|
|
7
|
+
g.test_framework :rspec
|
8
|
+
end
|
7
9
|
end
|
8
10
|
end
|
9
11
|
end
|
data/lib/mobile_workflow.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,49 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mobile_workflow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Brooke-Smith
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk-s3
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.60'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.60.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.60'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.60.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: aws-sdk-sns
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.23'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.23'
|
13
47
|
- !ruby/object:Gem::Dependency
|
14
48
|
name: rails
|
15
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -97,9 +131,19 @@ files:
|
|
97
131
|
- app/controllers/concerns/mobile_workflow/s3_storable.rb
|
98
132
|
- app/controllers/mobile_workflow/sns_notifications_controller.rb
|
99
133
|
- app/helpers/mobile_workflow/application_helper.rb
|
134
|
+
- app/jobs/mobile_workflow/add_attachment_job.rb
|
100
135
|
- app/jobs/mobile_workflow/application_job.rb
|
101
136
|
- app/mailers/mobile_workflow/application_mailer.rb
|
137
|
+
- app/models/concerns/mobile_workflow/attachable.rb
|
102
138
|
- app/models/concerns/mobile_workflow/displayable.rb
|
139
|
+
- app/models/concerns/mobile_workflow/displayable/steps/form.rb
|
140
|
+
- app/models/concerns/mobile_workflow/displayable/steps/list.rb
|
141
|
+
- app/models/concerns/mobile_workflow/displayable/steps/map.rb
|
142
|
+
- app/models/concerns/mobile_workflow/displayable/steps/pie_chart.rb
|
143
|
+
- app/models/concerns/mobile_workflow/displayable/steps/question.rb
|
144
|
+
- app/models/concerns/mobile_workflow/displayable/steps/stack.rb
|
145
|
+
- app/models/concerns/mobile_workflow/displayable/steps/styled_content/grid.rb
|
146
|
+
- app/models/concerns/mobile_workflow/displayable/steps/styled_content/stack.rb
|
103
147
|
- app/views/layouts/mobile_workflow/application.html.erb
|
104
148
|
- bin/mwf
|
105
149
|
- config/routes.rb
|
@@ -116,6 +160,8 @@ files:
|
|
116
160
|
- lib/generators/mobile_workflow/install/templates/app/views/layouts/application.html.erb
|
117
161
|
- lib/generators/mobile_workflow/install/templates/app/views/sessions/new.html.erb
|
118
162
|
- lib/generators/mobile_workflow/install/templates/create_users.rb
|
163
|
+
- lib/generators/mobile_workflow/install/templates/deserializer.rb.erb
|
164
|
+
- lib/generators/mobile_workflow/install/templates/deserializer_spec.rb.erb
|
119
165
|
- lib/generators/mobile_workflow/install/templates/seeds.rb.erb
|
120
166
|
- lib/generators/mobile_workflow/install/templates/sessions_controller.rb.erb
|
121
167
|
- lib/generators/mobile_workflow/install/templates/spec/factories/users.rb
|
@@ -133,6 +179,7 @@ files:
|
|
133
179
|
- lib/mobile_workflow/cli/aws_backend.rb
|
134
180
|
- lib/mobile_workflow/cli/dokku_backend.rb
|
135
181
|
- lib/mobile_workflow/cli/heroku_backend.rb
|
182
|
+
- lib/mobile_workflow/displayable.rb
|
136
183
|
- lib/mobile_workflow/engine.rb
|
137
184
|
- lib/mobile_workflow/open_api_spec/parser.rb
|
138
185
|
- lib/mobile_workflow/railtie.rb
|