puffer 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +149 -0
- data/VERSION +1 -1
- data/app/helpers/puffer_helper.rb +21 -0
- data/app/views/puffer/_form.html.erb +1 -1
- data/app/views/puffer/index.html.erb +32 -43
- data/lib/generators/puffer/install/templates/puffer/stylesheets/puffer.css +121 -0
- data/lib/puffer/base.rb +1 -1
- data/lib/puffer/controller/config.rb +1 -1
- data/lib/puffer/controller/mutate.rb +1 -0
- data/lib/puffer/fields.rb +1 -1
- data/puffer.gemspec +5 -4
- data/spec/dummy/public/puffer/stylesheets/puffer.css +121 -0
- metadata +7 -6
- data/README.rdoc +0 -3
data/README.md
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
# Puffer - YARAI (Yet Another Rails Admin Interface). Rails 3 only.
|
2
|
+
|
3
|
+
Puffer was created to help project owner or moderators view and edit all the project`s data models. It is rails 3 only
|
4
|
+
|
5
|
+
## Keyfeatures
|
6
|
+
|
7
|
+
* Full rails integration. Puffer has no configs, just DSL to create interfaces. And this DLS depends on rails convensions.
|
8
|
+
* Flexibility. Puffer designed to be as flexible as possible, so you can create your own modules easily.
|
9
|
+
* I18n. Surely.
|
10
|
+
* Bla bla
|
11
|
+
|
12
|
+
## Installation.
|
13
|
+
|
14
|
+
You can instal puffer as a gem:
|
15
|
+
<pre>gem install puffer</pre>
|
16
|
+
Or in Gemfile:
|
17
|
+
<pre>gem "puffer"</pre>
|
18
|
+
Next step is:
|
19
|
+
<pre>rails g puffer:install</pre>
|
20
|
+
This will install main puffer config file in your initializers and some css/js.
|
21
|
+
|
22
|
+
## Introduction.
|
23
|
+
|
24
|
+
So, you have some data structure of your project. Let it`ll be like this:
|
25
|
+
|
26
|
+
<pre>
|
27
|
+
create_table "users", :force => true do |t|
|
28
|
+
t.string "email"
|
29
|
+
t.string "password"
|
30
|
+
t.datetime "created_at"
|
31
|
+
t.datetime "updated_at"
|
32
|
+
end
|
33
|
+
|
34
|
+
create_table "posts", :force => true do |t|
|
35
|
+
t.integer "user_id"
|
36
|
+
t.string "title"
|
37
|
+
t.text "body"
|
38
|
+
t.datetime "created_at"
|
39
|
+
t.datetime "updated_at"
|
40
|
+
end
|
41
|
+
</pre>
|
42
|
+
|
43
|
+
Also, you have two models:
|
44
|
+
|
45
|
+
<pre>
|
46
|
+
class User < ActiveRecord::Base
|
47
|
+
has_many :posts
|
48
|
+
validates_presence_of :email, :password
|
49
|
+
validates_length_of :password, :minimum => 6
|
50
|
+
end
|
51
|
+
</pre>
|
52
|
+
|
53
|
+
<pre>
|
54
|
+
class Profile < ActiveRecord::Base
|
55
|
+
belongs_to :user
|
56
|
+
validates_presence_of :name, :surname
|
57
|
+
end
|
58
|
+
</pre>
|
59
|
+
|
60
|
+
At first, lets generate puffers controllers:
|
61
|
+
<pre>rails g puffer:controller User</pre>
|
62
|
+
and
|
63
|
+
<pre>rails g puffer:controller Post</pre>
|
64
|
+
|
65
|
+
This will generate a kind of:
|
66
|
+
<pre>
|
67
|
+
class Admin::PostsController < Puffer::Base
|
68
|
+
before_filter :i_didnt_forget_to_protect_this
|
69
|
+
|
70
|
+
index do
|
71
|
+
field :id
|
72
|
+
field :user_id
|
73
|
+
field :title
|
74
|
+
field :body
|
75
|
+
field :created_at
|
76
|
+
field :updated_at
|
77
|
+
end
|
78
|
+
|
79
|
+
form do
|
80
|
+
field :id
|
81
|
+
field :user_id
|
82
|
+
field :title
|
83
|
+
field :body
|
84
|
+
field :created_at
|
85
|
+
field :updated_at
|
86
|
+
end
|
87
|
+
end
|
88
|
+
</pre>
|
89
|
+
|
90
|
+
Puffer controller`s DSL creates all the actions we need. Next step - routing
|
91
|
+
|
92
|
+
<pre>
|
93
|
+
namespace :admin
|
94
|
+
resources :users do
|
95
|
+
resources :posts
|
96
|
+
end
|
97
|
+
resources :posts
|
98
|
+
end
|
99
|
+
</pre>
|
100
|
+
|
101
|
+
Let me explain this feature. Puffer tracks all the nested resources. So, with this routing structure we can access, for example, only specified user`s posts:
|
102
|
+
|
103
|
+
<pre>
|
104
|
+
/admin/users/1/post
|
105
|
+
</pre>
|
106
|
+
|
107
|
+
Routing nesting defines admin interface resources nesting.
|
108
|
+
|
109
|
+
## Advanced usage
|
110
|
+
|
111
|
+
Puffer can be used in other namespaces, then admin:
|
112
|
+
|
113
|
+
<pre>rails g puffer:controller moderator/posts</pre>
|
114
|
+
|
115
|
+
And we`ll get posts controller for moderator:
|
116
|
+
|
117
|
+
<pre>
|
118
|
+
class Moderator::PostsController < Puffer::Base
|
119
|
+
before_filter :require_moderator
|
120
|
+
|
121
|
+
config do
|
122
|
+
destroy false
|
123
|
+
group :posting
|
124
|
+
end
|
125
|
+
|
126
|
+
index do
|
127
|
+
field :user_id
|
128
|
+
field :title
|
129
|
+
field :body
|
130
|
+
end
|
131
|
+
|
132
|
+
form do
|
133
|
+
field :user_id
|
134
|
+
field :title
|
135
|
+
field :body
|
136
|
+
field :created_at
|
137
|
+
field :updated_at
|
138
|
+
end
|
139
|
+
end
|
140
|
+
</pre>
|
141
|
+
|
142
|
+
As you can see, moderators can not destroy posts, also moderator`s posts controller placed at Posting tab of admin interface.
|
143
|
+
And don`t forget about routing:
|
144
|
+
|
145
|
+
<pre>
|
146
|
+
namespace :moderator do
|
147
|
+
resources :posts
|
148
|
+
end
|
149
|
+
</pre>
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.7
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module PufferHelper
|
2
|
+
|
3
|
+
def render_head field
|
4
|
+
field.label
|
5
|
+
end
|
6
|
+
|
7
|
+
def render_field field, record
|
8
|
+
if field.options[:render]
|
9
|
+
case field.options[:render]
|
10
|
+
when Symbol then
|
11
|
+
res = send(field.options[:render], record)
|
12
|
+
when Proc then
|
13
|
+
res = field.options[:render].bind(self).call(record)
|
14
|
+
else ''
|
15
|
+
end
|
16
|
+
else
|
17
|
+
res = h(record.call_chain(field.name))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -1,49 +1,37 @@
|
|
1
1
|
<% @title = resource.human %>
|
2
2
|
<h1><%= @title %></h1>
|
3
|
-
<%= will_paginate
|
4
|
-
|
5
|
-
<
|
6
|
-
<
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
<
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
<p><%= link_to "#{child.member ? 'Edit' : 'Add'} #{child.human}", child.index_path %></p>
|
34
|
-
<% end %>
|
35
|
-
<% end %>
|
36
|
-
</td>
|
37
|
-
</tr>
|
38
|
-
<% end -%>
|
39
|
-
</tbody>
|
40
|
-
</table>
|
41
|
-
<% end %>
|
42
|
-
</div>
|
43
|
-
</div>
|
44
|
-
</div>
|
45
|
-
<%= will_paginate @records, :url => resource.index_path(:page => '') %>
|
3
|
+
<%= will_paginate records, :url => resource.index_path(:page => '') %>
|
4
|
+
<% if records.present? %>
|
5
|
+
<table class="list_table">
|
6
|
+
<thead>
|
7
|
+
<tr>
|
8
|
+
<% index_fields.each do |field| -%>
|
9
|
+
<th><%= render_head field %></th>
|
10
|
+
<% end -%>
|
11
|
+
<th class="actions">Actions</th>
|
12
|
+
</tr>
|
13
|
+
</thead>
|
14
|
+
<tbody>
|
15
|
+
<% records.each do |record| -%>
|
16
|
+
<tr>
|
17
|
+
<% index_fields.each do |field| -%>
|
18
|
+
<td><%= render_field field, record %></td>
|
19
|
+
<% end -%>
|
20
|
+
<td class="actions">
|
21
|
+
<%= link_to 'show', resource.path(record), :class => 'show_entry' if show_fields.present? || resource.children.present? %>
|
22
|
+
<%= link_to 'edit', resource.edit_path(record), :class => 'edit_entry' if update_fields.present? %>
|
23
|
+
<%= link_to 'destroy', resource.path(record), :confirm => "Are you sure?", :method => :delete, :class => 'remove_entry' if configuration.destroy %>
|
24
|
+
</td>
|
25
|
+
</tr>
|
26
|
+
<% end -%>
|
27
|
+
</tbody>
|
28
|
+
</table>
|
29
|
+
<% else %>
|
30
|
+
<p>Sorry, but there is no records in <%= resource.human %></p>
|
31
|
+
<% end %>
|
32
|
+
<%= will_paginate records, :url => resource.index_path(:page => '') %>
|
46
33
|
|
34
|
+
<% if false %>
|
47
35
|
<% content_for :additional_navigation do %>
|
48
36
|
<ul class="buttons">
|
49
37
|
<% resource.ancestors.each do |resource| %>
|
@@ -53,4 +41,5 @@
|
|
53
41
|
<%= link_to resource.plural? ? resource.human : resource.member.to_title, resource.index_path %>
|
54
42
|
</ul>
|
55
43
|
<% end %>
|
44
|
+
<% end %>
|
56
45
|
|
@@ -20,6 +20,12 @@ a:hover
|
|
20
20
|
text-decoration: none;
|
21
21
|
}
|
22
22
|
|
23
|
+
h1
|
24
|
+
{
|
25
|
+
font-size: 21px;
|
26
|
+
margin-bottom: 10px;
|
27
|
+
}
|
28
|
+
|
23
29
|
.body
|
24
30
|
{
|
25
31
|
position: relative;
|
@@ -166,3 +172,118 @@ a:hover
|
|
166
172
|
background: #fff;
|
167
173
|
padding: 15px;
|
168
174
|
}
|
175
|
+
|
176
|
+
.list_table
|
177
|
+
{
|
178
|
+
width: 100%;
|
179
|
+
margin: 0;
|
180
|
+
line-height: 140%;
|
181
|
+
border-collapse: separate;
|
182
|
+
border-spacing: 0px;
|
183
|
+
border: 1px solid #ddd;
|
184
|
+
border-radius: 5px;
|
185
|
+
-moz-border-radius: 5px;
|
186
|
+
-webkit-border-radius: 5px;
|
187
|
+
}
|
188
|
+
|
189
|
+
.list_table tr
|
190
|
+
{
|
191
|
+
vertical-align: top;
|
192
|
+
}
|
193
|
+
|
194
|
+
.list_table th.actions
|
195
|
+
{
|
196
|
+
position: static;
|
197
|
+
}
|
198
|
+
|
199
|
+
.list_table td.actions
|
200
|
+
{
|
201
|
+
position: static;
|
202
|
+
white-space: nowrap;
|
203
|
+
width: 100px;
|
204
|
+
}
|
205
|
+
|
206
|
+
.list_table td.actions a
|
207
|
+
{
|
208
|
+
line-height: 140%;
|
209
|
+
}
|
210
|
+
|
211
|
+
.list_table th
|
212
|
+
{
|
213
|
+
text-align: left;
|
214
|
+
background: #eee;
|
215
|
+
white-space: nowrap;
|
216
|
+
overflow: hidden;
|
217
|
+
max-width: 15px;
|
218
|
+
}
|
219
|
+
|
220
|
+
.list_table th a
|
221
|
+
{
|
222
|
+
text-decoration: none;
|
223
|
+
border-bottom: 1px dotted #333;
|
224
|
+
}
|
225
|
+
|
226
|
+
.list_table th a:hover
|
227
|
+
{
|
228
|
+
border-bottom: none;
|
229
|
+
}
|
230
|
+
|
231
|
+
.list_table td, .list_table th
|
232
|
+
{
|
233
|
+
padding: 5px;
|
234
|
+
}
|
235
|
+
|
236
|
+
.list_table td:first-child, .list_table th:first-child
|
237
|
+
{
|
238
|
+
padding-left: 10px;
|
239
|
+
}
|
240
|
+
|
241
|
+
.list_table td:last-child, .list_table th:last-child
|
242
|
+
{
|
243
|
+
padding-right: 10px;
|
244
|
+
}
|
245
|
+
.list_table td
|
246
|
+
{
|
247
|
+
border-bottom: 1px solid #ddd;
|
248
|
+
vertical-align: top;
|
249
|
+
}
|
250
|
+
|
251
|
+
.list_table tr:last-child td
|
252
|
+
{
|
253
|
+
border: none;
|
254
|
+
}
|
255
|
+
|
256
|
+
.list_table tbody tr:hover
|
257
|
+
{
|
258
|
+
background: #f4f4f4;
|
259
|
+
}
|
260
|
+
|
261
|
+
.form
|
262
|
+
{
|
263
|
+
margin-bottom: 30px;
|
264
|
+
}
|
265
|
+
|
266
|
+
.form li
|
267
|
+
{
|
268
|
+
list-style: none;
|
269
|
+
margin-bottom: 8px;
|
270
|
+
}
|
271
|
+
|
272
|
+
.form li label
|
273
|
+
{
|
274
|
+
display: block;
|
275
|
+
font-size: 11pt;
|
276
|
+
font-weight: bold;
|
277
|
+
}
|
278
|
+
|
279
|
+
.form li input[type=text], .form li input[type=password], .form li textarea
|
280
|
+
{
|
281
|
+
border: #ccc solid 1px;
|
282
|
+
width: 100%;
|
283
|
+
font-size: 12pt;
|
284
|
+
}
|
285
|
+
|
286
|
+
.form li textarea
|
287
|
+
{
|
288
|
+
height: 60px;
|
289
|
+
}
|
data/lib/puffer/base.rb
CHANGED
data/lib/puffer/fields.rb
CHANGED
@@ -19,7 +19,7 @@ module Puffer
|
|
19
19
|
:password if field.name =~ /password/
|
20
20
|
end
|
21
21
|
offer_type do |field|
|
22
|
-
field.model.reflect_on_association(name.to_sym).macro if field.model.reflect_on_association name.to_sym
|
22
|
+
field.model.reflect_on_association(field.name.to_sym).macro if field.model.reflect_on_association field.name.to_sym
|
23
23
|
end
|
24
24
|
|
25
25
|
def field *args
|
data/puffer.gemspec
CHANGED
@@ -5,15 +5,15 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{puffer}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.7"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["pyromaniac"]
|
12
|
-
s.date = %q{2011-01-
|
12
|
+
s.date = %q{2011-01-28}
|
13
13
|
s.description = %q{In Soviet Russia puffer admins you}
|
14
14
|
s.email = %q{kinwizard@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
|
-
"README.
|
16
|
+
"README.md"
|
17
17
|
]
|
18
18
|
s.files = [
|
19
19
|
".rspec",
|
@@ -21,12 +21,13 @@ Gem::Specification.new do |s|
|
|
21
21
|
"Gemfile",
|
22
22
|
"Gemfile.lock",
|
23
23
|
"MIT-LICENSE",
|
24
|
-
"README.
|
24
|
+
"README.md",
|
25
25
|
"Rakefile",
|
26
26
|
"VERSION",
|
27
27
|
"app/cells/puffer/base/additional.html.erb",
|
28
28
|
"app/cells/puffer/base_cell.rb",
|
29
29
|
"app/controllers/admin/dashboard_controller.rb",
|
30
|
+
"app/helpers/puffer_helper.rb",
|
30
31
|
"app/views/admin/dashboard/index.html.erb",
|
31
32
|
"app/views/layouts/puffer.html.erb",
|
32
33
|
"app/views/puffer/_form.html.erb",
|
@@ -20,6 +20,12 @@ a:hover
|
|
20
20
|
text-decoration: none;
|
21
21
|
}
|
22
22
|
|
23
|
+
h1
|
24
|
+
{
|
25
|
+
font-size: 21px;
|
26
|
+
margin-bottom: 10px;
|
27
|
+
}
|
28
|
+
|
23
29
|
.body
|
24
30
|
{
|
25
31
|
position: relative;
|
@@ -166,3 +172,118 @@ a:hover
|
|
166
172
|
background: #fff;
|
167
173
|
padding: 15px;
|
168
174
|
}
|
175
|
+
|
176
|
+
.list_table
|
177
|
+
{
|
178
|
+
width: 100%;
|
179
|
+
margin: 0;
|
180
|
+
line-height: 140%;
|
181
|
+
border-collapse: separate;
|
182
|
+
border-spacing: 0px;
|
183
|
+
border: 1px solid #ddd;
|
184
|
+
border-radius: 5px;
|
185
|
+
-moz-border-radius: 5px;
|
186
|
+
-webkit-border-radius: 5px;
|
187
|
+
}
|
188
|
+
|
189
|
+
.list_table tr
|
190
|
+
{
|
191
|
+
vertical-align: top;
|
192
|
+
}
|
193
|
+
|
194
|
+
.list_table th.actions
|
195
|
+
{
|
196
|
+
position: static;
|
197
|
+
}
|
198
|
+
|
199
|
+
.list_table td.actions
|
200
|
+
{
|
201
|
+
position: static;
|
202
|
+
white-space: nowrap;
|
203
|
+
width: 100px;
|
204
|
+
}
|
205
|
+
|
206
|
+
.list_table td.actions a
|
207
|
+
{
|
208
|
+
line-height: 140%;
|
209
|
+
}
|
210
|
+
|
211
|
+
.list_table th
|
212
|
+
{
|
213
|
+
text-align: left;
|
214
|
+
background: #eee;
|
215
|
+
white-space: nowrap;
|
216
|
+
overflow: hidden;
|
217
|
+
max-width: 15px;
|
218
|
+
}
|
219
|
+
|
220
|
+
.list_table th a
|
221
|
+
{
|
222
|
+
text-decoration: none;
|
223
|
+
border-bottom: 1px dotted #333;
|
224
|
+
}
|
225
|
+
|
226
|
+
.list_table th a:hover
|
227
|
+
{
|
228
|
+
border-bottom: none;
|
229
|
+
}
|
230
|
+
|
231
|
+
.list_table td, .list_table th
|
232
|
+
{
|
233
|
+
padding: 5px;
|
234
|
+
}
|
235
|
+
|
236
|
+
.list_table td:first-child, .list_table th:first-child
|
237
|
+
{
|
238
|
+
padding-left: 10px;
|
239
|
+
}
|
240
|
+
|
241
|
+
.list_table td:last-child, .list_table th:last-child
|
242
|
+
{
|
243
|
+
padding-right: 10px;
|
244
|
+
}
|
245
|
+
.list_table td
|
246
|
+
{
|
247
|
+
border-bottom: 1px solid #ddd;
|
248
|
+
vertical-align: top;
|
249
|
+
}
|
250
|
+
|
251
|
+
.list_table tr:last-child td
|
252
|
+
{
|
253
|
+
border: none;
|
254
|
+
}
|
255
|
+
|
256
|
+
.list_table tbody tr:hover
|
257
|
+
{
|
258
|
+
background: #f4f4f4;
|
259
|
+
}
|
260
|
+
|
261
|
+
.form
|
262
|
+
{
|
263
|
+
margin-bottom: 30px;
|
264
|
+
}
|
265
|
+
|
266
|
+
.form li
|
267
|
+
{
|
268
|
+
list-style: none;
|
269
|
+
margin-bottom: 8px;
|
270
|
+
}
|
271
|
+
|
272
|
+
.form li label
|
273
|
+
{
|
274
|
+
display: block;
|
275
|
+
font-size: 11pt;
|
276
|
+
font-weight: bold;
|
277
|
+
}
|
278
|
+
|
279
|
+
.form li input[type=text], .form li input[type=password], .form li textarea
|
280
|
+
{
|
281
|
+
border: #ccc solid 1px;
|
282
|
+
width: 100%;
|
283
|
+
font-size: 12pt;
|
284
|
+
}
|
285
|
+
|
286
|
+
.form li textarea
|
287
|
+
{
|
288
|
+
height: 60px;
|
289
|
+
}
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puffer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 7
|
10
|
+
version: 0.0.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- pyromaniac
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-01-
|
18
|
+
date: 2011-01-28 00:00:00 +03:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -188,19 +188,20 @@ executables: []
|
|
188
188
|
extensions: []
|
189
189
|
|
190
190
|
extra_rdoc_files:
|
191
|
-
- README.
|
191
|
+
- README.md
|
192
192
|
files:
|
193
193
|
- .rspec
|
194
194
|
- .rvmrc
|
195
195
|
- Gemfile
|
196
196
|
- Gemfile.lock
|
197
197
|
- MIT-LICENSE
|
198
|
-
- README.
|
198
|
+
- README.md
|
199
199
|
- Rakefile
|
200
200
|
- VERSION
|
201
201
|
- app/cells/puffer/base/additional.html.erb
|
202
202
|
- app/cells/puffer/base_cell.rb
|
203
203
|
- app/controllers/admin/dashboard_controller.rb
|
204
|
+
- app/helpers/puffer_helper.rb
|
204
205
|
- app/views/admin/dashboard/index.html.erb
|
205
206
|
- app/views/layouts/puffer.html.erb
|
206
207
|
- app/views/puffer/_form.html.erb
|
data/README.rdoc
DELETED