Sep
OCT
Nov
24
2019
2020
2021
About this capture
Organization:
Internet Archive
Focused crawls are collections of frequently-updated webcrawl data from narrow (as opposed to broad or wide) web crawls, often focused on a single domain or subdomain.
T h e W a y b a c k M a c h i n e - h t t p : / / w e b . a r c h i v e . o r g / w e b / 2 0 2 0 1 0 2 4 0 9 0 1 2 8 / h t t p s : / / g i t h u b . c o m / g i t h u b / v i e w _ c o m p o n e n t
S k i p t o c o n t e n t
/ ; r e f _ c t a : S i g n u p ; r e f _ l o c : h e a d e r l o g g e d o u t " >
S i g n up
●
F e a t u r e s →
● C o d e r e v i e w
● P r o j e c t m a n a g e m e n t
● I n t e g r a t i o n s
● A c t i o n s
● P a c k a g e s
● S e c u r i t y
● T e a m m a n a g e m e n t
● H o s t i n g
● M o b i l e
● C u s t o m e r s t o r i e s →
● S e c u r i t y →
●
●
●
● E x p l o r e G i t H u b →
L e a r n & c o n t r i b u t e
● T o p i c s
● C o l l e c t i o n s
● T r e n d i n g
● L e a r n i n g L a b
● O p e n s o u r c e g u i d e s
C o n n e c t w i t h o t h e r s
● E v e n t s
● C o m m u n i t y f o r u m
● G i t H u b E d u c a t i o n
● G i t H u b S t a r s p r o g r a m
●
●
P l a n s →
● C o m p a r e p l a n s
● C o n t a c t S a l e s
● N o n p r o f i t →
● E d u c a t i o n →
In this repository
All GitHub
↵
Jump to
↵
No suggested jump to results
{ { m e s s a g e } }
●
W a t c h
55
●
S t a r
1 . 3 k
●
F o r k
1 3 4
V i e w c o m p o n e n t s f o r R a i l s
M I T L i c e n s e
1 . 3 k
s t a r s
1 3 4
f o r k s
S t a r
W a t c h
●
C o d e
●
I s s u e s
12
●
P u l l r e q u e s t s
12
●
D i s c u s s i o n s
●
A c t i o n s
●
S e c u r i t y
●
I n s i g h t s
M o r e
●
C o d e
●
I s s u e s
●
P u l l r e q u e s t s
●
D i s c u s s i o n s
●
A c t i o n s
●
S e c u r i t y
●
I n s i g h t s
D i s m i s s
J o i n G i t H u b t o d a y
G i t H u b i s h o m e t o o v e r 5 0 m i l l i o n d e v e l o p e r s w o r k i n g t o g e t h e r t o h o s t a n d r e v i e w c o d e , m a n a g e p r o j e c t s , a n d b u i l d s o f t w a r e t o g e t h e r .
S i g n u p
5
b r a n c h e s
65
t a g s
G o t o f i l e
C o d e
C l o n e
U s e G i t o r c h e c k o u t w i t h S V N u s i n g t h e w e b U R L .
W o r k f a s t w i t h o u r o f f i c i a l C L I .
L e a r n m o r e .
●
O p e n w i t h G i t H u b D e s k t o p
●
D o w n l o a d Z I P
L a u n c h i n g G i t H u b D e s k t o p
I f n o t h i n g h a p p e n s , d o w n l o a d G i t H u b D e s k t o p a n d t r y a g a i n .
G o b a c k
L a u n c h i n g G i t H u b D e s k t o p
I f n o t h i n g h a p p e n s , d o w n l o a d G i t H u b D e s k t o p a n d t r y a g a i n .
G o b a c k
L a u n c h i n g X c o d e
I f n o t h i n g h a p p e n s , d o w n l o a d X c o d e a n d t r y a g a i n .
G o b a c k
L a u n c h i n g V i s u a l S t u d i o
I f n o t h i n g h a p p e n s , d o w n l o a d t h e G i t H u b e x t e n s i o n f o r V i s u a l S t u d i o a n d t r y a g a i n .
G o b a c k
L a t e s t c o m m i t
j o e l h a w k s l e y
M e r g e p u l l r e q u e s t # 5 0 9 f r o m g i t h u b / e a g e r - l o a d - c o m p i l e
…
0 f b 9 d a a
O c t 2 3 , 2 0 2 0
M e r g e p u l l r e q u e s t # 5 0 9 f r o m g i t h u b / e a g e r - l o a d - c o m p i l e
Eager load compile
0 f b 9 d a a
G i t s t a t s
●
1 , 2 4 0
c o m m i t s
F i l e s
P e r m a l i n k
F a i l e d t o l o a d l a t e s t c o m m i t i n f o r m a t i o n .
T y p e
N a m e
L a t e s t c o m m i t m e s s a g e
C o m m i t t i m e
. g i t h u b
u s e n i c e r e n v v a r
S e p 2 1 , 2 0 2 0
a p p
F i x b u g w h e r e p r e v i e w s d i d n ' t w o r k w h e n m o n k e y p a t c h w a s d i s a b l e d .
A u g 4 , 2 0 2 0
d o c s / c a s e - s t u d i e s
f i x s p a c i n g l i n t s
S e p 1 1 , 2 0 2 0
l i b
o n l y p r e c o m p i l e c o m p o n e n t s i f e a g e r l o a d i n g i s t r u e
O c t 2 3 , 2 0 2 0
p e r f o r m a n c e
U p d a t e p e r f o r m a n c e / b e n c h m a r k . r b
A u g 2 7 , 2 0 2 0
s c r i p t
r e n a m e A c t i o n V i e w : : C o m p o n e n t t o V i e w C o m p o n e n t
M a r 2 , 2 0 2 0
t e s t
B r i n g b a c k t e m p l a t e p a t h t e s t
O c t 1 6 , 2 0 2 0
. g i t i g n o r e
c h e c k i n s i m p l e c o v r e p o r t
M a y 2 0 , 2 0 2 0
. r u b o c o p . y m l
a d d n e w l i n e b a c k
A u g 3 1 , 2 0 2 0
. r u b y - v e r s i o n
u s e R u b y 2 . 7 . 1 i n l o c a l d e v e l o p m e n t
J u n 1 7 , 2 0 2 0
C H A N G E L O G . m d
o n l y p r e c o m p i l e c o m p o n e n t s i f e a g e r l o a d i n g i s t r u e
O c t 2 3 , 2 0 2 0
C O D E _ O F _ C O N D U C T . m d
l i n k l i n t s
S e p 1 1 , 2 0 2 0
C O N T R I B U T I N G . m d
m o d i f y c o n t r i b u t i n g . m d
S e p 1 5 , 2 0 2 0
G e m f i l e
m o v e t o w o r k i n g a g a i n s t R a i l s m a s t e r b y d e f a u l t
A u g 3 1 , 2 0 2 0
G e m f i l e . l o c k
u p d a t e l o c k
O c t 2 3 , 2 0 2 0
L I C E N S E . t x t
E x t r a c t A c t i o n V i e w : : C o m p o n e n t i n t o g e m
A u g 1 6 , 2 0 1 9
R E A D M E . m d
U p d a t i n g t h e r e a d m e
O c t 6 , 2 0 2 0
R a k e f i l e
M e r g e b r a n c h ' m a s t e r ' i n t o b e n c h m a r k - v 1
A u g 2 7 , 2 0 2 0
v i e w _ c o m p o n e n t . g e m s p e c
M e r g e b r a n c h ' m a s t e r ' i n t o b e n c h m a r k - v 1
A u g 2 7 , 2 0 2 0
V i e w c o d e
R E A D M E . m d
V i e w C o m p o n e n t
V i e w C o m p o n e n t i s a f r a m e w o r k f o r b u i l d i n g v i e w c o m p o n e n t s t h a t a r e r e u s a b l e , t e s t a b l e & e n c a p s u l a t e d , i n R u b y o n R a i l s .
D e s i g n p h i l o s o p h y
V i e w C o m p o n e n t i s d e s i g n e d t o i n t e g r a t e a s s e a m l e s s l y a s p o s s i b l e w i t h R a i l s , w i t h t h e l e a s t s u r p r i s e .
C o m p a t i b i l i t y
V i e w C o m p o n e n t i s s u p p o r t e d n a t i v e l y i n R a i l s 6 . 1 , a n d c o m p a t i b l e w i t h R a i l s 5 . 0 + v i a a n i n c l u d e d m o n k e y p a t c h .
V i e w C o m p o n e n t i s t e s t e d f o r c o m p a t i b i l i t y w i t h c o m b i n a t i o n s o f R u b y 2 . 4 + a n d R a i l s 5 + .
I n s t a l l a t i o n
In G e m f i l e , a d d :
gem "view_component" , require : "view_component/engine"
G u i d e
W h a t a r e c o m p o n e n t s ?
V i e w C o m p o n e n t s a r e R u b y o b j e c t s t h a t o u t p u t H T M L . T h i n k o f t h e m a s a n e v o l u t i o n o f t h e p r e s e n t e r p a t t e r n , i n s p i r e d b y R e a c t .
C o m p o n e n t s a r e m o s t e f f e c t i v e i n c a s e s w h e r e v i e w c o d e i s r e u s e d o r b e n e f i t s f r o m b e i n g t e s t e d d i r e c t l y .
W h y s h o u l d I u s e c o m p o n e n t s ?
T e s t i n g
U n l i k e t r a d i t i o n a l R a i l s v i e w s , V i e w C o m p o n e n t s c a n b e u n i t - t e s t e d . I n t h e G i t H u b c o d e b a s e , c o m p o n e n t u n i t t e s t s t a k e a r o u n d 2 5 m i l l i s e c o n d s e a c h , c o m p a r e d t o a b o u t s i x s e c o n d s f o r c o n t r o l l e r t e s t s .
R a i l s v i e w s a r e t y p i c a l l y t e s t e d w i t h s l o w i n t e g r a t i o n t e s t s t h a t a l s o e x e r c i s e t h e r o u t i n g a n d c o n t r o l l e r l a y e r s i n a d d i t i o n t o t h e v i e w . T h i s c o s t o f t e n d i s c o u r a g e s t h o r o u g h t e s t c o v e r a g e .
W i t h V i e w C o m p o n e n t , i n t e g r a t i o n t e s t s c a n b e r e s e r v e d f o r e n d - t o - e n d a s s e r t i o n s , w i t h p e r m u t a t i o n s a n d c o r n e r c a s e s c o v e r e d a t t h e u n i t l e v e l .
D a t a F l o w
T r a d i t i o n a l R a i l s v i e w s h a v e a n i m p l i c i t i n t e r f a c e , m a k i n g i t h a r d t o r e a s o n a b o u t w h a t i n f o r m a t i o n i s n e e d e d t o r e n d e r , l e a d i n g t o s u b t l e b u g s w h e n r e n d e r i n g t h e s a m e v i e w i n d i f f e r e n t c o n t e x t s .
V i e w C o m p o n e n t s u s e a s t a n d a r d R u b y i n i t i a l i z e r t h a t c l e a r l y d e f i n e s w h a t i s n e e d e d t o r e n d e r , m a k i n g t h e m e a s i e r ( a n d s a f e r ) t o r e u s e t h a n p a r t i a l s .
P e r f o r m a n c e
B a s e d o n o u r b e n c h m a r k s , V i e w C o m p o n e n t s a r e ~ 1 0 x f a s t e r t h a n p a r t i a l s .
S t a n d a r d s
V i e w s o f t e n f a i l b a s i c R u b y c o d e q u a l i t y s t a n d a r d s : l o n g m e t h o d s , d e e p c o n d i t i o n a l n e s t i n g , a n d m y s t e r y g u e s t s a b o u n d .
V i e w C o m p o n e n t s a r e R u b y o b j e c t s , m a k i n g i t e a s y t o f o l l o w ( a n d e n f o r c e ) c o d e q u a l i t y s t a n d a r d s .
B u i l d i n g c o m p o n e n t s
C o n v e n t i o n s
C o m p o n e n t s a r e s u b c l a s s e s o f V i e w C o m p o n e n t : : B a s e a n d l i v e i n a p p / c o m p o n e n t s . I t ' s c o m m o n p r a c t i c e t o c r e a t e a n d i n h e r i t f r o m a n A p p l i c a t i o n C o m p o n e n t t h a t i s a s u b c l a s s o f V i e w C o m p o n e n t : : B a s e .
C o m p o n e n t n a m e s e n d i n - C o m p o n e n t .
C o m p o n e n t m o d u l e n a m e s a r e p l u r a l , a s f o r c o n t r o l l e r s a n d j o b s : U s e r s : : A v a t a r C o m p o n e n t
N a m e c o m p o n e n t s f o r w h a t t h e y r e n d e r , n o t w h a t t h e y a c c e p t . ( A v a t a r C o m p o n e n t i n s t e a d o f U s e r C o m p o n e n t )
Q u i c k s t a r t
U s e t h e c o m p o n e n t g e n e r a t o r t o c r e a t e a n e w V i e w C o m p o n e n t .
T h e g e n e r a t o r a c c e p t s a c o m p o n e n t n a m e a n d a l i s t o f a r g u m e n t s :
bin/rails generate component Example title content
invoke test_unit
create test/components/example_component_test.rb
create app/components/example_component.rb
create app/components/example_component.html.erb
V i e w C o m p o n e n t i n c l u d e s t e m p l a t e g e n e r a t o r s f o r t h e e r b , h a m l , a n d s l i m t e m p l a t e e n g i n e s a n d w i l l d e f a u l t t o t h e t e m p l a t e e n g i n e s p e c i f i e d i n c o n f i g . g e n e r a t o r s . t e m p l a t e _ e n g i n e .
T h e t e m p l a t e e n g i n e c a n a l s o b e p a s s e d a s a n o p t i o n t o t h e g e n e r a t o r :
bin/rails generate component Example title content --template-engine slim
I m p l e m e n t a t i o n
A V i e w C o m p o n e n t i s a R u b y f i l e a n d c o r r e s p o n d i n g t e m p l a t e f i l e w i t h t h e s a m e b a s e n a m e :
a p p / c o m p o n e n t s / t e s t _ c o m p o n e n t . r b :
class TestComponent < ViewComponent ::Base
def initialize ( title :)
@title = title
end
end
a p p / c o m p o n e n t s / t e s t _ c o m p o n e n t . h t m l . e r b :
< span title ="<%= @title %> "> <%= content %> </ span >
R e n d e r e d i n a v i e w a s :
<%= render(TestComponent.new(title: "my title")) do %>
Hello, World!
<% end %>
R e t u r n i n g :
< span title ="my title "> Hello, World!</ span >
C o n t e n t A r e a s
C o n t e n t p a s s e d t o a V i e w C o m p o n e n t a s a b l o c k i s c a p t u r e d a n d a s s i g n e d t o t h e c o n t e n t a c c e s s o r .
V i e w C o m p o n e n t s c a n d e c l a r e a d d i t i o n a l c o n t e n t a r e a s . F o r e x a m p l e :
a p p / c o m p o n e n t s / m o d a l _ c o m p o n e n t . r b :
class ModalComponent < ViewComponent ::Base
with_content_areas :header , :body
end
a p p / c o m p o n e n t s / m o d a l _ c o m p o n e n t . h t m l . e r b :
< div class ="modal ">
< div class ="header "> <%= header %> </ div >
< div class ="body "> <%= body %> </ div >
</ div >
R e n d e r e d i n a v i e w a s :
<%= render(ModalComponent.new) do |component| %>
<% component.with(:header) do %>
Hello Jane
<% end %>
<% component.with(:body) do %>
<p > Have a great day.</ p >
<% end %>
<% end %>
R e t u r n i n g :
< div class ="modal ">
< div class ="header "> Hello Jane</ div >
< div class ="body "> < p > Have a great day.</ p > </ div >
</ div >
S l o t s ( e x p e r i m e n t a l )
S l o t s a r e c u r r e n t l y u n d e r d e v e l o p m e n t a s a s u c c e s s o r t o C o n t e n t A r e a s . T h e S l o t A P I s s h o u l d b e c o n s i d e r e d u n f i n i s h e d a n d s u b j e c t t o b r e a k i n g c h a n g e s i n n o n - m a j o r r e l e a s e s o f V i e w C o m p o n e n t .
S l o t s e n a b l e m u l t i p l e b l o c k s o f c o n t e n t t o b e p a s s e d t o a s i n g l e V i e w C o m p o n e n t , r e d u c i n g t h e n e e d f o r s u b - c o m p o n e n t s ( e . g . M o d a l H e a d e r , M o d a l B o d y ) .
B y d e f a u l t , s l o t s c a n b e r e n d e r e d o n c e p e r c o m p o n e n t . T h e y p r o v i d e a n a c c e s s o r w i t h t h e n a m e o f t h e s l o t ( # h e a d e r ) t h a t r e t u r n s a n i n s t a n c e o f V i e w C o m p o n e n t : : S l o t , e t c .
S l o t s d e c l a r e d w i t h c o l l e c t i o n : t r u e c a n b e r e n d e r e d m u l t i p l e t i m e s . T h e y p r o v i d e a n a c c e s s o r w i t h t h e p l u r a l i z e d n a m e o f t h e s l o t ( # r o w s ) , w h i c h i s a n A r r a y o f V i e w C o m p o n e n t : : S l o t i n s t a n c e s .
T o l e a r n m o r e a b o u t t h e d e s i g n o f t h e S l o t s A P I , s e e # 3 4 8 a n d # 3 2 5 .
D e f i n i n g S l o t s
S l o t s a r e d e f i n e d b y w i t h _ s l o t :
w i t h _ s l o t : h e a d e r
T o d e f i n e a c o l l e c t i o n s l o t , a d d c o l l e c t i o n : t r u e :
w i t h _ s l o t : r o w , c o l l e c t i o n : t r u e
T o d e f i n e a s l o t w i t h a c u s t o m R u b y c l a s s , p a s s c l a s s _ n a m e :
w i t h _ s l o t : b o d y , c l a s s _ n a m e : ' B o d y S l o t '
N o t e : S l o t c l a s s e s m u s t b e s u b c l a s s e s o f V i e w C o m p o n e n t : : S l o t .
E x a m p l e V i e w C o m p o n e n t w i t h S l o t s
# b o x _ c o m p o n e n t . r b
class BoxComponent < ViewComponent ::Base
include ViewComponent ::Slotable
with_slot :body , :footer
with_slot :header , class_name : "Header"
with_slot :row , collection : true , class_name : "Row"
class Header < ViewComponent ::Slot
def initialize ( classes : "" )
@classes = classes
end
def classes
"Box-header #{ @classes } "
end
end
class Row < ViewComponent ::Slot
def initialize ( theme : :gray )
@theme = theme
end
def theme_class_name
case @theme
when :gray
"Box-row--gray"
when :hover_gray
"Box-row--hover-gray"
when :yellow
"Box-row--yellow"
when :blue
"Box-row--blue"
when :hover_blue
"Box-row--hover-blue"
else
"Box-row--gray"
end
end
end
end
# b o x _ c o m p o n e n t . h t m l . e r b
< div class ="Box ">
<% if header %>
< div class ="<%= header . classes %> ">
<%= header . content %>
</ div >
<% end %>
<% if body %>
< div class ="Box-body ">
<%= body . content %>
</ div >
<% end %>
<% if rows . any? %>
< ul >
<% rows . each do |row | %>
< li class ="Box-row <%= row . theme_class_name %> ">
<%= row . content %>
</ li >
<% end %>
</ ul >
<% end %>
<% if footer %>
< div class ="Box-footer ">
<%= footer . content %>
</ div >
<% end %>
</ div >
# i n d e x . h t m l . e r b
<%= render(BoxComponent.new) do |component| %>
<% component.slot(:header, classes: "my-class-name") do %>
This is my header!
<% end %>
<% component.slot(:body) do %>
This is the body.
<% end %>
<% component.slot(:row) do %>
Row one
<% end %>
<% component.slot(:row, theme: :yellow) do %>
Yellow row
<% end %>
<% component.slot(:footer) do %>
This is the footer.
<% end %>
<% end %>
I n l i n e C o m p o n e n t
V i e w C o m p o n e n t s c a n r e n d e r w i t h o u t a t e m p l a t e f i l e , b y d e f i n i n g a c a l l m e t h o d :
a p p / c o m p o n e n t s / i n l i n e _ c o m p o n e n t . r b :
class InlineComponent < ViewComponent ::Base
def call
if active?
link_to "Cancel integration" , integration_path , method : :delete
else
link_to "Integrate now!" , integration_path
end
end
end
I t i s a l s o p o s s i b l e t o d e f i n e m e t h o d s f o r v a r i a n t s :
class InlineVariantComponent < ViewComponent ::Base
def call_phone
link_to "Phone" , phone_path
end
def call
link_to "Default" , default_path
end
end
T e m p l a t e I n h e r i t a n c e
C o m p o n e n t s t h a t s u b c l a s s a n o t h e r c o m p o n e n t i n h e r i t t h e p a r e n t c o m p o n e n t ' s
t e m p l a t e i f t h e y d o n ' t d e f i n e t h e i r o w n t e m p l a t e .
# If `my_link_component.html.erb` is not defined the component will fall back
# to `LinkComponent`s template
class MyLinkComponent < LinkComponent
end
S i d e c a r A s s e t s
V i e w C o m p o n e n t s s u p p o r t s t w o o p t i o n s f o r d e f i n i n g v i e w f i l e s .
S i d e c a r v i e w
T h e s i m p l e s t o p t i o n i s t o p l a c e t h e v i e w n e x t t o t h e R u b y c o m p o n e n t :
app/components
├── ...
├── test_component.rb
├── test_component.html.erb
├── ...
S i d e c a r d i r e c t o r y
A s a n a l t e r n a t i v e , v i e w s a n d o t h e r a s s e t s c a n b e p l a c e d i n a s i d e c a r d i r e c t o r y w i t h t h e s a m e n a m e a s t h e c o m p o n e n t , w h i c h c a n b e u s e f u l f o r o r g a n i z i n g v i e w s a l o n g s i d e o t h e r a s s e t s l i k e J a v a s c r i p t a n d C S S .
app/components
├── ...
├── example_component.rb
├── example_component
| ├── example_component.css
| ├── example_component.html.erb
| └── example_component.js
├── ...
T o g e n e r a t e a c o m p o n e n t w i t h a s i d e c a r d i r e c t o r y , u s e t h e - - s i d e c a r f l a g :
bin/rails generate component Example title content --sidecar
invoke test_unit
create test/components/example_component_test.rb
create app/components/example_component.rb
create app/components/example_component/example_component.html.erb
C o m p o n e n t f i l e i n s i d e S i d e c a r d i r e c t o r y
I t ' s a l s o p o s s i b l e t o p l a c e t h e R u b y c o m p o n e n t f i l e i n s i d e t h e s i d e c a r d i r e c t o r y , g r o u p i n g a l l r e l a t e d f i l e s i n t h e s a m e f o l d e r :
N o t e : A v o i d g i v i n g y o u r c o n t a i n i n g f o l d e r t h e s a m e n a m e a s y o u r . r b f i l e o r t h e r e w i l l b e a c o n f l i c t b e t w e e n M o d u l e a n d C l a s s d e f i n i t i o n s
app/components
├── ...
├── example
| ├── component.rb
| ├── component.css
| ├── component.html.erb
| └── component.js
├── ...
T h e c o m p o n e n t c a n t h e n b e r e n d e r e d u s i n g t h e f o l d e r n a m e a s a n a m e s p a c e :
<%= render(Example::Component.new(title: "my title")) do %>
Hello, World!
<% end %>
C o n d i t i o n a l R e n d e r i n g
C o m p o n e n t s c a n i m p l e m e n t a # r e n d e r ? m e t h o d t o b e c a l l e d a f t e r i n i t i a l i z a t i o n t o d e t e r m i n e i f t h e c o m p o n e n t s h o u l d r e n d e r .
T r a d i t i o n a l l y , t h e l o g i c f o r w h e t h e r t o r e n d e r a v i e w c o u l d g o i n e i t h e r t h e c o m p o n e n t t e m p l a t e :
a p p / c o m p o n e n t s / c o n f i r m _ e m a i l _ c o m p o n e n t . h t m l . e r b
<% if user.requires_confirmation? %>
<div class ="alert "> Please confirm your email address.</ div >
<% end %>
o r t h e v i e w t h a t r e n d e r s t h e c o m p o n e n t :
a p p / v i e w s / _ b a n n e r s . h t m l . e r b
<% if current_user.requires_confirmation? %>
<%= render(ConfirmEmailComponent.new(user: current_user)) %>
<% end %>
U s i n g t h e # r e n d e r ? h o o k s i m p l i f i e s t h e v i e w :
a p p / c o m p o n e n t s / c o n f i r m _ e m a i l _ c o m p o n e n t . r b
class ConfirmEmailComponent < ViewComponent ::Base
def initialize ( user :)
@user = user
end
def render?
@user . requires_confirmation?
end
end
a p p / c o m p o n e n t s / c o n f i r m _ e m a i l _ c o m p o n e n t . h t m l . e r b
< div class ="banner ">
Please confirm your email address.
</ div >
a p p / v i e w s / _ b a n n e r s . h t m l . e r b
<%= render(ConfirmEmailComponent.new(user: current_user)) %>
T o a s s e r t t h a t a c o m p o n e n t h a s n o t b e e n r e n d e r e d , u s e r e f u t e _ c o m p o n e n t _ r e n d e r e d f r o m V i e w C o m p o n e n t : : T e s t H e l p e r s .
b e f o r e _ r e n d e r
C o m p o n e n t s c a n d e f i n e a b e f o r e _ r e n d e r m e t h o d t o b e c a l l e d b e f o r e a c o m p o n e n t i s r e n d e r e d , w h e n h e l p e r s i s a b l e t o b e u s e d :
a p p / c o m p o n e n t s / c o n f i r m _ e m a i l _ c o m p o n e n t . r b
class MyComponent < ViewComponent ::Base
def before_render
@my_icon = helpers . star_icon
end
end
R e n d e r i n g c o l l e c t i o n s
U s e w i t h _ c o l l e c t i o n t o r e n d e r a V i e w C o m p o n e n t w i t h a c o l l e c t i o n :
a p p / v i e w / p r o d u c t s / i n d e x . h t m l . e r b
<%= render(ProductComponent.with_collection(@products)) %>
a p p / c o m p o n e n t s / p r o d u c t _ c o m p o n e n t . r b
class ProductComponent < ViewComponent ::Base
def initialize ( product :)
@product = product
end
end
B y d e f a u l t , t h e c o m p o n e n t n a m e i s u s e d t o d e f i n e t h e p a r a m e t e r p a s s e d i n t o t h e c o m p o n e n t f r o m t h e c o l l e c t i o n .
w i t h _ c o l l e c t i o n _ p a r a m e t e r
U s e w i t h _ c o l l e c t i o n _ p a r a m e t e r t o c h a n g e t h e n a m e o f t h e c o l l e c t i o n p a r a m e t e r :
a p p / c o m p o n e n t s / p r o d u c t _ c o m p o n e n t . r b
class ProductComponent < ViewComponent ::Base
with_collection_parameter :item
def initialize ( item :)
@item = item
end
end
A d d i t i o n a l a r g u m e n t s
A d d i t i o n a l a r g u m e n t s b e s i d e s t h e c o l l e c t i o n a r e p a s s e d t o e a c h c o m p o n e n t i n s t a n c e :
a p p / v i e w / p r o d u c t s / i n d e x . h t m l . e r b
<%= render(ProductComponent.with_collection(@products, notice: "hi")) %>
a p p / c o m p o n e n t s / p r o d u c t _ c o m p o n e n t . r b
class ProductComponent < ViewComponent ::Base
with_collection_parameter :item
def initialize ( item :, notice :)
@item = item
@notice = notice
end
end
a p p / c o m p o n e n t s / p r o d u c t _ c o m p o n e n t . h t m l . e r b
< li >
< h2 > <%= @item . name %> </ h2 >
< span > <%= @notice %> </ span >
</ li >
C o l l e c t i o n c o u n t e r
V i e w C o m p o n e n t d e f i n e s a c o u n t e r v a r i a b l e m a t c h i n g t h e p a r a m e t e r n a m e a b o v e , f o l l o w e d b y _ c o u n t e r . T o a c c e s s t h e v a r i a b l e , a d d i t t o i n i t i a l i z e a s a n a r g u m e n t :
a p p / c o m p o n e n t s / p r o d u c t _ c o m p o n e n t . r b
class ProductComponent < ViewComponent ::Base
def initialize ( product :, product_counter :)
@product = product
@counter = product_counter
end
end
a p p / c o m p o n e n t s / p r o d u c t _ c o m p o n e n t . h t m l . e r b
< li >
<%= @counter %> <%= @product . name %>
</ li >
U s i n g h e l p e r s
H e l p e r m e t h o d s c a n b e u s e d t h r o u g h t h e h e l p e r s p r o x y :
module IconHelper
def icon ( name )
tag . i data : { feather : name . to_s . dasherize }
end
end
class UserComponent < ViewComponent ::Base
def profile_icon
helpers . icon :user
end
end
W h i c h c a n b e u s e d w i t h d e l e g a t e :
class UserComponent < ViewComponent ::Base
delegate :icon , to : :helpers
def profile_icon
icon :user
end
end
H e l p e r s c a n a l s o b e u s e d b y i n c l u d i n g t h e h e l p e r :
class UserComponent < ViewComponent ::Base
include IconHelper
def profile_icon
icon :user
end
end
W r i t i n g t e s t s
U n i t t e s t c o m p o n e n t s d i r e c t l y , u s i n g t h e r e n d e r _ i n l i n e t e s t h e l p e r , a s s e r t i n g a g a i n s t t h e r e n d e r e d o u t p u t .
C a p y b a r a m a t c h e r s a r e a v a i l a b l e i f t h e g e m i s i n s t a l l e d :
require "view_component/test_case"
class MyComponentTest < ViewComponent ::TestCase
def test_render_component
render_inline ( TestComponent . new ( title : "my title" ) ) { "Hello, World!" }
assert_selector ( "span[title='my title']" , text : "Hello, World!" )
end
end
I n t h e a b s e n c e o f c a p y b a r a , a s s e r t a g a i n s t t h e r e t u r n v a l u e o f r e n d e r _ i n l i n e , w h i c h i s a n i n s t a n c e o f N o k o g i r i : : H T M L : : D o c u m e n t F r a g m e n t :
def test_render_component
result = render_inline ( TestComponent . new ( title : "my title" ) ) { "Hello, World!" }
assert_includes result . css ( "span[title='my title']" ) . to_html , "Hello, World!"
end
A l t e r n a t i v e l y , a s s e r t a g a i n s t t h e r a w o u t p u t o f t h e c o m p o n e n t , w h i c h i s e x p o s e d a s r e n d e r e d _ c o m p o n e n t :
def test_render_component
render_inline ( TestComponent . new ( title : "my title" ) ) { "Hello, World!" }
assert_includes rendered_component , "Hello, World!"
end
T o t e s t c o m p o n e n t s t h a t u s e w i t h _ c o n t e n t _ a r e a s :
def test_renders_content_areas_template_with_content
render_inline ( ContentAreasComponent . new ( footer : "Bye!" ) ) do |component |
component . with ( :title , "Hello!" )
component . with ( :body ) { "Have a nice day." }
end
assert_selector ( ".title" , text : "Hello!" )
assert_selector ( ".body" , text : "Have a nice day." )
assert_selector ( ".footer" , text : "Bye!" )
end
A c t i o n P a c k V a r i a n t s
U s e t h e w i t h _ v a r i a n t h e l p e r t o t e s t s p e c i f i c v a r i a n t s :
def test_render_component_for_tablet
with_variant :tablet do
render_inline ( TestComponent . new ( title : "my title" ) ) { "Hello, tablets!" }
assert_selector ( "span[title='my title']" , text : "Hello, tablets!" )
end
end
P r e v i e w i n g C o m p o n e n t s
V i e w C o m p o n e n t : : P r e v i e w , l i k e A c t i o n M a i l e r : : P r e v i e w , p r o v i d e s a w a y t o p r e v i e w c o m p o n e n t s i n i s o l a t i o n :
t e s t / c o m p o n e n t s / p r e v i e w s / t e s t _ c o m p o n e n t _ p r e v i e w . r b
class TestComponentPreview < ViewComponent ::Preview
def with_default_title
render ( TestComponent . new ( title : "Test component default" ) )
end
def with_long_title
render ( TestComponent . new ( title : "This is a really long title to see how the component renders this" ) )
end
def with_content_block
render ( TestComponent . new ( title : "This component accepts a block of content" ) ) do
tag . div do
content_tag ( :span , "Hello" )
end
end
end
end
W h i c h g e n e r a t e s h t t p : / / l o c a l h o s t : 3 0 0 0 / r a i l s / v i e w _ c o m p o n e n t s / t e s t _ c o m p o n e n t / w i t h _ d e f a u l t _ t i t l e ,
h t t p : / / l o c a l h o s t : 3 0 0 0 / r a i l s / v i e w _ c o m p o n e n t s / t e s t _ c o m p o n e n t / w i t h _ l o n g _ t i t l e ,
a n d h t t p : / / l o c a l h o s t : 3 0 0 0 / r a i l s / v i e w _ c o m p o n e n t s / t e s t _ c o m p o n e n t / w i t h _ c o n t e n t _ b l o c k .
I t ' s a l s o p o s s i b l e t o s e t d y n a m i c v a l u e s f r o m t h e p a r a m s b y s e t t i n g t h e m a s a r g u m e n t s :
t e s t / c o m p o n e n t s / p r e v i e w s / t e s t _ c o m p o n e n t _ p r e v i e w . r b
class TestComponentPreview < ViewComponent ::Preview
def with_dynamic_title ( title : "Test component default" )
render ( TestComponent . new ( title : title ) )
end
end
W h i c h e n a b l e s p a s s i n g i n a v a l u e w i t h h t t p : / / l o c a l h o s t : 3 0 0 0 / r a i l s / c o m p o n e n t s / t e s t _ c o m p o n e n t / w i t h _ d y n a m i c _ t i t l e ? t i t l e = C u s t o m + t i t l e .
T h e V i e w C o m p o n e n t : : P r e v i e w b a s e c l a s s i n c l u d e s
A c t i o n V i e w : : H e l p e r s : : T a g H e l p e r , w h i c h p r o v i d e s t h e t a g
a n d c o n t e n t _ t a g v i e w h e l p e r m e t h o d s .
P r e v i e w s u s e t h e a p p l i c a t i o n l a y o u t b y d e f a u l t , b u t c a n u s e a s p e c i f i c l a y o u t w i t h t h e l a y o u t o p t i o n :
t e s t / c o m p o n e n t s / p r e v i e w s / t e s t _ c o m p o n e n t _ p r e v i e w . r b
class TestComponentPreview < ViewComponent ::Preview
layout "admin"
...
end
Y o u c a n a l s o s e t a c u s t o m l a y o u t t o b e u s e d b y d e f a u l t f o r p r e v i e w s a s w e l l a s t h e p r e v i e w i n d e x p a g e s v i a t h e d e f a u l t _ p r e v i e w _ l a y o u t c o n f i g u r a t i o n o p t i o n :
c o n f i g / a p p l i c a t i o n . r b
# Set the default layout to app/views/layouts/component_preview.html.erb
config . view_component . default_preview_layout = "component_preview"
P r e v i e w c l a s s e s l i v e i n t e s t / c o m p o n e n t s / p r e v i e w s , w h i c h c a n b e c o n f i g u r e d u s i n g t h e p r e v i e w _ p a t h s o p t i o n :
c o n f i g / a p p l i c a t i o n . r b
config . view_component . preview_paths << "#{ Rails . root } /lib/component_previews"
P r e v i e w s a r e s e r v e d f r o m h t t p : / / l o c a l h o s t : 3 0 0 0 / r a i l s / v i e w _ c o m p o n e n t s b y d e f a u l t . T o u s e a d i f f e r e n t e n d p o i n t , s e t t h e p r e v i e w _ r o u t e o p t i o n :
c o n f i g / a p p l i c a t i o n . r b
config . view_component . preview_route = "/previews"
T h i s e x a m p l e w i l l m a k e t h e p r e v i e w s a v a i l a b l e f r o m h t t p : / / l o c a l h o s t : 3 0 0 0 / p r e v i e w s .
P r e v i e w t e m p l a t e s
G i v e n a p r e v i e w t e s t / c o m p o n e n t s / p r e v i e w s / c e l l _ c o m p o n e n t _ p r e v i e w . r b , t e m p l a t e f i l e s c a n b e d e f i n e d a t t e s t / c o m p o n e n t s / p r e v i e w s / c e l l _ c o m p o n e n t _ p r e v i e w / :
t e s t / c o m p o n e n t s / p r e v i e w s / c e l l _ c o m p o n e n t _ p r e v i e w . r b
class CellComponentPreview < ViewComponent ::Preview
def default
end
end
t e s t / c o m p o n e n t s / p r e v i e w s / c e l l _ c o m p o n e n t _ p r e v i e w / d e f a u l t . h t m l . e r b
< table class ="table ">
< tbody >
< tr >
<%= render CellComponent . new %>
</ tr >
</ tbody >
</ div >
T o u s e a d i f f e r e n t l o c a t i o n f o r p r e v i e w t e m p l a t e s , p a s s t h e t e m p l a t e a r g u m e n t :
( t h e p a t h s h o u l d b e r e l a t i v e t o c o n f i g . v i e w _ c o m p o n e n t . p r e v i e w _ p a t h ) :
t e s t / c o m p o n e n t s / p r e v i e w s / c e l l _ c o m p o n e n t _ p r e v i e w . r b
class CellComponentPreview < ViewComponent ::Preview
def default
render_with_template ( template : 'custom_cell_component_preview/my_preview_template' )
end
end
V a l u e s f r o m p a r a m s c a n b e a c c e s s e d t h r o u g h l o c a l s :
t e s t / c o m p o n e n t s / p r e v i e w s / c e l l _ c o m p o n e n t _ p r e v i e w . r b
class CellComponentPreview < ViewComponent ::Preview
def default ( title : "Default title" , subtitle : "A subtitle" )
render_with_template ( locals : {
title : title ,
subtitle : subtitle
} )
end
end
W h i c h e n a b l e s p a s s i n g i n a v a l u e w i t h h t t p : / / l o c a l h o s t : 3 0 0 0 / r a i l s / c o m p o n e n t s / c e l l _ c o m p o n e n t / d e f a u l t ? t i t l e = C u s t o m + t i t l e & s u b t i t l e = A n o t h e r + s u b t i t l e .
C o n f i g u r i n g p r e v i e w c o n t r o l l e r
P r e v i e w s c a n b e e x t e n d e d t o a l l o w u s e r s t o a d d a u t h e n t i c a t i o n , a u t h o r i z a t i o n , b e f o r e a c t i o n s , o r a n y t h i n g t h a t t h e e n d u s e r w o u l d n e e d t o m e e t t h e i r n e e d s u s i n g t h e p r e v i e w _ c o n t r o l l e r o p t i o n :
c o n f i g / a p p l i c a t i o n . r b
config . view_component . preview_controller = "MyPreviewController"
C o n f i g u r i n g T e s t C o n t r o l l e r
C o m p o n e n t t e s t s a s s u m e t h e e x i s t e n c e o f a n A p p l i c a t i o n C o n t r o l l e r c l a s s , w h i c h b e c a n b e c o n f i g u r e d u s i n g t h e t e s t _ c o n t r o l l e r o p t i o n :
c o n f i g / a p p l i c a t i o n . r b
config . view_component . test_controller = "BaseController"
S e t t i n g u p R S p e c
T o u s e R S p e c , a d d t h e f o l l o w i n g :
s p e c / r a i l s _ h e l p e r . r b
require "view_component/test_helpers"
RSpec . configure do |config |
config . include ViewComponent ::TestHelpers , type : :component
end
S p e c s c r e a t e d b y t h e g e n e r a t o r h a v e a c c e s s t o t e s t h e l p e r s l i k e r e n d e r _ i n l i n e .
T o u s e c o m p o n e n t p r e v i e w s :
c o n f i g / a p p l i c a t i o n . r b
config . view_component . preview_paths << "#{ Rails . root } /spec/components/previews"
D i s a b l i n g t h e r e n d e r m o n k e y p a t c h ( R a i l s < 6 . 1 )
I n o r d e r t o a v o i d c o n f l i c t s b e t w e e n V i e w C o m p o n e n t a n d o t h e r g e m s t h a t a l s o m o n k e y p a t c h t h e r e n d e r m e t h o d , i t i s p o s s i b l e t o c o n f i g u r e V i e w C o m p o n e n t t o n o t i n c l u d e t h e r e n d e r m o n k e y p a t c h :
c o n f i g . v i e w _ c o m p o n e n t . r e n d e r _ m o n k e y _ p a t c h _ e n a b l e d = f a l s e # d e f a u l t s t o t r u e
W i t h t h e m o n k e y p a t c h d i s a b l e d , u s e r e n d e r _ c o m p o n e n t ( or r e n d e r _ c o m p o n e n t _ t o _ s t r i n g ) i n s t e a d :
<%= render_component Component.new(message: "bar") %>
S i d e c a r a s s e t s ( e x p e r i m e n t a l )
I t ’ s p o s s i b l e t o i n c l u d e J a v a s c r i p t a n d C S S a l o n g s i d e c o m p o n e n t s , s o m e t i m e s c a l l e d " s i d e c a r " a s s e t s o r f i l e s .
T o u s e t h e W e b p a c k e r g e m t o c o m p i l e s i d e c a r a s s e t s l o c a t e d i n a p p / c o m p o n e n t s :
(一) In c o n f i g / w e b p a c k e r . y m l , a d d " a p p / c o m p o n e n t s " t o t h e r e s o l v e d _ p a t h s a r r a y ( e . g . r e s o l v e d _ p a t h s : [ " a p p / c o m p o n e n t s " ] ) .
(二) I n t h e W e b p a c k e n t r y f i l e ( o f t e n a p p / j a v a s c r i p t / p a c k s / a p p l i c a t i o n . j s ) , a d d a n i m p o r t s t a t e m e n t t o a h e l p e r f i l e , a n d i n t h e h e l p e r f i l e , i m p o r t t h e c o m p o n e n t s ' J a v a s c r i p t :
import "../components"
T h e n , i n a p p / j a v a s c r i p t / c o m p o n e n t s . j s , a d d :
function importAll ( r ) {
r . keys ( ) . forEach ( r )
}
importAll ( require . context ( "../components" , true , /_component.js$ / ) )
A n y f i l e w i t h t h e _ c o m p o n e n t . j s s u f f i x ( s u c h a s a p p / c o m p o n e n t s / w i d g e t _ c o m p o n e n t . j s ) w i l l b e c o m p i l e d i n t o t h e W e b p a c k b u n d l e . I f t h a t f i l e i t s e l f i m p o r t s a n o t h e r f i l e , f o r e x a m p l e a p p / c o m p o n e n t s / w i d g e t _ c o m p o n e n t . c s s , i t w i l l a l s o b e c o m p i l e d a n d b u n d l e d i n t o W e b p a c k ' s o u t p u t s t y l e s h e e t i f W e b p a c k i s b e i n g u s e d f o r s t y l e s .
E n c a p s u l a t i n g s i d e c a r a s s e t s
I d e a l l y , s i d e c a r J a v a s c r i p t / C S S s h o u l d n o t " l e a k " o u t o f t h e c o n t e x t o f i t s a s s o c i a t e d c o m p o n e n t .
O n e a p p r o a c h i s t o u s e W e b C o m p o n e n t s , w h i c h c o n t a i n a l l J a v a s c r i p t f u n c t i o n a l i t y , i n t e r n a l m a r k u p , a n d s t y l e s w i t h i n t h e s h a d o w r o o t o f t h e W e b C o m p o n e n t .
F o r e x a m p l e :
a p p / c o m p o n e n t s / c o m m e n t _ c o m p o n e n t . r b
class CommentComponent < ViewComponent ::Base
def initialize ( comment :)
@comment = comment
end
def commenter
@comment . user
end
def commenter_name
commenter . name
end
def avatar
commenter . avatar_image_url
end
def formatted_body
simple_format ( @comment . body )
end
private
attr_reader :comment
end
a p p / c o m p o n e n t s / c o m m e n t _ c o m p o n e n t . h t m l . e r b
< my-comment comment-id ="<%= comment . id %> ">
< time slot ="posted " datetime ="<%= comment . created_at . iso8601 %> "> <%= comment . created_at . strftime ( "%b %-d" ) %> </ time >
< div slot ="avatar "> < img src ="<%= avatar %> " /></ div >
< div slot ="author "> <%= commenter_name %> </ div >
< div slot ="body "> <%= formatted_body %> </ div >
</ my-comment >
a p p / c o m p o n e n t s / c o m m e n t _ c o m p o n e n t . j s
class Comment extends HTMLElement {
styles ( ) {
return `
:host {
display: block;
}
::slotted(time) {
float: right;
font-size: 0.75em;
}
.commenter { font-weight: bold; }
.body { … }
`
}
constructor ( ) {
super ( )
const shadow = this . attachShadow ( { mode : 'open' } ) ;
shadow . innerHTML = `
<style>
${ this . styles ( ) }
</style>
<slot name="posted"></slot>
<div class="commenter">
<slot name="avatar"></slot> <slot name="author"></slot>
</div>
<div class="body">
<slot name="body"></slot>
</div>
`
}
}
customElements . define ( 'my-comment' , Comment )
S t i m u l u s
I n S t i m u l u s , c r e a t e a 1 : 1 m a p p i n g b e t w e e n a S t i m u l u s c o n t r o l l e r a n d a c o m p o n e n t . I n o r d e r t o l o a d i n S t i m u l u s c o n t r o l l e r s f r o m t h e a p p / c o m p o n e n t s t r e e , a m e n d t h e S t i m u l u s b o o t c o d e i n a p p / j a v a s c r i p t / p a c k s / a p p l i c a t i o n . j s :
const application = Application . start ( )
const context = require . context ( "controllers" , true , /.js$ / )
const context_components = require . context ( "../../components" , true , /_controller.js$ / )
application . load (
definitionsFromContext ( context ) . concat (
definitionsFromContext ( context_components )
)
)
T h i s e n a b l e s t h e c r e a t i o n o f f i l e s s u c h a s a p p / c o m p o n e n t s / w i d g e t _ c o n t r o l l e r . j s , w h e r e t h e c o n t r o l l e r i d e n t i f i e r m a t c h e s t h e d a t a - c o n t r o l l e r a t t r i b u t e i n t h e c o m p o n e n t ' s H T M L t e m p l a t e .
A f t e r c o n f i g u r i n g W e b p a c k t o l o a d S t i m u l u s c o n t r o l l e r f i l e s f r o m t h e c o m p o n e n t s d i r e c t o r y , a d d t h e p a t h t o r e s o l v e d _ p a t h s in c o n f i g / w e b p a c k e r . y m l :
resolved_paths : ["app/components"]
W h e n p l a c i n g a S t i m u l u s c o n t r o l l e r i n s i d e a s i d e c a r d i r e c t o r y , b e a w a r e t h a t w h e n r e f e r e n c i n g t h e c o n t r o l l e r e a c h f o r w a r d s l a s h i n a n a m e s p a c e d c o n t r o l l e r f i l e ’ s p a t h b e c o m e s t w o d a s h e s i n i t s i d e n t i f i e r :
app/components
├── ...
├── example
| ├── component.rb
| ├── component.css
| ├── component.html.erb
| └── component_controller.js
├── ...
c o m p o n e n t _ c o n t r o l l e r . j s ' s S t i m u l u s i d e n t i f i e r b e c o m e s : e x a m p l e - - c o m p o n e n t :
< div data-controller ="example--component ">
< input type ="text ">
< button data-action ="click->example--component#greet "> Greet</ button >
</ div >
F r e q u e n t l y A s k e d Q u e s t i o n s
C a n I u s e o t h e r t e m p l a t i n g l a n g u a g e s b e s i d e s E R B ?
Y e s . V i e w C o m p o n e n t i s t e s t e d a g a i n s t E R B , H a m l , a n d S l i m , b u t i t s h o u l d s u p p o r t m o s t R a i l s t e m p l a t e h a n d l e r s .
I s n ' t t h i s j u s t l i k e X l i b r a r y ?
V i e w C o m p o n e n t i s f a r f r o m a n o v e l i d e a ! P o p u l a r i m p l e m e n t a t i o n s o f v i e w c o m p o n e n t s i n R u b y i n c l u d e , b u t a r e n o t l i m i t e d t o :
● t r a i l b l a z e r / c e l l s
● d r y - r b / d r y - v i e w
● k o m p o s a b l e / k o m p o n e n t
● a c t i v e a d m i n / a r b r e
R e s o u r c e s
● E n c a p s u l a t i n g V i e w s , R a i l s C o n f 2 0 2 0
● R e t h i n k i n g t h e V i e w L a y e r w i t h C o m p o n e n t s , R u b y R o g u e s P o d c a s t
● V i e w C o m p o n e n t s i n A c t i o n w i t h A n d r e w M a s o n , R u b y o n R a i l s P o d c a s t
● V i e w C o m p o n e n t a t G i t H u b w i t h J o e l H a w k s l e y
● C o m p o n e n t s , H A M L v s E R B , a n d D e s i g n S y s t e m s
● C h o o s i n g t h e R i g h t T e c h S t a c k w i t h D a v e P a o l a
● R e t h i n k i n g t h e V i e w L a y e r w i t h C o m p o n e n t s , R a i l s C o n f 2 0 1 9
● I n t r o d u c i n g A c t i o n V i e w : : C o m p o n e n t w i t h J o e l H a w k s l e y , R u b y o n R a i l s P o d c a s t
● R a i l s t o I n t r o d u c e V i e w C o m p o n e n t s , D e v . t o
● A c t i o n V i e w : : C o m p o n e n t s i n R a i l s 6 . 1 , D r i f t i n g R u b y
● D e m o r e p o s i t o r y , v i e w - c o m p o n e n t - d e m o
C o n t r i b u t i n g
T h i s p r o j e c t i s i n t e n d e d t o b e a s a f e , w e l c o m i n g s p a c e f o r c o l l a b o r a t i o n . C o n t r i b u t o r s a r e e x p e c t e d t o a d h e r e t o t h e C o n t r i b u t o r C o v e n a n t c o d e o f c o n d u c t . W e r e c o m m e n d r e a d i n g t h e c o n t r i b u t i n g g u i d e a s w e l l .
C o n t r i b u t o r s
V i e w C o m p o n e n t i s b u i l t b y :
@joelhawksley
@tenderlove
@jonspalmer
@juanmanuelramallo
@vinistock
Denver
Seattle
Boston
Toronto
@metade
@asgerb
@xronos-i-am
@dylnclrk
@kaspermeyer
London
Copenhagen
Russia, Kirov
Berkeley, CA
Denmark
@rdavid1099
@kylefox
@traels
@rainerborene
@jcoyne
Los Angeles
Edmonton
Odense, Denmark
Brazil
Minneapolis
@elia
@cesariouy
@spdawson
@rmacklin
@michaelem
Milan
United Kingdom
Berlin
@mellowfish
@horacio
@dukex
@dark-panda
@smashwilson
Spring Hill, TN
Buenos Aires
São Paulo
Gambrills, MD
@blakewilliams
@seanpdoyle
@tclem
@nashby
@jaredcwhite
Boston, MA
New York, NY
San Francisco, CA
Minsk
Portland, OR
@simonrand
@fugufish
@cover
@franks921
@fsateler
Dublin, Ireland
Salt Lake City, Utah
Barcelona
South Africa
Chile
@maxbeizer
@franco
@tbroad-ramsey
@jensljungblad
@bbugh
Nashville, TN
Switzerland
Spring Hill, TN
New York, NY
Austin, TX
@johannesengl
@czj
@mrrooijen
@bradparker
@mattbrictson
Berlin, Germany
Paris, France
The Netherlands
Brisbane, Australia
San Francisco
@mixergtz
@jules2689
@g13ydson
@swanson
Medellin, Colombia
Toronto, Canada
João Pessoa, Brazil
Indianapolis, IN
L i c e n s e
V i e w C o m p o n e n t i s a v a i l a b l e a s o p e n s o u r c e u n d e r t h e t e r m s o f t h e M I T L i c e n s e .
A b o u t
V i e w c o m p o n e n t s f o r R a i l s
R e s o u r c e s
R e a d m e
L i c e n s e
M I T L i c e n s e
v 2 . 2 0 . 0
L a t e s t
O c t 1 9 , 2 0 2 0
+ 6 4 r e l e a s e s
N o p a c k a g e s p u b l i s h e d
+ 2 0 8
+ 6 4 c o n t r i b u t o r s
L a n g u a g e s
●
R u b y
9 3 . 3 %
●
H T M L
5 . 3 %
●
C S S
0 . 8 %
●
S h e l l
0 . 3 %
●
H a m l
0 . 2 %
●
J a v a S c r i p t
0 . 1 %
● © 2 0 2 0 G i t H u b , I n c .
● T e r m s
● P r i v a c y
●
C o o k i e P r e f e r e n c e s
● S e c u r i t y
● S t a t u s
● H e l p
● C o n t a c t G i t H u b
● P r i c i n g
● A P I
● T r a i n i n g
● B l o g
● A b o u t
Y o u c a n ’ t p e r f o r m t h a t a c t i o n a t t h i s t i m e .
Y o u s i g n e d i n w i t h a n o t h e r t a b o r w i n d o w . R e l o a d t o r e f r e s h y o u r s e s s i o n .
Y o u s i g n e d o u t i n a n o t h e r t a b o r w i n d o w . R e l o a d t o r e f r e s h y o u r s e s s i o n .
W e u s e o p t i o n a l t h i r d - p a r t y a n a l y t i c s c o o k i e s t o u n d e r s t a n d h o w y o u u s e G i t H u b . c o m s o w e c a n b u i l d b e t t e r p r o d u c t s .
L e a r n m o r e .
A c c e p t
R e j e c t
W e u s e o p t i o n a l t h i r d - p a r t y a n a l y t i c s c o o k i e s t o u n d e r s t a n d h o w y o u u s e G i t H u b . c o m s o w e c a n b u i l d b e t t e r p r o d u c t s .
Y o u c a n a l w a y s u p d a t e y o u r s e l e c t i o n b y c l i c k i n g C o o k i e P r e f e r e n c e s a t t h e b o t t o m o f t h e p a g e .
F o r m o r e i n f o r m a t i o n , s e e o u r P r i v a c y S t a t e m e n t .
E s s e n t i a l c o o k i e s
W e u s e e s s e n t i a l c o o k i e s t o p e r f o r m e s s e n t i a l w e b s i t e f u n c t i o n s , e . g . t h e y ' r e u s e d t o l o g y o u i n .
L e a r n m o r e
A l w a y s a c t i v e
A n a l y t i c s c o o k i e s
W e u s e a n a l y t i c s c o o k i e s t o u n d e r s t a n d h o w y o u u s e o u r w e b s i t e s s o w e c a n m a k e t h e m b e t t e r , e . g . t h e y ' r e u s e d t o g a t h e r i n f o r m a t i o n a b o u t t h e p a g e s y o u v i s i t a n d h o w m a n y c l i c k s y o u n e e d t o a c c o m p l i s h a t a s k .
L e a r n m o r e
A c c e p t
R e j e c t
S a v e p r e f e r e n c e s