ditty 0.7.2 → 0.8.0
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.
- 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
|