textris 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +118 -0
- data/lib/textris.rb +8 -0
- data/lib/textris/base.rb +75 -0
- data/lib/textris/delivery.rb +16 -0
- data/lib/textris/delivery/mail.rb +95 -0
- data/lib/textris/delivery/test.rb +29 -0
- data/lib/textris/message.rb +65 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2fd3e9eb6888e74d470bde1afe44a785ecbec855
|
4
|
+
data.tar.gz: b32e98392db542754742c288849125af7f7b28fd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 352443cb1e2001e43ae58a11d863f80ec5b11b84956aae8f5cd654f75a0da56f313d79b683b27194a5768d8485733a5f11a016366cbf404450bb1ba6bf5b620b
|
7
|
+
data.tar.gz: 4b90542d764e5b78a0cd682065f5df71050cb05f0a971d21979a43627c2e631b8f55b7420efa97fe2ef07fe2416799e4112688a337755355bdfc0e7533322a4a
|
data/README.md
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# Textris
|
2
|
+
|
3
|
+
Simple gem for implementing texter classes which allow sending SMS messages in similar way to how e-mails are implemented and sent with ActionMailer-based mailers.
|
4
|
+
|
5
|
+
Unlike similar gems, **Textris** has some unique features:
|
6
|
+
|
7
|
+
- phone number E164 validation and normalization with the [Phony](https://github.com/floere/phony) gem
|
8
|
+
- multiple, per-environment configurable delivery methods
|
9
|
+
- e-mail proxy allowing to inspect messages using [Mailinator](https://mailinator.com/) or similar service
|
10
|
+
- support for testing using self-explanatory `Textris::Base.deliveries`
|
11
|
+
- simple, extensible code written from the ground up instead of copying *ActionMailer*
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add to `Gemfile`:
|
16
|
+
|
17
|
+
gem 'textris'
|
18
|
+
|
19
|
+
And run:
|
20
|
+
|
21
|
+
bundle install
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
Place texter classes in `app/texters` (e.g. `app/texters/user_texter.rb`):
|
26
|
+
|
27
|
+
class UserTexter < Textris::Base
|
28
|
+
default :from => "Our Team <+48 666-777-888>"
|
29
|
+
|
30
|
+
def welcome(user)
|
31
|
+
@user = user
|
32
|
+
|
33
|
+
text :to => @user.phone
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Place relevant view templates in `app/views/<texter_name>/<action_name>.text.*`, (e.g. `app/views/user_texter/welcome.text.erb`):
|
38
|
+
|
39
|
+
Welcome to our system, <%= @user.name %>!
|
40
|
+
|
41
|
+
Invoke them from application logic:
|
42
|
+
|
43
|
+
class User < ActiveRecord::Base
|
44
|
+
after_create do
|
45
|
+
UserTexter.welcome(self).deliver
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
## Testing
|
50
|
+
|
51
|
+
Access all messages that were sent with the `:test` delivery:
|
52
|
+
|
53
|
+
Textris::Base.deliveries
|
54
|
+
|
55
|
+
You may want to clear the delivery queue before each test:
|
56
|
+
|
57
|
+
before(:each) do
|
58
|
+
Textris::Base.deliveries.clear
|
59
|
+
end
|
60
|
+
|
61
|
+
Keep in mind that messages targeting multiple phone numbers, like:
|
62
|
+
|
63
|
+
text :to => ['48111222333', '48222333444']
|
64
|
+
|
65
|
+
will yield multiple message deliveries, each for specific phone number.
|
66
|
+
|
67
|
+
## Configuration
|
68
|
+
|
69
|
+
You can change default settings by placing them in any of environment files, like `development.rb` or `test.rb`, or setting them globally in `application.rb`.
|
70
|
+
|
71
|
+
Choose the delivery method:
|
72
|
+
|
73
|
+
# Don't send anything, access your messages via Textris::Base.deliveries
|
74
|
+
config.textris_delivery_method = :test
|
75
|
+
|
76
|
+
# Send e-mails instead of SMSes in order to inspect their content
|
77
|
+
config.textris_delivery_method = :mail
|
78
|
+
|
79
|
+
Configure the mail delivery:
|
80
|
+
|
81
|
+
# E-mail sender, here: "our-team-48666777888@test.app-name.com"
|
82
|
+
config.textris_mail_from_template = '%{from_name:d}-%{from_phone}@%{env:d}.%{app:d}.com'
|
83
|
+
|
84
|
+
# E-mail target, here: "app-name-test-48111222333-texts@mailinator.com"
|
85
|
+
config.textris_mail_to_template = '%{app:d}-%{env:d}-%{to_phone}-texts@mailinator.com'
|
86
|
+
|
87
|
+
# E-mail subject, here: "User texter: Welcome"
|
88
|
+
config.textris_mail_subject_template = '%{texter:dh} texter: %{action:h}'
|
89
|
+
|
90
|
+
# E-mail body, here: "Welcome to our system, Mr Jones!"
|
91
|
+
config.textris_mail_body_template = '%{content}'
|
92
|
+
|
93
|
+
### Template interpolation
|
94
|
+
|
95
|
+
You can use the following interpolations in your mail templates:
|
96
|
+
|
97
|
+
- `app`: application name (like `AppName`)
|
98
|
+
- `env`: enviroment name (like `test` or `production`)
|
99
|
+
- `texter`: texter name (like `User`)
|
100
|
+
- `action`: action name (like `welcome`)
|
101
|
+
- `from_name`: name of the sender (like `Our Team`)
|
102
|
+
- `from_phone`: phone number of the sender (like `48666777888`)
|
103
|
+
- `to_phone`: phone number of the recipient (like `48111222333`)
|
104
|
+
- `content`: message content (like `Welcome to our system, Mr Jones!`)
|
105
|
+
|
106
|
+
You can add optional interpolation modifiers using the `%{variable:modifiers}` syntax. These are most useful for making names e-mail friendly. The following modifiers are available:
|
107
|
+
|
108
|
+
- `d`: dasherize (for instance, `AppName` becomes `app-name`)
|
109
|
+
- `h`: humanize (for instance, `user_name` becomes `User name`)
|
110
|
+
- `p`: format phone (for instance, `48111222333` becomes `+48 111 222 333`)
|
111
|
+
|
112
|
+
## Contributing
|
113
|
+
|
114
|
+
1. Fork it (https://github.com/visualitypl/codegrade/fork)
|
115
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
116
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
117
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
118
|
+
5. Create a new Pull Request
|
data/lib/textris.rb
ADDED
data/lib/textris/base.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'render_anywhere'
|
2
|
+
|
3
|
+
module Textris
|
4
|
+
class Base
|
5
|
+
include RenderAnywhere
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def deliveries
|
9
|
+
::Textris::Delivery::Test.messages
|
10
|
+
end
|
11
|
+
|
12
|
+
def with_defaults(options)
|
13
|
+
(@defaults || {}).merge(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def default(options)
|
19
|
+
@defaults ||= {}
|
20
|
+
@defaults.merge!(options)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def method_missing(method_name, *args)
|
26
|
+
self.new(method_name, *args).call_action
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class RenderingController < RenderAnywhere::RenderingController
|
31
|
+
def default_url_options
|
32
|
+
ActionMailer::Base.default_url_options || {}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(action, *args)
|
37
|
+
@action = action
|
38
|
+
@args = args
|
39
|
+
end
|
40
|
+
|
41
|
+
def call_action
|
42
|
+
send(@action, *@args)
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def text(options = {})
|
48
|
+
set_instance_variables_for_rendering
|
49
|
+
|
50
|
+
options = self.class.with_defaults(options)
|
51
|
+
options.merge!(
|
52
|
+
:texter => self.class,
|
53
|
+
:action => @action,
|
54
|
+
:content => options[:body].is_a?(String) ? options[:body] : render(
|
55
|
+
:template => template_name, :formats => ['text']))
|
56
|
+
|
57
|
+
::Textris::Message.new(options)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def template_name
|
63
|
+
class_name = self.class.to_s.underscore.sub('texter/', '')
|
64
|
+
action_name = @action
|
65
|
+
|
66
|
+
"#{class_name}/#{action_name}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_instance_variables_for_rendering
|
70
|
+
instance_variables.each do |var|
|
71
|
+
set_instance_variable(var.to_s.sub('@', ''), instance_variable_get(var))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Textris
|
2
|
+
module Delivery
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def get
|
6
|
+
case Rails.application.config.try(:textris_delivery_method).to_s
|
7
|
+
when 'mail'
|
8
|
+
::Textris::Delivery::Mail
|
9
|
+
when 'test'
|
10
|
+
::Textris::Delivery::Test
|
11
|
+
else
|
12
|
+
::Textris::Delivery::Test
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Textris
|
2
|
+
module Delivery
|
3
|
+
class Mail
|
4
|
+
class Mailer < ActionMailer::Base
|
5
|
+
def notify(from, to, subject, body)
|
6
|
+
mail :from => from, :to => to, :subject => subject, :body => body
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def send_message_to_all(message)
|
12
|
+
message.to.each do |to|
|
13
|
+
send_message(to, message)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def send_message(to, message)
|
20
|
+
template_vars = { :to_phone => to }
|
21
|
+
|
22
|
+
from = apply_template from_template, message, template_vars
|
23
|
+
to = apply_template to_template, message, template_vars
|
24
|
+
subject = apply_template subject_template, message, template_vars
|
25
|
+
body = apply_template body_template, message, template_vars
|
26
|
+
|
27
|
+
::Textris::Delivery::Mail::Mailer.notify(
|
28
|
+
from, to, subject, body).deliver
|
29
|
+
end
|
30
|
+
|
31
|
+
def from_template
|
32
|
+
Rails.application.config.try(:textris_mail_from_template) ||
|
33
|
+
"%{from_name:d}-%{from_phone}@%{env:d}.%{app:d}.com"
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_template
|
37
|
+
Rails.application.config.try(:textris_mail_to_template) ||
|
38
|
+
"%{app:d}-%{env:d}-%{to_phone}-texts@mailinator.com"
|
39
|
+
end
|
40
|
+
|
41
|
+
def subject_template
|
42
|
+
Rails.application.config.try(:textris_mail_subject_template) ||
|
43
|
+
"%{texter:dh} texter: %{action:h}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def body_template
|
47
|
+
Rails.application.config.try(:textris_mail_body_template) ||
|
48
|
+
"%{content}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def apply_template(template, message, vars)
|
52
|
+
template.gsub(/\%\{[a-z_:]+\}/) do |match|
|
53
|
+
directive = match.gsub(/[%{}]/, '')
|
54
|
+
var = directive.split(':').first
|
55
|
+
modifiers = directive.split(':')[1] || ''
|
56
|
+
|
57
|
+
content = if var == 'app'
|
58
|
+
Rails.application.class.parent_name
|
59
|
+
elsif var == 'env'
|
60
|
+
Rails.env
|
61
|
+
elsif var == 'texter' && (texter = message.texter).present?
|
62
|
+
texter.to_s.split('::').last.sub(/Texter$/, '')
|
63
|
+
elsif var == 'action' && (action = message.action).present?
|
64
|
+
action.to_s
|
65
|
+
elsif var == 'from_name' && (from = message.from_name).present?
|
66
|
+
from.to_s
|
67
|
+
elsif (value = vars[var.to_sym]).present?
|
68
|
+
value
|
69
|
+
elsif ['content', 'from_phone'].include?(var)
|
70
|
+
message.send(var)
|
71
|
+
end.to_s.strip
|
72
|
+
|
73
|
+
unless content.present?
|
74
|
+
content = 'unknown'
|
75
|
+
end
|
76
|
+
|
77
|
+
if modifiers.include?('d')
|
78
|
+
content = content.underscore.dasherize
|
79
|
+
end
|
80
|
+
|
81
|
+
if modifiers.include?('h')
|
82
|
+
content = content.humanize.gsub(/[-_]/, ' ')
|
83
|
+
end
|
84
|
+
|
85
|
+
if modifiers.include?('p')
|
86
|
+
content = Phony.format(content) rescue content
|
87
|
+
end
|
88
|
+
|
89
|
+
content
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Textris
|
2
|
+
module Delivery
|
3
|
+
class Test
|
4
|
+
class << self
|
5
|
+
def send_message_to_all(message)
|
6
|
+
message.to.each do |to|
|
7
|
+
send_message(to, message)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def messages
|
12
|
+
@messages ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def send_message(to, message)
|
18
|
+
messages.push(::Textris::Message.new(
|
19
|
+
:content => message.content,
|
20
|
+
:from_name => message.from_name,
|
21
|
+
:from_phone => message.from_phone,
|
22
|
+
:texter => message.texter,
|
23
|
+
:action => message.action,
|
24
|
+
:to => to))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Textris
|
2
|
+
class Message
|
3
|
+
attr_reader :content, :from_name, :from_phone, :to, :texter, :action
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@to = parse_to options[:to]
|
7
|
+
@content = parse_content options[:content]
|
8
|
+
|
9
|
+
if options.has_key?(:from)
|
10
|
+
@from_name, @from_phone = parse_from options[:from]
|
11
|
+
else
|
12
|
+
@from_name = options[:from_name]
|
13
|
+
@from_phone = options[:from_phone]
|
14
|
+
end
|
15
|
+
|
16
|
+
unless @content.present?
|
17
|
+
raise(ArgumentError, "Content must be provided")
|
18
|
+
end
|
19
|
+
|
20
|
+
unless @to.present?
|
21
|
+
raise(ArgumentError, "Recipients must be provided and E.164 compilant")
|
22
|
+
end
|
23
|
+
|
24
|
+
@texter = options[:texter]
|
25
|
+
@action = options[:action]
|
26
|
+
end
|
27
|
+
|
28
|
+
def deliver
|
29
|
+
delivery = ::Textris::Delivery.get
|
30
|
+
delivery.send_message_to_all(self)
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def parse_from(from)
|
38
|
+
if from.blank?
|
39
|
+
nil
|
40
|
+
elsif (matches = from.match(/(.*)\<(.*)\>\s*$/).to_a).size == 3 &&
|
41
|
+
Phony.plausible?(matches[2])
|
42
|
+
[matches[1].strip, Phony.normalize(matches[2])]
|
43
|
+
elsif Phony.plausible?(from)
|
44
|
+
[nil, Phony.normalize(from)]
|
45
|
+
else
|
46
|
+
[from.strip, nil]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_to(to)
|
51
|
+
to = [*to]
|
52
|
+
to = to.select { |phone| Phony.plausible?(phone) }
|
53
|
+
to = to.map { |phone| Phony.normalize(phone) }
|
54
|
+
|
55
|
+
to
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_content(content)
|
59
|
+
content = content.gsub(/\s{1,}/, ' ')
|
60
|
+
content = content.strip
|
61
|
+
|
62
|
+
content
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: textris
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Karol Słuszniak
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-12-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: render_anywhere
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.10
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.10
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: actionmailer
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 4.0.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 4.0.0
|
41
|
+
description: Implement texter classes for sending SMS messages in similar way to how
|
42
|
+
e-mails are sent with ActionMailer-based mailers. Take advantage of e-mail proxying
|
43
|
+
and enhanced phone number parsing, among others.
|
44
|
+
email: k.sluszniak@visuality.pl
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files:
|
48
|
+
- README.md
|
49
|
+
files:
|
50
|
+
- README.md
|
51
|
+
- lib/textris.rb
|
52
|
+
- lib/textris/base.rb
|
53
|
+
- lib/textris/delivery.rb
|
54
|
+
- lib/textris/delivery/mail.rb
|
55
|
+
- lib/textris/delivery/test.rb
|
56
|
+
- lib/textris/message.rb
|
57
|
+
homepage: http://github.com/visualitypl/textris
|
58
|
+
licenses:
|
59
|
+
- MIT
|
60
|
+
metadata: {}
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 2.2.2
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Simple SMS messaging gem for Rails based on concepts and conventions similar
|
81
|
+
to ActionMailer, with some extra features.
|
82
|
+
test_files: []
|