view snippets

How to render a view or partial from a Rake task, model or anywhere

Tagged actionview, render, rake, view, rails, routes  Languages ruby

This is tested with Rails 3.0.7 and will most likely change in future versions of Rails:

# @newsletter in the view file
assigns = { :newsletter => self }

routes = Rails::Application.routes # Rails => YourApp
routes.default_url_options = { :host => 'xxx.com' }

av = ActionView::Base.new(Rails::Application::Configuration.new(Rails.root).paths.app.views.first, assigns)
av.class_eval do
  include ApplicationHelper
  include routes.url_helpers
end

self.html = av.render :template => 'newsletters/show'

After some experimentation I ended up with this code:

class RenderHelper
  class << self
    def render(assigns, options, request = {})
      request = { 
        "SERVER_PROTOCOL" => "http", 
        "REQUEST_URI" => "/",
        "SERVER_NAME" => "localhost", 
        "SERVER_PORT" => 80
      }.merge(request)

      av = ActionView::Base.new(ActionController::Base.view_paths, assigns)
      
      av.config = Rails.application.config.action_controller
      av.extend ApplicationController._helpers
      av.controller = ActionController::Base.new
      av.controller.request = ActionController::Request.new(request)
      av.controller.response = ActionController::Response.new
      av.controller.headers = Rack::Utils::HeaderHash.new

      av.class_eval do
        include Rails.application.routes.url_helpers
      end

      av.render options 
    end
  end
end

And this is how I use it:

RenderHelper.render(:newsletter => self, :template => 'mailchimp/daily')

How to prepend a directory to the view path in Rails (3, 4, etc)

Tagged prepend_view_path, view, paths, rails  Languages ruby

prepend_view_path is your friend when you want to use different views in the same app for different configurations, e.g. same code for different apps/sites/etc:

class ApplicationController ....
   prepend_view_path Rails.root.join('app', 'views', ENV['APP'])
end

prepend_view_path can also be called e.g. once per request before rendering the view to have different view templates for each subdomain, user, cat, etc.

Refreshing all materialized views in the correct order with one command

Tagged dynamic, materialized, postgres, sql, view  Languages sql

The following code is from https://wiki.postgresql.org/wiki/Refresh_All_Materialized_Views:

CREATE OR REPLACE VIEW mat_view_dependencies AS
WITH RECURSIVE s(start_schemaname,start_relname,start_relkind,
         schemaname,relname,relkind,reloid,owneroid,ownername,depth)
         AS (
-- List of tables and views that mat views depend on
SELECT n.nspname AS start_schemaname, c.relname AS start_relname,
c.relkind AS start_relkind,
n2.nspname AS schemaname, c2.relname, c2.relkind,
c2.oid AS reloid,
au.oid AS owneroid,
au.rolname AS ownername,
0 AS depth
FROM pg_class c JOIN pg_namespace n
     ON c.relnamespace=n.oid AND c.relkind IN ('r','m','v','t','f')
JOIN pg_depend d ON c.oid=d.refobjid
JOIN pg_rewrite r ON d.objid=r.oid
JOIN pg_class c2 ON r.ev_class=c2.oid -- AND c2.relkind='m'
JOIN pg_namespace n2 ON n2.oid=c2.relnamespace
JOIN pg_authid au ON au.oid=c2.relowner

UNION

-- Recursively find all mat views depending on previous level
SELECT s.start_schemaname, s.start_relname, s.start_relkind,
n.nspname AS schemaname, c2.relname,
c2.relkind, c2.oid,
au.oid AS owneroid, au.rolname AS ownername,
s.depth+1 AS depth
FROM s
JOIN pg_depend d ON s.reloid=d.refobjid
JOIN pg_rewrite r ON d.objid=r.oid
JOIN pg_class c2 ON r.ev_class=c2.oid AND (c2.relkind IN ('m','v'))
JOIN pg_namespace n ON n.oid=c2.relnamespace
JOIN pg_authid au ON au.oid=c2.relowner

WHERE s.reloid <> c2.oid -- exclude the current MV which always depends on itself
)
SELECT * FROM s;

--------------------------------------------------
--- A view that returns the list of mat views in the
--- order they should be refreshed.
--------------------------------------------------
CREATE OR REPLACE VIEW mat_view_refresh_order AS
WITH b AS (
-- Select the highest depth of each mat view name
SELECT DISTINCT ON (schemaname,relname) schemaname, relname, ownername, depth
FROM mat_view_dependencies
WHERE relkind='m'
ORDER BY schemaname, relname, depth DESC
)
-- Reorder appropriately
SELECT schemaname, relname, ownername, depth AS refresh_order
FROM b
ORDER BY depth, schemaname, relname
;

We can now write a custom function to execute the refresh of all materialized views in the correct order:

CREATE OR REPLACE FUNCTION refresh_materialized_views () RETURNS VOID AS
$$
DECLARE
BEGIN
  EXECUTE (SELECT string_agg('REFRESH MATERIALIZED VIEW "' || schemaname || '"."' || relname || '";', '' ORDER BY refresh_order) AS script FROM mat_view_refresh_order);
END;
$$ LANGUAGE plpgsql;

We use a simple select to execute the dynamically generated SQL

SELECT refresh_materialized_views();