textris 0.1.0

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.
@@ -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
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'action_controller'
2
+ require 'action_mailer'
3
+
4
+ require 'textris/base'
5
+ require 'textris/message'
6
+ require 'textris/delivery'
7
+ require 'textris/delivery/test'
8
+ require 'textris/delivery/mail'
@@ -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: []