mobile_workflow 0.7.3 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +83 -0
- data/app/controllers/concerns/mobile_workflow/s3_storable.rb +14 -11
- 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,19 +1,23 @@
|
|
1
|
+
require 'aws-sdk-s3'
|
2
|
+
|
1
3
|
module MobileWorkflow
|
2
4
|
module S3Storable
|
3
5
|
if Object.const_defined?("Aws::S3")
|
4
6
|
extend ActiveSupport::Concern
|
5
7
|
|
6
8
|
def binary_urls(object)
|
7
|
-
return unless params[
|
9
|
+
return unless params[:binaries]
|
8
10
|
|
9
|
-
params[
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
params[:binaries].map do |binary|
|
12
|
+
identifier = binary[:identifier]
|
13
|
+
object_attribute = identifier.split(".")[0] # ensure extension doesnt get added here
|
14
|
+
extension = binary[:mimetype].split('/')[1] # i.e. image/jpg --> jpg, video/mp4 --> mp4
|
15
|
+
metadata = binary[:metadata]
|
16
|
+
|
13
17
|
{
|
14
|
-
|
15
|
-
|
16
|
-
|
18
|
+
identifier: identifier,
|
19
|
+
url: presigned_url("#{object.class.name.underscore}/#{object.id}/#{object_attribute}/#{s3_object_uuid}.#{extension}", metadata: metadata),
|
20
|
+
method: "PUT"
|
17
21
|
}
|
18
22
|
end
|
19
23
|
end
|
@@ -23,8 +27,8 @@ module MobileWorkflow
|
|
23
27
|
SecureRandom.uuid
|
24
28
|
end
|
25
29
|
|
26
|
-
def presigned_url(key)
|
27
|
-
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)
|
28
32
|
end
|
29
33
|
|
30
34
|
def presigner
|
@@ -34,7 +38,6 @@ module MobileWorkflow
|
|
34
38
|
def s3_client
|
35
39
|
Aws::S3::Client.new(region: ENV['AWS_REGION'], access_key_id: ENV['AWS_ACCESS_ID'], secret_access_key: ENV['AWS_SECRET_KEY'])
|
36
40
|
end
|
37
|
-
|
38
41
|
end
|
39
42
|
end
|
40
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: attachment_host)
|
144
|
-
elsif attachment.previewable?
|
145
|
-
Rails.application.routes.url_helpers.rails_representation_url(attachment.preview(options), host: 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' : 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
|