apps 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/README.md +103 -19
  4. data/bin/setup +1 -2
  5. data/exe/apps +0 -0
  6. data/lib/apps/common/schema.rb +8 -0
  7. data/lib/apps/common/schema/base.rb +91 -0
  8. data/lib/apps/common/schema/concerns.rb +10 -0
  9. data/lib/apps/common/schema/concerns/potential_action.rb +35 -0
  10. data/lib/apps/gmail/markup.rb +158 -0
  11. data/lib/apps/gmail/schema.rb +21 -0
  12. data/lib/apps/gmail/schema/action.rb +19 -0
  13. data/lib/apps/gmail/schema/confirm_action.rb +21 -0
  14. data/lib/apps/gmail/schema/email_message.rb +15 -0
  15. data/lib/apps/gmail/schema/event.rb +41 -0
  16. data/lib/apps/gmail/schema/parcel_delivery.rb +43 -0
  17. data/lib/apps/gmail/schema/place.rb +24 -0
  18. data/lib/apps/gmail/schema/postal_address.rb +22 -0
  19. data/lib/apps/gmail/schema/rsvp_action.rb +25 -0
  20. data/lib/apps/gmail/schema/save_action.rb +20 -0
  21. data/lib/apps/gmail/schema/track_action.rb +20 -0
  22. data/lib/apps/gmail/schema/view_action.rb +20 -0
  23. data/lib/apps/outlook/actionable_messages.rb +13 -0
  24. data/lib/apps/outlook/schema.rb +20 -0
  25. data/lib/apps/outlook/schema/action.rb +14 -0
  26. data/lib/apps/outlook/schema/action_card.rb +42 -0
  27. data/lib/apps/outlook/schema/concerns.rb +14 -0
  28. data/lib/apps/outlook/schema/concerns/sections.rb +27 -0
  29. data/lib/apps/outlook/schema/concerns/sorted_potential_action.rb +17 -0
  30. data/lib/apps/outlook/schema/date_input.rb +17 -0
  31. data/lib/apps/outlook/schema/http_post.rb +44 -0
  32. data/lib/apps/outlook/schema/input.rb +23 -0
  33. data/lib/apps/outlook/schema/invoke_add_in_command.rb +33 -0
  34. data/lib/apps/outlook/schema/message_card.rb +39 -0
  35. data/lib/apps/outlook/schema/multichoice_input.rb +32 -0
  36. data/lib/apps/outlook/schema/open_uri.rb +24 -0
  37. data/lib/apps/outlook/schema/section.rb +52 -0
  38. data/lib/apps/outlook/schema/text_input.rb +20 -0
  39. data/lib/apps/version.rb +4 -1
  40. data/lib/examples.rb +121 -0
  41. data/package.json +12 -0
  42. metadata +37 -16
  43. data/lib/apps/adapters/gmail/markup.rb +0 -42
  44. data/lib/apps/adapters/gmail/markup/action.rb +0 -23
  45. data/lib/apps/adapters/gmail/markup/base.rb +0 -37
  46. data/lib/apps/adapters/gmail/markup/base_context.rb +0 -23
  47. data/lib/apps/adapters/gmail/markup/confirm_action.rb +0 -23
  48. data/lib/apps/adapters/gmail/markup/email_message.rb +0 -22
  49. data/lib/apps/adapters/gmail/markup/event.rb +0 -40
  50. data/lib/apps/adapters/gmail/markup/parcel_delivery.rb +0 -47
  51. data/lib/apps/adapters/gmail/markup/place.rb +0 -27
  52. data/lib/apps/adapters/gmail/markup/postal_address.rb +0 -26
  53. data/lib/apps/adapters/gmail/markup/rsvp_action.rb +0 -27
  54. data/lib/apps/adapters/gmail/markup/save_action.rb +0 -22
  55. data/lib/apps/adapters/gmail/markup/track_action.rb +0 -24
  56. data/lib/apps/adapters/gmail/markup/view_action.rb +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d762a4824f66d6229928fc34470ea42b524e3b1bf3a4caf9a0fb8404f1ef24d
4
- data.tar.gz: b091af6a9df6243fb7e266eb49a01c1b0b8e711cb713d7c236423af626f52549
3
+ metadata.gz: 76c0dd94631910ba52d822088647ffbe60838983b938472cb3d94d006e632720
4
+ data.tar.gz: 9001b062a90772948b2472bf08ca3ce4e1cdb58d74a590c08a67bed9e91d23b6
5
5
  SHA512:
6
- metadata.gz: fc96c1269946cda1d7bfd47af3b90866cfb205e2a069b0db3e4e17ad5dce08f12344b5d48c0da5db22d699132b4d46caec0107963f20b0da6adf98526c73dbdb
7
- data.tar.gz: 8c0d93de8530add374a3f006086f03b40752a38d1e18aa6561a9f00d9ceb987027f376b4cfddf9315a0f3c41ef1dd45ab9482bdfaf4de277485c3c259c5e0f33
6
+ metadata.gz: '058630fac77f03e0e9feb6b8648ad1809b8ba8de5990ae9323be349c16aad07734c153626180237e81013c920bcb4cca8fa98afb760738c13ce7214d20294dbd'
7
+ data.tar.gz: e937dcfa758a607370a5548eff21772f77c21c7445542b3a81e8225da497f689d355e27ee1d7ebafcb9724dd1a0606813fb25164443dec39fd1a386b518e98a6
data/.gitignore CHANGED
@@ -11,3 +11,7 @@
11
11
  .rspec_status
12
12
  *.gem
13
13
  Gemfile.lock
14
+
15
+ node_modules
16
+ yarn.lock
17
+ package-lock.json
data/README.md CHANGED
@@ -4,9 +4,9 @@ This is a toolkit to easily integrate into platform marketplaces and extension p
4
4
 
5
5
  The Apps gem is currently under development, with the goal of facilitating the following:
6
6
 
7
- * Google
8
- * [Gmail Add-ons](https://developers.google.com/gmail/add-ons/)
9
- * [Gmail Email Markup](https://developers.google.com/gmail/markup/)
7
+ * Gmail
8
+ * [Add-ons](https://developers.google.com/gmail/add-ons/)
9
+ * [Email Markup](https://developers.google.com/gmail/markup/)
10
10
  * AMP Pages
11
11
  * Microsoft Office 365
12
12
  * [Outlook Add-ins](https://docs.microsoft.com/en-us/outlook/add-ins/)
@@ -14,8 +14,8 @@ The Apps gem is currently under development, with the goal of facilitating the f
14
14
  * Slack
15
15
  * Bots
16
16
  * Commands
17
- * Heroku
18
- * Addons Marketplace (SSO)
17
+ * Heroku Elements Marketplace
18
+ * [Add-ons](https://devcenter.heroku.com/articles/building-an-add-on)
19
19
  * Facebook
20
20
  * Instant Articles
21
21
 
@@ -39,20 +39,75 @@ Or install it yourself as:
39
39
 
40
40
  ### Gmail Email Markup
41
41
 
42
- You can generate [schema.org](http://schema.org) markup for email in Gmail utilizing simple helper classes.
43
-
44
- * Use the helper methods in `Apps::Adapters::Gmail::Markup` to easily define your markup
45
- * Helper methods to generate an object model for you to "fill in":
46
- * Confirm Action - `Apps::Adapters::Gmail::Markup#build_confirm_action`
47
- * Save Action - `Apps::Adapters::Gmail::Markup#build_save_action`
48
- * RSVP Action - `Apps::Adapters::Gmail::Markup#build_rsvp_action`
49
- * View Action - `Apps::Adapters::Gmail::Markup#build_view_action`
50
- * Track Action - `Apps::Adapters::Gmail::Markup#build_track_action`
51
- * Helper methods to serialize your object model:
52
- * `as_json` generates a `Hash` represenation of metadata
53
- * `to_json` generates a JSON string representation of metadata
54
- * `to_script` generates a "pretty" JSON enclosed by `script` tags specifying `JSON-LD` type
55
- * NOTE: `to_script` is only available on "context" objects that represent the root of the markup needed for embedding in emails. This is the object returned by each of the helper methods above for each "Action"
42
+ You can generate [schema.org](http://schema.org) markup for email in Gmail utilizing simple helper classes. Use the helper methods in `Apps::Gmail::Markup` to easily define your markup.
43
+
44
+ * [Confirm Action](https://developers.google.com/gmail/markup/reference/one-click-action)
45
+ * [Save Action](https://developers.google.com/gmail/markup/reference/one-click-action)
46
+ * [RSVP Action](https://developers.google.com/gmail/markup/reference/rsvp-action)
47
+ * [View Action](https://developers.google.com/gmail/markup/reference/go-to-action)
48
+ * [Track Action](https://developers.google.com/gmail/markup/reference/go-to-action)
49
+
50
+ Explore `Apps::Gmail::Markup` and related classes to see how you can customize your metadata.
51
+
52
+ ```ruby
53
+ require 'apps/gmail/markup'
54
+
55
+ # Confirm Action (takes additional keyword options)
56
+ confirm_action = Apps::Gmail::Markup.confirm_action('Button Text', 'http://example.org/confirm/endpoint')
57
+
58
+ confirm_action.as_json
59
+ # => {"@context"=>"http://schema.org", "@type"=>"EmailMessage", "potentialAction"=>{"@type"=>"ConfirmAction", "handler"=>{"@type"=>"HttpActionHandler", "url"=>"http://example.org/confirm/endpoint"}}}
60
+
61
+ puts confirm_action.to_json
62
+ # => {"@context":"http://schema.org","@type":"EmailMessage","potentialAction":{"@type":"ConfirmAction","handler":{"@type":"HttpActionHandler","url":"http://example.org/confirm/endpoint"}}}
63
+
64
+ # NOTE: `to_script` is only available on "context" objects that represent the root of the markup
65
+ # needed for embedding in emails. This is the object returned by each of the helper methods
66
+ # above for each "Action"
67
+ puts confirm_action.to_script
68
+ # =>
69
+ # <script type="application/ld+json">
70
+ # {
71
+ # "@context": "http://schema.org",
72
+ # "@type": "EmailMessage",
73
+ # "potentialAction": {
74
+ # "@type": "ConfirmAction",
75
+ # "handler": {
76
+ # "@type": "HttpActionHandler",
77
+ # "url": "http://example.org/confirm/endpoint"
78
+ # }
79
+ # }
80
+ # }
81
+ # </script>
82
+
83
+ # Save Action (takes additional keyword options)
84
+ save_action = Apps::Gmail::Markup.save_action('Button Text', 'http://example.org/save/endpoint')
85
+
86
+ # RSVP Action (takes additional keyword options)
87
+ rsvp_action = Apps::Gmail::Markup.rsvp_action('Event Name',
88
+ yes_url: 'http://example.org/yes/endpoint',
89
+ no_url: 'http://example.org/no/endpoint',
90
+ maybe_url: 'http://example.org/maybe/endpoint'
91
+ )
92
+
93
+ # View Action (takes additional keyword options)
94
+ view_action = Apps::Gmail::Markup.view_action('Button Text', 'http://example.org/save/endpoint')
95
+
96
+ # Track Action (takes additional keyword options)
97
+ track_action = Apps::Gmail::Markup.track_action('Button Text', 'http://example.org/save/endpoint')
98
+ ```
99
+
100
+ You need to [register with Google](https://developers.google.com/gmail/markup/registering-with-google) to get your actions approved.
101
+
102
+ **Related Resoures:**
103
+
104
+ * [Embeding JSON-LD in HTML Documents](https://json-ld.org/spec/latest/json-ld/#embedding-json-ld-in-html-documents)
105
+ * [Test Your Schemas](https://developers.google.com/gmail/markup/testing-your-schema)
106
+ * [Email Markup Tester](https://www.google.com/webmasters/markup-tester/)
107
+ * [Structured Data Markup Helper](https://www.google.com/webmasters/markup-helper/?email=true)
108
+ * [Register with Google](https://developers.google.com/gmail/markup/registering-with-google)
109
+ * Articles:
110
+ * [A Guide to Setting up Gmail Inbox Actions](http://blog.meldium.com/home/2014/5/19/setting-up-gmail-inbox-actions) by Meldium
56
111
 
57
112
  #### TODO
58
113
 
@@ -60,6 +115,35 @@ You can generate [schema.org](http://schema.org) markup for email in Gmail utili
60
115
  * Email Markup helpers for other Gmail-supported metadata need to be implemented
61
116
  * Additional Gmail integrations need to be developed
62
117
 
118
+ ### Microsoft Office 365
119
+
120
+ * [Office Developer](https://github.com/OfficeDev) - git repositories of example projects for Outlook Add-ins, etc.
121
+ * [Yeoman generator](https://github.com/OfficeDev/generator-office) - generator for creating [Microsoft office Add-ins](https://docs.microsoft.com/en-us/office/dev/add-ins/)
122
+ * [Message Card Playground](https://messagecardplayground.azurewebsites.net/) - test schema.org markup and how it generates add-in UI
123
+
124
+ #### Outlook: Actionable Messages via Email
125
+
126
+ All Actionable Card markup schemas supported by Microsoft Office are implemented in this library (see [Actionable message card reference](https://docs.microsoft.com/en-us/outlook/actionable-messages/card-reference)).
127
+
128
+ ```ruby
129
+ require 'apps/outlook/actionable_messages'
130
+
131
+ message_card = Apps::Outlook::ActionableMessages.message_card('Summary text', 'Title text')
132
+ ```
133
+
134
+ You can take a look at (and run) [lib/examples.rb](./lib/examples.rb) to see some sample markup.
135
+
136
+ $ ruby lib/examples.rb
137
+
138
+ You can get more example schemas or copy/paste the output of `examples.rb` to test rendering here: https://messagecardplayground.azurewebsites.net/
139
+
140
+ **Related Resources:**
141
+
142
+ * [Actionable Messages via Email](https://docs.microsoft.com/en-us/outlook/actionable-messages/actionable-messages-via-email)
143
+ * [Actionable message card reference](https://docs.microsoft.com/en-us/outlook/actionable-messages/card-reference)
144
+ * [Register your service with the actionable email developer dashboard](https://docs.microsoft.com/en-us/outlook/actionable-messages/actionable-email-dev-dashboard)
145
+ * [Register your connector with the Office 365 Connectors developer dashboard](https://docs.microsoft.com/en-us/outlook/actionable-messages/connectors-dev-dashboard) - setup a connector for users to authorize your add-in
146
+
63
147
  ## Development
64
148
 
65
149
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/bin/setup CHANGED
@@ -4,5 +4,4 @@ IFS=$'\n\t'
4
4
  set -vx
5
5
 
6
6
  bundle install
7
-
8
- # Do any other automated setup that you need to do here
7
+ yarn install
data/exe/apps CHANGED
File without changes
@@ -0,0 +1,8 @@
1
+ require_relative 'schema/base'
2
+
3
+ module Apps
4
+ module Common
5
+ module Schema
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,91 @@
1
+ require 'json'
2
+
3
+ module Apps
4
+ module Common
5
+ module Schema
6
+ class Base
7
+ DEFAULT_CONTEXT = 'http://schema.org'
8
+
9
+ attr_reader :context, # the schema.org URL if not the default
10
+ :force_context # force including the default schema.org context if none specified
11
+
12
+ def initialize(**attrs)
13
+ prune(attrs).each { |name, value| instance_variable_set(:"@#{name}", value) }
14
+ end
15
+
16
+ def type
17
+ @type ||= self.class.name.split('::')[-1]
18
+ end
19
+
20
+ def serialize
21
+ {
22
+ "@context" => serialized_context,
23
+ "@type" => type
24
+ }
25
+ end
26
+
27
+ def serialize_hash(hash, key: 'name', value: 'value')
28
+ hash.map do |k, v|
29
+ {
30
+ key => k,
31
+ value => v
32
+ }
33
+ end
34
+ end
35
+
36
+ def serialized_context
37
+ if force_context
38
+ context || DEFAULT_CONTEXT
39
+ else
40
+ context
41
+ end
42
+ end
43
+
44
+ def as_json(force_context: false)
45
+ serialized = serialize
46
+
47
+ if force_context == true
48
+ serialized['@context'] ||= DEFAULT_CONTEXT
49
+ elsif force_context.is_a?(String)
50
+ serialized['@context'] = force_context
51
+ end
52
+
53
+ prune serialized
54
+ end
55
+
56
+ def to_json(force_context: false, pretty: false)
57
+ hash = as_json(force_context: force_context)
58
+
59
+ pretty ? JSON.pretty_generate(hash) : JSON.dump(hash)
60
+ end
61
+
62
+ def to_script(force_context: false, pretty: true)
63
+ json = to_json(pretty: pretty, force_context: force_context)
64
+
65
+ %`<script type="application/ld+json">\n#{json}\n</script>`
66
+ end
67
+
68
+ protected
69
+
70
+ def prune(hash)
71
+ hash.each_with_object({}) do |(key, value), result|
72
+ # recursive prune
73
+ result[key] = case value
74
+ when nil
75
+ next
76
+ when Array
77
+ next if value.empty?
78
+ value.map{ |v| v.is_a?(Hash) ? prune(v) : v }
79
+ when Hash
80
+ value = prune value
81
+ next if value.empty?
82
+ value
83
+ else
84
+ value
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'concerns/potential_action'
2
+
3
+ module Apps
4
+ module Common
5
+ module Schema
6
+ module Concerns
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ module Apps
2
+ module Common
3
+ module Schema
4
+ module Concerns
5
+ module PotentialAction
6
+ # Handles the case when there is one action
7
+ def action
8
+ actions.first if actions.size == 1
9
+ end
10
+
11
+ def actions
12
+ @actions ||= [@action].compact
13
+ end
14
+
15
+ def serialize
16
+ if actions.any?
17
+ super.merge("potentialAction" => serialize_actions)
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ def serialize_actions
24
+ # Gmail uses a hash for one action and an array for multiple.
25
+ # Outlook uses an array regardless and breaks if it's a hash.
26
+ # So we will always return an array.
27
+
28
+ # action&.serialize || actions.map(&:serialize)
29
+ actions.map(&:serialize)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,158 @@
1
+ require_relative 'schema'
2
+
3
+ module Apps
4
+ module Gmail
5
+ module Markup
6
+ class << self
7
+ # {
8
+ # "@context": "http://schema.org",
9
+ # "@type": "EmailMessage",
10
+ # "potentialAction": {
11
+ # "@type": "ConfirmAction",
12
+ # "handler": {
13
+ # "@type": "HttpActionHandler",
14
+ # "url": "https://example.org/confirm/endpoint"
15
+ # },
16
+ # "name": "Action Name"
17
+ # }
18
+ # }
19
+ def confirm_action(name, url, **attrs)
20
+ action = Schema::ConfirmAction.new(name: name, url: url)
21
+ root = Schema::EmailMessage.new(action: action, **attrs)
22
+
23
+ root
24
+ end
25
+
26
+ # {
27
+ # "@context": "http://schema.org",
28
+ # "@type": "EmailMessage",
29
+ # "potentialAction": {
30
+ # "@type": "SaveAction",
31
+ # "handler": {
32
+ # "@type": "HttpActionHandler",
33
+ # "url": "https://example.org/save/endpoint"
34
+ # },
35
+ # "name": "Action Name"
36
+ # }
37
+ # }
38
+ def save_action(name, url, **attrs)
39
+ action = Schema::SaveAction.new(name: name, url: url)
40
+ root = Schema::EmailMessage.new(action: action, **attrs)
41
+
42
+ root
43
+ end
44
+
45
+ # {
46
+ # "@context": "http://schema.org",
47
+ # "@type": "Event",
48
+ # "name": "Action Name",
49
+ # "location": {
50
+ # "@type": "Place",
51
+ # "address": {
52
+ # "@type": "PostalAddress"
53
+ # }
54
+ # },
55
+ # "potentialAction": [
56
+ # {
57
+ # "@type": "RsvpAction",
58
+ # "handler": {
59
+ # "@type": "HttpActionHandler",
60
+ # "url": "http://example.org/yes/endpoint"
61
+ # },
62
+ # "rsvpResponse": "yes",
63
+ # "attendance": "http://schema.org/RsvpAttendance/Yes"
64
+ # },
65
+ # {
66
+ # "@type": "RsvpAction",
67
+ # "handler": {
68
+ # "@type": "HttpActionHandler",
69
+ # "url": "http://example.org/no/endpoint"
70
+ # },
71
+ # "rsvpResponse": "no",
72
+ # "attendance": "http://schema.org/RsvpAttendance/No"
73
+ # },
74
+ # {
75
+ # "@type": "RsvpAction",
76
+ # "handler": {
77
+ # "@type": "HttpActionHandler",
78
+ # "url": "http://example.org/maybe/endpoint"
79
+ # },
80
+ # "rsvpResponse": "maybe",
81
+ # "attendance": "http://schema.org/RsvpAttendance/Maybe"
82
+ # }
83
+ # ]
84
+ # }
85
+ def rsvp_action(name, yes_url: nil, no_url: nil, maybe_url: nil, **attrs)
86
+ Schema::Event.new(name: name, **attrs).tap do |root|
87
+ root.build_location unless root.location
88
+ root.build_rsvp_actions unless root.actions.any?
89
+
90
+ responses = {
91
+ 'Yes' => yes_url,
92
+ 'No' => no_url,
93
+ 'Maybe' => maybe_url
94
+ }
95
+
96
+ # Specify RSVP URLs
97
+ responses.each do |response, url|
98
+ next if url.nil?
99
+
100
+ action = root.actions.find { |action| action.response == response }
101
+ action ||= root.add_action_for(response)
102
+
103
+ action.url = url
104
+ end
105
+ end
106
+ end
107
+
108
+ # {
109
+ # "@context": "http://schema.org",
110
+ # "@type": "EmailMessage",
111
+ # "potentialAction": {
112
+ # "@type": "ViewAction",
113
+ # "name": "Action Name",
114
+ # "target": "http://example.org/view/endpoint"
115
+ # }
116
+ # }
117
+ def view_action(name, target, **attrs)
118
+ action = Schema::ViewAction.new(name: name, target: target)
119
+ root = Schema::EmailMessage.new(action: action, **attrs)
120
+
121
+ root
122
+ end
123
+
124
+ # {
125
+ # "@context": "http://schema.org",
126
+ # "@type": "ParcelDelivery",
127
+ # "deliveryAddress": {
128
+ # "@type": "PostalAddress"
129
+ # },
130
+ # "carrier": {
131
+ # "@type": "Organization"
132
+ # },
133
+ # "itemShipped": {
134
+ # "@type": "Product"
135
+ # },
136
+ # "partOfOrder": {
137
+ # "@type": "Order",
138
+ # "merchant": {
139
+ # "@type": "Organization"
140
+ # }
141
+ # },
142
+ # "potentialAction": {
143
+ # "@type": "TrackAction",
144
+ # "name": "Action Name",
145
+ # "target": "https://example.org/track/endpoint"
146
+ # }
147
+ # }
148
+ def track_action(name, target, **attrs)
149
+ action = Schema::TrackAction.new(name: name, target: target)
150
+
151
+ Schema::ParcelDelivery.new(action: action, **attrs).tap do |root|
152
+ root.build_delivery_address unless root.delivery_address
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end