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
data/bin/lux ADDED
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if File.exists?('lux-fw.gemspec')
4
+ puts 'Cant run in lux folder'
5
+ exit
6
+ end
7
+
8
+ require 'colorize'
9
+ require 'optparse'
10
+ require 'awesome_print'
11
+ require 'dotenv'
12
+
13
+ module LuxCli
14
+ extend self
15
+
16
+ def run what
17
+ puts what.green
18
+ system what
19
+ end
20
+
21
+ def die text
22
+ puts text.red
23
+ exit
24
+ end
25
+ end
26
+
27
+ Dotenv.load
28
+
29
+ @port = 3000
30
+ lux_root = File.expand_path '../..', __FILE__
31
+
32
+ puts 'Lux (%s, v%s)' % [lux_root, File.read('%s/.version' % lux_root)] unless ARGV[0]
33
+
34
+ ARGV.options do |opts|
35
+ opts.on("-e", "--environment=val", "RACK_ENV enviroment", String) do |v|
36
+ v = 'production' if v[0,1] == 'p'
37
+ v = 'development' if v[0,1] == 'd'
38
+ ENV['RACK_ENV'] = v if v
39
+ end
40
+ opts.on("-p", "--port=val", "port (3000)", String) do |v|
41
+ @port = v.to_i
42
+ end
43
+ end.parse!
44
+
45
+ commands = {}
46
+ commands['am'] = 'auto migrate'
47
+ commands['assets'] = 'precompile assets and genereate mainifest.json'
48
+ commands['console'] = 'Lux console'
49
+ commands['dev'] = 'dev on port 3000 with hot reload on code change'
50
+ commands['eval'] = 'eval code or file'
51
+ commands['exceptions'] = 'show stored exceptions, last created first'
52
+ commands['generate'] = 'generates models and views based on templates'
53
+ commands['get'] = 'get local URL'
54
+ # commands['job_que'] = 'process job que with loaded enviroment'
55
+ commands['nginx'] = 'parse and install config/nginx.conf'
56
+ commands['production'] = 'run puma in production mode'
57
+ commands['routes'] = 'list routes'
58
+ commands['server'] = 'run puma development server'
59
+ commands['stat'] = 'goaccess stat to public/goaccess.html'
60
+
61
+ command = ARGV.shift
62
+
63
+ if command
64
+ for c in commands
65
+ command = c[0] if c[0] if c[0].start_with?(command)
66
+ end
67
+
68
+ if commands[command]
69
+ path = '%s/bin/cli/%s' % [lux_root, command]
70
+ load path
71
+ else
72
+ LuxCli.die "Can't find command [%s]" % command
73
+ end
74
+ else
75
+ puts "\nCommands:"
76
+ for c in commands
77
+ # puts " #{c[0].sub(/(\w)/,'[\\1]').ljust(15)} # #{c[1]}"
78
+ puts " #{c[0].ljust(15)} # #{c[1]}"
79
+ end
80
+
81
+ puts "\nExamples:"
82
+ puts ' lux c # lux console'
83
+ puts ' lux g user # lux generate user users'
84
+ puts ''
85
+ exit
86
+ end
87
+
@@ -0,0 +1,29 @@
1
+ worker_processes 1;
2
+
3
+ events {
4
+ worker_connections 1024;
5
+ }
6
+
7
+ server {
8
+ listen 80; #default deferred;
9
+ server_name .color-mont.com;
10
+ root /home/deployer/apps/app/public;
11
+ error_log /home/deployer/apps/app/log/nginx-error.log;
12
+
13
+ passenger_enabled on;
14
+ passenger_friendly_error_pages on;
15
+
16
+ location = /favicon.ico {
17
+ log_not_found off;
18
+ access_log off;
19
+ }
20
+
21
+ location ^~ /assets/ {
22
+ gzip_static on;
23
+ expires max;
24
+ add_header Cache-Control public;
25
+ }
26
+
27
+ client_max_body_size 1G;
28
+ keepalive_timeout 20;
29
+ }
@@ -0,0 +1,3 @@
1
+ puma -w 5 -t 2:2 -p 3000 -e production --preload
2
+
3
+ siege -c 200 -t 10s -q --log=./siege.log http://profile.lvh.me:3000/en
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Base32
4
+
5
+ class Chunk
6
+ def initialize(bytes)
7
+ @table = "abcdefghijklmnopqrstuvwxyz234567"
8
+ @bytes = bytes
9
+ end
10
+
11
+ def decode
12
+ bytes = @bytes.take_while {|c| c != 61} # strip padding
13
+ n = (bytes.length * 5.0 / 8.0).floor
14
+ p = bytes.length < 8 ? 5 - (n * 8) % 5 : 0
15
+ c = bytes.inject(0) {|m,o| (m << 5) + @table.index(o.chr)} >> p
16
+ (0..n-1).to_a.reverse.collect {|i| ((c >> i * 8) & 0xff).chr} # bla
17
+ end
18
+
19
+ def encode
20
+ n = (@bytes.length * 8.0 / 5.0).ceil
21
+ p = n < 8 ? 5 - (@bytes.length * 8) % 5 : 0
22
+ c = @bytes.inject(0) {|m,o| (m << 8) + o} << p
23
+ [(0..n-1).to_a.reverse.collect {|i| @table[(c >> i * 5) & 0x1f].chr},
24
+ ("=" * (8-n))]
25
+ end
26
+ end
27
+
28
+ class << self
29
+ def chunks(str, size)
30
+ result = []
31
+ bytes = str.bytes
32
+ while bytes.any? do
33
+ result << Chunk.new(bytes.take(size))
34
+ bytes = bytes.drop(size)
35
+ end
36
+ result
37
+ end
38
+
39
+ def encode(str)
40
+ chunks(str, 5).collect(&:encode).flatten.join.sub(/=+$/,'')
41
+ end
42
+
43
+ def decode(str)
44
+ chunks(str, 8).collect(&:decode).flatten.join
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # in some class
4
+ # def self.before(proc=nil, &block)
5
+ # BeforeAndAfter.add(self, caller[0], :before, proc || block)
6
+ # end
7
+ # or
8
+ # BeforeAndAfter.define self, :before, :after
9
+ #
10
+ # then to execute
11
+ # instance_object = SomeClass.new
12
+ # BeforeAndAfter.execute(instance_object, :before)
13
+ # or
14
+ # before do
15
+ # ...
16
+ # end
17
+ # or
18
+ # def before
19
+ # super
20
+ # ...
21
+ # end
22
+ #
23
+ # logic is very simple, keep all pointers to all blocks in one class, resolve and execute as needed
24
+ # we keep methods and ponters in different hashes to allow hot reload while development
25
+
26
+ module BeforeAndAfter
27
+ extend self
28
+
29
+ @@methods = {}
30
+ @@pointers = {}
31
+
32
+ def add(klass, unique_id, action, method)
33
+ key = Crypt.md5(unique_id)
34
+ @@pointers[key] = method
35
+ @@methods[klass.to_s] ||= {}
36
+ @@methods[klass.to_s][action] ||= []
37
+ @@methods[klass.to_s][action].push(key) unless @@methods[klass.to_s][action].index(key)
38
+ end
39
+
40
+ def execute(instance_object, action)
41
+ instance_object.send(action)
42
+
43
+ # execute for self and parent
44
+ for name in instance_object.class.ancestors.reverse.map(&:to_s)
45
+ next if name == 'Object'
46
+
47
+ if @@methods[name] && @@methods[name][action]
48
+ for el in @@methods[name][action].map { |o| @@pointers[o] }
49
+ if el.kind_of?(Symbol)
50
+ instance_object.send(el)
51
+ else
52
+ instance_object.instance_exec &el
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def define(klass, *args)
60
+ for action in args
61
+ klass.class_eval %[
62
+ def #{action}
63
+ end
64
+
65
+ def self.#{action}(proc=nil, opts={}, &block)
66
+ BeforeAndAfter.add(self, caller[0], :#{action}, proc || block)
67
+ end
68
+ ]
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ClassAttributes.define klass, :layout, 'default_value_optional'
4
+ # klass.layout -> get value
5
+ # klass.layout = value -> set value
6
+
7
+ # class A
8
+ # ClassAttributes.define self, :layout, 'default'
9
+ # end
10
+ # class B < A
11
+ # layout 'l B'
12
+ # end
13
+ # class C < B
14
+ # end
15
+ # puts A.layout # default
16
+ # puts B.layout # l B
17
+ # puts C.layout # l B
18
+
19
+ # class User
20
+ # ClassAttributes.define_in_current_thread self, :current
21
+ # end
22
+
23
+ # User.current = User.first
24
+
25
+ module ClassAttributes
26
+ extend self
27
+
28
+ CA_DEFAULTS = {}
29
+
30
+ # defines class variable
31
+ def define klass, name, default=nil, &block
32
+ raise ArgumentError, 'name must be symbol' unless name.class == Symbol
33
+
34
+ default ||= block if block
35
+
36
+ ::ClassAttributes::CA_DEFAULTS[name] = { 'Object'=>default }
37
+
38
+ klass.class.send(:define_method, name) do |*args|
39
+ root = ::ClassAttributes::CA_DEFAULTS[name]
40
+
41
+ # set and return if argument defined
42
+ return root[self.to_s] = args[0] if args.length > 0
43
+
44
+ # find value and return
45
+ self.ancestors.map(&:to_s).each do |el|
46
+ value = root[el]
47
+ if value || el == 'Object'
48
+ value = self.instance_exec(&value) if value.is_a?(Proc)
49
+ return value
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ # defines class variable in current lux thread
56
+ def current klass, name
57
+ klass.class.send(:define_method, name) do |*args|
58
+ Thread.current[:lux]['%s-%s' % [klass, name]]
59
+ end
60
+
61
+ klass.class.send(:define_method, '%s=' % name) do |value|
62
+ Thread.current[:lux] ||= {}
63
+ Thread.current[:lux]['%s-%s' % [klass, name]] = value
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ # allows adding of params to methods
4
+ # used in Lux::Api
5
+ #
6
+ # param :model_id, type: :integer
7
+ # param :age, max: 120
8
+ # def show
9
+ # end
10
+
11
+ # class ModelApi
12
+ # class << self
13
+ # def param *args
14
+ # ClassMethodParams.add_param self, args
15
+ # end
16
+
17
+ # def method_added(m)
18
+ # ClassMethodParams.add_method self, m
19
+ # end
20
+ # end
21
+
22
+ # ###
23
+
24
+ # param :id
25
+ # param :user_id, Integer
26
+ # param :model_id, type: :integer
27
+ # param :age, max: 120
28
+ # def show
29
+
30
+ # end
31
+
32
+ # end
33
+
34
+ # class UserApi < ModelApi
35
+
36
+ # param :name
37
+ # def edit
38
+
39
+ # end
40
+
41
+ # end
42
+
43
+ # ap ClassMethodParams.params
44
+ # ap ClassMethodParams.params UserApi, :show
45
+ # ap ClassMethodParams.all UserApi
46
+
47
+ module ClassMethodParams
48
+ extend self
49
+
50
+ @params = {}
51
+ @cache = {}
52
+
53
+ def add_param klass, param, args
54
+ @cache[klass.to_s] ||= {}
55
+ @cache[klass.to_s][param] ||= []
56
+ @cache[klass.to_s][param].push args
57
+ end
58
+
59
+ def add_method klass, m
60
+ k = klass.to_s
61
+ @params[k] ||= {}
62
+ @params[k][m] = @cache[k] ? @cache[k].dup : {}
63
+ @cache = {}
64
+ end
65
+
66
+ def all_params
67
+ @params
68
+ end
69
+
70
+ # return all params method in a class
71
+ def params klass, m
72
+ return @params unless m
73
+
74
+ klass.ancestors.map(&:to_s).each do |klass|
75
+ return if klass == 'Object'
76
+ data = @params[klass]
77
+ return data[m] if data && data[m]
78
+ end
79
+
80
+ nil
81
+ end
82
+
83
+ # return all methos and all parameters for all methods in a class
84
+ def all klass
85
+ data = {}
86
+ all_methods = klass.instance_methods - Object.instance_methods
87
+ all_methods.each do |m|
88
+ data[m] = ClassMethodParams.params(klass, m)
89
+ end
90
+ data
91
+ end
92
+
93
+ end
94
+
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @author: Dino Reic
4
+ # @description:
5
+ # module for easy and convenient access to frequently used crypt operations
6
+
7
+ require 'openssl'
8
+ require 'base64'
9
+ require 'digest/md5'
10
+ require 'securerandom'
11
+
12
+ module Crypt
13
+ extend self
14
+
15
+ ALGORITHM = 'HS512'
16
+
17
+ def secret
18
+ Lux.env('SECRET')
19
+ end
20
+
21
+ def base64(str)
22
+ Base64.urlsafe_encode64(str)
23
+ end
24
+
25
+ def uid
26
+ SecureRandom.hex
27
+ end
28
+
29
+ def sha1(str)
30
+ Digest::SHA1.hexdigest(str.to_s + secret)
31
+ end
32
+
33
+ def md5(str)
34
+ Digest::MD5.hexdigest(str.to_s + secret)
35
+ end
36
+
37
+ def bc(str)
38
+ BCrypt::Password.create(str + secret)
39
+ end
40
+
41
+ # Crypt.encrypt('secret')
42
+ # Crypt.encrypt('secret', ttl:1.hour, password:'pa$$w0rd')
43
+ def encrypt(data, opts={})
44
+ diff = opts.keys - [:ttl, :password]
45
+ raise 'Unallowed key(s) found %s' % diff.join(', ') if diff.length > 0
46
+
47
+ payload = { data:data }
48
+ payload[:ttl] = Time.now.to_i + opts[:ttl] if opts[:ttl]
49
+ hmac_secret = "#{secret}#{opts.delete(:password)}"
50
+ JWT.encode payload, hmac_secret, ALGORITHM
51
+ end
52
+
53
+ # Crypt.decrypt('secret')
54
+ # Crypt.decrypt('secret', password:'pa$$w0rd')
55
+ def decrypt(token, opts={})
56
+ diff = opts.keys - [:password]
57
+ raise 'Unallowed key(s) found %s' % diff.join(', ') if diff.length > 0
58
+
59
+ hmac_secret = "#{secret}#{opts.delete(:password)}"
60
+ token_data = JWT.decode token, hmac_secret, true, { :algorithm => ALGORITHM }
61
+ data = token_data[0]
62
+ raise "Crpted data expired before #{Time.now.to_i - data['ttl']} seconds" if data['ttl'] && data['ttl'] < Time.now.to_i
63
+ data['data']
64
+ end
65
+
66
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FolderModel
4
+
5
+ class << self
6
+ def find(key)
7
+ new(key)
8
+ end
9
+
10
+ def all
11
+ Dir["db/#{self.to_s.tableize}/*.json"].map{|file| new file.split('/').last.sub('.json') }
12
+ end
13
+ end
14
+
15
+ ###
16
+
17
+ def [](key)
18
+ @data[key]
19
+ end
20
+
21
+ def []=(key, value)
22
+ @data[key] = value
23
+ end
24
+
25
+ def method_missing(name, *args)
26
+ func = name.to_s.split('=')[0].to_sym
27
+
28
+ if name.to_s.index('=')
29
+ @data[func] = args[0]
30
+ else
31
+ # raise "Field #{name} not found"
32
+ @data[func]
33
+ end
34
+ end
35
+
36
+ def save
37
+ @storage.write JSON.pretty_generate(@data)
38
+ @data
39
+ end
40
+ alias :save! :save
41
+
42
+ def initialize(key)
43
+ @key = key
44
+ @storage = Pathname.new("db/#{self.class.to_s.tableize}/#{key}.json")
45
+ @data = JSON.parse @storage.read
46
+ @data = @data.h
47
+ end
48
+
49
+ end
50
+
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # class FooBar < GenericModel
4
+ #
5
+ # values [1, 'LinkedIn'],
6
+ # [2, 'Facebook'],
7
+ # [3, 'Twitter'],
8
+ # [4, 'Google'],
9
+ # [5, 'Email'],
10
+ # [6, 'Mobile']
11
+ #
12
+ # def ico
13
+ # %{<img src="/images/type/#{code}.png" style="width:16px; height:16px; vertical-align:middle; " />}
14
+ # end
15
+ #
16
+ # end
17
+
18
+ class GenericModel
19
+
20
+ @@values = {}
21
+
22
+ class << self
23
+ def values(vals)
24
+ @@values[self.to_s] = []
25
+ vals.map { |el| add_value(el) }
26
+ end
27
+
28
+ def add_value(val)
29
+ o = new(val)
30
+ for key in val.keys
31
+ eval %[def o.#{key}; @_vals[:#{key}]; end]
32
+ end
33
+ @@values[self.to_s].push(o)
34
+ end
35
+
36
+ def find(id)
37
+ for el in all
38
+ return el if el.id == id
39
+ end
40
+ nil
41
+ end
42
+
43
+ def all
44
+ @@values[self.to_s]
45
+ end
46
+
47
+ def where(opts)
48
+ @@values[self.to_s].select{ |el| el[opts.keys[0]] == opts.values[0] }
49
+ end
50
+ end
51
+
52
+ ###
53
+
54
+ def initialize(vals)
55
+ @_vals = vals
56
+ end
57
+
58
+ def [](key)
59
+ @_vals[key]
60
+ end
61
+
62
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # base caller
4
+ # UserPolicy.new(model: @model, user: User.current).can?(:update) -> can current user update @user
5
+ # ApplicationPolicy.new(user: Lux.page.var.user).can?(:admin_login?) -> can current user login to admin
6
+
7
+ # block will capture error message and be triggered only if error is present
8
+ # ApplicationPolicy.can?(:login) { |msg| http_error 401, "Err: #{msg}".red; return 'no access' }
9
+
10
+ class Policy
11
+
12
+ def initialize hash
13
+ for k, v in hash
14
+ instance_variable_set "@#{k}", v
15
+ end
16
+ end
17
+
18
+ # pass block if you want to handle errors yourself
19
+ # return true if false if block is passed
20
+ def can? action, &block
21
+ @action = action.to_s.sub('?','') + '?'
22
+ @action = @action.to_sym
23
+
24
+ # pre check
25
+ raise RuntimeError, 'Method name not allowed' if %w(can).index(@action)
26
+ raise NoMethodError, %[Policy check "#{action}" not found in #{self.class}] unless respond_to?(@action)
27
+
28
+ call &block
29
+ end
30
+
31
+ # call has to be isolated because specific of error handling
32
+ def call &block
33
+ return true if before(@action)
34
+ return true if send(@action)
35
+ raise UnauthorizedError, 'Access disabled in policy'
36
+ rescue UnauthorizedError
37
+ error = $!.message
38
+ error += " - #{self.class}.#{@action}" if Lux.config(:show_server_errors)
39
+ raise UnauthorizedError, error unless block
40
+ block.call(error)
41
+ false
42
+ end
43
+
44
+ ###
45
+
46
+ def before action
47
+ false
48
+ end
49
+
50
+ def error message
51
+ raise UnauthorizedError, message
52
+ end
53
+
54
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StringBase
4
+ extend self
5
+
6
+ KEYS = 'bcdghjklmnpqrstvwxyz'
7
+ MULTIPLIER = 99
8
+
9
+ def encode value
10
+ value = value * MULTIPLIER
11
+ ring = Hash[KEYS.chars.map.with_index.to_a.map(&:reverse)]
12
+ base = KEYS.length
13
+ result = []
14
+ until value == 0
15
+ result << ring[ value % base ]
16
+ value /= base
17
+ end
18
+ result.reverse.join
19
+ end
20
+
21
+ def decode string
22
+ ring = Hash[KEYS.chars.map.with_index.to_a]
23
+ base = KEYS.length
24
+ ret = string.reverse.chars.map.with_index.inject(0) do |sum,(char,i)|
25
+ sum + ring[char] * (base**i)
26
+ end
27
+ raise 'Invalid decode base' if ret%MULTIPLIER>0
28
+ ret/MULTIPLIER
29
+ end
30
+
31
+ # extract ID from url
32
+ def extract(url_part)
33
+ id_str = url_part.split('-').last
34
+ return nil unless id_str
35
+ StringBase.decode(id_str) rescue nil
36
+ end
37
+ end
38
+
39
+ class Integer
40
+ def string_id
41
+ StringBase.encode self
42
+ end
43
+ end
44
+
45
+ class String
46
+ def string_id
47
+ StringBase.decode self
48
+ end
49
+ end