lux-fw 0.5.32 → 0.5.33

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/lib/README.md +30 -0
  4. data/lib/lux/README.md +10 -0
  5. data/lib/lux/application/README.md +147 -0
  6. data/lib/lux/cache/README.md +47 -0
  7. data/lib/lux/config/README.md +63 -0
  8. data/lib/lux/controller/README.md +49 -0
  9. data/lib/lux/current/README.md +14 -0
  10. data/lib/lux/delayed_job/README.md +3 -0
  11. data/lib/lux/error/README.md +41 -0
  12. data/lib/lux/event_bus/README.md +36 -0
  13. data/lib/lux/mailer/README.md +73 -0
  14. data/lib/lux/response/README.md +1 -0
  15. data/lib/lux/view/README.md +85 -0
  16. data/misc/demo/Gemfile +4 -0
  17. data/misc/demo/Rakefile +3 -0
  18. data/misc/demo/app/assets/css/main/index.scss +1 -0
  19. data/misc/demo/app/cells/cell.scss +5 -0
  20. data/misc/demo/app/cells/demo.haml +1 -0
  21. data/misc/demo/app/views/layouts/main.haml +10 -0
  22. data/misc/demo/app/views/main/root/index.haml +9 -0
  23. data/misc/demo/config.ru +5 -0
  24. data/misc/demo/public/manifest.json +5 -0
  25. data/misc/demo/tmp/assets/d-app-assets-css-main-index.scss +1 -0
  26. data/misc/demo/tmp/assets/d-app-cells-cell.scss +5 -0
  27. data/misc/demo/yarn.lock +2656 -0
  28. data/misc/lux.png +0 -0
  29. data/misc/nginx.conf +60 -0
  30. data/misc/siege-and-puma.txt +3 -0
  31. data/plugins/api/README.md +49 -0
  32. data/plugins/db/README.md +29 -0
  33. data/plugins/db/auto_migrate/db.rake +15 -0
  34. data/plugins/exceptions/exceptions.rake +43 -0
  35. data/plugins/html/README.md +3 -0
  36. data/plugins/js_widgets/README.md +5 -0
  37. data/plugins/js_widgets/js/html_tag.coffee +42 -0
  38. data/plugins/js_widgets/js/widgets.coffee +161 -0
  39. data/tasks/nginx.rake +23 -0
  40. metadata +38 -1
Binary file
@@ -0,0 +1,60 @@
1
+ # /etc/nginx/nginx.conf
2
+ # worker_processes 4; # one per proc "grep processor /proc/cpuinfo | wc -l"
3
+ # worker_connections 1024;
4
+ # SSL https://certbot.eff.org/
5
+ # 11 11 * * * certbot renew --post-hook "service nginx restart"
6
+
7
+ server {
8
+ listen 80;
9
+ server_name .domain.com;
10
+ return 301 https://$host$request_uri;
11
+ }
12
+
13
+ server {
14
+ server_name .stemical.com;
15
+
16
+ root $ROOT/public;
17
+ error_log $ROOT/log/nginx-error.log;
18
+
19
+ passenger_enabled on;
20
+ passenger_friendly_error_pages on;
21
+
22
+ client_body_timeout 12;
23
+ client_body_in_file_only clean;
24
+ client_body_buffer_size 32K; # form POST
25
+ client_header_timeout 12;
26
+ client_max_body_size 50M;
27
+ keepalive_timeout 20;
28
+ send_timeout 10;
29
+ sendfile on;
30
+
31
+ gzip on;
32
+ gzip_comp_level 2;
33
+ gzip_min_length 1000;
34
+ gzip_proxied expired no-cache no-store private auth;
35
+ gzip_types text/plain application/x-javascript text/xml text/css application/xml;
36
+
37
+ access_log off;
38
+
39
+ # ssl_session_timeout 1d;
40
+ ssl_session_cache shared:SSL:50m;
41
+ ssl_stapling on;
42
+ ssl_stapling_verify on;
43
+
44
+ location = /favicon.ico {
45
+ log_not_found off;
46
+ access_log off;
47
+ }
48
+
49
+ location ^~ /assets/ {
50
+ gzip_static on;
51
+ expires max;
52
+ add_header Cache-Control public;
53
+ }
54
+
55
+ location ^~ /.well-known {
56
+ allow all;
57
+ default_type "text/plain";
58
+ root $ROOT/public;
59
+ }
60
+ }
@@ -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,49 @@
1
+ ## Lux::Api
2
+
3
+ Default class for writing APIs
4
+
5
+ ### General rules
6
+
7
+ * defines Lux::Api, ApplicationApi and ModelApi
8
+ * ApplicationApi - defines decorator and default rescue for output messages
9
+ * ModelApi - defines create, read, update and delete methods
10
+ * checks every action via Policy.can?(current_user, action, model)
11
+ ``` Policy.can?(Lux.current.var.user, :update?, @note) ```
12
+ checks if current user can update defined model
13
+ * you shuld modify and inherit from ApplicationApi or ModelApi
14
+
15
+ * you can use before and after filters
16
+ * attributes that are requested and checked via "param :name" are available as @_name
17
+
18
+
19
+ ### Example
20
+
21
+ ```
22
+ class BlogApi < ModelApi
23
+
24
+ name 'List all blogs'
25
+ desc 'will list all blogs'
26
+ def index
27
+ Blog.order(:id).select(:id, :name).page
28
+ end
29
+
30
+ name 'Show single blog by id'
31
+ param :id, Integer
32
+ def show
33
+ Blog.find(@_id)
34
+ end
35
+
36
+ end
37
+ ```
38
+
39
+ ### Rescues
40
+
41
+ Define API rescue handler
42
+
43
+ ```
44
+ ApplicationApi.on_error do |e|
45
+ key = SimpleException.log(e)
46
+ response.meta :error_key, key
47
+ response.meta :error_class, e.class
48
+ end
49
+ ```
@@ -0,0 +1,29 @@
1
+ ### Create Sequel plugin
2
+
3
+ ```
4
+ module Sequel::Plugins::LuxHelp
5
+
6
+ module ClassMethods
7
+ def bla
8
+ model # => refrence to model
9
+ end
10
+ end
11
+
12
+ module InstanceMethods
13
+
14
+ end
15
+
16
+ module DatasetMethods
17
+
18
+ end
19
+
20
+ end
21
+
22
+ Sequel::Model.plugin :lux_help
23
+ ```
24
+
25
+ Default scope: http://stackoverflow.com/questions/11669880/default-scope-in-sequel
26
+
27
+ ### init
28
+
29
+ If init.rb is defined it will be called, else all files will be loaded.
@@ -0,0 +1,15 @@
1
+ namespace :db do
2
+ desc 'Automigrate schema'
3
+ task am: :env do
4
+
5
+ # Sequel extension and plugin test
6
+ DB.run %[DROP TABLE IF EXISTS lux_tests;]
7
+ DB.run %[CREATE TABLE lux_tests (int_array integer[] default '{}', text_array text[] default '{}');]
8
+ class LuxTest < Sequel::Model; end;
9
+ LuxTest.new.save
10
+ die('"DB.extension :pg_array" not loaded') unless LuxTest.first.int_array.class == Sequel::Postgres::PGArray
11
+ DB.run %[DROP TABLE IF EXISTS lux_tests;]
12
+
13
+ require './config/schema.rb'
14
+ end
15
+ end
@@ -0,0 +1,43 @@
1
+ # require_relative 'simple_exception'
2
+
3
+ eval File.read Lux.fw_root.join('plugins/exceptions/simple_exception.rb')
4
+
5
+ desc 'Show exceptions'
6
+ task :exceptions do
7
+ case ARGV[1]
8
+ when 'help'
9
+ puts ' lux exceptions - show all exceptions'
10
+ puts ' lux exceptions clear - to clear error folder'
11
+ puts ' lux exceptions NUMBER - to show error on specific number'
12
+ exit
13
+ end
14
+
15
+ show = ARGV[1] ? ARGV[1].to_i : nil
16
+ puts 'Add error number as last argument to show full erros, "clear" to clear all'.light_blue unless show
17
+
18
+ cnt = 0
19
+
20
+ list = SimpleException.list
21
+
22
+ die('No exceptions found') unless list[0]
23
+
24
+ list.each do |ex|
25
+ cnt += 1
26
+ next if show && show != cnt
27
+
28
+ puts '%s. %s, %s (%s)' % [cnt.to_s.rjust(2), ex[:age].yellow, ex[:desc], ex[:code]]
29
+
30
+ if show
31
+ puts "\n" + File.read(ex[:file])
32
+ exit
33
+ end
34
+ end
35
+ end
36
+
37
+ namespace :exceptions do
38
+ desc 'Clear all excpetions'
39
+ task :clear do
40
+ SimpleException.clear
41
+ puts 'Cleared from %s' % SimpleException::ERROR_FOLDER.yellow
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ ### Lux::Html
2
+
3
+ Html form helper
@@ -0,0 +1,5 @@
1
+ ### Use as
2
+
3
+ ```
4
+ //= req plugin:js_widgets/*
5
+ ```
@@ -0,0 +1,42 @@
1
+ #$tag 'button.btn.btn-xs', button_name, class: 'btn-primary'
2
+ @$tag = (name, args...) ->
3
+ # evaluate function if data is function
4
+ args = args.map (el) -> if typeof el == 'function' then el() else el
5
+
6
+ # fill second value
7
+ args[1] ||= if typeof args[0] == 'object' then '' else {}
8
+
9
+ # swap args if first option is object
10
+ [opts, data] = if typeof args[0] == 'object' then args else args.reverse()
11
+ opts ||= {}
12
+
13
+ # haml style id define
14
+ name = name.replace /#([\w\-]+)/, (_, id) ->
15
+ opts['id'] = id
16
+ ''
17
+
18
+ # haml style class add with a dot
19
+ name_parts = name.split('.')
20
+ name = name_parts.shift() || 'div'
21
+
22
+ if name_parts[0]
23
+ old_class = if opts['class'] then ' '+opts['class'] else ''
24
+ opts['class'] = name_parts.join(' ') + old_class
25
+
26
+ node = ['<'+name]
27
+
28
+ for key in Object.keys(opts)
29
+ val = opts[key]
30
+
31
+ if typeof val == 'function'
32
+ val = String(val).replace(/\s+/g,' ')
33
+ val = """(#{val})();"""
34
+
35
+ node.push ' '+key+'="'+val+'"'
36
+
37
+ if ['input', 'img'].indexOf(name) > -1
38
+ node.push ' />'
39
+ else
40
+ node.push '>'+data+'</'+name+'>'
41
+
42
+ node.join('')
@@ -0,0 +1,161 @@
1
+ 'use strict'
2
+
3
+ # Micro Widget/Component lib by @dux
4
+ # Super simple component lib for server side rendered templates
5
+ # if you need someting similar but more widely adopted, use https://stimulusjs.org/
6
+
7
+ # instance public interface
8
+ # init() - called on every wiget $init
9
+ # once() - called once on every page
10
+ # css() - will add css to document head if present
11
+ # set(k,v) - set state k to v and call render() if render defined
12
+ # id - instance_id
13
+ # node - dom_node
14
+ # ref - "Widget.ref[this.id]", dom instance reference
15
+ # state - data-json="{...}" -> @state all data-attributes are translated to state
16
+
17
+ # Widget public interface
18
+ # registere(name, object) - register widget
19
+ # bind(node) - init widget by id or dom node
20
+ # get(node) - get closest widget instance
21
+ # refresh() - call render() on all widgets instances
22
+
23
+ # Example code
24
+ # <div class="w yes_no" data-filed="yes"></div>
25
+ # Widget.register 'yes_no',
26
+ # init:
27
+ # @root = $ @node
28
+ # @state =
29
+ # field: @root.data('field')
30
+
31
+ # render_button: (name, state) ->
32
+ # $tag 'button.btn.btn-sm', name,
33
+ # class: "btn-#{klass}"
34
+ # onclick: @ref+".update_state('"+state+"')"
35
+
36
+ # render: ->
37
+ # data = @render_button(@state.no, 0)
38
+ # @root.html $tag('div.btn-group', data)
39
+
40
+ # update_state: (state) ->
41
+ # @state.state = state
42
+ # @render()
43
+
44
+ # $ -> Widget.bind()
45
+
46
+ @Widget =
47
+ css_klass: 'w'
48
+ inst_id_name: 'data-widget_id'
49
+ registered: {},
50
+ count: 0,
51
+ ref: {},
52
+
53
+ get: (node) ->
54
+ parts = node.split('#', 2)
55
+
56
+ if parts[1]
57
+ node = document.getElementById(parts[1])
58
+ @bind node
59
+
60
+ node = node.closest(".#{@css_klass}") || alert('Cant find closest widgets')
61
+ Widget.ref[parseInt(node.getAttribute(@inst_id_name))]
62
+
63
+ clear: ->
64
+ for i, w of @ref
65
+ delete @ref[i] unless document.body.contains(w.node)
66
+
67
+ init: (data) ->
68
+ @clear()
69
+
70
+ while node = @get_next_widget_node(data)
71
+ @bind(node)
72
+
73
+ get_next_widget_node: (root) ->
74
+ root ||= window.document
75
+
76
+ for node in root.getElementsByClassName(@css_klass)
77
+ return node if node && !node.getAttribute('data-widget_id')
78
+
79
+ null
80
+
81
+ # refresh all widgets
82
+ refresh: ->
83
+ @clear()
84
+
85
+ for node in @registered.values()
86
+ node.render() if node.render
87
+
88
+ # register widget, trigger once method, insert css if present
89
+ register: (name, widget) ->
90
+ return if @registered[name]
91
+
92
+ @registered[name] = widget
93
+
94
+ if widget.once
95
+ widget.once()
96
+ delete widget.once
97
+
98
+ if widget.css
99
+ data = if typeof(widget.css) == 'function' then widget.css() else widget.css
100
+ document.head.innerHTML += """<style id="widget_#{name}_css">#{data}</style>"""
101
+ delete widget.css
102
+
103
+ # create set method unless defined
104
+ widget.set ||= (name, value) ->
105
+ @state[name] = value
106
+ @render() if @render
107
+
108
+ # runtime apply registered widget to dom node
109
+ bind: (dom_node) ->
110
+ dom_node = document.getElementById(dom_node) if typeof(dom_node) == 'string'
111
+
112
+ instance_id = dom_node.getAttribute(@inst_id_name)
113
+
114
+ if instance_id
115
+ instance_id = parseInt instance_id
116
+ else
117
+ instance_id = ++@count
118
+ dom_node.setAttribute(@inst_id_name, instance_id)
119
+
120
+ return if @ref[instance_id]
121
+
122
+ dom_node.setAttribute('id', "widget-#{instance_id}") unless dom_node.getAttribute('id')
123
+ dom_node.setAttribute(@inst_id_name, instance_id)
124
+
125
+ widget_name = dom_node.getAttribute('class').split(' ')[1]
126
+ widget_opts = @registered[widget_name]
127
+
128
+ # return if widget is not defined
129
+ return alert "Widget #{widget_name} is not registred" unless widget_opts
130
+
131
+ # define widget instance
132
+ widget = {}
133
+
134
+ # apply basic methods
135
+ widget[key] = widget_opts[key] for key in Object.keys(widget_opts)
136
+
137
+ # bind root to root
138
+ widget.id = instance_id
139
+ widget.ref = "Widget.ref[#{instance_id}]"
140
+ widget.node = dom_node
141
+
142
+ # set widget state, copy all date-attributes to state
143
+ json = dom_node.getAttribute('data-json') || '{}'
144
+ json = JSON.parse json
145
+ widget.state = Object.assign(json, dom_node.dataset)
146
+
147
+ # store in global object
148
+ @ref[instance_id] = widget
149
+
150
+ # init and render
151
+ widget.init() if widget.init
152
+ widget.render() if widget.render
153
+
154
+ is_widget: (node) ->
155
+ klass = node.getAttribute('class')
156
+
157
+ if klass?.split(' ')[0] == 'w'
158
+ node
159
+ else
160
+ undefined
161
+
@@ -0,0 +1,23 @@
1
+ namespace :nginx do
2
+ desc 'Generate sample config'
3
+ task :generate do
4
+ command = ARGV[1]
5
+
6
+ ROOT = Dir.pwd
7
+ FOLDER = Dir.pwd.split('/').last
8
+
9
+ conf = Lux.fw_root.join('misc/nginx.conf').read
10
+ conf = conf.gsub(/`([^`]+)`/) { `#{$1}`.chomp }
11
+ conf = conf.gsub('$ROOT', ROOT)
12
+ conf = conf.gsub('$FOLDER', FOLDER)
13
+ puts conf
14
+ end
15
+
16
+ desc 'Edit nginx config'
17
+ task :edit do
18
+ folder = Dir.pwd.split('/').last
19
+
20
+ run 'sudo vim /etc/sites-enabled/%s.conf' % folder
21
+ end
22
+ end
23
+