langalex-components 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/{README → README.md} +73 -59
- data/VERSION.yml +1 -1
- data/components.gemspec +4 -4
- data/lib/components/caching.rb +2 -0
- data/test/caching_test.rb +1 -0
- data/test/test_helper.rb +1 -1
- metadata +6 -5
data/{README → README.md}
RENAMED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
## Components
|
2
2
|
|
3
3
|
This plugin attempts to implement components in the simplest, cleanest, fastest way possible. Inspired by the Cells plugin (http://cells.rubyforge.org) by Nick Sutterer and Peter Bex.
|
4
4
|
|
@@ -6,129 +6,143 @@ A component can be thought of as a very lightweight controller with supporting v
|
|
6
6
|
|
7
7
|
Speaking imprecisely, components prepare for and then render templates.
|
8
8
|
|
9
|
-
|
9
|
+
## Usage
|
10
10
|
|
11
11
|
Note that these examples are very simplistic and would be better implemented using Rails partials.
|
12
12
|
|
13
|
-
|
13
|
+
### Generator
|
14
14
|
|
15
15
|
Running `script/generator users details` will create a UsersComponent with a "details" view. You might then flesh out the templates like this:
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
class UsersComponent < Components::Base
|
18
|
+
def details(user_or_id)
|
19
|
+
@user = user_or_id.is_a?(User) ? user_or_id : User.find(user_or_id)
|
20
|
+
render
|
21
|
+
end
|
21
22
|
end
|
22
|
-
end
|
23
23
|
|
24
|
-
|
24
|
+
### From ActionController
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
class UsersController < ApplicationController
|
27
|
+
def show
|
28
|
+
return :text => component("users/detail", params[:id])
|
29
|
+
end
|
29
30
|
end
|
30
|
-
end
|
31
31
|
|
32
|
-
|
32
|
+
### From ActionView
|
33
33
|
|
34
|
-
|
34
|
+
<%= component "users/detail", @user %>
|
35
35
|
|
36
|
-
|
36
|
+
## More Features
|
37
37
|
|
38
|
-
|
38
|
+
### Caching
|
39
39
|
|
40
40
|
Any component action may be cached using the fragment caching you've configured on ActionController::Base. The command to cache a component action must come after the definition of the action itself. This is because the caching method wraps the action, which makes the caching work even if you call the action directly.
|
41
41
|
|
42
42
|
Example:
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
class UsersComponent < Components::Base
|
45
|
+
def details(user_id)
|
46
|
+
@user = User.find(user_id)
|
47
|
+
render
|
48
|
+
end
|
49
|
+
cache :details, :expires_in => 15.minutes
|
48
50
|
end
|
49
|
-
cache :details, :expires_in => 15.minutes
|
50
|
-
end
|
51
51
|
|
52
52
|
This will cache the returns from UsersComponent#details using a cache key like "users/details/5", where 5 is the user_id. The cache will only be good for fifteen minutes. See Components::Caching for more information.
|
53
53
|
|
54
|
-
|
54
|
+
### Helpers
|
55
55
|
|
56
56
|
All of the standard helper functionality exists for components. You may define a method on your component controller and use :helper_method to make it available in your views, or you may use :helper to add entire modules of extra methods to your views.
|
57
57
|
|
58
58
|
Be careful importing existing helpers, though, as some of them may try and break encapsulation by reading from the session, the request, or the params. You may need to rewrite these helpers so they accept the necessary information as arguments.
|
59
59
|
|
60
|
-
|
60
|
+
### Inherited Views
|
61
61
|
|
62
62
|
Assume two components:
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
class ParentComponent < Components::Base
|
65
|
+
def one
|
66
|
+
render
|
67
|
+
end
|
68
68
|
|
69
|
-
|
70
|
-
|
69
|
+
def two
|
70
|
+
render
|
71
|
+
end
|
71
72
|
end
|
72
|
-
end
|
73
73
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
74
|
+
class ChildComponent < ParentComponent
|
75
|
+
def one
|
76
|
+
render
|
77
|
+
end
|
78
78
|
|
79
|
-
|
80
|
-
|
79
|
+
def three
|
80
|
+
render "one"
|
81
|
+
end
|
81
82
|
end
|
82
|
-
end
|
83
83
|
|
84
84
|
Both methods on the ChildComponent class would first try and render "/app/components/child/one.erb", and if that file did not exist, would render "/app/components/parent/one.erb".
|
85
85
|
|
86
|
-
|
86
|
+
### Standard Argument Options
|
87
87
|
|
88
88
|
You may find yourself constantly needing to pass a standard set of options to each component. If so, you can define a method on your controller that returns a hash of standard options that will be merged with the component arguments and passed to every component.
|
89
89
|
|
90
90
|
Suppose a given component:
|
91
91
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
92
|
+
class GroupsComponent < Components::Base
|
93
|
+
def details(group_id, options = {})
|
94
|
+
@user = options[:user]
|
95
|
+
@group = Group.find(group_id)
|
96
|
+
render
|
97
|
+
end
|
97
98
|
end
|
98
|
-
end
|
99
99
|
|
100
100
|
Then the following setup:
|
101
101
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
class GroupsController < ApplicationController
|
103
|
+
def show
|
104
|
+
render :text => component("groups/details", params[:id])
|
105
|
+
end
|
106
106
|
|
107
|
-
|
107
|
+
protected
|
108
108
|
|
109
|
-
|
110
|
-
|
109
|
+
def standard_component_options
|
110
|
+
{:user => current_user}
|
111
|
+
end
|
111
112
|
end
|
112
|
-
end
|
113
113
|
|
114
114
|
Would expand to:
|
115
115
|
|
116
|
-
|
116
|
+
component("groups/details", params[:id], :user => current_user)
|
117
117
|
|
118
|
-
|
118
|
+
## Components Philosophy
|
119
119
|
|
120
120
|
I wrote this components plugin after evaluating a couple of existing ones, reflecting a bit, and either stealing or composing the following principles. I welcome all debate on the subject.
|
121
121
|
|
122
|
-
|
122
|
+
### Components <em>should not</em> simply embed existing controller actions.
|
123
123
|
|
124
124
|
Re-using existing controller actions introduces intractable performance problems related to redundant controller filters and duplicate request-cached variables.
|
125
125
|
|
126
|
-
|
126
|
+
### Components <em>should not</em> have the concept of a "request" or "current user".
|
127
127
|
|
128
128
|
Everything should be provided as an argument to the component - it should not have direct access to the session, the params, or any other aspect of the request. This means that components will never intelligently respond_to :html, :js, :xml, etc.
|
129
129
|
|
130
|
-
|
130
|
+
### Components _should_ complement RESTful controller design.
|
131
131
|
|
132
132
|
The path of least resistance in Rails includes RESTful controller design to reduce code redundancy. Components should only be designed for use cases where RESTful controller design is either awkward or impossible. This compatibility will reduce the maintenance effort for components and help them grow with Rails itself.
|
133
133
|
|
134
|
+
## Troubleshooting
|
135
|
+
|
136
|
+
Q: I want to render partials from my app/views directory in a component view but it doesn't work
|
137
|
+
A: You have to add the view path of your normal views to the view paths of the components. One solution would be to overwrite the `view_paths` method in your component:
|
138
|
+
|
139
|
+
def self.view_paths
|
140
|
+
super + [Rails.root + 'app/views']
|
141
|
+
end
|
142
|
+
|
143
|
+
Q: I want to use a method from one of my helpers in app/helpers but I get a method missing error
|
144
|
+
A: Call `helper :all` in your component, just like you would in a controller
|
145
|
+
|
146
|
+
## Copyright
|
147
|
+
|
134
148
|
Copyright (c) 2008 Lance Ivy, released under the MIT license
|
data/VERSION.yml
CHANGED
data/components.gemspec
CHANGED
@@ -2,20 +2,20 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{components}
|
5
|
-
s.version = "0.0.
|
5
|
+
s.version = "0.0.3"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Lance Ivy", "Alexander Lang"]
|
9
|
-
s.date = %q{2009-
|
9
|
+
s.date = %q{2009-08-26}
|
10
10
|
s.email = %q{alex@upstream-berlin.com}
|
11
11
|
s.extra_rdoc_files = [
|
12
|
-
"README"
|
12
|
+
"README.md"
|
13
13
|
]
|
14
14
|
s.files = [
|
15
15
|
".document",
|
16
16
|
".gitignore",
|
17
17
|
"MIT-LICENSE",
|
18
|
-
"README",
|
18
|
+
"README.md",
|
19
19
|
"Rakefile",
|
20
20
|
"VERSION.yml",
|
21
21
|
"components.gemspec",
|
data/lib/components/caching.rb
CHANGED
data/test/caching_test.rb
CHANGED
@@ -19,6 +19,7 @@ class CachingTest < Test::Unit::TestCase
|
|
19
19
|
assert_equal "components/hello_world/say_it/trumpapum", @component.send(:cache_key, :say_it, ["trumpapum"]), "uses arguments"
|
20
20
|
assert_equal "components/hello_world/say_it/a/1/2/3/foo=bar", @component.send(:cache_key, :say_it, ["a", [1,2,3], {:foo => :bar}]), "handles mixed types"
|
21
21
|
assert_equal "components/hello_world/say_it/a=1&b=2", @component.send(:cache_key, :say_it, [{:b => 2, :a => 1}]), "hash keys are ordered"
|
22
|
+
assert_equal "components/834876df77918cf2bbfb42253d5977aa", @component.send(:cache_key, :say_it, [{:a => 'x' * 190}]), "hash keys are MD5ed when too long"
|
22
23
|
end
|
23
24
|
|
24
25
|
def test_conditional_caching
|
data/test/test_helper.rb
CHANGED
@@ -19,6 +19,6 @@ RAILS_DEFAULT_LOGGER = Logger.new(File.dirname(__FILE__) + '/debug.log')
|
|
19
19
|
ActiveSupport::Dependencies.load_paths << File.dirname(__FILE__) + "/" + load_path
|
20
20
|
end
|
21
21
|
|
22
|
-
require File.dirname(__FILE__) + '/../init'
|
22
|
+
require File.dirname(__FILE__) + '/../rails/init'
|
23
23
|
|
24
24
|
ActionController::Base.cache_store = :memory_store
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: langalex-components
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lance Ivy
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-
|
13
|
+
date: 2009-08-26 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -21,12 +21,12 @@ executables: []
|
|
21
21
|
extensions: []
|
22
22
|
|
23
23
|
extra_rdoc_files:
|
24
|
-
- README
|
24
|
+
- README.md
|
25
25
|
files:
|
26
26
|
- .document
|
27
27
|
- .gitignore
|
28
28
|
- MIT-LICENSE
|
29
|
-
- README
|
29
|
+
- README.md
|
30
30
|
- Rakefile
|
31
31
|
- VERSION.yml
|
32
32
|
- components.gemspec
|
@@ -59,6 +59,7 @@ files:
|
|
59
59
|
- test/test_helper.rb
|
60
60
|
has_rdoc: true
|
61
61
|
homepage: http://github.com/langalex/components
|
62
|
+
licenses:
|
62
63
|
post_install_message:
|
63
64
|
rdoc_options:
|
64
65
|
- --charset=UTF-8
|
@@ -79,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
80
|
requirements: []
|
80
81
|
|
81
82
|
rubyforge_project:
|
82
|
-
rubygems_version: 1.
|
83
|
+
rubygems_version: 1.3.5
|
83
84
|
signing_key:
|
84
85
|
specification_version: 2
|
85
86
|
summary: TODO
|