ditty 0.7.2 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -7
- data/.travis.yml +5 -5
- data/Gemfile.ci +2 -0
- data/Rakefile +4 -3
- data/Readme.md +24 -2
- data/ditty.gemspec +4 -3
- data/lib/ditty.rb +24 -0
- data/lib/ditty/cli.rb +6 -2
- data/lib/ditty/components/app.rb +10 -1
- data/lib/ditty/controllers/application.rb +72 -10
- data/lib/ditty/controllers/audit_logs.rb +1 -5
- data/lib/ditty/controllers/auth.rb +37 -17
- data/lib/ditty/controllers/component.rb +15 -5
- data/lib/ditty/controllers/main.rb +1 -5
- data/lib/ditty/controllers/roles.rb +2 -5
- data/lib/ditty/controllers/user_login_traits.rb +18 -0
- data/lib/ditty/controllers/users.rb +4 -9
- data/lib/ditty/db.rb +3 -1
- data/lib/ditty/emails/base.rb +13 -4
- data/lib/ditty/helpers/authentication.rb +6 -5
- data/lib/ditty/helpers/component.rb +9 -1
- data/lib/ditty/helpers/response.rb +24 -3
- data/lib/ditty/helpers/views.rb +20 -0
- data/lib/ditty/listener.rb +38 -10
- data/lib/ditty/middleware/accept_extension.rb +2 -0
- data/lib/ditty/middleware/error_catchall.rb +2 -0
- data/lib/ditty/models/audit_log.rb +1 -0
- data/lib/ditty/models/base.rb +4 -0
- data/lib/ditty/models/identity.rb +3 -0
- data/lib/ditty/models/role.rb +1 -0
- data/lib/ditty/models/user.rb +9 -1
- data/lib/ditty/models/user_login_trait.rb +17 -0
- data/lib/ditty/policies/audit_log_policy.rb +6 -6
- data/lib/ditty/policies/role_policy.rb +2 -2
- data/lib/ditty/policies/user_login_trait_policy.rb +45 -0
- data/lib/ditty/policies/user_policy.rb +2 -2
- data/lib/ditty/rubocop.rb +3 -0
- data/lib/ditty/seed.rb +2 -0
- data/lib/ditty/services/authentication.rb +7 -2
- data/lib/ditty/services/email.rb +8 -2
- data/lib/ditty/services/logger.rb +11 -0
- data/lib/ditty/services/pagination_wrapper.rb +2 -0
- data/lib/ditty/services/settings.rb +14 -3
- data/lib/ditty/tasks/ditty.rake +109 -0
- data/lib/ditty/tasks/omniauth-ldap.rake +43 -0
- data/lib/ditty/version.rb +1 -1
- data/lib/rubocop/cop/ditty/call_services_directly.rb +42 -0
- data/migrate/20181209_add_user_login_traits.rb +16 -0
- data/migrate/20181209_extend_audit_log.rb +12 -0
- data/views/403.haml +2 -0
- data/views/audit_logs/index.haml +11 -6
- data/views/auth/ldap.haml +17 -0
- data/views/emails/forgot_password.haml +1 -1
- data/views/emails/layouts/action.haml +10 -6
- data/views/emails/layouts/alert.haml +2 -1
- data/views/emails/layouts/billing.haml +2 -1
- data/views/error.haml +8 -3
- data/views/partials/form_control.haml +24 -20
- data/views/partials/navbar.haml +11 -12
- data/views/partials/sidebar.haml +1 -1
- data/views/roles/index.haml +2 -0
- data/views/user_login_traits/display.haml +32 -0
- data/views/user_login_traits/edit.haml +10 -0
- data/views/user_login_traits/form.haml +5 -0
- data/views/user_login_traits/index.haml +30 -0
- data/views/user_login_traits/new.haml +10 -0
- data/views/users/display.haml +1 -1
- data/views/users/login_traits.haml +27 -0
- data/views/users/profile.haml +2 -0
- metadata +50 -21
- data/lib/ditty/rake_tasks.rb +0 -102
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'ditty/controllers/application'
|
2
4
|
require 'ditty/services/email'
|
3
5
|
require 'securerandom'
|
@@ -6,48 +8,56 @@ module Ditty
|
|
6
8
|
class Auth < Application
|
7
9
|
set track_actions: true
|
8
10
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
11
|
+
def redirect_path
|
12
|
+
return "#{settings.map_path}/" if omniauth_redirect_path.nil?
|
13
|
+
return "#{settings.map_path}/" if omniauth_redirect_path =~ %r{/#{settings.map_path}/auth/?}
|
14
|
+
|
15
|
+
omniauth_redirect_path
|
12
16
|
end
|
13
17
|
|
14
|
-
def
|
15
|
-
|
16
|
-
return "#{settings.map_path}/" if env['omniauth.origin'] =~ %r{/#{settings.map_path}/auth/?}
|
17
|
-
env['omniauth.origin']
|
18
|
+
def omniauth_redirect_path
|
19
|
+
env['omniauth.origin'] || request.session['omniauth.origin']
|
18
20
|
end
|
19
21
|
|
20
22
|
def omniauth_callback(provider)
|
21
23
|
return failed_login unless env['omniauth.auth']
|
24
|
+
|
25
|
+
broadcast("before_#{provider}_login".to_sym, env['omniauth.auth'])
|
22
26
|
user = User.first(email: env['omniauth.auth']['info']['email'])
|
23
|
-
user = register_user if user.nil? &&
|
27
|
+
user = register_user if user.nil? && authorize(current_user, :register?)
|
24
28
|
return failed_login if user.nil?
|
29
|
+
|
30
|
+
broadcast("#{provider}_login".to_sym, user)
|
25
31
|
successful_login(user)
|
26
32
|
end
|
27
33
|
|
28
34
|
def failed_login
|
29
|
-
|
30
|
-
|
35
|
+
details = params[:message] || 'None'
|
36
|
+
logger.warn "Invalid Login: #{details}"
|
37
|
+
broadcast(:user_failed_login, target: self, details: details)
|
38
|
+
flash[:warning] = 'Invalid credentials. Please try again'
|
39
|
+
headers 'X-Authentication-Failure' => params[:message] if params[:message]
|
31
40
|
redirect "#{settings.map_path}/auth/login"
|
32
41
|
end
|
33
42
|
|
34
43
|
def successful_login(user)
|
35
44
|
halt 200 if request.xhr?
|
36
45
|
self.current_user = user
|
37
|
-
broadcast(:user_login, target: self
|
46
|
+
broadcast(:user_login, target: self)
|
38
47
|
flash[:success] = 'Logged In'
|
39
48
|
redirect redirect_path
|
40
49
|
end
|
41
50
|
|
42
51
|
def register_user
|
43
52
|
user = User.create(email: env['omniauth.auth']['info']['email'])
|
44
|
-
broadcast(:user_register, target: self, values: { user: user }
|
53
|
+
broadcast(:user_register, target: self, values: { user: user })
|
45
54
|
flash[:info] = 'Successfully Registered.'
|
46
55
|
user
|
47
56
|
end
|
48
57
|
|
49
58
|
before '/login' do
|
50
59
|
return if User.where(roles: Role.find_or_create(name: 'super_admin')).count.positive?
|
60
|
+
|
51
61
|
flash[:info] = 'Please register the super admin user.'
|
52
62
|
redirect "#{settings.map_path}/auth/register"
|
53
63
|
end
|
@@ -55,10 +65,20 @@ module Ditty
|
|
55
65
|
# TODO: Make this work for both LDAP and Identity
|
56
66
|
get '/login' do
|
57
67
|
authorize ::Ditty::Identity, :login
|
68
|
+
redirect settings.map_path if authenticated?
|
58
69
|
|
59
70
|
haml :'auth/login', locals: { title: 'Log In' }
|
60
71
|
end
|
61
72
|
|
73
|
+
# Custom login form for LDAP to allow CSRF checks. Set the `request_path` for
|
74
|
+
# the omniauth-ldap provider to another path so that this gest triggered
|
75
|
+
get '/ldap' do
|
76
|
+
authorize ::Ditty::Identity, :login
|
77
|
+
redirect settings.map_path if authenticated?
|
78
|
+
|
79
|
+
haml :'auth/ldap', locals: { title: 'Company Log In' }
|
80
|
+
end
|
81
|
+
|
62
82
|
get '/forgot-password' do
|
63
83
|
authorize ::Ditty::Identity, :forgot_password
|
64
84
|
|
@@ -92,7 +112,7 @@ module Ditty
|
|
92
112
|
|
93
113
|
param :token, String, required: true
|
94
114
|
identity = Identity[reset_token: params[:token]]
|
95
|
-
halt 404 unless identity
|
115
|
+
halt 404 unless identity&.reset_requested && identity.reset_requested > (Time.now - (24 * 60 * 60))
|
96
116
|
|
97
117
|
haml :'auth/reset_password', locals: { title: 'Reset your password', identity: identity }
|
98
118
|
end
|
@@ -107,11 +127,11 @@ module Ditty
|
|
107
127
|
identity_params = permitted_attributes(Identity, :update)
|
108
128
|
identity.set identity_params.merge(reset_token: nil, reset_requested: nil)
|
109
129
|
if identity.valid? && identity.save
|
110
|
-
broadcast(:identity_update_password, target: self
|
130
|
+
broadcast(:identity_update_password, target: self)
|
111
131
|
flash[:success] = 'Password Updated'
|
112
132
|
redirect "#{settings.map_path}/auth/login"
|
113
133
|
else
|
114
|
-
broadcast(:identity_update_password_failed, target: self
|
134
|
+
broadcast(:identity_update_password_failed, target: self)
|
115
135
|
haml :'auth/reset_password', locals: { title: 'Reset your password', identity: identity }
|
116
136
|
end
|
117
137
|
end
|
@@ -135,7 +155,7 @@ module Ditty
|
|
135
155
|
DB.transaction do
|
136
156
|
user.save
|
137
157
|
user.add_identity identity
|
138
|
-
broadcast(:user_register, target: self, values: { user: user }
|
158
|
+
broadcast(:user_register, target: self, values: { user: user })
|
139
159
|
flash[:info] = 'Successfully Registered. Please log in'
|
140
160
|
redirect "#{settings.map_path}/auth/login"
|
141
161
|
end
|
@@ -147,7 +167,7 @@ module Ditty
|
|
147
167
|
|
148
168
|
# Logout Action
|
149
169
|
delete '/' do
|
150
|
-
broadcast(:user_logout, target: self
|
170
|
+
broadcast(:user_logout, target: self)
|
151
171
|
logout
|
152
172
|
|
153
173
|
halt 200 if request.xhr?
|
@@ -13,6 +13,7 @@ module Ditty
|
|
13
13
|
set dehumanized: nil
|
14
14
|
set view_location: nil
|
15
15
|
set track_actions: false
|
16
|
+
set heading: nil
|
16
17
|
|
17
18
|
def read(id)
|
18
19
|
dataset.first(settings.model_class.primary_key => id)
|
@@ -22,8 +23,15 @@ module Ditty
|
|
22
23
|
@skip_verify = true
|
23
24
|
end
|
24
25
|
|
26
|
+
def trigger(event, attribs = {})
|
27
|
+
attribs[:target] ||= self
|
28
|
+
send(event, attribs) if self.respond_to? event
|
29
|
+
broadcast(event, attribs)
|
30
|
+
end
|
31
|
+
|
25
32
|
after do
|
26
33
|
return if settings.environment == 'production'
|
34
|
+
|
27
35
|
if (response.successful? || response.redirection?) && @skip_verify == false
|
28
36
|
verify_authorized if settings.environment != 'production'
|
29
37
|
end
|
@@ -31,6 +39,7 @@ module Ditty
|
|
31
39
|
|
32
40
|
after '/' do
|
33
41
|
return if settings.environment == 'production' || request.request_method != 'GET'
|
42
|
+
|
34
43
|
verify_policy_scoped if (response.successful? || response.redirection?) && @skip_verify == false
|
35
44
|
end
|
36
45
|
|
@@ -40,7 +49,8 @@ module Ditty
|
|
40
49
|
|
41
50
|
result = list
|
42
51
|
|
43
|
-
|
52
|
+
trigger :component_list
|
53
|
+
|
44
54
|
list_response(result)
|
45
55
|
end
|
46
56
|
|
@@ -62,7 +72,7 @@ module Ditty
|
|
62
72
|
|
63
73
|
entity.db.transaction do
|
64
74
|
entity.save # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
|
65
|
-
|
75
|
+
trigger :component_create, entity: entity
|
66
76
|
end
|
67
77
|
|
68
78
|
create_response(entity)
|
@@ -74,7 +84,7 @@ module Ditty
|
|
74
84
|
halt 404 unless entity
|
75
85
|
authorize entity, :read
|
76
86
|
|
77
|
-
|
87
|
+
trigger :component_read, entity: entity
|
78
88
|
read_response(entity)
|
79
89
|
end
|
80
90
|
|
@@ -98,7 +108,7 @@ module Ditty
|
|
98
108
|
entity.db.transaction do
|
99
109
|
entity.set(permitted_attributes(settings.model_class, :update))
|
100
110
|
entity.save # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
|
101
|
-
|
111
|
+
trigger :component_update, entity: entity
|
102
112
|
end
|
103
113
|
|
104
114
|
update_response(entity)
|
@@ -111,7 +121,7 @@ module Ditty
|
|
111
121
|
|
112
122
|
entity.db.transaction do
|
113
123
|
entity.destroy
|
114
|
-
|
124
|
+
trigger :component_delete, entity: entity
|
115
125
|
end
|
116
126
|
|
117
127
|
delete_response(entity)
|
@@ -8,13 +8,9 @@ module Ditty
|
|
8
8
|
class Main < Application
|
9
9
|
set track_actions: true
|
10
10
|
|
11
|
-
def find_template(views, name, engine, &block)
|
12
|
-
super(views, name, engine, &block) # Root
|
13
|
-
super(::Ditty::App.view_folder, name, engine, &block) # Basic Plugin
|
14
|
-
end
|
15
|
-
|
16
11
|
before '/' do
|
17
12
|
return if User.where(roles: Role.find_or_create(name: 'super_admin')).count.positive?
|
13
|
+
|
18
14
|
flash[:info] = 'Please register the super admin user.'
|
19
15
|
redirect "#{settings.map_path}/auth/register"
|
20
16
|
end
|
@@ -6,11 +6,8 @@ require 'ditty/policies/role_policy'
|
|
6
6
|
|
7
7
|
module Ditty
|
8
8
|
class Roles < Ditty::Component
|
9
|
-
|
9
|
+
SEARCHABLE = %i[name].freeze
|
10
10
|
|
11
|
-
|
12
|
-
super(views, name, engine, &block) # Root
|
13
|
-
super(::Ditty::App.view_folder, name, engine, &block) # Ditty
|
14
|
-
end
|
11
|
+
set model_class: Role
|
15
12
|
end
|
16
13
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ditty/controllers/component'
|
4
|
+
require 'ditty/models/user_login_trait'
|
5
|
+
require 'ditty/policies/user_login_trait_policy'
|
6
|
+
|
7
|
+
module Ditty
|
8
|
+
class UserLoginTraits < Ditty::Component
|
9
|
+
SEARCHABLE = %i[platform device browser ip_address].freeze
|
10
|
+
FILTERS = [
|
11
|
+
{ name: :user, field: 'user.email' }
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
set base_path: 'login-traits'
|
15
|
+
set model_class: UserLoginTrait
|
16
|
+
# set track_actions: true
|
17
|
+
end
|
18
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'ditty/controllers/component'
|
4
4
|
require 'ditty/models/user'
|
5
|
+
require 'ditty/models/user_login_trait'
|
5
6
|
require 'ditty/policies/user_policy'
|
6
7
|
require 'ditty/models/identity'
|
7
8
|
require 'ditty/policies/identity_policy'
|
@@ -13,11 +14,6 @@ module Ditty
|
|
13
14
|
set model_class: User
|
14
15
|
set track_actions: true
|
15
16
|
|
16
|
-
def find_template(views, name, engine, &block)
|
17
|
-
super(views, name, engine, &block) # Root
|
18
|
-
super(::Ditty::App.view_folder, name, engine, &block) # Ditty
|
19
|
-
end
|
20
|
-
|
21
17
|
# New
|
22
18
|
get '/new' do
|
23
19
|
authorize settings.model_class, :create
|
@@ -45,6 +41,7 @@ module Ditty
|
|
45
41
|
identity.save
|
46
42
|
rescue Sequel::ValidationFailed
|
47
43
|
raise unless request.accept? 'text/html'
|
44
|
+
|
48
45
|
status 400
|
49
46
|
locals = { title: heading(:new), entity: user, identity: identity }
|
50
47
|
return haml(:"#{view_location}/new", locals: locals)
|
@@ -52,10 +49,8 @@ module Ditty
|
|
52
49
|
user.save
|
53
50
|
user.add_identity identity
|
54
51
|
|
55
|
-
|
56
|
-
roles.
|
57
|
-
user.add_role(role_id) unless user.roles.map(&:id).include? role_id.to_i
|
58
|
-
end
|
52
|
+
roles&.each do |role_id|
|
53
|
+
user.add_role(role_id) unless user.roles.map(&:id).include? role_id.to_i
|
59
54
|
end
|
60
55
|
user.check_roles
|
61
56
|
end
|
data/lib/ditty/db.rb
CHANGED
@@ -18,8 +18,10 @@ elsif ENV['DATABASE_URL'].blank? == false
|
|
18
18
|
)
|
19
19
|
|
20
20
|
DB.sql_log_level = (ENV['SEQUEL_LOGGING_LEVEL'] || :debug).to_sym
|
21
|
-
DB.loggers << Ditty::Services::Logger.instance
|
21
|
+
DB.loggers << Ditty::Services::Logger.instance if ENV['DB_DEBUG'].to_i == 1
|
22
22
|
DB.extension(:pagination)
|
23
|
+
DB.extension(:schema_caching)
|
24
|
+
DB.load_schema_cache?('./config/schema.dump')
|
23
25
|
|
24
26
|
Sequel::Model.plugin :validation_helpers
|
25
27
|
Sequel::Model.plugin :update_or_create
|
data/lib/ditty/emails/base.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'haml'
|
2
4
|
require 'ditty/components/app'
|
3
5
|
|
@@ -15,8 +17,11 @@ module Ditty
|
|
15
17
|
def deliver!(to = nil, locals = {})
|
16
18
|
options[:to] = to unless to.nil?
|
17
19
|
@locals.merge!(locals)
|
18
|
-
%i[to from subject].each do |param|
|
19
|
-
|
20
|
+
%i[to from subject content_type].each do |param|
|
21
|
+
next unless options[param]
|
22
|
+
|
23
|
+
@locals[param] ||= options[param]
|
24
|
+
mail.send(param, options[param])
|
20
25
|
end
|
21
26
|
mail.body content
|
22
27
|
mail.deliver!
|
@@ -24,6 +29,7 @@ module Ditty
|
|
24
29
|
|
25
30
|
def method_missing(method, *args, &block)
|
26
31
|
return super unless respond_to_missing?(method)
|
32
|
+
|
27
33
|
mail.send(method, *args, &block)
|
28
34
|
end
|
29
35
|
|
@@ -36,7 +42,8 @@ module Ditty
|
|
36
42
|
def content
|
37
43
|
result = Haml::Engine.new(content_haml).render(Object.new, locals)
|
38
44
|
return result unless options[:layout]
|
39
|
-
|
45
|
+
|
46
|
+
Haml::Engine.new(layout_haml).render(Object.new, locals.merge(content: result))
|
40
47
|
end
|
41
48
|
|
42
49
|
def content_haml
|
@@ -52,14 +59,16 @@ module Ditty
|
|
52
59
|
end
|
53
60
|
|
54
61
|
def base_options
|
55
|
-
{ subject: '(No Subject)', from: 'no-reply@ditty.io', view: :base }
|
62
|
+
{ subject: '(No Subject)', from: 'no-reply@ditty.io', view: :base, content_type: 'text/html; charset=UTF-8' }
|
56
63
|
end
|
57
64
|
|
58
65
|
def find_template(file)
|
59
66
|
template = File.expand_path("./views/#{file}.haml")
|
60
67
|
return template if File.file? template
|
68
|
+
|
61
69
|
template = File.expand_path("./#{file}.haml", App.view_folder)
|
62
70
|
return template if File.file? template
|
71
|
+
|
63
72
|
file
|
64
73
|
end
|
65
74
|
|
@@ -1,14 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'ditty/models/user'
|
4
|
-
require 'ditty/models/role'
|
5
|
-
require 'ditty/models/identity'
|
6
4
|
|
7
5
|
module Ditty
|
8
6
|
module Helpers
|
9
7
|
module Authentication
|
10
8
|
def current_user
|
11
9
|
return nil if current_user_id.nil?
|
10
|
+
|
12
11
|
@current_user ||= User[current_user_id]
|
13
12
|
end
|
14
13
|
|
@@ -19,8 +18,9 @@ module Ditty
|
|
19
18
|
end
|
20
19
|
|
21
20
|
def current_user_id
|
22
|
-
return env['rack.session']['user_id'] if env['rack.session']
|
23
|
-
|
21
|
+
return env['rack.session']['user_id'] if env['rack.session'] && env['rack.session']['user_id']
|
22
|
+
|
23
|
+
env['omniauth.auth']&.uid
|
24
24
|
end
|
25
25
|
|
26
26
|
def authenticate
|
@@ -33,11 +33,12 @@ module Ditty
|
|
33
33
|
|
34
34
|
def authenticate!
|
35
35
|
raise NotAuthenticated unless authenticated?
|
36
|
+
|
36
37
|
true
|
37
38
|
end
|
38
39
|
|
39
40
|
def logout
|
40
|
-
env['rack.session']
|
41
|
+
env['rack.session']&.delete('user_id')
|
41
42
|
env.delete('omniauth.auth')
|
42
43
|
end
|
43
44
|
end
|
@@ -13,8 +13,10 @@ module Ditty
|
|
13
13
|
# param :count, Integer, min: 1, default: 10 # Can't do this, since count can be `all`
|
14
14
|
def check_count
|
15
15
|
return 10 if params[:count].nil?
|
16
|
+
|
16
17
|
count = params[:count].to_i
|
17
18
|
return count if count >= 1
|
19
|
+
|
18
20
|
excp = Sinatra::Param::InvalidParameterError.new 'Parameter cannot be less than 1'
|
19
21
|
excp.param = :count
|
20
22
|
raise excp
|
@@ -37,16 +39,18 @@ module Ditty
|
|
37
39
|
ds = dataset
|
38
40
|
ds = ds.dataset if ds.respond_to?(:dataset)
|
39
41
|
return ds if params[:count] == 'all'
|
42
|
+
|
40
43
|
params[:count] = check_count
|
41
44
|
|
42
45
|
# Account for difference between sequel paginate and will paginate
|
43
46
|
return ds.paginate(page: params[:page], per_page: params[:count]) if ds.is_a?(Array)
|
47
|
+
|
44
48
|
ds.paginate(params[:page], params[:count])
|
45
49
|
end
|
46
50
|
|
47
51
|
def heading(action = nil)
|
48
52
|
@headings ||= begin
|
49
|
-
heading = settings.model_class.to_s.demodulize.titleize
|
53
|
+
heading = settings.heading || settings.model_class.to_s.demodulize.singularize.titleize
|
50
54
|
h = Hash.new(heading)
|
51
55
|
h[:new] = "New #{heading}"
|
52
56
|
h[:list] = pluralize heading
|
@@ -78,6 +82,7 @@ module Ditty
|
|
78
82
|
def filters
|
79
83
|
filter_fields.map do |filter|
|
80
84
|
next if params[filter[:name]].blank?
|
85
|
+
|
81
86
|
filter[:field] ||= filter[:name]
|
82
87
|
filter[:modifier] ||= :to_s # TODO: Do this with Sinatra Param?
|
83
88
|
filter
|
@@ -86,6 +91,7 @@ module Ditty
|
|
86
91
|
|
87
92
|
def ordering
|
88
93
|
return if params[:sort].blank?
|
94
|
+
|
89
95
|
Sequel.send(params[:order].to_sym, params[:sort].to_sym)
|
90
96
|
end
|
91
97
|
|
@@ -112,11 +118,13 @@ module Ditty
|
|
112
118
|
assoc = filter[:field].to_s.split('.').first.to_sym
|
113
119
|
assoc = settings.model_class.association_reflection(assoc)
|
114
120
|
raise "Unknown association #{assoc}" if assoc.nil?
|
121
|
+
|
115
122
|
assoc
|
116
123
|
end
|
117
124
|
|
118
125
|
def search_filters
|
119
126
|
return [] if params[:q].blank?
|
127
|
+
|
120
128
|
searchable_fields.map { |f| Sequel.ilike(f.to_sym, "%#{params[:q]}%") }
|
121
129
|
end
|
122
130
|
end
|