GithubHelp home page GithubHelp logo

chamnap / liquid-rails Goto Github PK

View Code? Open in Web Editor NEW
117.0 23.0 100.0 142 KB

Renders liquid templates with layout and partial support

Home Page: https://rubygems.org/gems/liquid-rails

License: MIT License

Ruby 92.82% JavaScript 0.74% CSS 0.58% Liquid 0.95% HTML 4.91%
liquid ruby rails

liquid-rails's Introduction

Build StatusCoverage StatusGem Version

Liquid-Rails

It allows you to render .liquid templates with layout and partial support. It also provides filters, tags, drops class to be used inside your liquid template.

Installation

Add this line to your application's Gemfile:

gem 'liquid-rails'

And then execute:

$ bundle

Or install it yourself as:

$ gem install liquid-rails

Usage

In order to render with layout, in your layout file app/views/layouts/application.liquid, put this:

{{ content_for_layout }}
# It will render app/views/home/_partial.liquid when the current controller is `HomeController`.
{% include 'partial' %}

# It will render app/views/shared/_partial.liquid.
{% include 'shared/partial' %}

Template Rendering

By default, Liquid-Rails makes all instance variables from controller available to liquid template. To limit only some instance variables, do this inside your controller:

def liquid_assigns
  { 'listing' => current_listing, 'content_for_header' => content_for_header, 'current_account' => current_account }
end

By default, Liquid-Rails makes all your helper methods inside your rails app available to liquid template. To limit only some helpers, do this inside your controller:

def liquid_filters
  []
end

You can render liquid templates from other template engines, eg. erb, haml, ...

= render 'shared/partial.liquid'

Filter

Filters are simple methods that modify the output of numbers, strings, variables and objects. They are placed within an output tag {{ }} and are separated with a pipe character |.

Currently, Liquid-Rails adds only the followings:

  1. AssetTagFilter
  2. AssetUrlFilter
  3. DateFilter
  4. NumberFilter
  5. SanitizeFilter
  6. TextFilter
  7. TranslateFilter
  8. UrlFilter
  9. MiscFilter

Tag

Liquid tags are the programming logic that tells templates what to do. Tags are wrapped in: {% %}.

Currently, Liquid-Rails adds only the followings:

  1. csrf_meta_tags
  2. google_analytics_tag
  3. javascript_tag
  4. paginate
  5. content_for

Drop Class

Drops let you provide the user with custom functionality. They are very much like a standard Ruby class, but have all unused and potentially dangerous methods removed. From the user's perspective a drop acts very much like a Hash, though methods are accessed with dot-notation as well as element selection. A drop method cannot be invoked with arguments. Drops are called just-in-time, thus allowing you to lazily load objects.

Given two models, a Post(title: string, body: text) and a Comment(name:string, body:text, post_id:integer), you will have two drops:

class PostDrop < Liquid::Rails::Drop
  attributes :id, :title, :body

  has_many :comments
end

and

class CommentDrop < Liquid::Rails::Drop
  attributes :id, :name, :body

  belongs_to :post
end

Check out more examples.

It works for any ORMs. The PORO should include Liquid::Rails::Droppable. That's all you need to do to have your POROs supported.

RSpec

In spec_helper.rb, you'll need to require the matchers:

require 'liquid-rails/matchers'

Example:

describe PostDrop do
  it { should have_attribute(:id) }
  it { should have_attribute(:title) }
  it { should have_attribute(:body) }
  it { should have_many(:comments) }
end
describe CommentDrop do
  it { should have_attribute(:id) }
  it { should have_attribute(:name) }
  it { should have_attribute(:body) }
  it { should belongs_to(:post) }
end

Contributors

Authors

liquid-rails's People

Contributors

chamnap avatar dkubb avatar qqwy avatar radinreth avatar tomash avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

liquid-rails's Issues

Liquid-Rails doesn't pull templates from resolvers

With an app we're currently building, we use a Panoramic resolver to pull our templates from the database instead of the filesystem, as we allow templates to be changed on the fly.

Our main problem is that liquid-rails doesn't pull templates using the resolver, for any templates included inside other templates. The main template called from the rails controller itself pulls from the database fine, but the partials that are included inside that template continue to pull from the filesystem instead.

Any thoughts on ways to get this working?

Not able to pass multiple parameters to form_tag method

I added a filter to use form_tag in liquid template and registered it but after invoking it in template it accepts first parameter only

filter ...

module Liquid
module Rails
module FormTagFilter
delegate :form_tag, :hidden_field_tag, to: :h
private
def h
@h ||= @context.registers[:view]
end
end
end
end
Liquid::Template.register_filter(Liquid::Rails::FormTagFilter)

liquid template

{{ '/posts', remote = true | form_tag }}
{{ end }}

Adding the above code to liquid template it accepts the first parameter only the other parameters passed to form_tag doesn't show up.

Liquid 4 support

First off, I want to thank @chamnap for creating and maintaining this gem. It has been incredibly helpful to us in adding Liquid support to our current rails project.

Is there any interest in support for Liquid 4? I saw the comment on Shopify/liquid#441 which indicated that 4.0.0 had a breaking change with how this gem resolves template and partial paths. I spent a day or two on it, and I wound up creating and passing a Liquid::Context into my Resolver class when it's set in ApplicationController. That works for me because the context won't change within a single request in my app.

If anyone else is working on Liquid 4 support, I'd love to hear how you have approached it.

Rendering using helpers within render inline:

I'm trying to render inline using liquid pulled from the DB. Anyway to pass the controller context when doing this?

Within controller:

template = Liquid::Template.parse(data_from_db)
render inline: template.render

If the template has something similar to {{ val | number_to_currency }}, I'll receive an error:

Liquid error: Liquid::Rails::NumberFilter#number_to_currency delegated to h.number_to_currency, but h is nil:

Is this gem still maintained?

Hi! Just found this gem as I am looking to use Liquid in my Rails 6 app. It looks like it hasn't been updated in 2 years tho. Is it because it still just works or because it's no longer maintained?

Thanks in advance

Does this gem break controller generators?

When I run the rails generate scaffold_controller Foo name:string I get an error liquid [not found].

$ rails g scaffold_controller Foo name:string                      
      create  app/controllers/foos_controller.rb
       error  liquid [not found]
      invoke  test_unit
      create    test/controllers/foos_controller_test.rb
      create    test/system/foos_test.rb
      invoke  helper
      create    app/helpers/foos_helper.rb
      invoke    test_unit
      invoke  jbuilder
      create    app/views/foos
      create    app/views/foos/index.json.jbuilder
      create    app/views/foos/show.json.jbuilder
      create    app/views/foos/_foo.json.jbuilder

Since it fails on "liquid", wondering if the issue may be related to this gem...

I don't really expect it to be scaffolding any liquid templates, I just want my typical .erb templates generated.

Filter overrides registered public methods as non public: h

Rails 5.0.0, Liquid 4.0.0rc2 --

untitled-1

C:/Dev/Dependencies/Ruby/ruby-2.3.0-x64-mingw32/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler/runtime.rb:89:in `rescue in block (2 levels) in require': There was an error while trying to load the gem 'liquid-rails'. (Bundler::GemRequireError)
Gem Load Error is: Liquid error: Filter overrides registered public methods as non public: h

Any advice?

has_many :through association ordering not honored

Hello! I'm running into some issues when using a has_many :through association and I was wondering if you could point me in the right direction. I've got a model association like this:

class Page < ActiveRecord::Base
  has_many :block_associations, as: :blockable, dependent: :destroy
  has_many :blocks, through: :block_associations
end

class Block < ActiveRecord::Base
  belongs_to :block_type
  has_many :block_associations
end

class BlockAssociation < ActiveRecord::Base
  belongs_to :block
  belongs_to :blockable, polymorphic: true

  default_scope { order(sort_order: :asc) }
end

My drop setup for the Page model is also fairly straightforward:

class PageDrop < Liquid::Rails::Drop
  has_many :blocks
end

However, when I reference page.blocks in the template, the ordering is not honoring the order set by the default_scope in the BlockAssociation model. Looking through the logs, I'm seeing these queries:

BlockAssociation Load (1.2ms)  SELECT "block_associations".* FROM "block_associations" WHERE "block_associations"."blockable_type" = 'Page' AND "block_associations"."blockable_id" IN (2)  ORDER BY "block_associations"."sort_order" ASC
Block Load (0.5ms)  SELECT "blocks".* FROM "blocks" WHERE "blocks"."id" IN (377, 305, 6765, 278, 604)

It looks like the block IDs are being retrieved and then a single query to get the blocks is done, but that leads to the blocks being sorted by ID, not by the sort_order field set on the BlockAssociation join table.

The typical query as generated by ActiveRecord is as follows:

SELECT "blocks".* FROM "blocks" INNER JOIN "block_associations" ON "blocks"."id" = "block_associations"."block_id" WHERE "block_associations"."blockable_id" = $1 AND "block_associations"."blockable_type" = $2  ORDER BY "block_associations"."sort_order" ASC  [["blockable_id", 2], ["blockable_type", "Page"]]

Unfortunately, all I've been able to figure out so far is to define a method to return the blocks manually:

def blocks
  @object.blocks.order('block_associations.sort_order ASC')
end

That, of course, generates another SQL query. Any thoughts on how I could fix this, or maybe point me towards the code? Thanks!

rails_52.gemfile all tests fail

rails_50.gemfile and rails_51.gemfile both require update to .translate method's second argument, switching it from options.with_indifferent_access to **options.to_sym

rails_52.gemifle fails every test with this stacktrace:

Failure/Error: super
       
NoMethodError:
undefined method `run_callbacks' for #<RSpec::ExampleGroups::Request::CustomActionviewResolver:0x00007f8ec3db3dd8>

Rails 6.0.0.rc1 deprecation

Hello! Looks like Rails 6 has a slight change in template handler signature.

DEPRECATION WARNING: Single arity template handlers are deprecated. Template handlers
must now accept two parameters, the view object and the source for the view object.
Change:
  >> Liquid::Rails::TemplateHandler.call(template)
To:
  >> Liquid::Rails::TemplateHandler.call(template, source)
 (called from <top (required)> at <...>/config/environment.rb:7)

How to change default path of templates directory?

Hi @radin-reth

I hold my templates in app/templates and when attempt to include partials inside that directory i'm getting error about "No such template 'v14/html/shared/region_select'" .

Is there way to change default template location?

thanks

liquid-rails with carrierwave

Hello everyone,
i'm using rails 5 with liquid 3.0.6 and liquid-rails 0.1.3
i'm trying to render a template in which i included a picture field (carrierwave).
I have an error "Liquid error: undefined method `to_liquid' for #PictureUploader:0xa91e2f0"

My question is how to render an image inside template using carrierwave ?

Thanks in advance

Do not assume that `spec/filters` are Drop filters

We have a project that has some classes in app/filters and consequently spec/filters that have nothing to do with Liquid, but the liquid-rails gem makes the assumption that whatever is in a filters directory relates to Drop filters. This should probably be a manual configuration option that you need to explicitly make in your RSpec configuration rather than something that is done automagically, as it is a simple configuration process and does not need to happen implicitly.

Layout Tag Implementation

Hi Chamnap,

Thank you for making this library. I have been working with the code and have a working prototype for implementing the layout tag similar to what shopify has: https://help.shopify.com/en/themes/liquid/tags/theme-tags#layout (I doubt my implementation is the same)

My method was this:

  1. Created a simple layout tag:
class Liquid::Tags::LayoutTag < Liquid::Tag

  def initialize(tag_name, markup, tokens)
    super
    @template = markup.delete("\"").strip
  end

  def render(context)
    context.environments.first["layout_override_filename"] = @template
    ""
  end

end
  1. Then extended the view class:
  class TemplateHandler

      def self.call(template)
        "Liquid::Handlers::TemplateHandler.new(self).render(#{template.source.inspect}, local_assigns)"
      end

      def initialize(view)
        @view       = view
        @controller = @view.controller
        @helper     = ActionController::Base.helpers
      end

      def render(template, local_assigns={})

        assigns = if @controller.respond_to?(:liquid_assigns, true)
          @controller.send(:liquid_assigns)
        else
          @view.assigns
        end

        # check if there is a layout override
        layout_override_filename = assigns["layout_override_filename"]

        # if we are processing the layout and there is an override, replace the template with the override content.
        if @view.content_for?(:layout) && !layout_override_filename.nil?
          Rails.logger.info "Rendering layout override from #{layout_override_filename}"
          template = read_template_from_file_system(registers, layout_override_filename)
        end

        assigns['content_for_layout'] = @view.content_for(:layout) if @view.content_for?(:layout)
        assigns.merge!(local_assigns.stringify_keys)

        liquid      = Liquid::Template.parse(template)
        liquid.send(render_method, assigns, filters: filters, registers: registers).html_safe
      end

      def filters
        if @controller.respond_to?(:liquid_filters, true)
          @controller.send(:liquid_filters)
        else
          [@controller._helpers]
        end
      end

      def registers
        {
          view: @view,
          controller: @controller,
          helper: @helper,
          file_system: Liquid::Handlers::FileSystem.new(@view)
        }
      end

      def compilable?
        false
      end

      def render_method
        (::Rails.env.development? || ::Rails.env.test?) ? :render! : :render
      end

      def read_template_from_file_system(registers, name)
        file_system = registers[:file_system] || Liquid::Template.file_system
        file_system.read_template_file(name)
      end

    end

The template handler then replaces the view with the select template when rendering occurs. This was really just a proof of concept. If I clean this up and submit it as a pull request would that be useful to you or others do you think?

Any suggestions on improvements? Liquid is very new to me and I had troubles finding a good way to pass the template override through and ended up using the environment.

cacheable? false

I notice that the rails templates are not cacheable. This makes the template get re-read from disk, then parsed every time the view is rendered, which is slow.

I understand that rails template compilable implies you are returning the function as source code (which is unacceptable), but I was wondering if it made sense to memoize the "parse" function, so that it returns the same template?

The render-ing function would have to be a pure function. Thus, I think, you'd have to use the render! function, as the render mutates the templates with errors.

Very non-informative error on non-existing Drop

In droppable.rb, safe_constantize is used. This means that whenever you call some_rails_model.to_liquid, instead of throwing a NameError: unitialized constant SomeRailsModelDrop, we get NoMethodError: undefined methodnew' for nil:NilClass`

So, I think that constantize would be better here than safe_constantize.

Liquid-Rails sets content-type to text/html during non-HTML rendering

I've just found that some of my liquid-templated outbound email is being junked, and the root cause is because Liquid::Rails::TemplateHandler#render sets the Content-Type for the entire view to text/html, which overrides the multipart/alternative that ActionMailer would normally use.

As a result, services like Hotmail, Gmail perceive an HTML-only message and this raises the spam score.

Any particular reason why this is so? After all liquid templates are generic, they're not just HTML, so it's a false assumption.

I've also just tested using a .liquid partial in a .text.erb template and found that this too incorrectly turns a text/plain response into text/html response. So this something that could change browser behaviour.

Project dead?

Pull requests ignored and no commits in almost a year. Is liquid-rails dead?

How to render instance variables?

Hello,

I haven't figured out how to render an attribute of an instance variable that contains a database record. What am I missing?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.