lux-fw 0.2.3 → 0.5.32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/bin/README.md +33 -0
  4. data/bin/build_gem +76 -0
  5. data/bin/cli/config.rb +44 -0
  6. data/bin/cli/console.rb +61 -0
  7. data/bin/cli/dbconsole.rb +8 -0
  8. data/bin/cli/eval.rb +32 -0
  9. data/bin/cli/generate.rb +88 -0
  10. data/bin/cli/get.rb +12 -0
  11. data/bin/cli/new.rb +22 -0
  12. data/bin/cli/routes.rb +90 -0
  13. data/bin/cli/secrets.rb +40 -0
  14. data/bin/cli/server.rb +24 -0
  15. data/bin/cli/stats.rb +133 -0
  16. data/bin/lux +24 -65
  17. data/lib/common/class_attributes.rb +40 -64
  18. data/lib/common/class_callbacks.rb +34 -51
  19. data/lib/common/crypt.rb +10 -8
  20. data/lib/common/free_struct.rb +42 -0
  21. data/lib/common/hash_with_indifferent_access.rb +1 -1
  22. data/lib/common/html_tag_builder.rb +26 -2
  23. data/lib/common/url.rb +58 -40
  24. data/lib/lux/application/application.rb +290 -117
  25. data/lib/lux/application/lib/nav.rb +64 -38
  26. data/lib/lux/application/lib/render.rb +2 -1
  27. data/lib/lux/cache/cache.rb +87 -62
  28. data/lib/lux/cache/lib/{ram.rb → memory.rb} +5 -7
  29. data/lib/lux/cache/lib/null.rb +6 -8
  30. data/lib/lux/config/config.rb +109 -52
  31. data/lib/lux/config/lib/plugin.rb +65 -0
  32. data/lib/lux/config/lib/secrets.rb +48 -0
  33. data/lib/lux/controller/controller.rb +241 -0
  34. data/lib/lux/current/current.rb +33 -47
  35. data/lib/lux/current/lib/session.rb +72 -0
  36. data/lib/lux/delayed_job/delayed_job.rb +14 -7
  37. data/lib/lux/delayed_job/lib/memory.rb +7 -5
  38. data/lib/lux/delayed_job/lib/redis.rb +1 -1
  39. data/lib/lux/error/error.rb +164 -62
  40. data/lib/lux/event_bus/event_bus.rb +27 -0
  41. data/lib/lux/lux.rb +103 -66
  42. data/lib/lux/mailer/mailer.rb +23 -25
  43. data/lib/lux/response/lib/file.rb +81 -0
  44. data/lib/lux/response/lib/header.rb +14 -1
  45. data/lib/lux/response/response.rb +64 -56
  46. data/lib/lux/view/cell.rb +102 -0
  47. data/lib/lux/{helper → view}/helper.rb +39 -23
  48. data/lib/lux/view/lib/cell_helpers.rb +29 -0
  49. data/lib/lux/{helper/helpers/mailer_helper.rb → view/lib/helper_modules.rb} +7 -1
  50. data/lib/lux/{template/template.rb → view/view.rb} +21 -24
  51. data/lib/lux-fw.rb +4 -2
  52. data/lib/overload/array.rb +12 -6
  53. data/lib/overload/blank.rb +0 -1
  54. data/lib/overload/dir.rb +18 -0
  55. data/lib/overload/file.rb +1 -6
  56. data/lib/overload/hash.rb +56 -13
  57. data/lib/overload/integer.rb +2 -2
  58. data/lib/overload/it.rb +4 -4
  59. data/lib/overload/object.rb +37 -8
  60. data/lib/overload/{r.rb → raise_variants.rb} +23 -4
  61. data/lib/overload/string.rb +22 -6
  62. data/misc/demo/app/cells/demo_cell.rb +12 -0
  63. data/misc/demo/app/controllers/application_controller.rb +7 -0
  64. data/misc/demo/app/controllers/main/root_controller.rb +9 -0
  65. data/misc/demo/app/routes.rb +5 -0
  66. data/misc/demo/config/application.rb +14 -0
  67. data/misc/demo/config/assets.rb +6 -0
  68. data/misc/demo/config/environment.rb +7 -0
  69. data/misc/puma_auto_tune.rb +43 -0
  70. data/misc/unicorn.rb +37 -0
  71. data/{lib/lux → plugins}/api/api.rb +46 -29
  72. data/plugins/api/lib/attr.rb +31 -0
  73. data/{lib/lux → plugins}/api/lib/dsl.rb +3 -6
  74. data/{lib/lux → plugins}/api/lib/error.rb +0 -0
  75. data/{lib/lux → plugins}/api/lib/model_api.rb +51 -12
  76. data/{lib/lux → plugins}/api/lib/response.rb +31 -17
  77. data/{bin/cli/am → plugins/db/auto_migrate/auto_migrate.rb} +18 -35
  78. data/plugins/db/helpers/array_search.rb +27 -0
  79. data/plugins/db/helpers/before_save_filters.rb +32 -0
  80. data/plugins/db/helpers/composite_primary_keys.rb +36 -0
  81. data/plugins/db/helpers/core.rb +94 -0
  82. data/plugins/db/helpers/dataset_methods.rb +138 -0
  83. data/plugins/db/helpers/enums_plugin.rb +52 -0
  84. data/plugins/db/helpers/find_precache.rb +31 -0
  85. data/plugins/db/helpers/link_objects.rb +84 -0
  86. data/plugins/db/helpers/schema_checks.rb +83 -0
  87. data/plugins/db/helpers/typero_adapter.rb +71 -0
  88. data/plugins/db/logger/config.rb +22 -0
  89. data/plugins/db/logger/lux_response_adapter.rb +10 -0
  90. data/plugins/db/paginate/helper.rb +32 -0
  91. data/plugins/db/paginate/sequel_adapter.rb +23 -0
  92. data/plugins/exceptions/simple_exception.rb +64 -0
  93. data/plugins/favicon/favicon.rb +10 -0
  94. data/plugins/html/html_form.rb +118 -0
  95. data/plugins/html/html_input.rb +98 -0
  96. data/plugins/html/html_menu.rb +79 -0
  97. data/plugins/html/input_types.rb +346 -0
  98. data/plugins/js_widgets/js_widgets.rb +15 -0
  99. data/plugins/oauth/lib/facebook.rb +35 -0
  100. data/plugins/oauth/lib/github.rb +38 -0
  101. data/plugins/oauth/lib/google.rb +41 -0
  102. data/plugins/oauth/lib/linkedin.rb +41 -0
  103. data/plugins/oauth/lib/stackexchange.rb +41 -0
  104. data/plugins/oauth/lib/twitter.rb +38 -0
  105. data/plugins/oauth/oauth.rb +42 -0
  106. data/{lib/common → plugins/policy}/policy.rb +6 -7
  107. data/tasks/loader.rb +49 -0
  108. metadata +151 -49
  109. data/bin/cli/assets +0 -41
  110. data/bin/cli/console +0 -51
  111. data/bin/cli/dev +0 -1
  112. data/bin/cli/eval +0 -24
  113. data/bin/cli/exceptions +0 -62
  114. data/bin/cli/generate +0 -86
  115. data/bin/cli/get +0 -5
  116. data/bin/cli/nginx +0 -34
  117. data/bin/cli/production +0 -1
  118. data/bin/cli/render +0 -18
  119. data/bin/cli/routes +0 -14
  120. data/bin/cli/server +0 -4
  121. data/bin/cli/stat +0 -1
  122. data/bin/cli/systemd +0 -36
  123. data/bin/txt/nginx.conf +0 -46
  124. data/bin/txt/siege-and-puma.txt +0 -3
  125. data/lib/common/base32.rb +0 -47
  126. data/lib/common/dynamic_class.rb +0 -28
  127. data/lib/common/folder_model.rb +0 -50
  128. data/lib/common/generic_model.rb +0 -62
  129. data/lib/lux/application/lib/plugs.rb +0 -10
  130. data/lib/lux/application/lib/route_test.rb +0 -64
  131. data/lib/lux/cache/lib/memcached.rb +0 -3
  132. data/lib/lux/cell/cell.rb +0 -261
  133. data/lib/lux/current/lib/static_file.rb +0 -103
  134. data/lib/lux/helper/helpers/application_helper.rb +0 -3
  135. data/lib/lux/helper/helpers/html_helper.rb +0 -3
  136. data/lib/overload/auto_loader.rb +0 -27
  137. data/lib/overload/module.rb +0 -10
  138. data/lib/overload/string_inflections.rb +0 -8
@@ -1,5 +1,41 @@
1
- class ModelApi < ApplicationApi
1
+ class Sequel::Model
2
+ module InstanceMethods
3
+ def same_as_last_field_value data
4
+ data = data.join('-') if data.is_array?
5
+ data = '' if data.is_hash?
6
+ data
7
+ end
8
+
9
+ def same_as_last?
10
+ @last = self.class.xorder('id desc').first
11
+ return unless @last
12
+
13
+ return if respond_to?(:name) && name != @last[:name]
14
+
15
+ new_o = self.to_h
16
+ new_o.delete :created_at
17
+ new_o.delete :updated_at
18
+ new_o.delete :id
2
19
 
20
+ old_o = new_o.keys.inject({}) do |t, key|
21
+ t[key] = @last.send(key)
22
+
23
+ new_o[key] = same_as_last_field_value new_o[key]
24
+ t[key] = same_as_last_field_value t[key]
25
+
26
+ t
27
+ end
28
+
29
+ if new_o.to_s.length == old_o.to_s.length
30
+ raise "#{self.class} is the copy of the last one created."
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ ###
37
+
38
+ class ModelApi < ApplicationApi
3
39
  class << self
4
40
  def toggle_ids name
5
41
  self.class_eval %[
@@ -12,7 +48,7 @@ class ModelApi < ApplicationApi
12
48
  end
13
49
 
14
50
  # toggles value in postgre array field
15
- def toggle!(object, field, value)
51
+ def toggle! object, field, value
16
52
  object[field] ||= []
17
53
 
18
54
  if object[field].include?(value)
@@ -43,15 +79,13 @@ class ModelApi < ApplicationApi
43
79
  def create
44
80
  @object = @class_name.constantize.new
45
81
 
46
- @last = @class_name.constantize.my.last rescue @class_name.constantize.last
47
- if @last && @last[:name].present? && @last[:name] == @params[:name]
48
- error "#{@class_name} is same as last one created."
49
- end
50
-
51
- for k,v in @params
82
+ for k, v in @params
83
+ v = nil if v.blank?
52
84
  @object.send("#{k}=", v) if @object.respond_to?(k.to_sym)
53
85
  end
54
86
 
87
+ @object.same_as_last? rescue error($!.message)
88
+
55
89
  can? :create, @object
56
90
  @object.save if @object.valid?
57
91
  return if report_errros_if_any @object
@@ -71,7 +105,9 @@ class ModelApi < ApplicationApi
71
105
  error "Object not found" unless @object
72
106
 
73
107
  for k,v in @params
74
- @object.send("#{k}=", v) if @object.respond_to?(k.to_sym)
108
+ m = "#{k}=".to_sym
109
+ v = nil if v.blank?
110
+ @object.send(m, v) if @object.respond_to?(m)
75
111
  end
76
112
 
77
113
  can? :update, @object
@@ -135,13 +171,13 @@ class ModelApi < ApplicationApi
135
171
  desc = v.join(', ')
136
172
 
137
173
  response.error k, desc
138
- response.error desc
139
174
  end
140
175
 
141
176
  error
142
177
  end
143
178
 
144
- def can? action, object
179
+ def can? action, object=nil
180
+ object ||= @object
145
181
  object.can?(action) do |err|
146
182
  msg = 'No %s permission for %s' % [action.to_s.sub('?',''), Lux.current.var.user ? Lux.current.var.user.email : :guests]
147
183
  msg += ' on %s' % object.class.name if object
@@ -151,7 +187,10 @@ class ModelApi < ApplicationApi
151
187
 
152
188
  def add_response_object_path
153
189
  begin
154
- response.meta :path, @object.path if @object.respond_to?(:path)
190
+ if @object.respond_to?(:path)
191
+ response.meta :path, @object.path
192
+ response.meta :string_id, @object.id.string_id
193
+ end
155
194
  rescue
156
195
  nil
157
196
  end
@@ -11,7 +11,7 @@ class Lux::Api::Response
11
11
  attr_accessor :message
12
12
 
13
13
  def initialize
14
- @meta = {}
14
+ @meta = {}.h_wia
15
15
  end
16
16
 
17
17
  def status num=nil
@@ -25,15 +25,20 @@ class Lux::Api::Response
25
25
  end
26
26
 
27
27
  def error key, data=nil
28
- if data
29
- @error_hash ||= {}
30
- @error_hash[key.to_s] = data
31
- data
32
- else
33
- @errors ||= []
34
- @errors.push key unless @errors.include?(key)
35
- key
28
+ unless data
29
+ data = key
30
+ key = :base
36
31
  end
32
+
33
+ key = key.to_s
34
+
35
+ @errors ||= {}
36
+ @errors[key] ||= []
37
+ @errors[key].push data unless @errors[key].include?(data)
38
+ end
39
+
40
+ def errors?
41
+ !!@errors
37
42
  end
38
43
 
39
44
  def message what
@@ -44,22 +49,26 @@ class Lux::Api::Response
44
49
  @meta['location'] = url
45
50
  end
46
51
 
47
- def errors?
48
- (@error_hash || @errors) ? true : false
52
+ def event name, opts={}
53
+ @meta['event'] ||= []
54
+ @meta['event'].push([name, opts])
49
55
  end
50
56
 
51
57
  def render
52
58
  output = {}
53
59
 
54
- if errors?
60
+ if @errors
55
61
  status 400
56
62
 
57
- output[:error] ||= {}
58
- output[:error][:messages] = @errors if @errors
59
- output[:error][:hash] = @error_hash if @error_hash
60
- end
63
+ errors = @errors.inject({}) { |t, (k, v)| t[k] = v.join(', '); t }
64
+ base = errors.values.uniq
61
65
 
62
- Lux.current.response.status status
66
+ errors.delete('base')
67
+
68
+ output[:error] ||= {}
69
+ output[:error][:messages] = base
70
+ output[:error][:hash] = errors if errors.keys.first
71
+ end
63
72
 
64
73
  output[:data] = @data if @data.present?
65
74
  output[:meta] = @meta if @meta.present?
@@ -68,4 +77,9 @@ class Lux::Api::Response
68
77
  output
69
78
  end
70
79
  alias :to_hash :render
80
+
81
+ def write
82
+ Lux.current.response.status status
83
+ Lux.current.response.body render
84
+ end
71
85
  end
@@ -1,27 +1,10 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'bundler/setup'
4
- require 'dotenv'
5
-
6
- Dotenv.load
7
- Bundler.require(:default)
8
-
9
- # load app config
10
- require './config/db'
11
-
12
- # Sequel extension and plugin test
13
- DB.run %[DROP TABLE IF EXISTS lux_tests;]
14
- DB.run %[CREATE TABLE lux_tests (int_array integer[] default '{}', text_array text[] default '{}');]
15
- class LuxTest < Sequel::Model; end;
16
- LuxTest.new.save
17
- die('"DB.extension :pg_array" not loaded') unless LuxTest.first.int_array.class == Sequel::Postgres::PGArray
18
- DB.run %[DROP TABLE IF EXISTS lux_tests;]
1
+ # https://github.com/jeremyevans/sequel/blob/master/doc/schema_modification.rdoc
19
2
 
20
3
  class AutoMigrate
21
4
  attr_accessor :fields
22
5
 
23
6
  class << self
24
- def table table_name
7
+ def table table_name, opts={}
25
8
  die "Table [#{table_name}] not in plural -> expected [#{table_name.to_s.pluralize}]" unless table_name.to_s.pluralize == table_name.to_s
26
9
 
27
10
  die 'Table name "%s" is not permited' % table_name if [:categories].include?(table_name)
@@ -34,7 +17,7 @@ class AutoMigrate
34
17
  end
35
18
  end
36
19
 
37
- t = new table_name
20
+ t = new table_name, opts
38
21
  yield t
39
22
  t.fix_fields
40
23
  t.update
@@ -57,20 +40,19 @@ class AutoMigrate
57
40
  raise $!
58
41
  end
59
42
  end
43
+
60
44
  end
61
45
 
62
46
  ###
63
47
 
64
- def initialize table_name
48
+ def initialize table_name, opts={}
49
+ @fields = {}
50
+ @opts = opts
65
51
  @table_name = table_name
66
- @fields = {}
67
52
 
68
53
  klass = @table_name.to_s.classify
69
54
 
70
- if Object.const_defined?(klass)
71
- puts 'Table name "%s" is not allowed - class "%s" is allready defined'.red % [table_name, klass]
72
- exit
73
- end
55
+ Object.send(:remove_const, klass) if Object.const_defined?(klass)
74
56
 
75
57
  eval %[class ::%s < Sequel::Model; end;] % klass
76
58
  @object = klass.constantize.new
@@ -119,11 +101,13 @@ class AutoMigrate
119
101
  puts "Table #{@table_name.to_s.yellow}, #{@fields.keys.length} fields"
120
102
 
121
103
  # remove extra fields
122
- for field in (@object.columns - @fields.keys - [:id])
123
- print "Remove colum #{@table_name}.#{field} (y/N): ".light_blue
124
- if STDIN.gets.chomp.downcase.index('y')
125
- DB.drop_column @table_name, field
126
- puts " drop_column #{field}".green
104
+ if @opts[:drop].class != FalseClass
105
+ for field in (@object.columns - @fields.keys - [:id])
106
+ print "Remove colum #{@table_name}.#{field} (y/N): ".light_blue
107
+ if STDIN.gets.chomp.downcase.index('y')
108
+ DB.drop_column @table_name, field
109
+ puts " drop_column #{field}".green
110
+ end
127
111
  end
128
112
  end
129
113
 
@@ -261,9 +245,9 @@ class AutoMigrate
261
245
  name = args[0]
262
246
  opts = args[1] || {}
263
247
 
264
- if [:string, :integer, :text, :boolean, :datetime, :date, :jsonb].index(type)
248
+ if [:string, :integer, :text, :boolean, :datetime, :date, :jsonb, :geography].index(type)
265
249
  @fields[name.to_sym] = [type, opts]
266
- elsif [:decimal].index(type)
250
+ elsif type == :decimal
267
251
  opts[:precision] ||= 8
268
252
  opts[:scale] ||= 2
269
253
  @fields[name.to_sym] = [:decimal, opts]
@@ -274,6 +258,7 @@ class AutoMigrate
274
258
  @fields[:updated_at] = [:datetime, opts]
275
259
  @fields[:updated_by] = [:integer, opts]
276
260
  elsif type == :polymorphic
261
+ name ||= :model
277
262
  @fields["#{name}_id".to_sym] = [:integer, opts.merge(index: true) ]
278
263
  @fields["#{name}_type".to_sym] = [:string, opts.merge(limit: 100, index: "#{name}_id")]
279
264
  else
@@ -281,5 +266,3 @@ class AutoMigrate
281
266
  end
282
267
  end
283
268
  end
284
-
285
- require './config/schema.rb'
@@ -0,0 +1,27 @@
1
+ Sequel::Model.dataset_module do
2
+ # only postgree
3
+ # Bucket.can.all_tags -> can_tags mora biti zadnji
4
+ def all_tags field=:tags, *args
5
+ sqlq = sql.split(' FROM ')[1]
6
+ sqlq = "select lower(unnest(#{field})) as tag FROM " + sqlq
7
+ sqlq = "select tag as name, count(tag) as cnt from (#{sqlq}) as tags group by tag order by cnt desc"
8
+ DB.fetch(sqlq).map(&:h).or([])
9
+ end
10
+
11
+ # were users.id in (select unnest(user_ids) from doors)
12
+ # def where_unnested klass
13
+ # target_table = klass.to_s.tableize
14
+ # where("#{table_name}.id in (select unnest(#{table_name.singularize}_ids) from #{target_table})")
15
+ # end
16
+ # assumes field name is tags
17
+ def where_any data, field
18
+ return self unless data.present?
19
+
20
+ if data.is_a?(Array)
21
+ xwhere data.map { |v| "#{v}=any(#{field})" }.join(' or ')
22
+ else
23
+ xwhere('?=any(%s)' % field, data)
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,32 @@
1
+ module Sequel::Plugins::LuxBeforeSave
2
+ module InstanceMethods
3
+ def before_save
4
+ # timestamps
5
+ self[:created_at] = Time.now.utc if !self[:id] && respond_to?(:created_at)
6
+ self[:updated_at] ||= Time.now.utc if respond_to?(:updated_at)
7
+
8
+ # return error if user needed and not defined
9
+ if !User.current && (respond_to?(:created_by) || respond_to?(:updated_by))
10
+ errors.add(:base, 'You have to be registered to save data')
11
+ return
12
+ end
13
+
14
+ # add timestamps
15
+ self[:created_by] = User.current.id if respond_to?(:created_by) && !id
16
+ self[:updated_by] ||= User.current.id if respond_to?(:updated_by)
17
+
18
+ # delete cache key if defined
19
+ Lux.cache.delete(cache_key)
20
+
21
+ super
22
+ end
23
+
24
+ def before_destroy
25
+ Lux.cache.delete(cache_key)
26
+
27
+ super
28
+ end
29
+ end
30
+ end
31
+
32
+ Sequel::Model.plugin :lux_before_save
@@ -0,0 +1,36 @@
1
+ # define composite key and check before save
2
+ # class OrgUser < ApplicationModel
3
+ # primary_keys :org_id, :user_id
4
+ # end
5
+
6
+ module Sequel::Plugins::PrimaryKeys
7
+ module ClassMethods
8
+ def primary_keys(*args)
9
+ unless args[0]
10
+ return respond_to?(:_primary_keys) ? _primary_keys : [:id]
11
+ end
12
+
13
+ define_singleton_method(:_primary_keys) { args }
14
+ end
15
+ end
16
+
17
+ module InstanceMethods
18
+ def before_save
19
+ klass = self.class
20
+
21
+ if klass.respond_to?(:_primary_keys)
22
+ check = klass._primary_keys.inject(klass.dataset) do |record, field|
23
+ record = record.where(field =>send(field))
24
+ end
25
+
26
+ check = check.xwhere('id<>?', id) if self[:id]
27
+
28
+ raise StandardError, 'Record allredy exists (primary_keys check)' if check.first
29
+ end
30
+
31
+ super
32
+ end
33
+ end
34
+ end
35
+
36
+ Sequel::Model.plugin :primary_keys
@@ -0,0 +1,94 @@
1
+ # http://ricostacruz.com/cheatsheets/sequel.html
2
+ # http://sequel.jeremyevans.net/rdoc/files/doc/model_plugins_rdoc.html
3
+
4
+ class Sequel::Model
5
+ module ClassMethods
6
+ def find_by what
7
+ where(what).first
8
+ end
9
+
10
+ # active record like define scope
11
+ # http://sequel.jeremyevans.net/rdoc/classes/Sequel/Model/ClassMethods.html
12
+ def scope name, body=nil, &block
13
+ block ||= body
14
+ dataset_module{define_method(name, &block)}
15
+ end
16
+
17
+ # instance scope, same as scope but runs on instance
18
+ # iscope(:notes) { Note.where(created_by:id) }
19
+ def iscope name, body=nil, &block
20
+ block ||= body
21
+ define_method(name, &block)
22
+ end
23
+
24
+ def first_or_new filter
25
+ where(filter).first || new(filter)
26
+ end
27
+
28
+ def first_or_create filter
29
+ where(filter).first || create(filter)
30
+ end
31
+ end
32
+
33
+ module InstanceMethods
34
+ def cache_key
35
+ "#{self.class}/#{id}"
36
+ end
37
+
38
+ def attributes
39
+ ret = {}
40
+ for el in columns
41
+ ret[el.to_s] = send(el.to_s) rescue '-'
42
+ end
43
+ ret
44
+ end
45
+
46
+ def touch
47
+ self[:updated_at] = Time.now.utc
48
+ save columns: [:updated_at]
49
+ end
50
+
51
+ def to_h
52
+ ret = {}
53
+ for el in self.keys
54
+ ret[el] = send el
55
+ end
56
+ ret
57
+ end
58
+
59
+ def creator
60
+ self[:created_by] ? User.find(self[:created_by]) : nil
61
+ end
62
+
63
+ def parent_model
64
+ model_type.constantize.find(model_id)
65
+ end
66
+
67
+ # has?(:name, "Name is not defined") -> errors.add("Name is not defined")
68
+ # has?(:name) -> false
69
+ def has?(*args)
70
+ if args[1] && args[1].kind_of?(String)
71
+ unless self[args[0]].present?
72
+ errors.add(args[0], args[1])
73
+ return false
74
+ end
75
+ return true
76
+ end
77
+ args.each { |el| return false unless self[el].present? }
78
+ true
79
+ end
80
+
81
+ def unique?(field)
82
+ select(field).xwhere('id<>?', id).count == 0
83
+ end
84
+
85
+ def save!
86
+ save
87
+ end
88
+ end
89
+
90
+ module DatasetMethods
91
+ end
92
+ end
93
+
94
+
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel::Model.dataset_module do
4
+ def random
5
+ order(Sequel.lit('random()'))
6
+ end
7
+
8
+ def xselect text
9
+ select Sequel.lit text
10
+ end
11
+
12
+ def xorder text
13
+ order Sequel.lit text
14
+ end
15
+
16
+ def xfrom text
17
+ from Sequel.lit text
18
+ end
19
+
20
+ def xwhere hash_or_string, *args
21
+ return where(Sequel.lit("coalesce(%s,'')!=''" % hash_or_string)) if hash_or_string.is_a?(Symbol)
22
+ return where(Sequel.lit(hash_or_string, *args)) if hash_or_string.is_a?(String)
23
+
24
+ # check if we do where in a array
25
+ if hash_or_string.class == Hash
26
+ key = hash_or_string.keys.first
27
+
28
+ if model.db_schema[key][:db_type].include?('[]')
29
+ value = hash_or_string.values.first
30
+
31
+ if value.is_a?(Array)
32
+ # { skills: ['ruby', 'perl'] }, 'or'
33
+ # { skills: ['ruby', 'perl'] }, 'and'
34
+ join_type = args.first or die('Define join type for xwhere array search ("or" or "and")')
35
+
36
+ data = value.map do |v|
37
+ val =
38
+ if v.is_a?(Integer)
39
+ v
40
+ else
41
+ v = v.gsub(/['"]/,'')
42
+ v = "'%s'" % v
43
+ end
44
+
45
+ "%s=any(#{key})" % v
46
+ end
47
+ .join(' %s ' % join_type)
48
+
49
+ return where(Sequel.lit(data))
50
+ else
51
+ # skills: 'ruby'
52
+ return where(Sequel.lit("?=any(#{key})", value))
53
+ end
54
+ end
55
+ end
56
+
57
+ q = hash_or_string.select{ |k,v| v.present? && v != 0 }
58
+ q.keys.blank? ? self : where(q)
59
+ end
60
+
61
+ def xlike search, *args
62
+ unless search.blank?
63
+ search = search.gsub(/'/,"''").downcase
64
+ where_str = []
65
+
66
+ for str in search.split(/\s+/).select(&:present?)
67
+ and_str = []
68
+ str = "%#{str}%"
69
+
70
+ for el in args
71
+ if model.db_schema[el][:db_type] == 'jsonb'
72
+ like_sql = "CAST(#{el} -> '#{Locale.current}' as text) ilike '#{str}'"
73
+ if Locale::DEFAULT != Locale.current
74
+ and_str << "(#{like_sql}) or (CAST(#{el} -> '#{Locale::DEFAULT}' as text) ilike '#{str}')"
75
+ else
76
+ and_str << like_sql
77
+ end
78
+ else
79
+ and_str << "#{el}::text ilike '#{str}'"
80
+ end
81
+ end
82
+
83
+ where_str.push '('+and_str.join(' or ')+')'
84
+ end
85
+
86
+ return where(Sequel.lit(where_str.join(' and ')))
87
+ end
88
+ self
89
+ end
90
+
91
+ def last_updated
92
+ field = model.db_schema[:updated_at] ? :updated_at : :id
93
+ order(Sequel.desc(field)).first
94
+ end
95
+
96
+ def for obj
97
+ # column_names
98
+ field_name = "#{obj.class.name.underscore}_id".to_sym
99
+ n1 = model.to_s.underscore
100
+ n2 = obj.class.to_s.underscore
101
+
102
+ cname = n1[0] < n2[0] ? n1+'_'+n2.pluralize : n2+'_'+n1.pluralize
103
+
104
+ if (cname.classify.constantize rescue false)
105
+ where Sequel.lit 'id in (select %s_id from %s where %s_id=%i)' % [n1, cname, n2, obj.id]
106
+ elsif model.db_schema["#{n2}_ids".to_sym]
107
+ return where Sequel.lit '%i=any(%s_ids)' % [obj.id, n2]
108
+ elsif model.db_schema[field_name]
109
+ return where Sequel.lit '%s=%i' % [field_name, obj.id]
110
+ elsif model.db_schema[:model_type]
111
+ return where(:model_type=>obj.class.to_s, :model_id=>obj.id)
112
+ elsif obj.class.to_s == 'User'
113
+ if obj.respond_to?(field_name)
114
+ return where Sequel.lit '%s=?' % [field_name, obj.id]
115
+ end
116
+ return where Sequel.lit 'created_by=%i' % obj.id
117
+ else
118
+ r "Unknown link for #{obj.class} (probably missing db field)"
119
+ end
120
+ end
121
+
122
+ def desc
123
+ reverse :id
124
+ end
125
+
126
+ def asc
127
+ order :id
128
+ end
129
+
130
+ def pluck field
131
+ select_map field
132
+ end
133
+
134
+ def ids
135
+ select(:id).map(&:id)
136
+ end
137
+ end
138
+
@@ -0,0 +1,52 @@
1
+ class Sequel::Model
2
+ module ClassMethods
3
+ # enums name: :steps, method: :step, field: :step_sid, default: 'o' do |list|
4
+ # list['o'] = 'Open'
5
+ # list['w'] = { name: 'Waiting', desc: '...' }.h
6
+ # end
7
+ # enums :steps
8
+ # list['o'] = 'Otvoreno'
9
+ # list['w'] = { name: 'Waiting', desc: '...' }.h
10
+ # end
11
+ # enums :steps, values: { 'o'=>'Open', 'w'=>'Waiting' }
12
+ # enums :kinds, ['string', 'boolean', 'textarea']
13
+ def enums name, opts={}, &block
14
+ if opts.class == Array
15
+ opts = {
16
+ values: opts,
17
+ field: false
18
+ }
19
+ end
20
+
21
+ opts[:default] ||= opts.first if opts[:values].class == Array
22
+
23
+ values = opts[:values] || {}.tap { |_| block.call(_) }
24
+ values = values.inject({}) { |h, (k,v)| h[k.to_s] = v; h }
25
+
26
+ opts[:method] ||= name.to_s.singularize
27
+ opts[:default] = values.keys.first unless opts.key?(:default)
28
+ opts[:default] = opts[:default].to_s
29
+
30
+ unless opts[:field].class == FalseClass
31
+ unless opts[:field]
32
+ opts[:field] = opts[:method] + '_id'
33
+ opts[:field] = opts[:method] + '_sid' unless db_schema[opts[:field].to_sym]
34
+ end
35
+
36
+ raise NameError.new('Field %s not found for enums %s' % [opts[:field], name]) unless db_schema[opts[:field].to_sym]
37
+
38
+ define_method(opts[:method]) do
39
+ value = send(opts[:field]).or opts[:default]
40
+ return unless value.present?
41
+ values[value.to_s] || raise('Key "%s" not found' % value)
42
+ end
43
+ end
44
+
45
+ # this is class method that will list all options
46
+ define_singleton_method(name) do |id=nil|
47
+ id ? values[id] : values
48
+ end
49
+ end
50
+ end
51
+ end
52
+