cream 0.8.6 → 0.8.7
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.txt +6 -1
- data/Design Ideas.textile +463 -0
- data/Gemfile +1 -0
- data/README.textile +158 -34
- data/Rakefile +8 -7
- data/VERSION +1 -1
- data/app/views/cream/menu/_admin_login_items.html.erb +2 -2
- data/app/views/cream/menu/_login_items.html.erb +2 -2
- data/app/views/cream/menu/_registration_items.html.erb +2 -2
- data/config/locales/cream.da.yml +16 -0
- data/cream.gemspec +38 -27
- data/lib/cream.rb +1 -0
- data/lib/cream/configure/after_init/role_config.rb +25 -21
- data/lib/cream/configure/rails.rb +8 -5
- data/lib/cream/controller/application_controller.rb +22 -0
- data/lib/cream/helper/role.rb +102 -11
- data/lib/generators/cream/app/app_generator.rb +50 -26
- data/lib/generators/cream/full_config/full_config_generator.rb +44 -9
- data/lib/generators/cream/helpers/all.rb +1 -0
- data/lib/generators/cream/helpers/execute_helper.rb +0 -4
- data/lib/generators/cream/helpers/gemfile_helper.rb +28 -0
- data/lib/generators/cream/helpers/orm_helper.rb +6 -2
- data/lib/generators/cream/helpers/strategy_helper.rb +28 -0
- data/lib/generators/cream/views/haml_util.rb +3 -4
- data/lib/generators/cream/views/views_generator.rb +13 -13
- data/lib/generators/devise/config/app_helper.rb +1 -1
- data/lib/generators/devise/config/config_generator.rb +1 -3
- data/lib/generators/devise/config/{gem_helper.rb → gem_config_helper.rb} +0 -23
- data/lib/generators/devise/customize/customize_generator.rb +49 -0
- data/lib/generators/devise/customize/customize_messages.rb +52 -0
- data/lib/generators/devise/customize/helpers/query_customizers.rb +43 -0
- data/lib/generators/devise/customize/helpers/recover_login.rb +80 -0
- data/lib/generators/devise/customize/helpers/username_helper.rb +149 -0
- data/lib/generators/devise/users/users_generator.rb +1 -3
- data/lib/generators/permits/config/config_generator.rb +1 -3
- data/lib/generators/roles/config/config_generator.rb +26 -4
- data/sandbox/any_user.rb +98 -0
- data/{lib/generators → sandbox}/cream_refactor.rb +0 -0
- data/sandbox/str_test.rb +50 -0
- data/spec/cream/configure/rails_role_spec.rb +1 -1
- data/spec/cream/helper/role_spec.rb +71 -21
- data/wiki/Cream-generators-overview.textile +79 -0
- data/wiki/How to gollum wiki.txt +13 -0
- metadata +107 -72
- data/wiki/CONFIG_GENERATOR.txt +0 -21
- data/wiki/DESIGN.txt +0 -21
- data/wiki/INSTALLATION.txt +0 -6
- data/wiki/PERMITS.txt +0 -32
- data/wiki/ROLE_STRATEGIES.txt +0 -40
- data/wiki/SPEC_NOTES.txt +0 -6
- data/wiki/VIEWS_GENERATOR.txt +0 -35
- data/wiki/VIEW_HELPERS.txt +0 -162
data/Changelog.txt
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
0.8.
|
1
|
+
0.8.6 - Jan 4, 2011
|
2
|
+
-----
|
3
|
+
Completed major refactoring to streamline generators and DRY things up!
|
4
|
+
Needs more testing using alternative class names etc.
|
5
|
+
|
6
|
+
0.8.5 - Jan 3, 2011
|
2
7
|
-----
|
3
8
|
Major refactoring to extract common logic in generators in order to make the logic more transparent and more stable and consistent!
|
4
9
|
|
@@ -0,0 +1,463 @@
|
|
1
|
+
h1. Create default Guest user
|
2
|
+
|
3
|
+
<pre>
|
4
|
+
class Guest
|
5
|
+
def create
|
6
|
+
Guest.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def has_role? role
|
10
|
+
role == :guest
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_roles? *roles
|
14
|
+
role == :guest
|
15
|
+
end
|
16
|
+
end
|
17
|
+
</pre>
|
18
|
+
|
19
|
+
h2. Add to ApplicationController
|
20
|
+
|
21
|
+
<pre>
|
22
|
+
# http://stackoverflow.com/questions/4275058/using-devise-with-guest-users
|
23
|
+
|
24
|
+
def current_user
|
25
|
+
super || Guest.create # role defaults to 'guest' in the model.
|
26
|
+
end
|
27
|
+
|
28
|
+
def user_signed_in?
|
29
|
+
current_user && !current_user.new_record?
|
30
|
+
end
|
31
|
+
|
32
|
+
def user_session
|
33
|
+
user_signed_in? ? super : session
|
34
|
+
end
|
35
|
+
</pre>
|
36
|
+
|
37
|
+
|
38
|
+
h1. Control who can create new users
|
39
|
+
|
40
|
+
<pre>
|
41
|
+
$ mkdir app/controllers/users
|
42
|
+
$ touch app/controllers/users/registrations_controller.rb
|
43
|
+
</pre>
|
44
|
+
|
45
|
+
<pre>
|
46
|
+
class Users::RegistrationsController < Devise::RegistrationsController
|
47
|
+
before_filter :check_permissions, :only => [:new, :create, :cancel]
|
48
|
+
skip_before_filter :require_no_authentication
|
49
|
+
|
50
|
+
def check_permissions
|
51
|
+
authorize! :create, resource
|
52
|
+
end
|
53
|
+
end
|
54
|
+
</pre>
|
55
|
+
|
56
|
+
The check permissions method is really simple. It calls the CanCan method, authorize!, and checks if the current user can create users. We use resource here because devise uses resource to refer to the model that can be authenticated. Also notice how I removed the require_no_authentication filter, a Devise filter which allows access to actions without authentication.
|
57
|
+
|
58
|
+
<pre>
|
59
|
+
# replace devise_for :users with:
|
60
|
+
devise_for :users, :controllers => { :registrations => "users/registrations" }
|
61
|
+
</pre>
|
62
|
+
|
63
|
+
At this point if you hit the users/sign_up page when not logged in, you will notice that a CanCan::AccessDenied is thrown. This exception is thrown anytime permission is denied so you should customize it to your liking. I put the handler in my ApplicationController:
|
64
|
+
|
65
|
+
<pre>
|
66
|
+
class ApplicationController < ActionController::Base
|
67
|
+
...
|
68
|
+
rescue_from CanCan::AccessDenied do |exception|
|
69
|
+
flash[:error] = exception.message
|
70
|
+
redirect_to root_url
|
71
|
+
end
|
72
|
+
...
|
73
|
+
end
|
74
|
+
</pre>
|
75
|
+
|
76
|
+
<pre>
|
77
|
+
class UsersController < ApplicationController
|
78
|
+
before_filter :get_user, :only => [:index,:new,:edit]
|
79
|
+
before_filter :accessible_roles, :only => [:new, :edit, :show, :update, :create]
|
80
|
+
load_and_authorize_resource :only => [:show,:new,:destroy,:edit,:update]
|
81
|
+
|
82
|
+
# GET /users
|
83
|
+
# GET /users.xml
|
84
|
+
# GET /users.json HTML and AJAX
|
85
|
+
#-----------------------------------------------------------------------
|
86
|
+
def index
|
87
|
+
@users = User.accessible_by(current_ability, :index).limit(20)
|
88
|
+
respond_to do |format|
|
89
|
+
format.json { render :json => @users }
|
90
|
+
format.xml { render :xml => @users }
|
91
|
+
format.html
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# GET /users/new
|
96
|
+
# GET /users/new.xml
|
97
|
+
# GET /users/new.json HTML AND AJAX
|
98
|
+
#-------------------------------------------------------------------
|
99
|
+
def new
|
100
|
+
respond_to do |format|
|
101
|
+
format.json { render :json => @user }
|
102
|
+
format.xml { render :xml => @user }
|
103
|
+
format.html
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# GET /users/1
|
108
|
+
# GET /users/1.xml
|
109
|
+
# GET /users/1.json HTML AND AJAX
|
110
|
+
#-------------------------------------------------------------------
|
111
|
+
def show
|
112
|
+
respond_to do |format|
|
113
|
+
format.json { render :json => @user }
|
114
|
+
format.xml { render :xml => @user }
|
115
|
+
format.html
|
116
|
+
end
|
117
|
+
|
118
|
+
rescue ActiveRecord::RecordNotFound
|
119
|
+
respond_to_not_found(:json, :xml, :html)
|
120
|
+
end
|
121
|
+
|
122
|
+
# GET /users/1/edit
|
123
|
+
# GET /users/1/edit.xml
|
124
|
+
# GET /users/1/edit.json HTML AND AJAX
|
125
|
+
#-------------------------------------------------------------------
|
126
|
+
def edit
|
127
|
+
respond_to do |format|
|
128
|
+
format.json { render :json => @user }
|
129
|
+
format.xml { render :xml => @user }
|
130
|
+
format.html
|
131
|
+
end
|
132
|
+
|
133
|
+
rescue ActiveRecord::RecordNotFound
|
134
|
+
respond_to_not_found(:json, :xml, :html)
|
135
|
+
end
|
136
|
+
|
137
|
+
# DELETE /users/1
|
138
|
+
# DELETE /users/1.xml
|
139
|
+
# DELETE /users/1.json HTML AND AJAX
|
140
|
+
#-------------------------------------------------------------------
|
141
|
+
def destroy
|
142
|
+
@user.destroy!
|
143
|
+
|
144
|
+
respond_to do |format|
|
145
|
+
format.json { respond_to_destroy(:ajax) }
|
146
|
+
format.xml { head :ok }
|
147
|
+
format.html { respond_to_destroy(:html) }
|
148
|
+
end
|
149
|
+
|
150
|
+
rescue ActiveRecord::RecordNotFound
|
151
|
+
respond_to_not_found(:json, :xml, :html)
|
152
|
+
end
|
153
|
+
|
154
|
+
# POST /users
|
155
|
+
# POST /users.xml
|
156
|
+
# POST /users.json HTML AND AJAX
|
157
|
+
#-----------------------------------------------------------------
|
158
|
+
def create
|
159
|
+
@user = User.new(params[:user])
|
160
|
+
|
161
|
+
if @user.save
|
162
|
+
respond_to do |format|
|
163
|
+
format.json { render :json => @user.to_json, :status => 200 }
|
164
|
+
format.xml { head :ok }
|
165
|
+
format.html { redirect_to :action => :index }
|
166
|
+
end
|
167
|
+
else
|
168
|
+
respond_to do |format|
|
169
|
+
format.json { render :text => "Could not create user", :status => :unprocessable_entity } # placeholder
|
170
|
+
format.xml { head :ok }
|
171
|
+
format.html { render :action => :new, :status => :unprocessable_entity }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
...
|
176
|
+
|
177
|
+
# PUT /users/1
|
178
|
+
# PUT /users/1.xml
|
179
|
+
# PUT /users/1.json HTML AND AJAX
|
180
|
+
#----------------------------------------------------------------------------
|
181
|
+
def update
|
182
|
+
if params[:user][:password].blank?
|
183
|
+
[:password,:password_confirmation,:current_password].collect{|p| params[:user].delete(p) }
|
184
|
+
else
|
185
|
+
@user.errors[:base] << "The password you entered is incorrect" unless @user.valid_password?(params[:user][:current_password])
|
186
|
+
end
|
187
|
+
|
188
|
+
respond_to do |format|
|
189
|
+
if @user.errors[:base].empty? and @user.update_attributes(params[:user])
|
190
|
+
flash[:notice] = "Your account has been updated"
|
191
|
+
format.json { render :json => @user.to_json, :status => 200 }
|
192
|
+
format.xml { head :ok }
|
193
|
+
format.html { render :action => :edit }
|
194
|
+
else
|
195
|
+
format.json { render :text => "Could not update user", :status => :unprocessable_entity } #placeholder
|
196
|
+
format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
|
197
|
+
format.html { render :action => :edit, :status => :unprocessable_entity }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
rescue
|
202
|
+
respond_to_not_found(:js, :xml, :html)
|
203
|
+
end
|
204
|
+
|
205
|
+
# FILTERS
|
206
|
+
|
207
|
+
|
208
|
+
# Get roles accessible by the current user
|
209
|
+
#----------------------------------------------------
|
210
|
+
def accessible_roles
|
211
|
+
@accessible_roles = Role.accessible_by(current_ability,:read)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Make the current user object available to views
|
215
|
+
#----------------------------------------
|
216
|
+
def get_user
|
217
|
+
@current_user = current_user
|
218
|
+
end
|
219
|
+
end
|
220
|
+
</pre>
|
221
|
+
|
222
|
+
h2. Views
|
223
|
+
|
224
|
+
<pre>
|
225
|
+
<h2>Register User</h2>
|
226
|
+
|
227
|
+
<%= form_for(@user) do |f| %>
|
228
|
+
<%= error_messages(@user,"Could not register user") %>
|
229
|
+
|
230
|
+
<%= render :partial => 'user_fields', :locals => { :f => f } %>
|
231
|
+
|
232
|
+
<p><%= f.label :password %></p>
|
233
|
+
<p><%= f.password_field :password %></p>
|
234
|
+
|
235
|
+
<p><%= f.label :password_confirmation %></p>
|
236
|
+
<p><%= f.password_field :password_confirmation %></p>
|
237
|
+
|
238
|
+
<p><%= f.submit "Register" %></p>
|
239
|
+
<% end %>
|
240
|
+
</pre>
|
241
|
+
|
242
|
+
h3. partial – _user_fields.html.erb
|
243
|
+
<pre>
|
244
|
+
<p><%= f.label :first_name %></p>
|
245
|
+
<p><%= f.text_field :first_name %></p>
|
246
|
+
|
247
|
+
<p><%= f.label :last_name %></p>
|
248
|
+
<p><%= f.text_field :last_name %></p>
|
249
|
+
|
250
|
+
<p><%= f.label :email %></p>
|
251
|
+
<p><%= f.text_field :email %></p>
|
252
|
+
|
253
|
+
<% if can? :read, Role %>
|
254
|
+
<p><%= f.label :role %></p>
|
255
|
+
<ul class="no-pad no-bullets">
|
256
|
+
<%= habtm_checkboxes(@user, :role_ids, @accessible_roles, :name) %>
|
257
|
+
</ul>
|
258
|
+
<% end %>
|
259
|
+
</pre>
|
260
|
+
|
261
|
+
Only allow editing of role attribute if allowed to!
|
262
|
+
|
263
|
+
h3. Edit existing user
|
264
|
+
|
265
|
+
<pre>
|
266
|
+
<h3><%= @user == @current_user ? "Your Account Settings" : "Edit User" %></h3>
|
267
|
+
|
268
|
+
<%= form_for(@user, :html => { :method => :put }) do |f| %>
|
269
|
+
<%= error_messages(@user,"Could not update user") %>
|
270
|
+
<%= render :partial => 'user_fields', :locals => { :f => f } %>
|
271
|
+
|
272
|
+
<p><%= f.label :password %> <i>(leave blank if you don't want to change it)</i></p>
|
273
|
+
<p><%= f.password_field :password %></p>
|
274
|
+
|
275
|
+
<p><%= f.label :password_confirmation %></p>
|
276
|
+
<p><%= f.password_field :password_confirmation %></p>
|
277
|
+
|
278
|
+
<p><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i></p>
|
279
|
+
<p><%= f.password_field :current_password %></p>
|
280
|
+
|
281
|
+
<p><%= f.submit "Update" %></p>
|
282
|
+
<% end %>
|
283
|
+
<%= link_to "Back", :back %>
|
284
|
+
</pre>
|
285
|
+
|
286
|
+
h3. Show User
|
287
|
+
|
288
|
+
<pre>
|
289
|
+
<h3><%= @user.name %></h3>
|
290
|
+
|
291
|
+
<%= link_to_if(can?(:update,@user), "Edit", edit_user_path(@user)) %> |
|
292
|
+
<%= link_to_if(can?(:delete, @user), "Delete", user_path(@user), :confirm => "Are you sure?", :method => :delete) {} %>
|
293
|
+
|
294
|
+
<table class="one-column-emphasis">
|
295
|
+
<tbody>
|
296
|
+
<tr>
|
297
|
+
<td class="oce-first">Email:</td>
|
298
|
+
<td><%= @user.email %></td>
|
299
|
+
</tr>
|
300
|
+
<tr>
|
301
|
+
<td class="oce-first">Role:</td>
|
302
|
+
<td><%= @user.roles.first.name %></td>
|
303
|
+
</tr>
|
304
|
+
<% if can?(:see_timestamps,User) %>
|
305
|
+
<tr>
|
306
|
+
<td class="oce-first">Created at:</td>
|
307
|
+
<td><%= @user.created_at %></td>
|
308
|
+
</tr>
|
309
|
+
<tr>
|
310
|
+
<td class="oce-first">Last Sign In:</td>
|
311
|
+
<td><%= @user.last_sign_in_at %></td>
|
312
|
+
</tr>
|
313
|
+
<tr>
|
314
|
+
<td class="oce-first">Sign In Count:</td>
|
315
|
+
<td><%= @user.sign_in_count %></td>
|
316
|
+
</tr>
|
317
|
+
<% end %>
|
318
|
+
</tbody>
|
319
|
+
</table>
|
320
|
+
</pre>
|
321
|
+
|
322
|
+
h3. Custom can see timestamps!
|
323
|
+
|
324
|
+
<pre>
|
325
|
+
if user.role? :admin
|
326
|
+
can :see_timestamps, User
|
327
|
+
elsif user.role? :normal
|
328
|
+
can :see_timestamps, User, :id => user.id
|
329
|
+
end
|
330
|
+
</pre>
|
331
|
+
|
332
|
+
h3. Recaptcha
|
333
|
+
|
334
|
+
<pre>
|
335
|
+
class RegistrationsController < Devise::RegistrationsController
|
336
|
+
|
337
|
+
def create
|
338
|
+
if verify_recaptcha
|
339
|
+
super
|
340
|
+
else
|
341
|
+
build_resource
|
342
|
+
clean_up_passwords(resource)
|
343
|
+
flash[:error] = "There was an error with the recaptcha code below. Please re-enter the code and click submit."
|
344
|
+
render_with_scope :new
|
345
|
+
end
|
346
|
+
end
|
347
|
+
.
|
348
|
+
.
|
349
|
+
.
|
350
|
+
.
|
351
|
+
end
|
352
|
+
</pre>
|
353
|
+
|
354
|
+
h3. Recaptcha with Omniauth
|
355
|
+
|
356
|
+
<pre>
|
357
|
+
class RegistrationsController < Devise::RegistrationsController
|
358
|
+
|
359
|
+
def create
|
360
|
+
if session[:omniauth] == nil #OmniAuth
|
361
|
+
if verify_recaptcha
|
362
|
+
super
|
363
|
+
session[:omniauth] = nil unless @user.new_record? #OmniAuth
|
364
|
+
else
|
365
|
+
build_resource
|
366
|
+
clean_up_passwords(resource)
|
367
|
+
flash[:error] = "There was an error with the recaptcha code below. Please re-enter the code and click submit."
|
368
|
+
render_with_scope :new
|
369
|
+
end
|
370
|
+
else
|
371
|
+
super
|
372
|
+
session[:omniauth] = nil unless @user.new_record? #OmniAuth
|
373
|
+
end
|
374
|
+
end
|
375
|
+
</pre>
|
376
|
+
|
377
|
+
h2. login with username
|
378
|
+
|
379
|
+
Customizing The Login Requirements
|
380
|
+
|
381
|
+
Our application currently uses an email address and password to log users in, but if we want to change it so that it asks for a username instead of an email address then we can do so fairly easily.
|
382
|
+
|
383
|
+
The first thing we need to do is create a username column in the User table, which we can do by generating a migration.
|
384
|
+
|
385
|
+
$ rails generate migration add_username_to_users username:string
|
386
|
+
|
387
|
+
That done we’ll run the migration.
|
388
|
+
|
389
|
+
$ rake db:migrate
|
390
|
+
|
391
|
+
As we only have one user in the database we can quickly log in to the Rails console and set a value for the username attribute for that user.
|
392
|
+
|
393
|
+
$ rails c
|
394
|
+
Loading development environment (Rails 3.0.0.beta2)
|
395
|
+
ruby-1.8.7-p249 > User.first.update_attribute(:username, "eifion")
|
396
|
+
=> true
|
397
|
+
|
398
|
+
Now that we’ve modified the database we need to modify devise’s configuration file, uncommenting the config.authentication_keys line and changing its value from :email to :username.
|
399
|
+
|
400
|
+
/config/initializers/devise.rb
|
401
|
+
|
402
|
+
1. config.authentication_keys = [ :username ]
|
403
|
+
|
404
|
+
config.authentication_keys = [ :username ]
|
405
|
+
|
406
|
+
With this value set devise will use the username field as the authentication key. We’ll also have to modify the sign in form so that it has a username field instead of the email field.
|
407
|
+
|
408
|
+
/app/views/devise/sessions/new.html.erb
|
409
|
+
|
410
|
+
1. <% title "Sign In" %>
|
411
|
+
2.
|
412
|
+
3. <%= form_for(resource_name, resource, :url => session_path(resource_name)) do |f| %>
|
413
|
+
4. <ol class="formList">
|
414
|
+
5. <li><%= f.label :username %> <%= f.text_field :username %></li>
|
415
|
+
6. <li><%= f.label :password %> <%= f.password_field :password %></li>
|
416
|
+
7. <% if devise_mapping.rememberable? -%>
|
417
|
+
8. <li><%= f.check_box :remember_me %> <%= f.label :remember_me %></li>
|
418
|
+
9. <% end %>
|
419
|
+
10. <li><%= f.submit "Sign in" %></li>
|
420
|
+
11. </ol>
|
421
|
+
12. <% end %>
|
422
|
+
13.
|
423
|
+
14. <%= render :partial => "devise/shared/links" %>
|
424
|
+
|
425
|
+
<% title "Sign In" %>
|
426
|
+
|
427
|
+
<%= form_for(resource_name, resource, :url => session_path(resource_name)) do |f| %>
|
428
|
+
<ol class="formList">
|
429
|
+
<li><%= f.label :username %> <%= f.text_field :username %></li>
|
430
|
+
<li><%= f.label :password %> <%= f.password_field :password %></li>
|
431
|
+
<% if devise_mapping.rememberable? -%>
|
432
|
+
<li><%= f.check_box :remember_me %> <%= f.label :remember_me %></li>
|
433
|
+
<% end %>
|
434
|
+
<li><%= f.submit "Sign in" %></li>
|
435
|
+
</ol>
|
436
|
+
<% end %>
|
437
|
+
|
438
|
+
<%= render :partial => "devise/shared/links" %>
|
439
|
+
|
440
|
+
The signup form will also need to be modified and we’ll have to add validation to the User model attribute but we won’t show that here.
|
441
|
+
|
442
|
+
Once we’ve restarted the server so that the configuration changes are picked up we can visit the signin page and login with a username instead of and email address.
|
443
|
+
|
444
|
+
h2. me.com and gmail style
|
445
|
+
|
446
|
+
Another way to do this is me.com and gmail style. You allow an email or the username of the email. For public facing accounts, this has more security. Rather than allow some hacker to enter a username and then just guess the password, they would have no clue what the user’s email is. Just to make it easier on the user for logging in, allow a short form of their email to be used e.g “someone@domain.com” or just “someone” for short.
|
447
|
+
|
448
|
+
before_create :create_login
|
449
|
+
|
450
|
+
def create_login
|
451
|
+
email = self.email.split(/@/)
|
452
|
+
login_taken = User.where( :login => email[0]).first
|
453
|
+
unless login_taken
|
454
|
+
self.login = email[0]
|
455
|
+
else
|
456
|
+
self.login = self.email
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def self.find_for_database_authentication(conditions)
|
461
|
+
self.where(:login => conditions[:email]).first || self.where(:email => conditions[:email]).first
|
462
|
+
end
|
463
|
+
|