textris 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []