lipsiadmin 3.3.4 → 3.4.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.
@@ -0,0 +1,101 @@
1
+ require 'loops/worker'
2
+ require 'loops/worker_pool'
3
+
4
+ module Lipsiadmin
5
+ module Loops
6
+ class ProcessManager#:nodoc:
7
+ attr_reader :logger
8
+
9
+ def initialize(config, logger)
10
+ @config = {
11
+ 'poll_period' => 1,
12
+ 'workers_engine' => 'fork'
13
+ }.merge(config)
14
+
15
+ @logger = logger
16
+ @worker_pools = {}
17
+ @shutdown = false
18
+ end
19
+
20
+ def start_workers(name, number, &blk)
21
+ raise "Need a worker block!" unless block_given?
22
+
23
+ logger.debug("Creating a workers pool of #{number} workers for #{name} loop...")
24
+ @worker_pools[name] = Lipsiadmin::Loops::WorkerPool.new(name, logger, @config['workers_engine'], &blk)
25
+ @worker_pools[name].start_workers(number)
26
+ end
27
+
28
+ def monitor_workers
29
+ setup_signals
30
+
31
+ logger.debug('Starting workers monitoring code...')
32
+ loop do
33
+ logger.debug('Checking workers\' health...')
34
+ @worker_pools.each do |name, pool|
35
+ break if @shutdown
36
+ pool.check_workers
37
+ end
38
+
39
+ break if @shutdown
40
+ logger.debug("Sleeping for #{@config['poll_period']} seconds...")
41
+ sleep(@config['poll_period'])
42
+ end
43
+ ensure
44
+ unless wait_for_workers(10)
45
+ logger.debug("Some workers are still alive after 10 seconds of waiting. Killing them...")
46
+ stop_workers(true)
47
+ wait_for_workers(5)
48
+ end
49
+ end
50
+
51
+ def setup_signals
52
+ # Zombie rippers
53
+ trap('CHLD') {}
54
+ trap('EXIT') {}
55
+ end
56
+
57
+ def wait_for_workers(seconds)
58
+ seconds.times do
59
+ logger.debug("Shutting down... waiting for workers to die (we have #{seconds} seconds)...")
60
+ running_total = 0
61
+
62
+ @worker_pools.each do |name, pool|
63
+ running_total += pool.wait_workers
64
+ end
65
+
66
+ if running_total.zero?
67
+ logger.debug("All workers are dead. Exiting...")
68
+ return true
69
+ end
70
+
71
+ logger.debug("#{running_total} workers are still running! Sleeping for a second...")
72
+ sleep(1)
73
+ end
74
+
75
+ return false
76
+ end
77
+
78
+ def stop_workers(force = false)
79
+ return unless start_shutdown || force
80
+ logger.debug("Stopping workers#{force ? '(forced)' : ''}...")
81
+
82
+ # Termination loop
83
+ @worker_pools.each do |name, pool|
84
+ pool.stop_workers(force)
85
+ end
86
+ end
87
+
88
+ def stop_workers!
89
+ return unless start_shutdown
90
+ stop_workers(false)
91
+ sleep(1)
92
+ stop_workers(true)
93
+ end
94
+
95
+ def start_shutdown
96
+ return false if @shutdown
97
+ @shutdown = true
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,81 @@
1
+ module Lipsiadmin
2
+ module Loops
3
+ class Worker#:nodoc:
4
+ attr_reader :logger
5
+ attr_reader :name
6
+ attr_reader :pid
7
+
8
+ def initialize(name, logger, engine, &blk)
9
+ raise "Need a worker block!" unless block_given?
10
+
11
+ @name = name
12
+ @logger = logger
13
+ @engine = engine
14
+ @worker_block = blk
15
+
16
+ @shutdown = false
17
+ end
18
+
19
+ def shutdown?
20
+ @shutdown
21
+ end
22
+
23
+ def run
24
+ return if shutdown?
25
+ if @engine == 'fork'
26
+ @pid = Kernel.fork do
27
+ $0 = "loop worker: #{@name}\0"
28
+ @pid = Process.pid
29
+ @worker_block.call
30
+ exit(0)
31
+ end
32
+ elsif @engine == 'thread'
33
+ @thread = Thread.start do
34
+ @worker_block.call
35
+ end
36
+ else
37
+ raise "Invalid engine name: #{@engine}"
38
+ end
39
+ rescue Exception => e
40
+ logger.error("Exception from worker: #{e} at #{e.backtrace.first}")
41
+ end
42
+
43
+ def running?(verbose = false)
44
+ return false if shutdown?
45
+ if @engine == 'fork'
46
+ return false unless @pid
47
+ begin
48
+ Process.waitpid(@pid, Process::WNOHANG)
49
+ res = Process.kill(0, @pid)
50
+ logger.debug("KILL(#{@pid}) = #{res}") if verbose
51
+ return true
52
+ rescue Errno::ESRCH, Errno::ECHILD, Errno::EPERM => e
53
+ logger.error("Exception from kill: #{e} at #{e.backtrace.first}") if verbose
54
+ return false
55
+ end
56
+ elsif @engine == 'thread'
57
+ @thread && @thread.alive?
58
+ else
59
+ raise "Invalid engine name: #{@engine}"
60
+ end
61
+ end
62
+
63
+ def stop(force = false)
64
+ @shutdown = true
65
+ if @engine == 'fork'
66
+ begin
67
+ sig = force ? 'SIGKILL' : 'SIGTERM'
68
+ logger.debug("Sending #{sig} to ##{@pid}")
69
+ Process.kill(sig, @pid)
70
+ rescue Errno::ESRCH, Errno::ECHILD, Errno::EPERM=> e
71
+ logger.error("Exception from kill: #{e} at #{e.backtrace.first}")
72
+ end
73
+ elsif @engine == 'thread'
74
+ force && !defined?(::JRuby) ? @thread.kill! : @thread.kill
75
+ else
76
+ raise "Invalid engine name: #{@engine}"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,53 @@
1
+ module Lipsiadmin
2
+ module Loops
3
+ class WorkerPool#:nodoc:
4
+ attr_reader :logger
5
+ attr_reader :name
6
+
7
+ def initialize(name, logger, engine, &blk)
8
+ @name = name
9
+ @logger = logger
10
+ @worker_block = blk
11
+ @shutdown = false
12
+ @engine = engine
13
+ @workers = []
14
+ end
15
+
16
+ def start_workers(number)
17
+ logger.debug("Creating #{number} workers for #{name} loop...")
18
+ number.times do
19
+ @workers << Lipsiadmin::Loops::Worker.new(name, logger, @engine, &@worker_block)
20
+ end
21
+ end
22
+
23
+ def check_workers
24
+ logger.debug("Checking loop #{name} workers...")
25
+ @workers.each do |worker|
26
+ next if worker.running? || worker.shutdown?
27
+ logger.debug("Worker #{worker.name} is not running. Restart!")
28
+ worker.run
29
+ end
30
+ end
31
+
32
+ def wait_workers
33
+ running = 0
34
+ @workers.each do |worker|
35
+ next unless worker.running?
36
+ running += 1
37
+ logger.debug("Worker #{name} is still running (#{worker.pid})")
38
+ end
39
+ return running
40
+ end
41
+
42
+ def stop_workers(force)
43
+ return if @shutdown
44
+ @shutdown = false
45
+ logger.debug("Stopping loop #{name} workers...")
46
+ @workers.each do |worker|
47
+ next unless worker.running?
48
+ worker.stop(force)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  module Lipsiadmin
2
3
  module Utils
3
4
  # Convert common utf-8 chars to html entities, this is beautifull (but the code not)
data/lib/version.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  module Lipsiadmin
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 3
4
- MINOR = 3
5
- TINY = 4
4
+ MINOR = 4
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -22,9 +22,12 @@ module Lipsiadmin
22
22
 
23
23
  # This method add tab for in your view
24
24
  def tab(name, padding=true, options={}, &block)
25
- options[:style] = "padding:10px;#{options[:style]}" if padding
26
- options[:id] ||= name.to_s.downcase.gsub(/[^a-z0-9]+/, '_').gsub(/-+$/, '').gsub(/^-+$/, '')
27
- concat content_tag(:div, capture(&block), { :id => options[:id], :class => "x-hide-display", :style => options[:style], :tabbed => true, :title => name })
25
+ options[:id] ||= name.to_s.downcase.gsub(/[^a-z0-9]+/, '_').gsub(/-+$/, '').gsub(/^-+$/, '')
26
+ options[:style] = "padding:10px;#{options[:style]}" if padding
27
+ options[:title] = name
28
+ options[:tabbed] = true
29
+ options[:class] = "x-hide-display"
30
+ concat content_tag(:div, capture(&block), options)
28
31
  end
29
32
 
30
33
  # Set the title of the page
@@ -49,7 +52,6 @@ module Lipsiadmin
49
52
  # Example:
50
53
  #
51
54
  # # in app/views/dossiers/_form.html.haml
52
-
53
55
  # %tr
54
56
  # %td{:style=>"width:100px"}
55
57
  # %b Customer:
@@ -178,6 +178,7 @@ module Lipsiadmin#:nodoc:
178
178
 
179
179
  def add_object(name, object)
180
180
  if object.class == Component || object.class.superclass == Component
181
+ @before.delete_if { |b| b.start_with?("var #{object.get_var} = new") }
181
182
  @before << object.to_s
182
183
  @config[name.to_sym] = l(object.get_var)
183
184
  else
@@ -157,22 +157,20 @@ module Lipsiadmin
157
157
  end
158
158
 
159
159
  # Generate or set a new Ext.grid.GroupingView
160
- # You can pass tbar :default options that will autocreate a correct GroupingView
160
+ # You can pass view :default options that will autocreate a correct GroupingView
161
161
  #
162
162
  # Examples:
163
163
  # view: new Ext.grid.GroupingView({
164
164
  # forceFit:true,
165
165
  # groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Foo" : "Bar"]})'
166
166
  # })
167
- # view :forceFit => true, :groupTextTpl => "{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Foo" : "Bar"]})"
167
+ # view :forceFit => true, :groupTextTpl => '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Foo" : "Bar"]})'
168
168
  #
169
169
  def view(object=nil, &block)
170
- if object == :default
171
- view = Component.new("Ext.grid.GroupingView", { :forceFit => true })
172
- elsif value.is_a?(Hash)
173
- view = Component.new("Ext.grid.GroupingView", { :forceFit => true }.merge(object))
174
- else
175
- view = object
170
+ view = case object
171
+ when :default then Component.new("Ext.grid.GroupingView", { :forceFit => true })
172
+ when Hash then Component.new("Ext.grid.GroupingView", { :forceFit => true }.merge(object))
173
+ else object
176
174
  end
177
175
  add_object(:view, view)
178
176
  end
@@ -14,6 +14,6 @@ Extra:
14
14
  in config/environment.rb
15
15
 
16
16
  - config.active_record.timestamped_migrations = false
17
- - comment config.time_zone = 'UTC'
17
+ - config.time_zone = 'Rome'
18
18
 
19
19
  ================================================================
@@ -30,6 +30,7 @@ class BackendGenerator < Rails::Generator::Base
30
30
  m.create_all("models", "app/models")
31
31
  m.create_all("views", "app/views")
32
32
  m.create_all("config", "config")
33
+ m.create_all("test", "test")
33
34
 
34
35
  # Using this for prevent raising errors
35
36
  migration = Dir.glob("db/migrate/[0-9]*_*.rb").grep(/[0-9]+_create_accounts.rb$/)
@@ -78,7 +78,7 @@ class Account < ActiveRecord::Base
78
78
  end
79
79
 
80
80
  def authenticated?(password)
81
- crypted_password == encrypt(password)
81
+ crypted_password.chomp == encrypt(password).chomp
82
82
  end
83
83
 
84
84
 
@@ -0,0 +1,8 @@
1
+ admin:
2
+ id: 1
3
+ name: Davide
4
+ surname: D'Agostino
5
+ email: d.dagostino@lipsiasoft.com
6
+ salt: 066f31caf14fdf474c6b5586738be7cc190d8b33
7
+ crypted_password: UlMOC7qYsxw= #Admin
8
+ role: administrator
@@ -0,0 +1,10 @@
1
+ require 'test_helper'
2
+
3
+ class AccountTest < ActiveSupport::TestCase
4
+ # Replace this with your real tests.
5
+ test "should be valid" do
6
+ Account.all.each do |a|
7
+ assert a.valid?
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ # This generator bootstraps a Rails project for use with loops
2
+ class LoopsGenerator < Rails::Generator::Base
3
+ def manifest
4
+ record do |m|
5
+ # Generate app/loops directory and an example loop files
6
+ m.directory 'app'
7
+ m.directory 'app/loops'
8
+ m.file 'app/loops/APP_README', 'app/loops/README'
9
+ m.file 'app/loops/simple_loop.rb', 'app/loops/simple_loop.rb'
10
+
11
+ # Generate script/loops file
12
+ m.directory 'script'
13
+ m.file 'script/loops', 'script/loops', :chmod => 0755
14
+
15
+ # Generate config/loops.yml file
16
+ m.directory 'config'
17
+ m.file 'config/loops.yml', 'config/loops.yml'
18
+ end
19
+ end
20
+
21
+ protected
22
+
23
+ def banner
24
+ "Usage: #{$0} loops"
25
+ end
26
+
27
+ end
@@ -0,0 +1 @@
1
+ This directory should be used to hold all application loops.
@@ -0,0 +1,9 @@
1
+ # Simple loop with its own custom run method
2
+ #
3
+ # Does nothing aside from printing loop's name, pid and current time every second
4
+ #
5
+ class SimpleLoop < Lipsiadmin::Loops::Base
6
+ def run
7
+ debug(Time.now)
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ # This file is a configuration file for loops rails plugin
2
+ #
3
+
4
+ # This section is used to control loops manager
5
+ global:
6
+ logger: stdout
7
+ poll_period: 5
8
+ workers_engine: fork
9
+
10
+ # Each record in this section represents one loop which could be ran using loops plugin.
11
+ # Each loop should have a file in app/loops directory with the same name as its config record.
12
+ loops:
13
+
14
+ # Simple time printing loop
15
+ simple_loop:
16
+ workers_number: 1
17
+
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'rubygems'
5
+
6
+ # We need this to be able to start the process w/o being
7
+ # in the RAILS_ROOT directory
8
+ ROOT_DIR = File.expand_path('..', File.dirname(__FILE__))
9
+ Dir.chdir(ROOT_DIR)
10
+
11
+ # Default options
12
+ options = {
13
+ :daemonize => false,
14
+ :loops => [],
15
+ :all_loops => false,
16
+ :list_loops => false,
17
+ :pid_file => nil,
18
+ :stop => false,
19
+ :framework => 'rails',
20
+ :environment => nil
21
+ }
22
+
23
+ # Parse command line options
24
+ opts = OptionParser.new do |opt|
25
+ opt.banner = "Usage: loops [options]"
26
+ opt.separator ""
27
+ opt.separator "Specific options:"
28
+
29
+ opt.on('-d', '--daemonize', 'Daemonize when all loops started') { |v| options[:daemonize] = v }
30
+ opt.on('-s', '--stop', 'Stop daemonized loops if running.') { |v| options[:stop] = v }
31
+ opt.on('-p', '--pid=file', 'Override loops.yml pid_file option') { |v| options[:pid_file] = v }
32
+ opt.on('-l', '--loop=loop_name', 'Start specified loop(s) only') { |v| options[:loops] << v }
33
+ opt.on('-a', '--all', 'Start all loops') { |v| options[:all_loops] = v }
34
+ opt.on('-L', '--list', 'Shows all available loops with their options') { |v| options[:list_loops] = v }
35
+ opt.on('-f', '--framework=name', 'Starts within a Rails (rails) or Merb (merb) project. Default is rails.') { |v| options[:framework] = v }
36
+ opt.on('-e', '--environment=env', 'Set RAILS_ENV value') { |v| options[:environment] = v }
37
+
38
+ opt.on_tail("-h", "--help", "Show this message") do
39
+ puts(opt)
40
+ exit(0)
41
+ end
42
+
43
+ opt.parse!(ARGV)
44
+ end
45
+
46
+ ENV['RAILS_ENV'] = options[:environment] if options[:environment]
47
+
48
+ # Bootstrap Rails
49
+ require File.join(ROOT_DIR, 'config/boot')
50
+ require File.join(ROOT_DIR, 'config/environment')
51
+
52
+ # Loops default logger
53
+ LOOPS_DEFAULT_LOGGER = Rails.logger
54
+
55
+ #$LOAD_PATH.unshift("vendor/plugins/loops/lib")
56
+
57
+ LOOPS_ROOT = ROOT_DIR
58
+ LOOPS_CONFIG_FILE = File.join(LOOPS_ROOT, 'config/loops.yml')
59
+
60
+ require 'loops'
61
+ require 'loops/daemonize'
62
+
63
+ # Load config file
64
+ puts "Loading config..."
65
+ Lipsiadmin::Loops.load_config(LOOPS_CONFIG_FILE)
66
+ options[:pid_file] ||= (Lipsiadmin::Loops.global_config['pid_file'] || "#{Rails.root}/tmp/loops.pid")
67
+ options[:pid_file] = File.join(LOOPS_ROOT, options[:pid_file]) unless options[:pid_file] =~ /^\//
68
+
69
+ # Stop/kill loops if requested
70
+ if options[:stop]
71
+ STDOUT.sync = true
72
+ raise "No pid file or a stale pid file!" unless Lipsiadmin::Loops::Daemonize.check_pid(options[:pid_file])
73
+ pid = Lipsiadmin::Loops::Daemonize.read_pid(options[:pid_file])
74
+ print "Killing the process: #{pid}: "
75
+
76
+ loop do
77
+ Process.kill('SIGTERM', pid)
78
+ sleep(1)
79
+ break unless Lipsiadmin::Loops::Daemonize.check_pid(options[:pid_file])
80
+ print(".")
81
+ end
82
+
83
+ puts " Done!"
84
+ exit(0)
85
+ end
86
+
87
+ # List loops if requested
88
+ if options[:list_loops]
89
+ puts "Available loops:"
90
+ Lipsiadmin::Loops.loops_config.each do |name, config|
91
+ puts "Loop: #{name}" + (config['disabled'] ? ' (disabled)' : '')
92
+ config.each do |k,v|
93
+ puts " - #{k}: #{v}"
94
+ end
95
+ end
96
+ puts
97
+ exit(0)
98
+ end
99
+
100
+ # Ignore --loop options if --all parameter passed
101
+ options[:loops] = :all if options[:all_loops]
102
+
103
+ # Check what loops we gonna run
104
+ if options[:loops] == []
105
+ puts opts
106
+ exit(0)
107
+ end
108
+
109
+ # Pid file check
110
+ raise "Can't start, another process exists!" if Lipsiadmin::Loops::Daemonize.check_pid(options[:pid_file])
111
+
112
+ # Daemonization
113
+ app_name = "loops monitor: #{ options[:loops].join(' ') rescue 'all' }\0"
114
+ Lipsiadmin::Loops::Daemonize.daemonize(app_name) if options[:daemonize]
115
+
116
+ # Pid file creation
117
+ Lipsiadmin::Loops::Daemonize.create_pid(options[:pid_file])
118
+
119
+ # Workers processing
120
+ puts "Starting workers"
121
+ Lipsiadmin::Loops.start_loops!(options[:loops])
122
+
123
+ # Workers exited, cleaning up
124
+ puts "Cleaning pid file..."
125
+ File.delete(options[:pid_file]) rescue nil