module PushRoute::ClassMethods
Public Instance Methods
If you have a route like /room/:id/messages where Message belongs_to room you can use add_belongs_to_trigger
:index, Message It will look at the url and try to add a trigger on Message save triggering the id as Message.room.id
# File lib/push_routes/push_route.rb, line 162 def add_belongs_to_trigger(action, model, type = :after_save) enable_push_route action unless push_routes[action] url = push_routes[action] warn "Push Routes WARNING: Url #{url.inspect} contains too many params" if url.param_associations.count > 1 warn "Push Routes WARNING: Url #{url.inspect} contains not enough notated params" if url.param_associations.count > 1 sym = url.param_associations.first[1].singularize.to_sym if model.table_exists? if model&.send(:new)&.respond_to?(sym) add_trigger(action, model, type) { |object| {id: object.send(sym)&.id} } else warn "Push Routes WARNING: #{model} does not respond to #{sym} in belongs_to trigger" end else warn("Push Routes WARNING: no table exists for #{model}") end end
# File lib/push_routes/push_route.rb, line 154 def add_id_trigger(action, model, id_name, type = :after_save) add_trigger(action, model, type) { |object| {id: object.send(id_name)} } end
# File lib/push_routes/push_route.rb, line 179 def add_show_trigger(action) if self.model add_id_trigger(action, self.model, :id) else warn "Push Routes WARNING: No model found for #{controller_path}" end end
Sets a trigger for pushing updates @param [symbol] the action in this controller for the listener to trigger @param model [ModelType] The type of model to listen to e.g. User, LabOrder @param type [Symbol] Listener type, e.g. :after_update, see below for list @param trigger_function [function] Function to determine if an update should be triggered, either proc or reference function should return true to trigger update of the route; if the route requires params to resolve (i.e /users/:id/files) the params should be returned as a hash. Return false or nil to not trigger an update
# File lib/push_routes/push_route.rb, line 106 def add_trigger(action, model, type, trigger_function = nil) enable_push_route action unless push_routes[action] # Get get the callback passed in if (!trigger_function and block_given?) # Block only # Note Proc.new gets the passed in block without instantiating an extra proc # See http://mudge.name/2011/01/26/passing-blocks-in-ruby-without-block.html callback = Proc.new elsif trigger_function && trigger_function.is_a?(Proc) # Proc passed callback = trigger_function elsif trigger_function #&& !trigger_function.is_a?(Proc) implied here # Symbol passed trigger_symbols << trigger_function callback = lambda { |e| self.send(trigger_function, e) } else #default value callback = Proc.new { true } end if [:after_commit, :after_save, :after_update, :after_create, :after_destroy].include? type this = self func = lambda do |e| result = callback.call(e) if (result) result = [result] unless result.kind_of?(Array) result.each do |params| PushRoutes.trigger(this.push_routes[action].notification_string(params)) end end end model.send(type, func) else raise ArgumentError.new("Invalid trigger type") end end
TODO: if not route already make, make it; set association between route(s) and action
test method aliasing, perhaps can still do caching explicitly, or maybe just expiring caches properly; ignore caches for now maybe just internal whisper pub sub with a routing adapter also pull out seperate class for each push route to store routes and triggers Maybe rails magic on for trigger function names
# File lib/push_routes/push_route.rb, line 42 def enable_push_route(action, route = nil) matched_url = nil Rails.application.routes.routes.each do |e| if e.defaults[:controller] == self.controller_path && e.defaults[:action] == action.to_s unless matched_url matched_url = PushRoutes::PushRouteUrl.new(e) else raise ArgumentError.new("Duplicate route for push route controller") end end end #If a route exists and one is provided, make sure they are compatable if matched_url and route raise ArgumentError.new("Provided route does not match pre-existing route") unless (matched_url.matches(route)) url = matched_url elsif route and !matched_url #There was no pre-existing route found and a route was specified # Add this route to the list of live routes url = PushRoutes::PushRouteUrl.new(route) elsif !route and matched_url #Matched one route only, we're good url = matched_url else #No route and no match raise ArgumentError.new("No route found and no route provided") end if push_routes[action] raise ArgumentError.new("Push Route enabled twice") else push_routes[action] = url end end
# File lib/push_routes/push_route.rb, line 93 def instance_trigger?(method_sym) trigger_symbols.include?(method_sym) && instance_methods.include?(method_sym) end
If it's defined as an instance method and not a static method we'll define a static method which wraps the functinality While this seems crazy, ActionMailer does this so there's precident
# File lib/push_routes/push_route.rb, line 80 def method_missing(method_sym, *arguments, &block) if instance_trigger?(method_sym) new.send(method_sym, *arguments, &block) else super end end
TODO: check for full class depth or shallow depth i.e. Api2::VitalsController
# File lib/push_routes/push_route.rb, line 145 def model #Note: const_defined is unrelialbe due to rails auto_loading, model might not be loaded begin Object.const_get(controller_path.classify) rescue nil end end
Overriding respond_to_missing to allow our method_missing
override to behave properly
# File lib/push_routes/push_route.rb, line 89 def respond_to_missing?(method_sym, include_private = false) instance_trigger?(method_sym) || super end