Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

major refactor again :)

  • Loading branch information...
commit ff8594d567b170597f771e522e1417114a86f3c5 1 parent a15dd8b
@kristianmandrup authored
Showing with 427 additions and 225 deletions.
  1. +49 −64 README.md
  2. +4 −0 lib/controll/commander.rb
  3. +7 −0 lib/controll/executor.rb
  4. +0 −4 lib/controll/executor/base.rb
  5. +9 −0 lib/controll/executor/delegator.rb
  6. +1 −1  lib/controll/executor/notificator.rb
  7. +7 −2 lib/controll/flow_handler.rb
  8. +9 −0 lib/controll/flow_handler/base.rb
  9. +19 −55 lib/controll/flow_handler/control.rb
  10. +53 −0 lib/controll/flow_handler/control/executor.rb
  11. +54 −0 lib/controll/flow_handler/control/macros.rb
  12. +9 −3 lib/controll/flow_handler/errors.rb
  13. +13 −14 lib/controll/flow_handler/{redirect.rb → redirecter.rb}
  14. +5 −3 lib/controll/flow_handler/{redirect → redirecter}/action.rb
  15. +4 −2 lib/controll/flow_handler/{redirect → redirecter}/mapper.rb
  16. +22 −13 lib/controll/flow_handler/{render.rb → renderer.rb}
  17. +6 −15 lib/controll/helper.rb
  18. +13 −4 lib/controll/notify.rb
  19. +13 −22 lib/controll/notify/base.rb
  20. +1 −1  lib/controll/notify/flash.rb
  21. +57 −0 lib/controll/notify/macros.rb
  22. +16 −0 lib/controll/notify/message.rb
  23. +43 −0 lib/controll/notify/translator.rb
  24. +13 −22 lib/controll/notify/typed.rb
View
113 README.md
@@ -112,7 +112,7 @@ class ApplicationController
end
```
-In your Controller you should define a MessageHandler and Commander to be used.
+In your Controller you should define a Notifier and Commander to be used.
```ruby
class ServicesController < ApplicationController
@@ -122,73 +122,65 @@ class ServicesController < ApplicationController
protected
- message_handler :services
+ notifier :services
commander :services
+
+ # or simply
+ controll :notifier, :commander
end
```
The Commander is where you register a set of related commands, typically for a specific controller.
```ruby
-class ServicesCommander < Controll::Commander
+module Commanders
+ class Services < Commander
- # register commands with controller
- commands :cancel_commit, :create_account, :signout
+ # register commands with controller
+ commands :cancel_commit, :create_account, :signout
- def sign_in_command
- @sign_in_command ||= SignInCommand.new auth_hash: auth_hash, user_id: user_id, service_id: service_id, service_hash: service_hash, initiator: self
- end
+ def sign_in_command
+ @sign_in_command ||= SignInCommand.new auth_hash: auth_hash, user_id: user_id, service_id: service_id, service_hash: service_hash, initiator: self
+ end
- # delegations
- controller_methods :auth_hash, :user_id, :service_id, :service_hash
+ command_method :sign_out do
+ @sign_out_command ||= SignOutCommand.new user_id: user_id, service_id: service_id, service_hash: service_hash, initiator: self
+ end
+
+ # delegations
+ controller_methods :auth_hash, :user_id, :service_id, :service_hash
+ end
end
```
The `#commands` class macro can be used to create command methods that only take the initiator (in this case the controller) as argument.
-For how to implement the commands, see the `imperator` gem, or see the `oauth_assist` engine for a full example.
+For how to implement the commands, see the `imperator` gem, or see the `oauth_assist` engine for a full example. There are now also nice macros available for creating command methods! The `Commander class extends `Imperator::Command::MethodFactory` making `#command_method` available-
-We will implement this MessageHandler later when we know which notifications and errors we want to use/issue.
+We will implement this Notifier later when we know which notifications and errors we want to use/issue.
+
+## FlowHandlers
For Controller actions that require complex flow control, use a FlowHandler:
```ruby
-module Controll::FlowHandler
+module FlowHandlers
class CreateService < Control
- protected
-
- # use for more advanced render/redirect logic (fx when using paths with arguments)
- def use_alternatives
- end
-
- def use_fallback
+ fallback do
event == :no_auth ? do_render(:text => omniauth.to_yaml) : fallback_action
end
- def action_handlers
- [Redirect, Render]
+ event do
+ Executors::Authenticator.new(controller).execute
end
- def event
- @event ||= authentication
+ renderer do
+ default_path :signup_services_path
+ events :signed_in_new_user, :signed_in
end
- def authentication
- @authentication ||= Authenticator.new(controller).execute
- end
-
- class Render < Controll::FlowHandler::Render
- def self.default_path
- :signup_services_path
- end
-
- def self.events
- [:signed_in_new_user]
- end
- end
-
- class Redirect < Controll::FlowHandler::Render
- def self.redirections
+ redirecter do
+ redirections :notice do
{
signup_services_path: :signed_in_new_user
services_path: [:signed_in_connect, :signed_in_new_connect]
@@ -196,11 +188,7 @@ module Controll::FlowHandler
}
end
- def self.error_redirections
- {
- signin_path: [:error, :invalid, :auth_error]
- }
- end
+ redirections :error, signin_path: [:error, :invalid, :auth_error]
end
end
end
@@ -215,7 +203,7 @@ If you are rendering or redirecting to paths that take arguments, you can either
The `Authenticator` inherits from `Executor::Notificator` which uses `#method_missing` in order to delegate any missing method back to the initiator of the Executor, in this case the FlowHandler. The `#result` call at the end of `#execute` ensures that the last notification event is returned, to be used for deciding what to render or where to redirect (see FlowHandler).
```ruby
-module Controll::Executor
+module Executors
class Authenticator < Notificator
def execute
# creates an error notification named :error
@@ -250,12 +238,10 @@ The example below demonstrates several different ways you can define messages fo
* i18n locale mapping [msghandler name].[notification type].[event name].
```ruby
-module Controll::Notify
+module Notifiers
class Services < Typed
- class ErrorMsg < Controll::Notify::Base
- type :error
-
- def messages
+ handler :error do
+ messages do
{
must_sign_in: 'You need to sign in before accessing this page!',
@@ -267,35 +253,34 @@ You have not been signed in.},
}
end
- def auth_error!
- 'Error while authenticating via ' + service_name + '. The service did not return valid data.'
+ msg :auth_error! do
+ "Error while authenticating via #{service_name}. The service did not return valid data."
end
- def auth_invalid!
+ msg :auth_invalid! do
'Error while authenticating via {{full_route}}. The service returned invalid data for the user id.'
end
end
- class NoticeMsg < Controll::Notify::Base
- type :notice
-
+ handler :notice do
# for :signed_in and :signed_out - defined in locale file under:
- # services:
- # notice:
- # signed_in: 'Your account has been created and you have been signed in!'
- # signed_out: 'You have been signed out!'
+ # notifiers:
+ # services:
+ # notice:
+ # signed_in: 'Your account has been created and you have been signed in!'
+ # signed_out: 'You have been signed out!'
- def already_connected
+ msg :already_connected do
'Your account at {{provider_name}} is already connected with this site.'
end
- def account_added
+ msg :account_added do
'Your {{provider_name}} account has been added for signing in at this site.'
end
- def sign_in_success
+ msg :sign_in_success do
'Signed in successfully via {{provider_name}}.'
end
end
View
4 lib/controll/commander.rb
@@ -21,4 +21,8 @@ def command! name, *args
alias_method :use_command, :command!
alias_method :perform_command, :command!
end
+end
+
+module Commanders
+ Commander = Controll::Commander
end
View
7 lib/controll/executor.rb
@@ -1,6 +1,13 @@
module Controll
module Executor
autoload :Base, 'controll/executor/base'
+ autoload :Delegator, 'controll/executor/delegator'
autoload :Notificator, 'controll/executor/notificator'
end
+end
+
+module Executors
+ Base = Controll::Executor::Base
+ Delegator = Controll::Executor::Delegator
+ Notificator = Controll::Executor::Notificator
end
View
4 lib/controll/executor/base.rb
@@ -7,10 +7,6 @@ def initialize initiator, options = {}
@initiator = initiator
@options = options
end
-
- def method_missing(meth, *args, &block)
- initiator.send(meth, *args, &block)
- end
end
end
end
View
9 lib/controll/executor/delegator.rb
@@ -0,0 +1,9 @@
+module Controll
+ module Executor
+ class Delegator < Base
+ def method_missing(meth, *args, &block)
+ initiator.send(meth, *args, &block)
+ end
+ end
+ end
+end
View
2  lib/controll/executor/notificator.rb
@@ -1,7 +1,7 @@
require 'controll/executor/base'
module Controll::Executor
- class Notificator < Base
+ class Notificator < Delegator
# return last notification or :success as result
# Hashie::Mash.new(name: name, type: type, options: options)
View
9 lib/controll/flow_handler.rb
@@ -4,8 +4,13 @@ module Controll
module FlowHandler
autoload :Base, 'controll/flow_handler/base'
autoload :Control, 'controll/flow_handler/control'
- autoload :Redirect, 'controll/flow_handler/redirect'
- autoload :Render, 'controll/flow_handler/render'
+ autoload :Redirecter, 'controll/flow_handler/redirecter'
+ autoload :Renderer, 'controll/flow_handler/renderer'
autoload :EventHelper, 'controll/flow_handler/event_helper'
end
+end
+
+module FlowHandlers
+ Base = Controll::FlowHandler::Base
+ Control = Controll::FlowHandler::Control
end
View
9 lib/controll/flow_handler/base.rb
@@ -11,6 +11,15 @@ def perform controller
end
class << self
+ # any subclass of Base should have an inherited(base) class method added
+ # which adds itself to the list of action_handler which the parent class
+ # (i.e class that subclass is contained in!) supports
+ def inherited(base)
+ (class << self; self; end).send :define_method, :inherited do |base|
+ base.parent.add_action_handler self.name.demodulize
+ end
+ end
+
def action event
raise NotImplementedError, 'You must implement the #action class method'
end
View
74 lib/controll/flow_handler/control.rb
@@ -1,6 +1,11 @@
module Controll::FlowHandler
- class Control
- class ActionEventError < StandardError; end
+ class Control
+ autoload :Macros, 'controll/flow_handler/control/macros'
+ autoload :Executor, 'controll/flow_handler/control/executor'
+
+ ActionEventError = Controll::FlowHandler::ActionEventError
+
+ include Macros
attr_reader :controller, :action_handlers
@@ -10,32 +15,29 @@ def initialize controller, action_handlers = []
end
def execute
- use_action_handlers
- use_alternatives
- use_fallback if !executed?
+ executor.execute
+ fallback if !executed?
self
end
- def action_handlers
- @action_handlers ||= [:redirect, :render]
+ def executor
+ @executor ||= Executor.new self, action_handlers: action_handlers
end
- def executed?
- @executed
+ def action_handlers
+ @action_handlers ||= []
end
+ class << self
+ def add_action_handler name
+ @action_handlers ||= []
+ @action_handlers << name.to_s.underscore.to_sym
+ end
+
protected
delegate :command!, to: :controller
- # can be used to set up control logic that fall outside what can be done
- # with the basic action_handlers but can not be considered fall-back.
- def use_alternatives
- end
-
- def use_fallback
- end
-
def event
raise NotImplementedError, 'You must define an #event method that at least returns an event (Symbol). You can use an Executor for this.'
end
@@ -43,43 +45,5 @@ def event
def fallback_action
do_redirect root_url
end
-
- NoEventsDefinedError = Controll::FlowHandler::Render::NoEventsDefinedError
- NoRedirectionFoundError = Controll::FlowHandler::Redirect::NoRedirectionFoundError
-
- def use_action_handlers
- errors = []
- action_handlers.each do |action_handler|
- begin
- action_handler_clazz = handler_class(action_handler)
- next unless action_handler_clazz
- action = action_handler_clazz.action(event)
- execute_with action
- return if executed?
- rescue NoEventsDefinedError => e
- errors << e
- rescue NoRedirectionFoundError => e
- errors << e
- end
- end
- raise ActionEventError, "#{errors.join ','}" unless errors.empty?
- end
-
- def handler_class action_handler
- clazz = "#{self.class}::#{action_handler.to_s.camelize}"
- clazz.constantize
- rescue NameError
- nil
- end
-
- def execute_with action
- return if !action
- action.perform(controller)
- executed!
- end
-
- def executed!
- @executed = true
- end
end
end
View
53 lib/controll/flow_handler/control/executor.rb
@@ -0,0 +1,53 @@
+module Controll::FlowHandler
+ class Control
+ class Executor < Controll::Executor::Base
+ NoEventsDefinedError = Controll::FlowHandler::Render::NoEventsDefinedError
+ NoRedirectionFoundError = Controll::FlowHandler::Redirect::NoRedirectionFoundError
+
+ def execute
+ errors = []
+ action_handlers.each do |action_handler|
+ begin
+ action_handler_clazz = handler_class(action_handler)
+ next unless action_handler_clazz
+ action = action_handler_clazz.action(event)
+ execute_with action
+ return if executed?
+ rescue NoEventsDefinedError => e
+ errors << e
+ rescue NoRedirectionFoundError => e
+ errors << e
+ end
+ end
+ raise ActionEventError, "#{errors.join ','}" unless errors.empty?
+ end
+
+ protected
+
+ def action_handlers
+ @action_handlers ||= options[:action_handlers]
+ end
+
+ def handler_class action_handler
+ clazz = "#{initiator.class}::#{action_handler.to_s.camelize}"
+ clazz.constantize
+ rescue NameError
+ nil
+ end
+
+ def execute_with action
+ return if !action
+ action.perform(controller)
+ executed!
+ end
+
+ def executed?
+ @executed
+ end
+
+ def executed!
+ @executed = true
+ end
+ end
+ end
+end
View
54 lib/controll/flow_handler/control/macros.rb
@@ -0,0 +1,54 @@
+module Controll::FlowHandler
+ class Control
+ module Macros
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def handler response_type, options = {}, &block
+ unless [:render, :repsonse].include? response_type.to_sym
+ raise ArgumentError, "Must be either :render or :response"
+ end
+
+ clazz_name = "#{parent}::#{response_type.to_s.camelize}"
+ parent = options[:parent] || "Controll::FlowHandler::#{response_type}".constantize
+
+ clazz = parent ? Class.new(parent) : Class.new
+ Object.const_set clazz_name, clazz
+ context = self.kind_of?(Class) ? self : self.class
+ clazz = context.const_get(clazz_name)
+
+ clazz.instance_eval(&block) if block_given?
+ clazz
+ end
+
+ def renderer options = {}, &block
+ handler :renderer, options = {}, &block
+ end
+
+ def redirecter options = {}, &block
+ handler :redirecter, options = {}, &block
+ end
+
+ def event &block
+ raise ArgumentError, "Must be called with a block" unless block_given?
+ define_method :event do
+ instance_variable_get("@event") || instance_variable_set("@event", instance_eval &block)
+ end
+ end
+
+ def fallback &block
+ raise ArgumentError, "Must be called with a block" unless block_given?
+ define_method(:fallback, &block)
+ end
+
+ def action_handlers *names, &block
+ raise ArgumentError, "Must be called with names of action handlers" if names.empty?
+ define_method :action_handlers do
+ value = block_given? ? instance_eval(&block) : names.flatten
+ instance_variable_get("@action_handlers") || instance_variable_set("@action_handlers", value)
+ end
+ end
+ end
+ end
+ end
+end
View
12 lib/controll/flow_handler/errors.rb
@@ -1,5 +1,11 @@
module Controll::FlowHandler
- class NoRedirectionFoundError < StandardError; end
- class NoEventsDefinedError < StandardError; end
- class BadPathError < StandardError; end
+ class ActionEventError < StandardError; end
+
+ # Redirect
+ class NoRedirectionFoundError < StandardError; end
+
+ # Render
+ class NoEventsDefinedError < StandardError; end
+ class NoDefaultPathDefinedError < StandardError; end
+ class BadPathError < StandardError; end
end
View
27 lib/controll/flow_handler/redirect.rb → lib/controll/flow_handler/redirecter.rb
@@ -1,7 +1,7 @@
require 'controll/flow_handler/base'
module Controll::FlowHandler
- class Redirect < Base
+ class Redirecter < Base
autoload :Action, 'controll/flow_handler/redirect/action'
autoload :Mapper, 'controll/flow_handler/redirect/mapper'
@@ -10,36 +10,35 @@ def initialize path
end
def perform controller
+ raise BadPathError, "Bad path: #{path}" if path.blank?
controller.do_redirect controller.send(path)
end
class << self
- attr_accessor :redirections
- attr_writer :redirect_maps, :action_clazz
+ attr_writer :action_clazz
+ attr_reader :types
def action event
path = action_clazz.new(event, redirections, types).map
self.new path unless path.blank?
end
- def types
- @types ||= [:notice, :error]
- end
-
+ # reader
def redirections_for type = :notice
@redirections ||= {}
@redirections[type.to_sym] || {}
end
- def set_redirections *args
- type = args.first.kind_of?(Symbol) ? args.shift : :notice
+ # writer
+ # also auto-adds type to types
+ def redirections *args, &block
@redirections ||= {}
- @redirections[type.to_sym] = args.first
- end
+ return @redirections if args.empty? && !block_given?
- def set_types *names
- @redirect_maps ||= names.flatten
- end
+ type = args.first.kind_of?(Symbol) ? args.shift : :notice
+ @redirections[type.to_sym] = block_given? ? yield : args.first
+ @types << type unless types.include?(type)
+ end
protected
View
8 lib/controll/flow_handler/redirect/action.rb → lib/controll/flow_handler/redirecter/action.rb
@@ -1,8 +1,10 @@
module Controll::FlowHandler
- class Redirect < Base
+ class Redirecter < Base
class Action
attr_accessor :event, :redirections, :types
+ NoRedirectionFoundError = Controll::FlowHandler::NoRedirectionFoundError
+
# event is a Hashie::Mash or simply a Symbol (default notice event)
def initialize event, redirections, types = []
raise ArgumentError, "Must take :event option, was: #{event}" if event.blank?
@@ -14,7 +16,7 @@ def initialize event, redirections, types = []
def map
if redirect.blank?
- raise Controll::FlowHandler::NoRedirectionFoundError, "No redirection could be found for: #{event} in: #{redirections}"
+ raise NoRedirectionFoundError, "No redirection could be found for: #{event} in: #{redirections}"
end
redirect
end
@@ -26,7 +28,7 @@ def map
def redirect
@redirect ||= mapper(redirect_map).map
rescue StandardError => e
- raise Controll::FlowHandler::NoRedirectionFoundError, "No redirection could be found for: #{event} in: #{redirections}. Cause: #{e}"
+ raise NoRedirectionFoundError, "No redirection could be found for: #{event} in: #{redirections}. Cause: #{e}"
end
def redirect_map
View
6 lib/controll/flow_handler/redirect/mapper.rb → lib/controll/flow_handler/redirecter/mapper.rb
@@ -1,8 +1,10 @@
module Controll::FlowHandler
- class Redirect < Base
+ class Redirecter < Base
class Mapper
attr_reader :redirect_map, :event
+ NoRedirectionFoundError = Controll::FlowHandler::NoRedirectionFoundError
+
def initialize event, redirect_map
@event ||= normalize event
@@ -18,7 +20,7 @@ def map
redirect_map.each do |path, events|
return path.to_s if valid? events
end
- raise Controll::FlowHandler::NoRedirectionFoundError, "No path could be found for event: #{event} in map: #{redirect_map}"
+ raise NoRedirectionFoundError, "No path could be found for event: #{event} in map: #{redirect_map}"
end
protected
View
35 lib/controll/flow_handler/render.rb → lib/controll/flow_handler/renderer.rb
@@ -1,13 +1,13 @@
require 'controll/flow_handler/base'
module Controll::FlowHandler
- class Render < Base
- class NoEventsDefinedError < StandardError; end
- class BadPathError < StandardError; end
+ class Renderer < Base
+ BadPathError = Controll::FlowHandler::BadPathError
+ NoEventsDefinedError = Controll::FlowHandler::NoEventsDefinedError
+ NoDefaultPathDefinedError = Controll::FlowHandler::NoDefaultPathDefinedError
- def initialize path #, events = []
+ def initialize path
super path
- # @events = events
end
def perform controller
@@ -16,28 +16,27 @@ def perform controller
end
class << self
+ def inherited(base)
+ base.parent.add_action_handler self.name.underscore
+ end
+
def action event, path = nil
- raise Controll::FlowHandler::NoEventsDefinedError, "You must define a #{self}#events class method that returns a render map" unless respond_to?(:events)
- raise Controll::FlowHandler::NoEventsDefinedError, "The #{self}#events class method must return a render map, was #{events}" if events.blank?
+ check!
event = normalize event
self.new(path || default_path) if events.include? event.name
end
- def default_path
- raise NotImplementedError, "You must set a default_path or override the #{self}#action class method"
- end
-
# http://bugs.ruby-lang.org/issues/1082
# hello.singleton_class
# Instead of always having to write:
# (class << hello; self; end)
- def set_default_path str = nil, &block
+ def default_path str = nil, &block
(class << self; self; end).send :define_method, :default_path do
block_given? ? yield : str
end
end
- def set_events *args, &block
+ def events *args, &block
(class << self; self; end).send :define_method, :events do
block_given? ? yield : args.flatten
end
@@ -45,6 +44,16 @@ def set_events *args, &block
protected
+ def check!
+ unless respond_to?(:events) && !events.blank?
+ raise NoEventsDefinedError, "You must define the events that can be rendered by this class"
+ end
+
+ unless respond_to?(:default_path) && !default_path.blank?
+ raise NoDefaultPathDefinedError, "You must set a default_path to be rendered if no event matches"
+ end
+ end
+
include Controll::FlowHandler::EventHelper
end
end
View
21 lib/controll/helper.rb
@@ -18,8 +18,8 @@ module ClassMethods
# TODO: refactor - all use exactly the same pattern - can be generated!
def commander name, options = {}
define_method :commander do
- unless instance_variable_get("@commander")
- clazz = "#{name.to_s.camelize}Commander".constantize
+ instance_variable_get("@commander") || begin
+ clazz = "Commanders::#{name.to_s.camelize}".constantize
instance_variable_set "@commander", clazz.new(self, options)
end
end
@@ -27,8 +27,8 @@ def commander name, options = {}
def message_handler name, options = {}
define_method :message_handler do
- unless instance_variable_get("@message_handler")
- clazz = "#{name.to_s.camelize}MessageHandler".constantize
+ instance_variable_get("@message_handler") || begin
+ clazz = "Notifiers::#{name.to_s.camelize}".constantize
instance_variable_set "@message_handler", clazz.new(self, options)
end
end
@@ -37,7 +37,7 @@ def message_handler name, options = {}
def assistant name, options = {}
define_method :assistant do
unless instance_variable_get("@assistant")
- clazz = "#{name.to_s.camelize}Assistant".constantize
+ clazz = "Assistants::#{name.to_s.camelize}".constantize
instance_variable_set "@assistant", clazz.new(self, options)
end
end
@@ -46,21 +46,12 @@ def assistant name, options = {}
def flow_handler name, options = {}
define_method :flow_handler do
unless instance_variable_get("@flow_handler")
- clazz = "#{name.to_s.camelize}FlowHandler".constantize
+ clazz = "FlowHandlers::#{name.to_s.camelize}".constantize
instance_variable_set "@flow_handler", clazz.new(self, options)
end
end
end
- def delegate_assistant name, options = {}
- define_method :assistant do
- unless instance_variable_get("@assistant")
- clazz = "#{name.to_s.camelize}DelegateAssistant".constantize
- instance_variable_set "@assistant", clazz.new(self, options)
- end
- end
- end
-
def redirect_map map = {}
@redirect_map ||= map
end
View
17 lib/controll/notify.rb
@@ -1,7 +1,16 @@
module Controll
module Notify
- autoload :Base, 'controll/notify/base'
- autoload :Flash, 'controll/notify/flash'
- autoload :Typed, 'controll/notify/typed'
+ TYPES = [:notice, :error, :warning, :success]
+
+ autoload :Base, 'controll/notify/base'
+ autoload :Flash, 'controll/notify/flash'
+ autoload :Typed, 'controll/notify/typed'
+ autoload :Macros, 'controll/notify/macros'
+ autoload :Translator, 'controll/notify/translator'
+ autoload :Message, 'controll/notify/message'
end
-end
+end
+
+module Notifiers
+ Typed = Controll::Notify::Typed
+end
View
35 lib/controll/notify/base.rb
@@ -16,10 +16,8 @@ def self.inherited(base)
end
module ClassMethods
- attr_reader :signal_type
-
- def type name
- @signal_type = name
+ def signal_type
+ self.name.demodulize.sub(/Handler$/, '').underscore.to_sym
end
end
@@ -32,17 +30,14 @@ def notify_msg name, opts = {}
msg ||= messages[name.to_sym] if respond_to? :messages
msg ||= name.to_sym
- # if name is not mapped to a message, it could well be, that the
- # event should not generate a nofification message
- return nil if !msg
+ message = create_message msg, opts
# try various approaches!
case msg
when Symbol
- translate msg, opts
+ translate message
when String
- msg.strip!
- return replace_args(msg, opts) if msg =~ /{{.*}}/
+ return replace_args(message) if msg =~ /{{.*}}/
msg
else
msg_error!
@@ -54,22 +49,18 @@ def msg_error!
raise NotifyMappingError, "Notify message could not be generated for: #{name}"
end
- def translate msg, opts = {}
- I18n.t i18n_key(msg), opts.symbolize_keys
+ def replace_args message
+ # Parses and compiles the template
+ Liquid::Template.parse(message.text).render(message.options)
end
- def replace_args msg, opts
- # Parses and compiles the template
- Liquid::Template.parse(msg).render(opts)
+ def translate message
+ translator(message).translate
end
- def i18n_key msg
- parts = self.class.name.split('::')
- middle = parts[1..-2].join('.').underscore
- type = parts.last.sub(/Msg$/, '').underscore
- ns = [middle, type].join('.').sub /^./, ''
- [ns,msg].join('.')
- end
+ def translator message
+ Controll::Notify::Translator.new self, message
+ end
end
end
end
View
2  lib/controll/notify/flash.rb
@@ -20,7 +20,7 @@ class << self
attr_writer :types
def types
- @types ||= [:notice, :error, :warning, :success]
+ @types ||= TYPES
end
def add_types *types
View
57 lib/controll/notify/macros.rb
@@ -0,0 +1,57 @@
+module Controll::Notify
+ module Macros
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def handler type, options = {}, &block
+ unless valid_type? type
+ raise ArgumentError, "Notification type must be any of: #{types}, was: #{type}"
+ end
+
+ clazz_name = "#{parent}::#{type.to_s.camelize}Handler"
+ parent = options[:parent] || Controll::Notify::Base
+
+ clazz = parent ? Class.new(parent) : Class.new
+ Object.const_set clazz_name, clazz
+ context = self.kind_of?(Class) ? self : self.class
+ clazz = context.const_get(clazz_name)
+
+ if block_given?
+ clazz.instance_eval &block
+ end
+ clazz
+ end
+
+ def messages hash = nil, &block
+ unless block_given? || !hash.blank?
+ raise ArgumentError, "Must be called with non-empty Hash or block"
+ end
+
+ define_method :messages do
+ block_given? ? instance_eval(&block) : hash
+ end
+ end
+
+ def message event_name, text = nil, &block
+ unless event_name.kind_of?(Symbol) || event_name.kind_of?(String)
+ raise ArgumentError, "First argument must be an event name, was: #{event_name}"
+ end
+
+ unless block_given? || !text.blank?
+ raise ArgumentError, "Must be called with non-empty String or block"
+ end
+
+ define_method event_name do
+ block_given? ? instance_eval(&block) : hash
+ end
+ end
+ alias_method :msg, :message
+
+ protected
+
+ def valid_type? type
+ types.include? type.to_sym
+ end
+ end
+ end
+end
View
16 lib/controll/notify/message.rb
@@ -0,0 +1,16 @@
+module Controll::Notify
+ class Message
+ attr_reader :text, :options
+
+ def initialize text, options = {}
+ raise ArgumentError, "Message text must be a String or Symbol" unless text.kind_of?(String)
+ @text, @options = [text.to_s.strip!, options]
+ end
+
+ protected
+
+ def valid_text? text
+ text.kind_of?(String) || text.kind_of?(Symbol)
+ end
+ end
+end
View
43 lib/controll/notify/translator.rb
@@ -0,0 +1,43 @@
+module Controll
+ module Notify
+ class Translator
+ attr_reader :caller, :key, :options
+
+ def initialize caller, message
+ @caller = caller
+ @key = message.text
+ @options = options.symbolize_keys unless options.blank?
+ end
+
+ def translate
+ options ? I18n.t(i18n_key) : I18n.t(i18n_key, options)
+ end
+
+ protected
+
+ def i18n_key
+ [namespace_key, key].join('.')
+ end
+
+ def namespace_key
+ [middle, type].join('.').sub /^./, ''
+ end
+
+ def parts
+ @parts ||= caller.class.name.split('::')
+ end
+
+ def middle
+ parts[1..-2].join('.').underscore
+ end
+
+ def type
+ parts.last.sub(/#{clazz_postfix}$/, '').underscore
+ end
+
+ def clazz_postfix
+ 'Handler'
+ end
+ end
+ end
+end
View
35 lib/controll/notify/typed.rb
@@ -3,36 +3,27 @@
module Controll
module Notify
class Typed < Flash
- class MissingNotifyClass < StandardError; end
+ class MissingNotifyHandlerClass < StandardError; end
- def notice
- raise MissingNotifyClass, "#{msg_class(:notice)} class missing" unless msg_class(:notice)
- @notice ||= msg_class(:notice).new flash #, options
- end
-
- def error
- raise MissingNotifyClass, "#{msg_class(:warning)} class missing" unless msg_class(:error)
- @error ||= msg_class(:error).new flash #, options
- end
-
- def success
- raise MissingNotifyClass, "#{msg_class(:success)} class missing" unless msg_class(:success)
- @success ||= msg_class(:success).new flash #, options
- end
+ include Controll::Notify::Macros
- def warning
- raise MissingNotifyClass, "#{msg_class(:warning)} class missing" unless msg_class(:warning)
- @warning ||= msg_class(:warning).new flash #, options
+ types.each do |type|
+ define_method type do
+ clazz = handler_class(type)
+ raise MissingNotifyHandlerClass, "#{clazz} class missing" unless clazz
+ var = "@#{type}"
+ instance_variable_get(var) || instance_variable_set(var, clazz.new flash)
+ end
end
protected
- def msg_class name
- msg_classes[name] ||= "#{self.class}::#{name.to_s.camelize}Msg".constantize
+ def handler_class name
+ handler_classes[name] ||= "#{self.class}::#{name.to_s.camelize}Handler".constantize
end
- def msg_classes
- @msg_classes ||= {}
+ def handler_classes
+ @handler_classes ||= {}
end
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.