Giter VIP home page Giter VIP logo

nestive's Introduction

Nestive Build Status Code Climate

A Nested Inheritable Layouts Helpers for Rails

Nestive adds powerful layout and view helpers to your Rails app. It's similar to the nested layout technique already documented in the Rails guides and found in many other nested layout plugins (a technique using content_for and rendering the parent layout at the end of the child layout). There's a bunch of problems with this technique, including:

  • you can only append content to the content buffer with content_for (you can't prepend to content, you can't replace it)
  • when combined with this nested layout technique, content_for actually prepends new content to the buffer, because each parent layout is rendered after it's child

Nestive is better because it addresses these problems.

Just six methods (so far)

Declaring an area of content with area:

The area helper is a lot like Rails' own <%= yield :foo %>, and is used in layouts to define and render a chunk of content in your layout:

<%= area :sidebar %>

Unlike yield, area will allow your parent layouts to add content to the area at the same time using either a String or a block:

<%= area :sidebar, "Some Content Here" %>

<%= area :sidebar do %>
  Some Content Here
<% end %>

It's important to note that this isn't default content, it is the content (unless a child changes it).

Appending content to an area with append:

The implementation details are quite different, but the append helper works much like Rails' built-in content_for. It will work with either a String or block, adding the new content onto the end of any content previously provided by parent layouts:

<%= extends :application do %>
  <% append :sidebar, "More content." %>
  <% append :sidebar do %>
    More content.
  <% end %>
<% end %>

Prepending content to an area with prepend:

Exactly what you think it is. The reverse of append (duh), adding the new content at the start of any content previously provided by parent layouts:

<%= extends :application do %>
  <%= prepend :sidebar, "Content." %>
  <%= prepend :sidebar do %>
    Content.
  <% end %>
<% end %>

Replacing content with replace

You can also replace any content provided by parent layouts:

<%= extends :application do %>
  <%= replace :sidebar, "New content." %>
  <%= replace :sidebar do %>
    New content.
  <% end %>
<% end %>

Removing content with purge

You can remove the content in the single or in multiple areas

<% purge :sidebar %>
<% purge :sidebar, :banner %>

... which is equal to:

<% replace :sidebar, nil %>

Extending a layout in a child layout (or view) with extends

Any layout (or view) can declare that it wants to inherit from and extend a parent layout, in this case we're extending app/views/layouts/application.html.erb:

<%= extends :application do %>
   ...
<% end %>

You can nest many levels deep:

app/views/layouts/application.html.erb:

<!DOCTYPE html>
  <html>
    <head>
      <%= area :head do %>
        <title><%= area :title, 'Nestive' %></title>
      <% end %>
    </head>
  <body>
    <%= yield %>
  </body>
</html>

app/views/layouts/with_sidebar.html.erb:

<%= extends :application do %>
  <div class="sidebar"><%= area(:sidebar) do %>
    here goes sidebar
  <% end %></div>
  <%= yield -%>
<% end %>

app/views/layouts/blog_posts.html.erb:

<%= extends :with_sidebar do %>
  <% append :sidebar do %>
    Blog archive:
    <%= render_blog_archive %>
  <% end %>

  <% append :head do %>
    <%= javascript_include_tag 'fancy_blog_archive_tag_cloud' %>
  <% end %>

  <%= yield %>
<% end %>

The token blog example

Set-up a global layout defining some content areas.

app/views/layouts/application.html.erb:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title><%= area :title, "JustinFrench.com" %></title>
  <meta name="description" content="<%= area :description, "This is my website." %>">
  <meta name="keywords" content="<%= area :keywords, "justin, french, ruby, design" %>">
</head>
<body>
  <div id="wrapper">
    <div id="content">
      <%= area :content do %>
        <p>Default content goes here.</p>
      <% end %>
    </div>
    <div id="sidebar">
      <%= area :sidebar do %>
        <h2>About Me</h2>
        <p>...</p>
      <% end %>
    </div>
  </div>
  <%= yield %>
</body>
</html>

Next, we set-up a blog layout that extends application, replacing, appending & prepending content to the areas we defined earlier.

app/views/layouts/blog.html.erb:

<%= extends :application do %>
  <% replace :title, "My Blog – " %>
  <% replace :description, "Justin French blogs here on Ruby, Rails, Design, Formtastic, etc" %>
  <% prepend :keywords, "blog, weblog, design links, ruby links, formtastic release notes, " %>
  <%= yield %>
<% end %>

Now in our blog index view we can use blog layout and fill in the areas with content specific to the index action.

app/views/posts/index.html.erb:

<% replace :content do %>
  <h1>My Blog</h1>
  <%= render @articles %>
<% end %>

<% append :sidebar do %>
  <h2>Blog Roll</h2>
  <%= render @links %>
<% end %>

We also need to instruct the PostsController to use this blog layout:

app/controllers/posts_controller.rb:

class PostsController < ApplicationController
  layout 'blog'
end

Caching

Nestive works the same way content_for does and has the same caching drawbacks. That means that nestive helpers are completely ignored when called from within cached block. You probably don't want to use fragment caching around dynamic nestive areas and have to be extra careful what and how you cache to avoid unpleasant surprises.

Installation

  • add gem 'nestive', '~> 0.5' to your Gemfile
  • run bundle

Compatibility

Nestive should work properly with any Rails 3 and 4. Since version 0.5 only Ruby 1.9.3 and newer are supported. For 1.8 compatibility use version 0.4.

Nestive doesn't monkey patch or fiddle with any default behaviors in Rails. Use it when you want to, don't when you don't.

You can help with...

  • feedback
  • reporting issues
  • fixing issues with pull requests
  • performance testing

Twitter

nestive's People

Contributors

justinfrench avatar kossnocorp avatar quamen avatar query-string avatar rwz avatar sj26 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

nestive's Issues

Nestable areas with a context-aware buffer

So, let me begin by saying that this gem is wonderful. The API is elegant, the code is precise and concise, and the functionality is very helpful. In fact, I have found this gem so wonderful that it has inspired me to build on top of it. I will probably be opening a few Issues as spaces to discuss some feature ideas that I have had and implemented. If it is helpful at all, I have my own, fairly radically altered version of the layout_helper.rb file that I am using in a project where all of the features I have added are implemented. I have made the file a gist. If you think any particular feature is worth bringing into nestive, though, I will work to make an individual PR for just that feature. This is the first such feature idea, and it concerns adding scope/context to the buffer (the @_area_for hash),

One of the best features of the codebase is its simplicity. The core functionality lives in one file, the code paths are clear and direct, and the implementation is immediately understandable. The central implementation detail is the array of instructions in a "buffer" hash under the key name of the area. One limitation that I immediately ran into, however, is that such a hash (only ever one level deep) does not allow for name collisions. So, if I have a complex layout where there are various sections in which some part may conceptually be the "title" for that section, I must name each area with a fully descriptive name (e.g. :section1_title and :section2_title); that is, I have to scope the context in a prefix in the name of the area. To my mind, what would be preferable is to simply nest areas:

<%= area :section1 do %>
  <%= area :title, 'Section 1 Title' %>
<% end %>
<%= area :section2 do %>
  <%= area :title, 'Section 2, Title' %>
<% end %>

The gem currently, given such a layout, would have an @_area_for hash that looked like:

{
  :title=>[[:push, "Section 1 Title"], [:push, "Section 2, Title"]],
  :section1=>[[:push, "\n  Section 1 Title\n"]],
  :section2=>[[:push, "\n  Section 2, TitleSection 1 Title\n"]]
}

So, the first feature I wanted to implement was a context-aware buffer hash. The implementation would be to have an N-level hash of hashes. So, for the dummy example, we would have a hash that looked like this:

{
  :section1=>{
    :title=>[[:push, "Section 1 Title"]]
  },
  :section2=>{
    :title=>[[:push, "Section 2, Title"]]
  }
}

This is achieved by keeping a queue and pushing an area on when the method begins and then popping it off right before the method returns. So, if, in the capture(&block), the area method is called again, it's parent area lives in the queue, and the child is pushed onto the queue after it. We then use this queue along with an auto-vivifying hash for the buffer and the new Ruby Hash.dig method to create the buffer (so, in our example, @_buffer.dig(*[:section1, :title]) would eventually get called, where @_buffer is an auto-vivifying hash). We can get into finer-grained implementation details later if this is a path you would be interested in pursuing.

What it provides, IMO, is a more flexible and expressive API for constructing layouts. Now, not only are layouts nestable, but the areas are too. In my gist file, I have implemented this by creating new helpers (section and within) and there are some specific issues for this kind of feature that are addressed in the code for those methods, but if you like the idea, I would try to implement this directly on top of the area API and nothing more.

Let me know what thoughts or questions you have. I know that this is a fairly large feature change that has numerous implications, and I've only briefly sketched out the implementation. But I wanted to start with the basics and see if you'd even be interested in pursuing this feature before really diving in.

purge happens after parent templates render

This is more of a feature request, but I would expect the parent template to not execute any code in it's block if the yield contains a purge of that area. For example, I have some url helpers that work on some pages, but don't exist on others, but the url helper is executed before the purge and so an exception is raised. Is that a feasible request?

Add method like `content_for?`

- unless area(:header).empty?
  .header-container
    .container
      .content-container
        = area(:header)

Just don't quite like the unless area(:header).empty? code
Should be if content_for_area?(:header) (method name can be changed)

Absoulte Path deprecation warning in Rails 6

I was using 5.x version of rails. recently, updated the rails version 6 now i'm getting below warning.

DEPRECATION WARNING: render file: should be given the absolute path to a file (called from _app_views_layouts_frame_html_haml___3291663533381257803_70120141752300 at

Please can you help me with this issue??

no implicit conversion of nil into String

Just tried to use nestive with Rails 4.2, here is a sample admin.html.erb:

<%=  extends :application do %>
  <% purge :menus %>
<% end %>

It blows up with

TypeError - no implicit conversion of nil into String:
  nestive (0.6.0) lib/nestive/layout_helper.rb:92:in `extends'

How does nestive interact with Rails caching?

A line or two in the README explaining how Nestive interacts with Rails' various caching methods would be helpful - if it works the same as content_for and is ignored in fragment caches, etc.

Define layout in view?

First off, this gem is wonderful. Thank you!

I have been futzing around in the source for a bit now trying to see if I could find someway where I wasn't forced to set the layout in the controller.

I am trying to write essentially a companion to extends (right now I'm calling it layout) that would allow me to write something like this:

#app/views/users/show.html.erb
<%= layout :manage do %>
  <% replace :header, 'Manage Access by User' %>
<% end %>

As I am introducing this gem into a team context, I'm trying to drop the barrier to entry while also making what is happening as explicit as possible. Currently, our team allows Rails to do its magic to render views from controller actions, so this would keep me from having to add a bunch of code to the controller layer. Moreover, I philosophically find it cleaner to declare the layout I want to use from the view layer itself.

In reading the source code, I had the spidey-sense that this gem was but one step away from making this possible. I have, as of yet, however, been able to make that step. Any thoughts or suggestions?

nil error in extends() method using Rails 4

I was getting an error in nestive while upgrading a Rails 3.2 app to 4.0, so I created a very simple blog app to test:

rails new Blog ; cd Blog
rails generate scaffold post title:string body:text
rake db:migrate

I updated the application.html.erb layout to match the nestive example, added the blog layout, updated app/views/posts/index.html.erb to follow the example, and updated the PostsController to use the new blog layout. When I try to visit the posts#index page, I get the following error:

nestive (0.4.0) app/helpers/nestive/layout_helper.rb:92:in `extends'
app/views/layouts/blog.html.erb:1:in `_app_views_layouts_blog_html_erb__2118491034999130098_70363411807260'
actionpack (4.0.0) lib/action_view/template.rb:143:in `block in render'
...

This is the same error I was getting with my app. It looks like the call to content_for(:layout) is returning nil. Not sure what's going on or how to debug further, but the original code I was testing worked fine with nestive and Rails 3.2.

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.