├── .gitignore
├── .travis.yml
├── Gemfile
├── MIT-LICENSE
├── README.md
├── Rakefile
├── gemfiles
└── active_record.gemfile
├── lib
├── panoramic.rb
└── panoramic
│ ├── orm
│ └── active_record.rb
│ └── resolver.rb
├── panoramic.gemspec
└── spec
├── controllers
└── rendering_spec.rb
├── dummy
├── Rakefile
├── app
│ ├── controllers
│ │ ├── application_controller.rb
│ │ └── foo_controller.rb
│ ├── helpers
│ │ └── application_helper.rb
│ ├── models
│ │ ├── .keep
│ │ └── database_template.rb
│ └── views
│ │ └── layouts
│ │ └── application.html.erb
├── bin
│ ├── bundle
│ ├── rails
│ ├── rake
│ └── setup
├── config.ru
├── config
│ ├── application.rb
│ ├── boot.rb
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ │ ├── development.rb
│ │ ├── production.rb
│ │ └── test.rb
│ ├── initializers
│ │ ├── assets.rb
│ │ ├── backtrace_silencers.rb
│ │ ├── cookies_serializer.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── inflections.rb
│ │ ├── mime_types.rb
│ │ ├── secret_token.rb
│ │ ├── session_store.rb
│ │ └── wrap_parameters.rb
│ ├── locales
│ │ └── en.yml
│ ├── routes.rb
│ └── secrets.yml
├── db
│ ├── migrate
│ │ └── 20110526132353_create_database_templates.rb
│ └── schema.rb
└── script
│ └── rails
├── orm
└── active_record_spec.rb
├── resolver_spec.rb
├── spec_helper.rb
└── support
├── describe_private.rb
└── factories.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | log/*.log
3 | pkg/
4 | spec/dummy/db/*.sqlite3
5 | spec/dummy/log/*.log
6 | spec/dummy/tmp/
7 | .rspec
8 | coverage/*
9 | .rvmrc
10 | gemfiles/*.lock
11 | *.rdb
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - 2.5.0
4 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | gemspec
3 |
4 | gem 'rails-controller-testing'
5 |
6 | group :test do
7 | gem 'rake'
8 | end
9 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2011 Andrea Pavoni - http://andreapavoni.com
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Panoramic [](http://travis-ci.org/apeacox/panoramic)
2 | An [ActionView::Resolver] implementation to store rails views (layouts, templates and partials) on database. Simply put: what you can do with views on filesystem, can be done on database.
3 |
4 | **NOTE:** at the moment, only ActiveRecord is supported, I've planned to add more ORMs (see Todo). If you can't wait, adding other ORMs should be very trivial.
5 |
6 | ## Installation
7 | Add the following line to Gemfile:
8 |
9 | ```ruby
10 | gem "panoramic"
11 | ```
12 |
13 | ## Usage
14 |
15 | ### Mandatory fields
16 | Your model should have the following fields:
17 |
18 | * body (text): the source of template
19 | * path (string): where to find template (ex: layouts/application,
20 | you_controller/action, etc...)
21 | * locale (string): it depends from available locales in your app
22 | * handler (string): as locale field, it depends from avaiable handlers
23 | (erb, haml, etc...)
24 | * partial (boolean): determines if it's a partial or not (false by
25 | default)
26 | * format (string): A valid mimetype from Mime::SET.symbols
27 |
28 | they're what the rails' Resolver API needs to lookup templates.
29 |
30 | ### Model
31 | A simple macro in model will activate your new Resolver. You can use a dedicated model to manage all the views in your app, or just for specific needs (ex: you want a custom template for some static pages, the other views will be fetched from filesystem).
32 |
33 | ```ruby
34 | class TemplateStorage < ActiveRecord::Base
35 | store_templates
36 | end
37 | ```
38 |
39 | ### Controller
40 | To add Panoramic::Resolver in controller, depending on your needs, you may choose:
41 |
42 | * [prepend_view_path]: search for templates *first* in your resolver, *then* on filesystem
43 | * [append_view_path]: search for templates *first* on filesystem, *then* in your resolver
44 |
45 | **NOTE**: the above methods are both class and instance methods.
46 |
47 |
48 | ```ruby
49 | class SomeController < ApplicationController
50 | prepend_view_path TemplateStorage.resolver
51 |
52 | def index
53 | # as you may already know, rails will serve 'some/index' template by default, but it doesn't care where it is stored.
54 | end
55 |
56 | def show
57 | # explicit render
58 | render :template => 'custom_template'
59 | end
60 |
61 | def custom_template
62 | # use another model to fetch templates
63 | prepend_view_path AnotherModel.resolver
64 | end
65 | end
66 | ```
67 |
68 | And let's say you want to use database template resolving in all your controllers, but
69 | want to use panoramic only for certain paths (prefixed with X) you can use
70 |
71 | ```ruby
72 | class ApplicationController < ActionController::Base
73 | prepend_view_path TemplateStorage.resolver(:only => 'use_this_prefix_only')
74 | end
75 | ```
76 |
77 | This helps reducing the number of database requests, if Rails for example tries to look
78 | for layouts per controller.
79 |
80 | ### ActionMailer
81 |
82 | ```ruby
83 | class MyEmail < ActionMailer::Base
84 | prepend_view_path TemplateStorage.resolver
85 | ```
86 | Using prepend_view_path/append_view_path you are stuck to the current context (e.g. the method calling "mail").
87 | If you want to dynamically change the path depending on a certain variable, call the prepend_view_path/append_view_path inside the method's context with an additional path variable.
88 | This could be useful, if you want to use only one method for sending different templates depending on the template.path .
89 |
90 | ```ruby
91 | class MyEmail < ActionMailer::Base
92 |
93 | def method_that_sets_resolver_path
94 | prepend_view_path TemplateStorage.resolver(:path => model.path)
95 | end
96 | ```
97 |
98 | ## Documentation
99 | Need more help? Check out ```spec/dummy/```, you'll find a *dummy* rails app I used to make tests ;-)
100 |
101 | ## Testing
102 | Enter Panoramic gem path, run ```bundle install``` to install development and test dependencies, then ```rake spec```.
103 |
104 |
105 | ## Todo
106 |
107 | ### Long term
108 | * add generators
109 |
110 | ## Contributing
111 | Fork, make your changes, then send a pull request.
112 |
113 | ## Credits
114 | The main idea was *heavily inspired* from José Valim's awesome book [Crafting Rails Applications]. It helped me to better understand some Rails internals.
115 |
116 | [ActionView::Resolver]: http://api.rubyonrails.org/classes/ActionView/Resolver.html
117 | [append_view_path]: http://apidock.com/rails/AbstractController/ViewPaths/ClassMethods/append_view_path
118 | [prepend_view_path]: http://apidock.com/rails/AbstractController/ViewPaths/ClassMethods/prepend_view_path
119 | [Crafting Rails Applications]: http://pragprog.com/titles/jvrails/crafting-rails-applications
120 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | begin
4 | require 'bundler'
5 | require 'bundler/setup'
6 | rescue LoadError
7 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
8 | end
9 |
10 | require 'rspec/core'
11 | require 'rspec/core/rake_task'
12 |
13 | Bundler::GemHelper.install_tasks
14 | RSpec::Core::RakeTask.new(:spec)
15 |
16 | ORMS = %w(active_record)
17 |
18 | task :default => "spec:all"
19 |
20 | namespace :spec do
21 | ORMS.each do |orm|
22 | desc "Run Tests against #{orm}"
23 | task orm do
24 | sh "BUNDLE_GEMFILE='gemfiles/#{orm}.gemfile' bundle --quiet"
25 | sh "BUNDLE_GEMFILE='gemfiles/#{orm}.gemfile' bundle exec rake -t spec"
26 | end
27 | end
28 |
29 | desc "Run Tests against all ORMs"
30 | task :all do
31 | ORMS.each { |orm| Rake::Task["spec:#{orm}"].invoke }
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/gemfiles/active_record.gemfile:
--------------------------------------------------------------------------------
1 | source :rubygems
2 |
3 | gem 'activerecord', '>= 3.2.8', :require => 'active_record'
4 |
5 | gemspec :path => '../'
6 |
--------------------------------------------------------------------------------
/lib/panoramic.rb:
--------------------------------------------------------------------------------
1 | require 'panoramic/resolver'
2 | require 'panoramic/orm/active_record'
3 |
--------------------------------------------------------------------------------
/lib/panoramic/orm/active_record.rb:
--------------------------------------------------------------------------------
1 | module Panoramic
2 | module Orm
3 | module ActiveRecord
4 | def store_templates
5 | class_eval do
6 | validates :body, :presence => true
7 | validates :path, :presence => true
8 | validates :format, :inclusion => Mime::SET.symbols.map(&:to_s)
9 | validates :locale, :inclusion => I18n.available_locales.map(&:to_s), :allow_blank => true
10 | validates :handler, :inclusion => ActionView::Template::Handlers.extensions.map(&:to_s)
11 |
12 | after_save { Panoramic::Resolver.instance.clear_cache }
13 |
14 | extend ClassMethods
15 | end
16 | end
17 |
18 | module ClassMethods
19 | def find_model_templates(conditions = {})
20 | self.where(conditions)
21 | end
22 |
23 | def resolver(options={})
24 | Panoramic::Resolver.using self, options
25 | end
26 | end
27 | end
28 | end
29 | end
30 |
31 | ActiveRecord::Base.extend Panoramic::Orm::ActiveRecord
32 |
--------------------------------------------------------------------------------
/lib/panoramic/resolver.rb:
--------------------------------------------------------------------------------
1 | module Panoramic
2 | class Resolver < ActionView::Resolver
3 | require "singleton"
4 | include Singleton
5 |
6 | # this method is mandatory to implement a Resolver
7 | def find_templates(name, prefix, partial, details, key=nil, locals=[])
8 | return [] if @@resolver_options[:only] && !@@resolver_options[:only].include?(prefix)
9 |
10 | path = @@resolver_options[:path].present? ? @@resolver_options[:path] : build_path(name, prefix)
11 |
12 | conditions = {
13 | :path => path,
14 | :locale => [normalize_array(details[:locale]).first, nil],
15 | :format => normalize_array(details[:formats]),
16 | :handler => normalize_array(details[:handlers]),
17 | :partial => partial || false
18 | }.merge(details[:additional_criteria].presence || {})
19 |
20 | @@model.find_model_templates(conditions)&.map do |record|
21 | Rails.logger.debug "Rendering template from database: #{path} (#{record.format})"
22 | initialize_template(record)
23 | end
24 | end
25 |
26 | # Instantiate Resolver by passing a model (decoupled from ORMs)
27 | def self.using(model, options={})
28 | @@model = model
29 | @@resolver_options = options
30 | self.instance
31 | end
32 |
33 | private
34 |
35 | # Initialize an ActionView::Template object based on the record found.
36 | def initialize_template(record)
37 | source = record.body
38 | identifier = "#{record.class} - #{record.id} - #{record.path.inspect}"
39 | handler = ActionView::Template.registered_template_handler(record.handler)
40 |
41 | ActionView::Template.new(source, identifier, handler,
42 | :locals => [],
43 | :format => Mime[record.format].to_sym,
44 | :virtual_path => virtual_path(record.path, record.partial))
45 | end
46 |
47 | # Build path with eventual prefix
48 | def build_path(name, prefix)
49 | prefix.present? ? "#{prefix}/#{name}" : name
50 | end
51 |
52 | # Normalize array by converting all symbols to strings.
53 | def normalize_array(array)
54 | array.map(&:to_s)
55 | end
56 |
57 | # returns a path depending if its a partial or template
58 | def virtual_path(path, partial)
59 | return path unless partial
60 | if index = path.rindex("/")
61 | path.insert(index + 1, "_")
62 | else
63 | "_#{path}"
64 | end
65 | end
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/panoramic.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 |
3 | Gem::Specification.new do |s|
4 | s.name = "panoramic"
5 | s.version ='0.0.7'
6 | s.platform = Gem::Platform::RUBY
7 | s.authors = ["Andrea Pavoni"]
8 | s.email = ["andrea.pavoni@gmail.com"]
9 | s.homepage = "http://github.com/apeacox/panoramic"
10 | s.summary = %q{Stores rails views on database}
11 | s.description = %q{A gem to store Rails views on database}
12 | s.license = 'MIT'
13 |
14 | s.add_runtime_dependency 'rails', '>= 4.2.0'
15 | s.add_development_dependency 'rails-controller-testing'
16 | s.add_development_dependency 'capybara', '~> 2.5', '>= 2.5.0'
17 | s.add_development_dependency 'factory_bot'
18 | s.add_development_dependency "simplecov"
19 | s.add_development_dependency "sqlite3"
20 | s.add_development_dependency "rspec-rails", '~> 3.0'
21 |
22 | s.files = `git ls-files`.split("\n")
23 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24 | s.require_paths = ["lib"]
25 | end
26 |
--------------------------------------------------------------------------------
/spec/controllers/rendering_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe FooController, type: :controller do
4 | include Capybara::DSL
5 | render_views
6 |
7 | context "renders views fetched from database with" do
8 | it "a basic template" do
9 | FactoryBot.create(:database_template, :path => 'foo/default_layout')
10 |
11 | visit '/foo/default_layout'
12 |
13 | expect(response).to render_template("foo/default_layout" )
14 | expect(page.body).to match(/something here in the body of the page: 4/)
15 | end
16 |
17 | it "a custom layout" do
18 | FactoryBot.create(:database_template, :path => 'foo/custom_layout')
19 | FactoryBot.create(:database_template, :path => 'layouts/custom', :body => 'This is a layout with body: <%= yield %>')
20 |
21 | visit '/foo/custom_layout'
22 |
23 | expect(response).to render_template("layouts/custom" )
24 | expect(response).to render_template("foo/custom_layout" )
25 | expect(page.body).to match(/This is a layout with body:/)
26 | expect(page.body).to match(/something here in the body of the page:/)
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require File.expand_path('../config/application', __FILE__)
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | # Prevent CSRF attacks by raising an exception.
3 | # For APIs, you may want to use :null_session instead.
4 | protect_from_forgery with: :exception
5 | end
6 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/foo_controller.rb:
--------------------------------------------------------------------------------
1 | class FooController < ApplicationController
2 | prepend_view_path DatabaseTemplate.resolver
3 |
4 | layout 'custom', :only => :custom_layout
5 |
6 | def default_layout
7 | end
8 |
9 | def custom_layout
10 | end
11 | end
12 |
13 |
--------------------------------------------------------------------------------
/spec/dummy/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/spec/dummy/app/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andreapavoni/panoramic/1e458d30a336e695aa1a34594b6fb0421afd5a6c/spec/dummy/app/models/.keep
--------------------------------------------------------------------------------
/spec/dummy/app/models/database_template.rb:
--------------------------------------------------------------------------------
1 | class DatabaseTemplate < ActiveRecord::Base
2 | # include DatabaseTemplates::Orm::ActiveRecord
3 | store_templates
4 | end
5 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dummy
5 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
6 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
7 | <%= csrf_meta_tags %>
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/spec/dummy/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/spec/dummy/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../../config/application', __FILE__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/spec/dummy/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/spec/dummy/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 |
4 | # path to your application root.
5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
6 |
7 | Dir.chdir APP_ROOT do
8 | # This script is a starting point to setup your application.
9 | # Add necessary setup steps to this file:
10 |
11 | puts "== Installing dependencies =="
12 | system "gem install bundler --conservative"
13 | system "bundle check || bundle install"
14 |
15 | # puts "\n== Copying sample files =="
16 | # unless File.exist?("config/database.yml")
17 | # system "cp config/database.yml.sample config/database.yml"
18 | # end
19 |
20 | puts "\n== Preparing database =="
21 | system "bin/rake db:setup"
22 |
23 | puts "\n== Removing old logs and tempfiles =="
24 | system "rm -f log/*"
25 | system "rm -rf tmp/cache"
26 |
27 | puts "\n== Restarting application server =="
28 | system "touch tmp/restart.txt"
29 | end
30 |
--------------------------------------------------------------------------------
/spec/dummy/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 | run Rails.application
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | # Pick the frameworks you want:
4 | require "active_record/railtie"
5 | require "action_controller/railtie"
6 | require "action_mailer/railtie"
7 | require "action_view/railtie"
8 | require "sprockets/railtie"
9 | # require "rails/test_unit/railtie"
10 |
11 | Bundler.require(*Rails.groups)
12 | require "panoramic"
13 |
14 | module Dummy
15 | class Application < Rails::Application
16 | # Settings in config/environments/* take precedence over those specified here.
17 | # Application configuration should go into files in config/initializers
18 | # -- all .rb files in that directory are automatically loaded.
19 |
20 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
21 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
22 | # config.time_zone = 'Central Time (US & Canada)'
23 |
24 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
25 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
26 | # config.i18n.default_locale = :de
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/dummy/config/boot.rb:
--------------------------------------------------------------------------------
1 | # Set up gems listed in the Gemfile.
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
3 |
4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5 | $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)
6 |
--------------------------------------------------------------------------------
/spec/dummy/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: 5
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/spec/dummy/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports and disable caching.
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send.
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger.
20 | config.active_support.deprecation = :log
21 |
22 | # Raise an error on page load if there are pending migrations.
23 | config.active_record.migration_error = :page_load
24 |
25 | # Debug mode disables concatenation and preprocessing of assets.
26 | # This option may cause significant delays in view rendering with a large
27 | # number of complex assets.
28 | config.assets.debug = true
29 |
30 | # Asset digests allow you to set far-future HTTP expiration dates on all assets,
31 | # yet still be able to expire them through the digest params.
32 | config.assets.digest = true
33 |
34 | # Adds additional error checking when serving assets at runtime.
35 | # Checks for improperly declared sprockets dependencies.
36 | # Raises helpful error messages.
37 | config.assets.raise_runtime_errors = true
38 |
39 | # Raises error for missing translations
40 | # config.action_view.raise_on_missing_translations = true
41 | end
42 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application
18 | # Add `rack-cache` to your Gemfile before enabling this.
19 | # For large-scale production use, consider using a caching reverse proxy like
20 | # NGINX, varnish or squid.
21 | # config.action_dispatch.rack_cache = true
22 |
23 | # Disable serving static files from the `/public` folder by default since
24 | # Apache or NGINX already handles this.
25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
26 |
27 | # Compress JavaScripts and CSS.
28 | config.assets.js_compressor = :uglifier
29 | # config.assets.css_compressor = :sass
30 |
31 | # Do not fallback to assets pipeline if a precompiled asset is missed.
32 | config.assets.compile = false
33 |
34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets,
35 | # yet still be able to expire them through the digest params.
36 | config.assets.digest = true
37 |
38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
39 |
40 | # Specifies the header that your server uses for sending files.
41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
43 |
44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
45 | # config.force_ssl = true
46 |
47 | # Use the lowest log level to ensure availability of diagnostic information
48 | # when problems arise.
49 | config.log_level = :debug
50 |
51 | # Prepend all log lines with the following tags.
52 | # config.log_tags = [ :subdomain, :uuid ]
53 |
54 | # Use a different logger for distributed setups.
55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
56 |
57 | # Use a different cache store in production.
58 | # config.cache_store = :mem_cache_store
59 |
60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
61 | # config.action_controller.asset_host = 'http://assets.example.com'
62 |
63 | # Ignore bad email addresses and do not raise email delivery errors.
64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
65 | # config.action_mailer.raise_delivery_errors = false
66 |
67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
68 | # the I18n.default_locale when a translation cannot be found).
69 | config.i18n.fallbacks = true
70 |
71 | # Send deprecation notices to registered listeners.
72 | config.active_support.deprecation = :notify
73 |
74 | # Use default logging formatter so that PID and timestamp are not suppressed.
75 | config.log_formatter = ::Logger::Formatter.new
76 |
77 | # Do not dump schema after migrations.
78 | config.active_record.dump_schema_after_migration = false
79 | end
80 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure static file server for tests with Cache-Control for performance.
16 | config.serve_static_files = true
17 | config.static_cache_control = 'public, max-age=3600'
18 |
19 | # Show full error reports and disable caching.
20 | config.consider_all_requests_local = true
21 | config.action_controller.perform_caching = false
22 |
23 | # Raise exceptions instead of rendering exception templates.
24 | config.action_dispatch.show_exceptions = false
25 |
26 | # Disable request forgery protection in test environment.
27 | config.action_controller.allow_forgery_protection = false
28 |
29 | # Tell Action Mailer not to deliver emails to the real world.
30 | # The :test delivery method accumulates sent emails in the
31 | # ActionMailer::Base.deliveries array.
32 | config.action_mailer.delivery_method = :test
33 |
34 | # Randomize the order test cases are executed.
35 | config.active_support.test_order = :random
36 |
37 | # Print deprecation notices to the stderr.
38 | config.active_support.deprecation = :stderr
39 |
40 | # Raises error for missing translations
41 | # config.action_view.raise_on_missing_translations = true
42 | end
43 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
9 | # Precompile additional assets.
10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
11 | # Rails.application.config.assets.precompile += %w( search.js )
12 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.action_dispatch.cookies_serializer = :json
4 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 | # Make sure the secret is at least 30 characters and all random,
6 | # no regular words or you'll be exposed to dictionary attacks.
7 | Dummy::Application.config.secret_token = 'd3115f72ad5880f2d9996fccbc8fea7778866b94b3653b9e712cf1ae8a21a7d6d3c1e6df116198db542f1be3a9112c9ebc15e43f8eb5138ac7e8a763a25a191c'
8 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.session_store :cookie_store, key: '_dummy_session'
4 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/spec/dummy/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # To learn more, please read the Rails Internationalization guide
20 | # available at http://guides.rubyonrails.org/i18n.html.
21 |
22 | en:
23 | hello: "Hello world"
24 |
--------------------------------------------------------------------------------
/spec/dummy/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | get '/foo/default_layout' => 'foo#default_layout'
3 | get '/foo/custom_layout' => 'foo#custom_layout'
4 | end
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/secrets.yml:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key is used for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 |
6 | # Make sure the secret is at least 30 characters and all random,
7 | # no regular words or you'll be exposed to dictionary attacks.
8 | # You can use `rake secret` to generate a secure secret key.
9 |
10 | # Make sure the secrets in this file are kept private
11 | # if you're sharing your code publicly.
12 |
13 | development:
14 | secret_key_base: 54877071639f591ceae3604d3816d0a2e525c2def764ecf760f94f42d055ef968eafaa915fb152caca417edbe1a5019c6692ff1590d69396ff1cbe7e2a676e1e
15 |
16 | test:
17 | secret_key_base: e4e03f1c7167d75bbf4be4ff3353c5cf607ca1d84cd27f7937a5c0d175a201d9c30810f691365f5c9ca20cf92442eb05f3b43dfa2cb38d152148bfefc8dc7b42
18 |
19 | # Do not keep production secrets in the repository,
20 | # instead read values from the environment.
21 | production:
22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
23 |
--------------------------------------------------------------------------------
/spec/dummy/db/migrate/20110526132353_create_database_templates.rb:
--------------------------------------------------------------------------------
1 | class CreateDatabaseTemplates < ActiveRecord::Migration
2 | def self.up
3 | create_table :database_templates do |t|
4 | t.text :body
5 | t.boolean :partial, :default => false
6 | t.string :path
7 | t.string :format
8 | t.string :locale
9 | t.string :handler
10 |
11 | t.timestamps
12 | end
13 | end
14 |
15 | def self.down
16 | drop_table :database_templates
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/dummy/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # Note that this schema.rb definition is the authoritative source for your
6 | # database schema. If you need to create the application database on another
7 | # system, you should be using db:schema:load, not running all the migrations
8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 | # you'll amass, the slower it'll run and the greater likelihood for issues).
10 | #
11 | # It's strongly recommended to check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(:version => 20110526132353) do
14 |
15 | create_table "database_templates", :force => true do |t|
16 | t.text "body"
17 | t.boolean "partial", :default => false
18 | t.string "path"
19 | t.string "format"
20 | t.string "locale"
21 | t.string "handler"
22 | t.datetime "created_at"
23 | t.datetime "updated_at"
24 | end
25 |
26 | end
27 |
--------------------------------------------------------------------------------
/spec/dummy/script/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3 |
4 | APP_PATH = File.expand_path('../../config/application', __FILE__)
5 | require File.expand_path('../../config/boot', __FILE__)
6 | require 'rails/commands'
7 |
--------------------------------------------------------------------------------
/spec/orm/active_record_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Panoramic::Orm::ActiveRecord, type: :model do
4 | let(:template) { FactoryBot.build :database_template }
5 |
6 | context "validations" do
7 | it "has body present" do
8 | template.body = nil
9 | expect(template).to_not be_valid
10 | expect(template.errors[:body].size).to eq(1)
11 | end
12 |
13 | context "MIME format" do
14 | it "is valid" do
15 | template.format = 'notknown'
16 | expect(template).to_not be_valid
17 | expect(template.errors[:format].size).to eq(1)
18 | end
19 |
20 | it "is present" do
21 | template.format = nil
22 | expect(template).to_not be_valid
23 | expect(template.errors[:format].size).to eq(1)
24 | end
25 | end
26 |
27 | context "locale" do
28 | it "is valid even if not present" do
29 | template.locale = nil
30 | expect(template).to be_valid
31 | end
32 | end
33 | end
34 |
35 | context "handler" do
36 | it "is valid" do
37 | template.handler = 'notknown'
38 | expect(template).to_not be_valid
39 | expect(template.errors[:handler].size).to eq(1)
40 | end
41 |
42 | it "is present" do
43 | template.handler = nil
44 | expect(template).to_not be_valid
45 | expect(template.errors[:handler].size).to eq(1)
46 | end
47 | end
48 |
49 | context "cache" do
50 | before do
51 | DatabaseTemplate.delete_all
52 | end
53 |
54 | it "is expired on update" do
55 | resolver = DatabaseTemplate.resolver
56 |
57 | cache_key = Object.new
58 | db_template = FactoryBot.create(:database_template, :path => 'foo/some_list', :body => 'Listing something')
59 |
60 | details = { :formats => [:html], :locale => [:en], :handlers => [:erb] }
61 |
62 | template = resolver.find_all("some_list", "foo", false, details, cache_key).first
63 | expect(template.source).to match(/Listing something/)
64 |
65 | db_template.update_attributes(:body => "New body for template")
66 |
67 | template = resolver.find_all("some_list", "foo", false, details, cache_key).first
68 | expect(template.source).to match(/New body for template/)
69 | end
70 | end
71 |
72 | context "#resolver" do
73 | it "#returns a Resolver instance" do
74 | expect(DatabaseTemplate.resolver).to be_a(Panoramic::Resolver)
75 | end
76 | end
77 |
78 | context "ActiveRecord::Base" do
79 | it "responds to store_templates" do
80 | expect(ActiveRecord::Base).to respond_to(:store_templates)
81 | end
82 | end
83 | end
84 |
--------------------------------------------------------------------------------
/spec/resolver_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Panoramic::Resolver do
4 | let(:resolver) { Panoramic::Resolver.using(DatabaseTemplate) }
5 |
6 | context ".find_templates" do
7 | it "should lookup templates for given params" do
8 | template = FactoryBot.create(:database_template, :path => 'foo/example')
9 | details = { :formats => [:html], :locale => [:en], :handlers => [:erb] }
10 | expect(resolver.find_templates('example', 'foo', false, details).first).to_not be_nil
11 | end
12 |
13 | it "should lookup locale agnostic templates for given params" do
14 | template = FactoryBot.create(:database_template, :path => 'foo/example', :locale => nil)
15 | details = { :formats => [:html], :locale => [:en], :handlers => [:erb] }
16 | expect(resolver.find_templates('example', 'foo', false, details).first).to_not be_nil
17 | end
18 |
19 | it "should lookup templates for given params and prefixes" do
20 | resolver = Panoramic::Resolver.using(DatabaseTemplate, :only => 'foo')
21 | details = { :formats => [:html], :locale => [:en], :handlers => [:erb] }
22 |
23 | template = FactoryBot.create(:database_template, :path => 'bar/example')
24 | expect(resolver.find_templates('example', 'bar', false, details).first).to be_nil
25 |
26 | template = FactoryBot.create(:database_template, :path => 'foo/example')
27 | expect(resolver.find_templates('example', 'foo', false, details).first).to_not be_nil
28 | end
29 |
30 | it "should lookup multiple templates" do
31 | resolver = Panoramic::Resolver.using(DatabaseTemplate, :only => 'foo')
32 | details = { :formats => [:html,:text], :locale => [:en], :handlers => [:erb] }
33 | details[:formats].each do |format|
34 | FactoryBot.create(:database_template, :path => 'foo/example', :format => format.to_s)
35 | end
36 | templates = resolver.find_templates('example', 'foo', false, details)
37 | expect(templates.length).to be >= 2
38 | expect(templates.map(&:formats).flatten.uniq).to eq([:html, :text])
39 | end
40 | end
41 | end
42 |
43 | describe_private Panoramic::Resolver, '(private methods)' do
44 | let(:resolver) { Panoramic::Resolver.using(DatabaseTemplate) }
45 |
46 | context "#build_path" do
47 | it "returns prefix/name if prefix is passed" do
48 | expect(resolver.build_path('path', 'prefix')).to eq('prefix/path')
49 | end
50 |
51 | it "returns name if prefix is nil" do
52 | expect(resolver.build_path('path',nil)).to eq('path')
53 | end
54 | end
55 |
56 | context "#normalize_array" do
57 | it "converts all symbols to strings" do
58 | expect(resolver.normalize_array([:something, 'that', :matters])).to eq(['something','that','matters'])
59 | end
60 | end
61 |
62 | context "#initialize_template" do
63 | let(:template) { FactoryBot.create :database_template }
64 |
65 | it "initializes an ActionView::Template object" do
66 | expect(resolver.initialize_template(template)).to be_a(ActionView::Template)
67 | end
68 | end
69 |
70 | context "#virtual_path" do
71 | it "returns 'path' if is not a partial" do
72 | expect(resolver.virtual_path('path',false)).to eq('path')
73 | end
74 |
75 | it "returns '_path' if is a partial" do
76 | expect(resolver.virtual_path('path',true)).to eq('_path')
77 | end
78 | end
79 |
80 | end
81 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | unless ENV['COVERAGE'].nil?
2 | require 'simplecov'
3 | SimpleCov.start 'rails' do
4 | coverage_dir 'coverage'
5 | end
6 | end
7 |
8 | ENV["RAILS_ENV"] = "test"
9 |
10 | require File.expand_path("../dummy/config/environment.rb", __FILE__)
11 | require "rails/test_help"
12 | require "rspec/rails"
13 | require 'factory_bot'
14 |
15 | ActionMailer::Base.delivery_method = :test
16 | ActionMailer::Base.perform_deliveries = true
17 | ActionMailer::Base.default_url_options[:host] = "test.com"
18 |
19 | Rails.backtrace_cleaner.remove_silencers!
20 |
21 | # Configure capybara for integration testing
22 | require "capybara/rails"
23 | Capybara.default_driver = :rack_test
24 | Capybara.default_selector = :css
25 |
26 | # Run any available migration
27 | ActiveRecord::Migrator.migrate File.expand_path("../dummy/db/migrate/", __FILE__)
28 |
29 | # Load support files
30 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
31 |
32 | RSpec.configure do |config|
33 | # Remove this line if you don't want RSpec's should and should_not
34 | # methods or matchers
35 | require 'rspec/expectations'
36 | config.include RSpec::Matchers
37 |
38 | # == Mock Framework
39 | config.mock_with :rspec
40 | end
41 |
--------------------------------------------------------------------------------
/spec/support/describe_private.rb:
--------------------------------------------------------------------------------
1 | # taken from: http://kailuowang.blogspot.com/2010/08/testing-private-methods-in-rspec.html
2 | def describe_private *args, &block
3 | example = describe *args, &block
4 | klass = args[0]
5 | if klass.is_a? Class
6 | saved_private_instance_methods = klass.private_instance_methods
7 | example.before do
8 | klass.class_eval { public *saved_private_instance_methods }
9 | end
10 | example.after do
11 | klass.class_eval { private *saved_private_instance_methods }
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/support/factories.rb:
--------------------------------------------------------------------------------
1 | # FactoryBot.define :database_template do |t|
2 | FactoryBot.define do
3 | factory :database_template do
4 | path { 'foo/index' }
5 | format { 'html' }
6 | locale { 'en' }
7 | handler { 'erb' }
8 | partial { 'false' }
9 | body { "something here in the body of the page: <%= 2 + 2 %>" }
10 | end
11 | end
12 |
--------------------------------------------------------------------------------