lux-fw 0.1.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/.version +1 -0
  3. data/bin/cli/am +250 -0
  4. data/bin/cli/assets +37 -0
  5. data/bin/cli/console +50 -0
  6. data/bin/cli/dev +1 -0
  7. data/bin/cli/eval +15 -0
  8. data/bin/cli/exceptions +62 -0
  9. data/bin/cli/generate +82 -0
  10. data/bin/cli/get +5 -0
  11. data/bin/cli/nginx +28 -0
  12. data/bin/cli/production +1 -0
  13. data/bin/cli/routes +12 -0
  14. data/bin/cli/server +1 -0
  15. data/bin/cli/stat +1 -0
  16. data/bin/forever +65 -0
  17. data/bin/job_que +39 -0
  18. data/bin/lux +87 -0
  19. data/bin/txt/nginx.conf +29 -0
  20. data/bin/txt/siege-and-puma.txt +3 -0
  21. data/lib/common/base32.rb +47 -0
  22. data/lib/common/before_and_after.rb +71 -0
  23. data/lib/common/class_attributes.rb +66 -0
  24. data/lib/common/class_method_params.rb +94 -0
  25. data/lib/common/crypt.rb +66 -0
  26. data/lib/common/folder_model.rb +50 -0
  27. data/lib/common/generic_model.rb +62 -0
  28. data/lib/common/policy.rb +54 -0
  29. data/lib/common/string_base.rb +49 -0
  30. data/lib/common/url.rb +171 -0
  31. data/lib/lux/api/api.rb +150 -0
  32. data/lib/lux/api/lib/application_api.rb +19 -0
  33. data/lib/lux/api/lib/doc_builder.rb +18 -0
  34. data/lib/lux/api/lib/dsl.rb +73 -0
  35. data/lib/lux/api/lib/model_api.rb +145 -0
  36. data/lib/lux/api/lib/rescue.rb +18 -0
  37. data/lib/lux/cache/cache.rb +71 -0
  38. data/lib/lux/cache/lib/memcached.rb +3 -0
  39. data/lib/lux/cache/lib/null.rb +23 -0
  40. data/lib/lux/cache/lib/ram.rb +38 -0
  41. data/lib/lux/cell/cell.rb +260 -0
  42. data/lib/lux/config/config.rb +88 -0
  43. data/lib/lux/controller/controller.rb +185 -0
  44. data/lib/lux/controller/lib/nav.rb +77 -0
  45. data/lib/lux/controller/lib/plugs.rb +10 -0
  46. data/lib/lux/delayed_job/delayed_job.rb +44 -0
  47. data/lib/lux/delayed_job/lib/memory.rb +14 -0
  48. data/lib/lux/delayed_job/lib/nsq.rb +3 -0
  49. data/lib/lux/delayed_job/lib/postgre.rb +6 -0
  50. data/lib/lux/delayed_job/lib/redis.rb +19 -0
  51. data/lib/lux/error/error.rb +75 -0
  52. data/lib/lux/helper/helper.rb +109 -0
  53. data/lib/lux/html/html.rb +3 -0
  54. data/lib/lux/html/lib/form.rb +81 -0
  55. data/lib/lux/html/lib/input.rb +71 -0
  56. data/lib/lux/html/lib/input_types.rb +277 -0
  57. data/lib/lux/lux.rb +164 -0
  58. data/lib/lux/mailer/mailer.rb +73 -0
  59. data/lib/lux/page/lib/encrypt_params.rb +44 -0
  60. data/lib/lux/page/lib/flash.rb +49 -0
  61. data/lib/lux/page/lib/static_file.rb +97 -0
  62. data/lib/lux/page/page.rb +271 -0
  63. data/lib/lux/rescue_from/rescue_from.rb +61 -0
  64. data/lib/lux/template/template.rb +95 -0
  65. data/lib/lux-fw.rb +48 -0
  66. data/lib/overload/array.rb +52 -0
  67. data/lib/overload/blank.rb +62 -0
  68. data/lib/overload/date.rb +58 -0
  69. data/lib/overload/file.rb +14 -0
  70. data/lib/overload/hash.rb +86 -0
  71. data/lib/overload/hash_wia.rb +282 -0
  72. data/lib/overload/inflections.rb +199 -0
  73. data/lib/overload/integer.rb +19 -0
  74. data/lib/overload/module.rb +10 -0
  75. data/lib/overload/nil.rb +8 -0
  76. data/lib/overload/object.rb +77 -0
  77. data/lib/overload/string.rb +89 -0
  78. data/lib/overload/string_inflections.rb +7 -0
  79. data/lib/overload/struct.rb +5 -0
  80. data/lib/plugins/assets/assets_plug.rb +26 -0
  81. data/lib/plugins/assets/helper_module_adapter.rb +49 -0
  82. data/lib/plugins/assets/init.rb +4 -0
  83. data/lib/plugins/db_helpers/array_and_hstore.rb +64 -0
  84. data/lib/plugins/db_helpers/arrays_and_tags.rb +23 -0
  85. data/lib/plugins/db_helpers/before_save.rb +44 -0
  86. data/lib/plugins/db_helpers/cached_find_by.rb +45 -0
  87. data/lib/plugins/db_helpers/class_and_instance.rb +120 -0
  88. data/lib/plugins/db_helpers/dataset_plugin.rb +101 -0
  89. data/lib/plugins/db_helpers/filter_wrappers.rb +21 -0
  90. data/lib/plugins/db_helpers/link_plugin.rb +95 -0
  91. data/lib/plugins/db_helpers/localize_plugin.rb +57 -0
  92. data/lib/plugins/db_helpers/primary_keys.rb +36 -0
  93. data/lib/plugins/db_helpers/typero_attributes.rb +69 -0
  94. data/lib/plugins/db_logger/init.rb +18 -0
  95. data/lib/plugins/db_logger/lux_response_adapter.rb +9 -0
  96. data/lib/plugins/paginate/helper.rb +32 -0
  97. data/lib/plugins/paginate/sequel_adapter.rb +18 -0
  98. data/lib/vendor/mini_assets/mini_asset/base.rb +167 -0
  99. data/lib/vendor/mini_assets/mini_asset/css.rb +38 -0
  100. data/lib/vendor/mini_assets/mini_asset/js.rb +38 -0
  101. data/lib/vendor/mini_assets/mini_asset.rb +31 -0
  102. data/lib/vendor/oauth/lib/facebook.rb +35 -0
  103. data/lib/vendor/oauth/lib/github.rb +37 -0
  104. data/lib/vendor/oauth/lib/google.rb +41 -0
  105. data/lib/vendor/oauth/lib/linkedin.rb +41 -0
  106. data/lib/vendor/oauth/lib/stackexchange.rb +37 -0
  107. data/lib/vendor/oauth/lib/twitter.rb +41 -0
  108. data/lib/vendor/oauth/oauth.rb +46 -0
  109. metadata +334 -0
@@ -0,0 +1,26 @@
1
+ # plug :local_assets
2
+ # /compiled_asset/www/js/pjax.coffee
3
+ # /raw_asset/www/js/pjax.coffee
4
+ class Lux::Controller
5
+ def lux_assets_plug
6
+
7
+ # only allow clear in dev
8
+ if Lux.page.no_cache?
9
+ puts '* Clearing assets from ./tmp/assets'
10
+ `rm -rf ./tmp/assets`
11
+ end
12
+
13
+ path = nav.path.join('/')
14
+
15
+ if nav.root == 'compiled_asset'
16
+ asset = MiniAsset.create(path)
17
+ page.content_type asset.content_type
18
+ page.body asset.compiled_data
19
+ elsif nav.root == 'raw_asset'
20
+ Lux.error "You can watch raw files only in development" unless Lux.dev?
21
+
22
+ file = Lux.root.join('app/assets/%s' % path)
23
+ body file.exist? ? file.read : "error: File not found"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,49 @@
1
+ # export to all templates
2
+ # = asset 'www/index.scss'
3
+ # = asset 'www/index.coffee'
4
+ module DefaultHelper
5
+ def asset_include path
6
+ raise ArgumentError.new("Path can't be empty") if path.empty?
7
+
8
+ url = if path.starts_with?('/') || path.include?('//')
9
+ path
10
+ else
11
+ '/compiled_asset/%s' % path
12
+ end
13
+
14
+ ext = url.split('?').first.split('.').last
15
+
16
+ if ['coffee', 'js'].include?(ext)
17
+ %[<script src="#{url}"></script>]
18
+ else
19
+ %[<link rel="stylesheet" href="#{url}" />]
20
+ end
21
+ end
22
+
23
+ # builds full asset path based on resource extension
24
+ # asset('main/index.coffee')
25
+ # will render 'app/assets/main/index.coffee' as http://aset.path/assets/main-index-md5hash.js
26
+ def asset file, dev_file=nil
27
+ # return second link if it is defined and we are in dev mode
28
+ return asset_include dev_file if dev_file && Lux.dev?
29
+
30
+ # return internet links
31
+ return asset_include file if file.starts_with?('/') || file.starts_with?('http')
32
+
33
+ # return asset link in production or faile unless able
34
+ unless Lux.config(:compile_assets)
35
+ mfile = MiniAsset.manifest['files'][file]
36
+ raise 'Compiled asset link for "%s" not found in manifest.json' % file if mfile.empty?
37
+ return asset_include Lux.config.assets_root.to_s + mfile
38
+ end
39
+
40
+ # try to create list of incuded files and show every one of them
41
+ data = []
42
+ asset = MiniAsset.create file
43
+ for file in asset.files
44
+ data.push asset_include file
45
+ end
46
+
47
+ data.join($/)
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ require 'open3'
2
+
3
+ ['./tmp', './public', './public/assets', './tmp/assets'].each { |d| `mkdir #{d}` unless Dir.exist?(d) }
4
+
@@ -0,0 +1,64 @@
1
+ # inits and saves postgre
2
+ # string and integer arrays
3
+ # hstore as hash with indifferent access to keys
4
+
5
+ module Sequel::Plugins::LuxArrayAndHstore
6
+ module ClassMethods
7
+ end
8
+
9
+ module DatasetMethods
10
+ end
11
+
12
+ module InstanceMethods
13
+ # set right values on
14
+ def after_initialize
15
+ db_schema.each do |field, schema|
16
+ type = schema[:db_type]
17
+ if type.include?('[]') && !self[field].is_a?(Array)
18
+ self[field] = self[field].to_s.gsub(/^\{|\}$/, '').split(',')
19
+ self[field] = self[field].map(&:to_i) if schema[:type] == :integer
20
+ elsif type == 'jsonb'
21
+ self[field] ||= {}
22
+ self[field] = HashWithIndifferentAccess.new(JSON.load(self[field]) || {})
23
+ end
24
+ end
25
+ end
26
+
27
+ def before_save
28
+ @_array_hash_cache_fields = {}
29
+
30
+ db_schema.each do |field, schema|
31
+ if schema[:db_type].include?('[]')
32
+ @_array_hash_cache_fields[field] = self[field].dup
33
+
34
+ data = self[field].to_a
35
+ data = data.map(&:to_i) if schema[:type] == :integer
36
+
37
+ db_data = data.to_json
38
+ db_data[0,1] = '{'
39
+ db_data[-1,1] = '}'
40
+ self[field] = db_data
41
+ elsif ['json', 'jsonb'].index(schema[:db_type])
42
+ @_array_hash_cache_fields[field] = self[field].dup
43
+
44
+ # we use this to convert symbols in keys to strings
45
+ self[field] = JSON.load(self[field].to_json).to_json
46
+ elsif schema[:db_type] == 'boolean'
47
+ self[field] = false if self[field] == 0
48
+ self[field] = true if self[field] == 1
49
+ end
50
+ end
51
+
52
+ super
53
+ end
54
+
55
+ def after_save
56
+ @_array_hash_cache_fields.each { |k, v| self[k] = v }
57
+
58
+ super
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ Sequel::Model.plugin :lux_array_and_hstore
@@ -0,0 +1,23 @@
1
+ module Sequel::Plugins::LuxArrays
2
+ module ClassMethods
3
+ end
4
+
5
+ module DatasetMethods
6
+ # def tagged_with(tag=nil, field=:tags)
7
+ # return self unless tag
8
+ # where("? = any (#{field})", tag)
9
+ # end
10
+
11
+ # only postgree
12
+ # Bucket.can.all_tags -> can_tags mora biti zadnji
13
+ def all_tags(field=:tags, *args)
14
+ sqlq = sql.split(' FROM ')[1]
15
+ sqlq = "select lower(unnest(#{field})) as tag FROM " + sqlq
16
+ sqlq = "select tag as name, count(tag) as cnt from (#{sqlq}) as tags group by tag order by cnt desc"
17
+ DB.fetch(sqlq).map(&:h).or([])
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ Sequel::Model.plugin :lux_arrays
@@ -0,0 +1,44 @@
1
+ module Sequel::Plugins::LuxBeforeSave
2
+ module InstanceMethods
3
+ def validate
4
+ for field in columns
5
+ schema = db_schema[field]
6
+
7
+ # alert, no trim on to big field length
8
+ if schema[:max_length] && self[field] && self[field].length > schema[:max_length]
9
+ msg = 'Field "%s" max length is %s, got %d' % [field, schema[:max_length], self[field].length]
10
+ errors.add(field, msg)
11
+ end
12
+ end
13
+ super
14
+ end
15
+
16
+ def before_save
17
+ # basic security
18
+ self[:created_at] = Time.now.utc if ! self[:id] && respond_to?(:created_at)
19
+ self[:updated_at] = Time.now.utc if respond_to?(:updated_at)
20
+
21
+ if respond_to?(:created_by) || respond_to?(:updated_by)
22
+ errors.add(:base, 'You have to be registered to save data') unless User.current
23
+ end
24
+
25
+ for f in [:created_by, :updated_by]
26
+ if respond_to?(f) && !self[:id] && !self[f]
27
+ raise UnauthorizedError, "Current user not defined, can't set :#{f} on #{self.class}" unless User.current
28
+ self[f] = User.current.id
29
+ end
30
+ end
31
+
32
+ Lux.cache.delete(cache_key)
33
+ super
34
+ end
35
+
36
+
37
+ def before_destroy
38
+ Lux.cache.delete(cache_key)
39
+ super
40
+ end
41
+ end
42
+ end
43
+
44
+ Sequel::Model.plugin :lux_before_save
@@ -0,0 +1,45 @@
1
+ # module Sequel::Plugins::LuxCachedFindBy
2
+
3
+ # module ClassMethods
4
+
5
+ # @@cached_find_by ||= {}
6
+ # @@cache_all_for ||= {}
7
+
8
+ # def find id
9
+ # return nil if id.blank?
10
+ # key = "#{self}/#{id}"
11
+ # Lux.cache.thread(key) { where(id:id).first }
12
+ # end
13
+
14
+ # def find_by what
15
+ # where(what).first
16
+ # end
17
+
18
+ # ###
19
+
20
+ # # in top of the class, defines cache to be run in memory
21
+ # # cache_all_for 10.minutes
22
+ # def cache_all_for duration
23
+ # @@cache_all_for[self.to_s] = duration
24
+ # end
25
+
26
+ # # City.cached_find_by(120, code: nav.root)
27
+ # def cached_find_by duration, opts
28
+ # key = opts.keys.first
29
+ # value = opts.values.first
30
+ # time = Time.now.to_i
31
+
32
+ # @@cached_find_by[self.to_s] = nil if @@cached_find_by[self.to_s] && (@@cached_find_by[self.to_s][0] + duration) < time
33
+ # @@cached_find_by[self.to_s] ||= [time, all]
34
+
35
+ # for el in @@cached_find_by[self.to_s][1]
36
+ # return el.dup if el[key] == value
37
+ # end
38
+
39
+ # nil
40
+ # end
41
+
42
+ # end
43
+ # end
44
+
45
+ # Sequel::Model.plugin :lux_cached_find_by
@@ -0,0 +1,120 @@
1
+ # http://ricostacruz.com/cheatsheets/sequel.html
2
+ # http://sequel.jeremyevans.net/rdoc/files/doc/model_plugins_rdoc.html
3
+
4
+ module Sequel::Plugins::LuxClassAndInstance
5
+
6
+ module ClassMethods
7
+ [:desc, :asc, :pluck, :ids].each do |m|
8
+ Sequel::Plugins.def_dataset_methods self, m
9
+ end
10
+
11
+ def find id
12
+ return nil if id.blank?
13
+ key = "#{self}/#{id}"
14
+ Lux.cache.thread(key) { where(id:id).first }
15
+ end
16
+
17
+ def find_by what
18
+ where(what).first
19
+ end
20
+
21
+ # active record like define scope
22
+ # http://sequel.jeremyevans.net/rdoc/classes/Sequel/Model/ClassMethods.html
23
+ def scope name, body=nil, &block
24
+ block ||= body
25
+ dataset_module{define_method(name, &block)}
26
+ end
27
+
28
+ # instance scope, same as scope but runs on instance
29
+ # iscope(:notes) { Note.where(created_by:id) }
30
+ def iscope name, body=nil, &block
31
+ block ||= body
32
+ define_method(name, &block)
33
+ end
34
+
35
+ def where_or_new filter
36
+ where(filter).first || new(filter)
37
+ end
38
+
39
+ def where_or_create filter
40
+ where(filter).first || create(filter)
41
+ end
42
+
43
+ end
44
+
45
+ module InstanceMethods
46
+ def cache_key
47
+ "#{self.class}/#{id}"
48
+ end
49
+
50
+ def attributes
51
+ ret = {}
52
+ for el in columns
53
+ ret[el.to_s] = send(el.to_s) rescue '-'
54
+ end
55
+ ret
56
+ end
57
+
58
+ def touch
59
+ save
60
+ end
61
+
62
+ def to_h
63
+ ret = {}
64
+ for el in self.keys
65
+ ret[el] = send el
66
+ end
67
+ ret
68
+ end
69
+
70
+ def creator
71
+ self[:created_by] ? User.find(self[:created_by]) : nil
72
+ end
73
+
74
+ def parent_model
75
+ model_type.constantize.find(model_id)
76
+ end
77
+
78
+ # has?(:name, "Name is not defined") -> errors.add("Name is not defined")
79
+ # has?(:name) -> false
80
+ def has?(*args)
81
+ if args[1] && args[1].kind_of?(String)
82
+ unless self[args[0]].present?
83
+ errors.add(args[0], args[1])
84
+ return false
85
+ end
86
+ return true
87
+ end
88
+ args.each { |el| return false unless self[el].present? }
89
+ true
90
+ end
91
+
92
+ def unique?(field)
93
+ select(field).xwhere('id<>?', id).count == 0
94
+ end
95
+
96
+ def save!
97
+ save
98
+ end
99
+ end
100
+
101
+ module DatasetMethods
102
+ def desc
103
+ reverse :id
104
+ end
105
+
106
+ def asc
107
+ order :id
108
+ end
109
+
110
+ def pluck field
111
+ select_map field
112
+ end
113
+
114
+ def ids
115
+ select(:id).map(&:id)
116
+ end
117
+ end
118
+ end
119
+
120
+ Sequel::Model.plugin :lux_class_and_instance
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sequel::Model
4
+
5
+ module ClassMethods
6
+ [:xselect, :xorder, :xfrom, :xwhere, :xlike, :last_updated, :qs_search, :for].each do |m|
7
+ Sequel::Plugins.def_dataset_methods self, m
8
+ end
9
+ end
10
+
11
+ module DatasetMethods
12
+ def xselect text
13
+ select Sequel.lit text
14
+ end
15
+
16
+ def xorder text
17
+ order Sequel.lit text
18
+ end
19
+
20
+ def xfrom text
21
+ from Sequel.lit text
22
+ end
23
+
24
+ def xwhere hash_or_string, *args
25
+ return where(Sequel.lit("coalesce(%s,'')!=''" % hash_or_string)) if hash_or_string.is_a?(Symbol)
26
+ return where(Sequel.lit(hash_or_string, *args)) if hash_or_string.is_a?(String)
27
+
28
+ q = hash_or_string.select{ |k,v| v.present? && v != 0 }
29
+ q.keys.blank? ? self : where(q)
30
+ end
31
+
32
+ def xlike search, *args
33
+ unless search.blank?
34
+ search = search.gsub(/'/,"''").downcase
35
+ where_str = []
36
+
37
+ for str in search.split(/\s+/).select(&:present?)
38
+ and_str = []
39
+ str = "%#{str}%"
40
+
41
+ for el in args
42
+ if model.db_schema[el][:db_type] == 'jsonb'
43
+ like_sql = "CAST(#{el} -> '#{Locale.current}' as text) ilike '#{str}'"
44
+ if Locale::DEFAULT != Locale.current
45
+ and_str << "(#{like_sql}) or (CAST(#{el} -> '#{Locale::DEFAULT}' as text) ilike '#{str}')"
46
+ else
47
+ and_str << like_sql
48
+ end
49
+ else
50
+ and_str << "#{el}::text ilike '#{str}'"
51
+ end
52
+ end
53
+
54
+ where_str.push '('+and_str.join(' or ')+')'
55
+ end
56
+
57
+ return where(Sequel.lit(where_str.join(' and ')))
58
+ end
59
+ self
60
+ end
61
+
62
+ def last_updated
63
+ field = model.db_schema[:updated_at] ? :updated_at : :id
64
+ order(Sequel.desc(field)).first
65
+ end
66
+
67
+ # search in array of qs arguments
68
+ def qs_search *args
69
+ for el in args
70
+ dataset = where("#{el}=?", Lux.page.params[el]) if !Lux.page.params[el].blank? && model.db_schema[el]
71
+ end
72
+ dataset || self
73
+ end
74
+
75
+ def for obj
76
+ # column_names
77
+ field_name = "#{obj.class.name.underscore}_id".to_sym
78
+ n1 = model.to_s.underscore
79
+ n2 = obj.class.to_s.underscore
80
+
81
+ cname = n1[0] < n2[0] ? n1+'_'+n2.pluralize : n2+'_'+n1.pluralize
82
+
83
+ if (cname.classify.constantize rescue false)
84
+ where Sequel.lit 'id in (select %s_id from %s where %s_id=%i)' % [n1, cname, n2, obj.id]
85
+ elsif model.db_schema["#{n2}_ids".to_sym]
86
+ return where Sequel.lit '%i=any(%s_ids)' % [obj.id, n2]
87
+ elsif model.db_schema[field_name]
88
+ return where Sequel.lit '%s=%i' % [field_name, obj.id]
89
+ elsif model.db_schema[:model_type]
90
+ return where(:model_type=>obj.class.to_s, :model_id=>obj.id)
91
+ elsif obj.class.to_s == 'User'
92
+ if obj.respond_to?(field_name)
93
+ return where Sequel.lit '%s=?' % [field_name, obj.id]
94
+ end
95
+ return where Sequel.lit 'created_by=%i' % obj.id
96
+ else
97
+ r "Unknown link for #{obj.class} (probably missing db field)"
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,21 @@
1
+ module Sequel::Plugins::FilterWrappers
2
+ module ClassMethods
3
+
4
+ def validate &block
5
+ define_method :validate do
6
+ block.call
7
+ super
8
+ end
9
+ end
10
+
11
+ def before_save &block
12
+ define_method :before_save do
13
+ block.call
14
+ super
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+
21
+ Sequel::Model.plugin :filter_wrappers
@@ -0,0 +1,95 @@
1
+ # @notes.preload(:bucket)
2
+
3
+ # class Array
4
+ # def preload(name)
5
+ # field = "#{name.to_s.singularize}_id".to_sym
6
+ # ids = self.map{ |o| o[field] }.uniq
7
+ # buckets = name.to_s.classify.constantize.where(id:ids).to_a
8
+
9
+ # for o in self
10
+ # o.send "#{name}=", buckets.select{ |el| o.id==el.id }[0]
11
+ # end
12
+ # end
13
+ # end
14
+
15
+ module Sequel::Plugins::LuxLink
16
+ module ClassMethods
17
+
18
+ # link :genders, default:1, collection:[ [1, 'male'], [2, 'Female'] ]
19
+ # link :genders, collection: { 1=>'male', 2=>'Female' }
20
+ # Object.genders
21
+ # @object.gender
22
+ def link(name, opts={})
23
+ name_s = name.to_s.singularize
24
+ name_p = name.to_s.pluralize
25
+
26
+ if opts[:collection]
27
+ field = opts[:field] || "#{name_s}_id"
28
+
29
+ class_eval %[
30
+ def #{to_s}.#{name_p}
31
+ #{opts[:collection].to_json}
32
+ end
33
+
34
+ def #{name_s}
35
+ val = self[:#{field}] || #{opts[:default] || 'nil'}
36
+ return unless val
37
+ for k, v in #{to_s}.#{name_p}
38
+ return v if k == val
39
+ end
40
+ nil
41
+ end
42
+ ]
43
+ return
44
+ end
45
+
46
+ klass = opts[:class] ? opts[:class].to_s : name.to_s.singularize.classify
47
+
48
+ # link :country, class:Country -> many_to_one :country
49
+ if name.to_s == name_s
50
+ class_eval %[
51
+ def #{name_s}
52
+ @#{name_s}_cached ||= #{klass}.find(#{name_s}_id)
53
+ end
54
+ def #{name_s}=(object)
55
+ @#{name_s}_cached = object
56
+ end
57
+ ]
58
+
59
+ # link :cities
60
+ # postgre integer array, has to be in form #{name.singularize}_ids
61
+ # city_ids = [1,2,4]
62
+ # cities -> [<City:1>, <City:2>, <City:4>]
63
+ elsif new.respond_to?("#{name_s}_ids")
64
+ # puts "* #{to_s}.link :#{name} -> inline #{name_s}_ids".yellow
65
+ class_eval %[
66
+ def #{name_p}
67
+ return [] if #{name_s}_ids.blank?
68
+ ids = #{name_s}_ids.join(',')
69
+ #{klass}.where(Sequel.lit('id in ('+ids+')'))
70
+ end
71
+ ]
72
+
73
+ # link :countries -> one_to_many :countries
74
+ else
75
+ # one_to_many name, opts
76
+ field = "#{to_s.tableize.singularize}_id"
77
+ if klass.constantize.db_schema[field.to_sym]
78
+ comm = "#{klass}.where(#{field}:id).default"
79
+ else
80
+ # we have a link table
81
+ cname = klass[0,1] > to_s[0,1] ? "#{to_s}#{klass}" : "#{klass}#{to_s}"
82
+ comm = "#{klass}.xwhere('id in (select #{klass.tableize.singularize}_id from #{cname.tableize} where #{to_s.tableize.singularize}_id=?)', id)"
83
+ puts "* #{to_s}.link :#{name} -> #{comm}"
84
+ end
85
+ class_eval %[
86
+ def #{name_p}
87
+ #{comm}
88
+ end
89
+ ]
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ Sequel::Model.plugin :lux_link
@@ -0,0 +1,57 @@
1
+ module Sequel::Plugins::LuxLocalize
2
+ module ClassMethods
3
+ # in any modal
4
+ # localize :name
5
+ # to make :name multilang
6
+ # and to create :name_en, :name_de, ...
7
+ def localize(name)
8
+ unless db_schema[name]
9
+ puts "Field [:#{name}] does not exist in #{self.to_s}".red
10
+ end
11
+
12
+ unless (db_schema[name][:db_type] rescue nil) == 'jsonb'
13
+ puts "Field [:#{name}] in #{self.to_s} is not of type [jsonb]".red
14
+ end
15
+
16
+ class_eval %[
17
+ def #{name}(locale=nil)
18
+ field = self[:#{name}]
19
+ field ||= {}
20
+ locale ||= Locale.current
21
+ return field[locale] if field[locale] && !field[locale].start_with?('?')
22
+ val = field[Locale::DEFAULT].to_s
23
+ Lux.dev? ? '?'+val : val
24
+ end
25
+
26
+ def #{name}=(value)
27
+ self[:#{name}] ||= {} # if self[:#{name}].class.to_s != 'Hash'
28
+ self[:#{name}][Locale.current] = value
29
+ end
30
+ ]
31
+
32
+ for lang in Locale.all
33
+ class_eval %[
34
+ def #{name}_#{lang}
35
+ #{name}('#{lang}')
36
+ end
37
+
38
+ def #{name}_#{lang}=(value)
39
+ value = nil if value.start_with?('?')
40
+ self[:#{name}] ||= {}
41
+ self[:#{name}]['#{lang}'] = value
42
+ end
43
+ ]
44
+ end
45
+ end
46
+ end
47
+
48
+ module InstanceMethods
49
+
50
+ end
51
+
52
+ def DatasetMethods
53
+
54
+ end
55
+ end
56
+
57
+ Sequel::Model.plugin :lux_localize
@@ -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