blockpile 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg/*
data/README.rdoc ADDED
@@ -0,0 +1,137 @@
1
+ == Blockpile
2
+
3
+ Piles of extendable rails view blocks, triggered and masked behind simple helpers
4
+
5
+ == What?
6
+
7
+ Sometimes within rails, view logic can become complex. As complexity begins to increase, a common theme or pattern occurs:
8
+
9
+ * View logic begins to grow overly complex for what makes sense in view templates. Lengthy, confusing, and downright dirty views cause you to loose sleep.
10
+ * Logic can be broken out of the view templates and placed into the controller.
11
+ * Eventually, when the view logic is needed in another request, the only solution is to pull the entire logic into a helper method.
12
+
13
+ So what's the problem?
14
+
15
+ Rails view helpers are great for view logic, and especially for small reusable view components. Unfortunately, complex view logic is almost always a rats nest of logic and string interpolation. In fact, I'm still not sure how this has become a viable solution for rails developers, as it seems to go against what I understand to be the "rails way". To their defense, there simply isn't any other way. Until now!
16
+
17
+ To better illustrate the problem, I decided to insert a snippet of a rails view helper from a popular project management tool "Redmine":
18
+
19
+ def render_project_hierarchy(projects)
20
+ s = ''
21
+ if projects.any?
22
+ ancestors = []
23
+ projects.each do |project|
24
+ if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
25
+ s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
26
+ else
27
+ ancestors.pop
28
+ s << "</li>"
29
+ while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
30
+ ancestors.pop
31
+ s << "</ul></li>\n"
32
+ end
33
+ end
34
+ classes = (ancestors.empty? ? 'root' : 'child')
35
+ s << "<li class='#{classes}'><div class='#{classes}'>" +
36
+ link_to(h(project), {:controller => 'projects', :action => 'show', :id => project}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
37
+ s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
38
+ s << "</div>\n"
39
+ ancestors << project
40
+ end
41
+ s << ("</li></ul>\n" * ancestors.size)
42
+ end
43
+ s
44
+ end
45
+
46
+ So even if you are talented enough to understand what this code is doing, let me ask 2 questions:
47
+
48
+ * Do you mind if we upgrade the markup language to fit a new design?
49
+ * Even though this is an acceptable solution, does it really feel good to have this nest under your hood?
50
+
51
+ Before we move on, let me add that I'm not trashing the Redmine code-base. In fact, I chose an example from this project simply because the author is fantastic and this example mirrors at least one helper method in every rails app.
52
+
53
+ == Enter Blockpile
54
+
55
+ A blockpile solves this particular problem by slicing the view layer into 2 finer layers: logic, and template. It is no different than a controller/template relationship, except that a blockpile can be easily distributed across the application just as a helper method can.
56
+
57
+ From a high level, a blockpile consists of 3 components:
58
+
59
+ * A Ruby Class file
60
+ * A template file (erb only for now, others coming soon)
61
+ * A function call to include the blockpile in your existing templates
62
+
63
+ == Example
64
+
65
+ To keep this example simple, I'll show you one way to clean up the example above. Since I'm lazy, I'm not actually going to fix the example above but show the concept which could be filled in later.
66
+
67
+ === Ruby Class file
68
+ # app/helpers/blockpiles/project_hierarchy.erb
69
+
70
+ class ProjectHierarchy < Blockpile::Base
71
+
72
+ def build(options={})
73
+ # use this instead of initialize
74
+ @projects = options[:project]
75
+ prepare_hierarchy
76
+ end
77
+
78
+ def prepare_hierarchy
79
+ # fill in hierarchy here
80
+ end
81
+
82
+ def projects
83
+ # fill in code
84
+ end
85
+
86
+ def project_children(project)
87
+ # could return project children
88
+ end
89
+
90
+ end
91
+
92
+ Here we can take care of all logic, in preparation for the view. We need not interpolate, as the template file will pull data from this class instance when needed. Here we can focus on the beauty and elegance of Ruby, and not on the mixture of presentation.
93
+
94
+ === A template file
95
+
96
+ # app/views/blockpiles/project_hierarchy.html.erb
97
+
98
+
99
+ <ul>
100
+ <% projects.each do |project| %>
101
+
102
+ <li><%= project.name %></li>
103
+
104
+ <% project_children(project).each do |child| %>
105
+ <li class="child"><%= child.name %></li>
106
+ <% end %>
107
+
108
+ <% end %>
109
+ </ul>
110
+
111
+ The purpose of the template file is to "pull" logic from the Blockpile Class. We are assuming all logic will be taken care of in the class, and here we can simply focus on our view presentation, using only conditional logic (if else etc).
112
+
113
+ === helper-like function call
114
+
115
+ # app/views/{controller}/{action}.html.erb
116
+
117
+ <%= project_hierarchy :project => @project %>
118
+
119
+ There is no boiler plate for turning your Blockpile Class into a helper method, it happens under the hood. Notice we are passing @project, that will be available in your class through the options hash in the build function. The input of the builder method is completely up to you. In this example I chose to use an "options" hash. You could replace that with 3 input arguments instead if you prefer, in which case you would simply pass @project instead of assigning to a hash key.
120
+
121
+ === Summary
122
+
123
+ In essence, what could be a horrible nest of string interpolation and logic, can become a clean isolation of logic and markup language. I realize this sample solution is lacking, but I'll leave the actual logic to the developer.
124
+
125
+ === Generator
126
+
127
+ You can quickly create a Blockpile Class and Template pair by using the generator:
128
+
129
+ rails generate blockpile project_hierarchy
130
+
131
+ And thats it! You now have a Blockpile ready for use, with a helper method called "project_hierarchy".
132
+
133
+ == Customize your piles
134
+
135
+ Coming soon.
136
+
137
+ Copyright (c) 2010 Tyler Flint, released under the MIT license
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
data/blockpile.gemspec CHANGED
@@ -5,24 +5,26 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{blockpile}
8
- s.version = "0.2.0"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tyler Flint"]
12
- s.date = %q{2010-08-08}
12
+ s.date = %q{2010-08-16}
13
13
  s.description = %q{This module attempts to create structured view helpers. Essentially, a blockpile consists of a ruby class file, and a template. This allows for isolated blocks of view logic, that can maintain a clean separation of markup language from ruby code. Blocks can be inherited from to DRY up view logic.}
14
14
  s.email = %q{tylerflint@gmail.com}
15
15
  s.extra_rdoc_files = [
16
- "README"
16
+ "README.rdoc"
17
17
  ]
18
18
  s.files = [
19
- "MIT-LICENSE",
20
- "README",
19
+ ".gitignore",
20
+ "MIT-LICENSE",
21
+ "README.rdoc",
21
22
  "Rakefile",
22
23
  "VERSION",
23
24
  "blockpile.gemspec",
24
25
  "lib/blockpile.rb",
25
26
  "lib/blockpile/base.rb",
27
+ "lib/blockpile/paths.rb",
26
28
  "lib/blockpile/setup.rb",
27
29
  "lib/generators/blockpile/USAGE",
28
30
  "lib/generators/blockpile/blockpile_generator.rb",
@@ -9,7 +9,7 @@ class Blockpile::Base
9
9
  end
10
10
 
11
11
  def to_html
12
- render_template @template
12
+ render_template
13
13
  end
14
14
 
15
15
  def build
@@ -19,9 +19,21 @@ class Blockpile::Base
19
19
  protected
20
20
 
21
21
  # Assumes /views/helper/ as base
22
- def render_template(template)
23
- @path ||= '/app/views/blockpiles/'
24
- ERB.new( File.read(Rails.root.to_s + @path + template + ".html.erb") ).result binding
22
+ def render_template
23
+ ERB.new( File.read( get_template ) ).result binding
24
+ end
25
+
26
+ def get_template
27
+ get_paths.each do |path|
28
+ if File::exists?( path + "/" + @template + ".html.erb")
29
+ return path + "/" + @template + ".html.erb"
30
+ end
31
+ end
32
+ raise "Unable to find template for this blockpile"
33
+ end
34
+
35
+ def get_paths
36
+ Blockpile::Paths.get_paths
25
37
  end
26
38
 
27
39
  def method_missing(*args, &block)
@@ -0,0 +1,15 @@
1
+ module Blockpile
2
+ module Paths
3
+
4
+ @@paths = []
5
+
6
+ def self.add_template_path(path)
7
+ @@paths << path
8
+ end
9
+
10
+ def self.get_paths
11
+ @@paths
12
+ end
13
+
14
+ end
15
+ end
@@ -1,15 +1,15 @@
1
1
  module Blockpile
2
2
  module Setup
3
3
  def self.add_load_path(path)
4
- ActiveSupport::Dependencies.autoload_paths << path
5
- Dir.glob(path + "*.rb") do |file|
4
+ Dir.glob(path + "/**/*.rb") do |file|
5
+ ActiveSupport::Dependencies.autoload_paths << File.dirname(file)
6
6
  file_name = file.split(/\//).pop.gsub(/.rb/, '')
7
- class_name = file_name.classify
7
+ class_name = file_name.classify
8
8
  ActionView::Base.class_eval %{
9
9
  def #{file_name}(*args, &block)
10
- block = #{class_name}.new(self, session, params, '#{file_name}', *args)
11
- yield block
12
- raw block.to_html
10
+ blockpile = #{class_name}.new(self, session, params, '#{file_name}', *args)
11
+ yield blockpile if block_given?
12
+ raw blockpile.to_html
13
13
  end
14
14
  }
15
15
  end
data/lib/blockpile.rb CHANGED
@@ -1,17 +1,20 @@
1
1
  require 'rails'
2
2
  require 'blockpile/setup'
3
3
  require 'blockpile/base'
4
+ require 'blockpile/paths'
4
5
 
5
6
  module Blockpile
6
7
  class Railtie < Rails::Railtie
7
8
  initializer "blockpile.setup default directories" do
8
9
  Blockpile.setup do |config|
9
- config.add_load_path Rails.root.to_s + '/app/helpers/blockpiles/'
10
+ config.add_load_path Rails.root.to_s + '/app/helpers/blockpiles'
10
11
  end
12
+ Blockpile::Paths.add_template_path Rails.root.to_s + '/app/views/blockpiles/'
11
13
  end
12
14
  end
13
15
 
14
16
  def self.setup
15
17
  yield Blockpile::Setup
16
18
  end
19
+
17
20
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
7
+ - 3
8
8
  - 0
9
- version: 0.2.0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Tyler Flint
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-08 00:00:00 -06:00
17
+ date: 2010-08-16 00:00:00 -06:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -25,15 +25,17 @@ executables: []
25
25
  extensions: []
26
26
 
27
27
  extra_rdoc_files:
28
- - README
28
+ - README.rdoc
29
29
  files:
30
+ - .gitignore
30
31
  - MIT-LICENSE
31
- - README
32
+ - README.rdoc
32
33
  - Rakefile
33
34
  - VERSION
34
35
  - blockpile.gemspec
35
36
  - lib/blockpile.rb
36
37
  - lib/blockpile/base.rb
38
+ - lib/blockpile/paths.rb
37
39
  - lib/blockpile/setup.rb
38
40
  - lib/generators/blockpile/USAGE
39
41
  - lib/generators/blockpile/blockpile_generator.rb
data/README DELETED
@@ -1,13 +0,0 @@
1
- Blockpile
2
- =========
3
-
4
- Introduction goes here.
5
-
6
-
7
- Example
8
- =======
9
-
10
- Example goes here.
11
-
12
-
13
- Copyright (c) 2010 [name of plugin creator], released under the MIT license