maiha-merb_background 0.1

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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 maiha@wota.jp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,105 @@
1
+ MerbBackground
2
+ ==============
3
+
4
+ A slice for the Merb framework that offers background tasks for DataMapper.
5
+ This is ported from background-fu that works with Rails + AR.
6
+
7
+ Original:
8
+ http://github.com/ncr/background-fu/
9
+
10
+
11
+ Methods
12
+ =======
13
+
14
+ same as background-fu.
15
+
16
+ MerbBackground::Job.enqueue!(ExampleWorker, :add, 1, 2)
17
+
18
+
19
+ Setup
20
+ =====
21
+
22
+ MerbBackground::Job.auto_upgrade!
23
+
24
+ or
25
+ rake slices:merb_background:migrate
26
+
27
+
28
+ Daemon
29
+ ======
30
+
31
+ ruby slices/merb_background/script/background.rb
32
+
33
+
34
+
35
+ ------------------------------------------------------------------------------
36
+
37
+
38
+ To see all available tasks for MerbBackground run:
39
+
40
+ rake -T slices:merb_background
41
+
42
+ ------------------------------------------------------------------------------
43
+
44
+ Instructions for installation:
45
+
46
+ file: config/init.rb
47
+
48
+ # add the slice as a regular dependency
49
+
50
+ dependency 'merb_background'
51
+
52
+ # if needed, configure which slices to load and in which order
53
+
54
+ Merb::Plugins.config[:merb_slices] = { :queue => ["MerbBackground", ...] }
55
+
56
+ # optionally configure the plugins in a before_app_loads callback
57
+
58
+ Merb::BootLoader.before_app_loads do
59
+
60
+ Merb::Slices::config[:merb_background][:option] = value
61
+
62
+ end
63
+
64
+ file: config/router.rb
65
+
66
+ # example: /merb_background/:controller/:action/:id
67
+
68
+ add_slice(:MerbBackground)
69
+
70
+ # example: /:lang/:controller/:action/:id
71
+
72
+ add_slice(:MerbBackground, :path => ':lang')
73
+
74
+ # example: /:controller/:action/:id
75
+
76
+ slice(:MerbBackground)
77
+
78
+ Normally you should also run the following rake task:
79
+
80
+ rake slices:merb_background:install
81
+
82
+ ------------------------------------------------------------------------------
83
+
84
+ You can put your application-level overrides in:
85
+
86
+ host-app/slices/merb_background/app - controllers, models, views ...
87
+
88
+ Templates are located in this order:
89
+
90
+ 1. host-app/slices/merb_background/app/views/*
91
+ 2. gems/merb_background/app/views/*
92
+ 3. host-app/app/views/*
93
+
94
+ You can use the host application's layout by configuring the
95
+ merb_background slice in a before_app_loads block:
96
+
97
+ Merb::Slices.config[:merb_background] = { :layout => :application }
98
+
99
+ By default :merb_background is used. If you need to override
100
+ stylesheets or javascripts, just specify your own files in your layout
101
+ instead/in addition to the ones supplied (if any) in
102
+ host-app/public/slices/merb_background.
103
+
104
+ In any case don't edit those files directly as they may be clobbered any time
105
+ rake merb_background:install is run.
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ require 'merb-core'
5
+ require 'merb-core/tasks/merb'
6
+
7
+ GEM_NAME = "merb_background"
8
+ AUTHOR = "maiha"
9
+ EMAIL = "maiha@wota.jp"
10
+ HOMEPAGE = "http://github.com/maiha/merb_background"
11
+ SUMMARY = "A slice for the Merb framework that offers background tasks for DataMapper"
12
+ GEM_VERSION = "0.1"
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.rubyforge_project = 'merb'
16
+ s.name = GEM_NAME
17
+ s.version = GEM_VERSION
18
+ s.platform = Gem::Platform::RUBY
19
+ s.has_rdoc = true
20
+ s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
21
+ s.summary = SUMMARY
22
+ s.description = s.summary
23
+ s.author = AUTHOR
24
+ s.email = EMAIL
25
+ s.homepage = HOMEPAGE
26
+ s.add_dependency('merb-slices', '>= 1.0.11')
27
+ s.require_path = 'lib'
28
+ s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec,app,public,stubs}/**/*")
29
+ end
30
+
31
+ Rake::GemPackageTask.new(spec) do |pkg|
32
+ pkg.gem_spec = spec
33
+ end
34
+
35
+ desc "Install the gem"
36
+ task :install do
37
+ Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
38
+ end
39
+
40
+ desc "Uninstall the gem"
41
+ task :uninstall do
42
+ Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
43
+ end
44
+
45
+ desc "Create a gemspec file"
46
+ task :gemspec do
47
+ File.open("#{GEM_NAME}.gemspec", "w") do |file|
48
+ file.puts spec.to_ruby
49
+ end
50
+ end
51
+
52
+ require 'spec/rake/spectask'
53
+ require 'merb-core/test/tasks/spectasks'
54
+ desc 'Default: run spec examples'
55
+ task :default => 'spec'
data/TODO ADDED
@@ -0,0 +1,13 @@
1
+ TODO:
2
+
3
+ - Add rake tasks especially for control daemons
4
+ - Add BonusFeature
5
+ - Add codes for AR
6
+
7
+ Remove anything that you don't need:
8
+
9
+ - app/controllers/main.rb MerbBackground::Main controller
10
+ - app/views/layout/merb_background.html.erb
11
+ - spec/controllers/main_spec.rb controller specs
12
+ - public/* any public files
13
+ - stubs/* any stub files
@@ -0,0 +1,5 @@
1
+ class MerbBackground::Application < Merb::Controller
2
+
3
+ controller_for_slice
4
+
5
+ end
@@ -0,0 +1,7 @@
1
+ class MerbBackground::Main < MerbBackground::Application
2
+
3
+ def index
4
+ render
5
+ end
6
+
7
+ end
@@ -0,0 +1,64 @@
1
+ module Merb
2
+ module MerbBackground
3
+ module ApplicationHelper
4
+
5
+ # @param *segments<Array[#to_s]> Path segments to append.
6
+ #
7
+ # @return <String>
8
+ # A path relative to the public directory, with added segments.
9
+ def image_path(*segments)
10
+ public_path_for(:image, *segments)
11
+ end
12
+
13
+ # @param *segments<Array[#to_s]> Path segments to append.
14
+ #
15
+ # @return <String>
16
+ # A path relative to the public directory, with added segments.
17
+ def javascript_path(*segments)
18
+ public_path_for(:javascript, *segments)
19
+ end
20
+
21
+ # @param *segments<Array[#to_s]> Path segments to append.
22
+ #
23
+ # @return <String>
24
+ # A path relative to the public directory, with added segments.
25
+ def stylesheet_path(*segments)
26
+ public_path_for(:stylesheet, *segments)
27
+ end
28
+
29
+ # Construct a path relative to the public directory
30
+ #
31
+ # @param <Symbol> The type of component.
32
+ # @param *segments<Array[#to_s]> Path segments to append.
33
+ #
34
+ # @return <String>
35
+ # A path relative to the public directory, with added segments.
36
+ def public_path_for(type, *segments)
37
+ ::MerbBackground.public_path_for(type, *segments)
38
+ end
39
+
40
+ # Construct an app-level path.
41
+ #
42
+ # @param <Symbol> The type of component.
43
+ # @param *segments<Array[#to_s]> Path segments to append.
44
+ #
45
+ # @return <String>
46
+ # A path within the host application, with added segments.
47
+ def app_path_for(type, *segments)
48
+ ::MerbBackground.app_path_for(type, *segments)
49
+ end
50
+
51
+ # Construct a slice-level path.
52
+ #
53
+ # @param <Symbol> The type of component.
54
+ # @param *segments<Array[#to_s]> Path segments to append.
55
+ #
56
+ # @return <String>
57
+ # A path within the slice source (Gem), with added segments.
58
+ def slice_path_for(type, *segments)
59
+ ::MerbBackground.slice_path_for(type, *segments)
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
5
+ <title>Fresh MerbBackground Slice</title>
6
+ <link href="<%= public_path_for :stylesheet, 'master.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" />
7
+ <script src="<%= public_path_for :javascript, 'master.js' %>" type="text/javascript" charset="utf-8"></script>
8
+ </head>
9
+ <!-- you can override this layout at slices/merb_background/app/views/layout/merb_background.html.erb -->
10
+ <body class="merb_background-slice">
11
+ <div id="container">
12
+ <h1>MerbBackground Slice</h1>
13
+ <div id="main"><%= catch_content :for_layout %></div>
14
+ </div>
15
+ </body>
16
+ </html>
@@ -0,0 +1 @@
1
+ <strong><%= slice.description %></strong> (v. <%= slice.version %>)
@@ -0,0 +1,88 @@
1
+ if defined?(Merb::Plugins)
2
+
3
+ $:.unshift File.dirname(__FILE__)
4
+
5
+ dependency 'merb-slices', :immediate => true
6
+ Merb::Plugins.add_rakefiles "merb_background/merbtasks", "merb_background/slicetasks", "merb_background/spectasks"
7
+
8
+ # Register the Slice for the current host application
9
+ Merb::Slices::register(__FILE__)
10
+
11
+ # Slice configuration - set this in a before_app_loads callback.
12
+ # By default a Slice uses its own layout, so you can swicht to
13
+ # the main application layout or no layout at all if needed.
14
+ #
15
+ # Configuration options:
16
+ # :layout - the layout to use; defaults to :merb_background
17
+ # :mirror - which path component types to use on copy operations; defaults to all
18
+ Merb::Slices::config[:merb_background][:layout] ||= :merb_background
19
+
20
+ # All Slice code is expected to be namespaced inside a module
21
+ module MerbBackground
22
+
23
+ # Slice metadata
24
+ self.description = "a merb slice for background-fu"
25
+ self.version = "0.0.1"
26
+ self.author = "maiha"
27
+
28
+ Config = Mash.new(
29
+ :cleanup_interval => :on_startup,
30
+ :monitor_interval => 10
31
+ )
32
+
33
+ # Stub classes loaded hook - runs before LoadClasses BootLoader
34
+ # right after a slice's classes have been loaded internally.
35
+ def self.loaded
36
+ require 'merb_background/job'
37
+ end
38
+
39
+ # Initialization hook - runs before AfterAppLoads BootLoader
40
+ def self.init
41
+ end
42
+
43
+ # Activation hook - runs after AfterAppLoads BootLoader
44
+ def self.activate
45
+ end
46
+
47
+ # Deactivation hook - triggered by Merb::Slices.deactivate(MerbBackground)
48
+ def self.deactivate
49
+ end
50
+
51
+ # Setup routes inside the host application
52
+ #
53
+ # @param scope<Merb::Router::Behaviour>
54
+ # Routes will be added within this scope (namespace). In fact, any
55
+ # router behaviour is a valid namespace, so you can attach
56
+ # routes at any level of your router setup.
57
+ #
58
+ # @note prefix your named routes with :merb_background_
59
+ # to avoid potential conflicts with global named routes.
60
+ def self.setup_router(scope)
61
+ # example of a named route
62
+ scope.match('/index(.:format)').to(:controller => 'main', :action => 'index').name(:index)
63
+ # the slice is mounted at /merb_background - note that it comes before default_routes
64
+ scope.match('/').to(:controller => 'main', :action => 'index').name(:home)
65
+ # enable slice-level default routes by default
66
+ scope.default_routes
67
+ end
68
+
69
+ end
70
+
71
+ # Setup the slice layout for MerbBackground
72
+ #
73
+ # Use MerbBackground.push_path and MerbBackground.push_app_path
74
+ # to set paths to merb_background-level and app-level paths. Example:
75
+ #
76
+ # MerbBackground.push_path(:application, MerbBackground.root)
77
+ # MerbBackground.push_app_path(:application, Merb.root / 'slices' / 'merb_background')
78
+ # ...
79
+ #
80
+ # Any component path that hasn't been set will default to MerbBackground.root
81
+ #
82
+ # Or just call setup_default_structure! to setup a basic Merb MVC structure.
83
+ MerbBackground.setup_default_structure!
84
+
85
+ # Add dependencies for other MerbBackground classes below. Example:
86
+ # dependency "merb_background/other"
87
+
88
+ end
File without changes
@@ -0,0 +1,138 @@
1
+ # Example:
2
+ #
3
+ # job = Job.enqueue!(MyWorker, :my_method, "my_arg_1", "my_arg_2")
4
+
5
+ #require File.dirname(__FILE__) + '/job/ar.rb'
6
+ require File.dirname(__FILE__) + '/job/dm.rb'
7
+
8
+ class MerbBackground::Job
9
+ cattr_accessor :states
10
+ self.states = %w(pending running finished failed)
11
+
12
+ # attr_readonly :worker_class, :worker_method, :args
13
+
14
+ def self.enqueue!(worker_class, worker_method, *args)
15
+ job = create!(
16
+ :worker_class => worker_class.to_s,
17
+ :worker_method => worker_method.to_s,
18
+ :args => args
19
+ )
20
+
21
+ logger.info("MerbBackground: Job enqueued. Job(id: #{job.id}, worker: #{worker_class}, method: #{worker_method}, argc: #{args.size}).")
22
+
23
+ job
24
+ end
25
+
26
+ def self.execute!(config = nil)
27
+ config = MerbBackground::Config.merge(config || {})
28
+ cleanup_finished_jobs if config[:cleanup_interval] == :at_start
29
+
30
+ config.each do |k,v|
31
+ logger.info "MerbBackground: #{k}: #{v}"
32
+ end
33
+
34
+ loop do
35
+ if job = pending.ready.first(:order => [:priority.desc, :start_at.asc])
36
+ job.get_done!
37
+ else
38
+ logger.info("MerbBackground: Waiting for jobs...")
39
+ sleep config[:monitor_interval]
40
+ end
41
+ cleanup_finished_jobs if config[:cleanup_interval] == :continuous
42
+ end
43
+ end
44
+
45
+
46
+ # Invoked by a background daemon.
47
+ def get_done!
48
+ initialize_worker
49
+ invoke_worker
50
+ rescue Exception => e
51
+ rescue_worker(e)
52
+ ensure
53
+ ensure_worker
54
+ end
55
+
56
+ # Restart a failed job.
57
+ def restart!
58
+ if failed?
59
+ update_attributes!(
60
+ :result => nil,
61
+ :progress => nil,
62
+ :started_at => nil,
63
+ :state => "pending"
64
+ )
65
+ logger.info("MerbBackground: Job restarted. Job(id: #{id}).")
66
+ end
67
+ end
68
+
69
+ def initialize_worker
70
+ update_attributes!(:started_at => Time.now, :state => "running")
71
+ @worker = worker_class.constantize.new
72
+ logger.info("MerbBackground: Job initialized. Job(id: #{id}).")
73
+ end
74
+
75
+ def invoke_worker
76
+ self.result = @worker.send(worker_method, *args)
77
+ self.state = "finished"
78
+ logger.info("MerbBackground: Job finished. Job(id: #{id}).")
79
+ end
80
+
81
+ def rescue_worker(exception)
82
+ self.result = [exception.message, exception.backtrace.join("\n")].join("\n\n")
83
+ self.state = "failed"
84
+ logger.info("MerbBackground: Job failed. Job(id: #{id}).")
85
+ end
86
+
87
+ def ensure_worker
88
+ self.progress = @worker.instance_variable_get("@progress")
89
+ save!
90
+ rescue StaleObjectError
91
+ # Ignore this exception as its only purpose is
92
+ # not allowing multiple daemons execute the same job.
93
+ logger.info("MerbBackground: Race condition handled (It's OK). Job(id: #{id}).")
94
+ end
95
+
96
+ # Delete finished jobs that are more than a week old.
97
+ def self.cleanup_finished_jobs
98
+ logger.info "MerbBackground: Cleaning up finished jobs."
99
+
100
+ # TODO: AR compats
101
+ Job.finished.all(:updated_at.lt => 1.week.ago).destroy!
102
+ end
103
+
104
+ def self.generate_state_helpers
105
+ states.each do |state_name|
106
+ define_method("#{state_name}?") do
107
+ state == state_name
108
+ end
109
+
110
+ # Job.running => array of running jobs, etc.
111
+ self.class.send(:define_method, state_name) do
112
+ all(:state => state_name, :order => [:id.desc])
113
+ end
114
+ end
115
+ end
116
+ generate_state_helpers
117
+
118
+ def setup_state
119
+ return unless state.blank?
120
+
121
+ self.state = "pending"
122
+ end
123
+
124
+ # Default priority is 0. Jobs will be executed in descending priority order (negative priorities allowed).
125
+ def setup_priority
126
+ return unless priority.blank?
127
+
128
+ self.priority = 0
129
+ end
130
+
131
+ # Job will be executed after this timestamp.
132
+ def setup_start_at
133
+ return unless start_at.blank?
134
+
135
+ self.start_at = Time.now
136
+ end
137
+
138
+ end
@@ -0,0 +1,10 @@
1
+ class MerbBackground::Job < ActiveRecord::Base
2
+ cattr_accessor :states
3
+ self.states = %w(pending running finished failed)
4
+
5
+ serialize :args, Array
6
+ serialize :result
7
+
8
+ before_create :setup_state, :setup_priority, :setup_start_at
9
+ validates_presence_of :worker_class, :worker_method
10
+ end
@@ -0,0 +1,37 @@
1
+ module DataMapper
2
+ module ActiveRecordMethods
3
+ def self.included(model)
4
+ model.class_eval do
5
+ extend ClassMethods
6
+ include InstanceMethods
7
+ end
8
+ end
9
+
10
+ class RecordNotSaved < DataMapper::PersistenceError; end
11
+
12
+ module InstanceMethods
13
+ def logger
14
+ self.class.logger
15
+ end
16
+
17
+ def bang(&block)
18
+ block.call or raise RecordNotSaved
19
+ return self
20
+ end
21
+
22
+ def update_attributes!(hash)
23
+ bang{update_attributes(hash)}
24
+ end
25
+ end
26
+
27
+ module ClassMethods
28
+ def logger
29
+ DataMapper.logger
30
+ end
31
+
32
+ def create!(attributes)
33
+ new(attributes).bang{save}
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,98 @@
1
+ module Job::BonusFeatures
2
+
3
+ def self.included(base)
4
+ base.states += %w(stopping stopped)
5
+ base.generate_state_helpers
6
+
7
+ base.alias_method_chain :invoke_worker, :threads
8
+ base.alias_method_chain :ensure_worker, :threads
9
+ base.alias_method_chain :restart!, :threads
10
+ end
11
+
12
+ def invoke_worker_with_threads
13
+ monitor_worker
14
+
15
+ res = catch(:stopping) do
16
+ invoke_worker_without_threads; nil
17
+ end
18
+
19
+ self.reload
20
+ self.state = res ? "stopped" : "finished"
21
+ end
22
+
23
+ def ensure_worker_with_threads
24
+ ensure_worker_without_threads
25
+ cleanup_after_threads
26
+ end
27
+
28
+ # The record_progress() method becomes available when your worker class includes
29
+ # Background::WorkerMonitoring.
30
+ #
31
+ # Every time worker invokes record_progress() is a possible stopping place.
32
+ #
33
+ # How it works:
34
+ # 1. invoke job.stop! to set a state (stopping) in a db
35
+ # 2. Monitoring thread picks up the state change from db
36
+ # and sets @stopping to true in the worker.
37
+ # 3. The worker invokes a register_progress() somewhere during execution.
38
+ # 4. The record_progress() method throws :stopping symbol if @stopping == true
39
+ # 5. The job catches the :stopping symbol and reacts upon it.
40
+ # 6. The job is stopped in a merciful way. No one gets harmed.
41
+ def stop!
42
+ if running?
43
+ update_attribute(:state, "stopping")
44
+ logger.info("BackgroundFu: Stopping job. Job(id: #{id}).")
45
+ end
46
+ end
47
+
48
+ # Overridden because of new "stopped" state.
49
+ def restart_with_threads!
50
+ if stopped? || failed?
51
+ update_attributes!(
52
+ :result => nil,
53
+ :progress => nil,
54
+ :started_at => nil,
55
+ :state => "pending"
56
+ )
57
+ logger.info("BackgroundFu: Restarting job. Job(id: #{id}).")
58
+ end
59
+ end
60
+
61
+ # Monitors the worker and updates the job progress. If the job's status
62
+ # is changed to 'stopping', the worker is requested to stop.
63
+ def monitor_worker
64
+ Thread.new do
65
+ while running? && !Job.find(id).stopping?
66
+ current_progress = @worker.instance_variable_get("@progress")
67
+
68
+ if current_progress == progress
69
+ sleep 5
70
+ else
71
+ update_attribute(:progress, current_progress)
72
+ sleep 1
73
+ end
74
+ end
75
+
76
+ if Job.find(id).stopping?
77
+ @worker.instance_variable_set("@stopping", true)
78
+ end
79
+ end
80
+
81
+ logger.info("BackgroundFu: Job monitoring started. Job(id: #{id}).")
82
+ end
83
+
84
+ # Closes database connections left after finished threads.
85
+ def cleanup_after_threads
86
+ ActiveRecord::Base.verify_active_connections!
87
+ end
88
+
89
+ def elapsed
90
+ (updated_at.to_f - started_at.to_f).to_i if !pending?
91
+ end
92
+
93
+ # seconds to go, based on estimated and progress
94
+ def estimated
95
+ ((elapsed * 100) / progress) - elapsed if running? && (1..99).include?(progress.to_i)
96
+ end
97
+
98
+ end
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/ar2dm'
2
+
3
+ class MerbBackground::Job
4
+ include DataMapper::Resource
5
+
6
+ property :id, Serial
7
+ property :worker_class, String, :size => 255
8
+ property :worker_method, String, :size => 255
9
+ property :args, Yaml
10
+ property :result, Yaml
11
+ property :priority, Integer
12
+ property :progress, Integer
13
+ property :state, String
14
+ property :lock_version, Integer, :default => 0
15
+
16
+ property :start_at, DateTime
17
+ property :started_at, DateTime
18
+
19
+ property :created_at, DateTime
20
+ property :updated_at, DateTime
21
+
22
+ before :save, :setup_state
23
+ before :save, :setup_priority
24
+ before :save, :setup_start_at
25
+
26
+ validates_present :worker_class, :worker_method
27
+
28
+ include DataMapper::ActiveRecordMethods
29
+
30
+ def self.ready
31
+ all(:start_at.lte => Time.now)
32
+ end
33
+ end
34
+
@@ -0,0 +1,103 @@
1
+ namespace :slices do
2
+ namespace :merb_background do
3
+
4
+ desc "Install MerbBackground"
5
+ task :install => [:preflight, :setup_directories, :copy_assets, :migrate]
6
+
7
+ desc "Test for any dependencies"
8
+ task :preflight do # see slicetasks.rb
9
+ end
10
+
11
+ desc "Setup directories"
12
+ task :setup_directories do
13
+ puts "Creating directories for host application"
14
+ MerbBackground.mirrored_components.each do |type|
15
+ if File.directory?(MerbBackground.dir_for(type))
16
+ if !File.directory?(dst_path = MerbBackground.app_dir_for(type))
17
+ relative_path = dst_path.relative_path_from(Merb.root)
18
+ puts "- creating directory :#{type} #{File.basename(Merb.root) / relative_path}"
19
+ mkdir_p(dst_path)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ # desc "Copy stub files to host application"
26
+ # task :stubs do
27
+ # puts "Copying stubs for MerbBackground - resolves any collisions"
28
+ # copied, preserved = MerbBackground.mirror_stubs!
29
+ # puts "- no files to copy" if copied.empty? && preserved.empty?
30
+ # copied.each { |f| puts "- copied #{f}" }
31
+ # preserved.each { |f| puts "! preserved override as #{f}" }
32
+ # end
33
+
34
+ # desc "Copy stub files and views to host application"
35
+ # task :patch => [ "stubs", "freeze:views" ]
36
+
37
+ desc "Copy public assets to host application"
38
+ task :copy_assets do
39
+ puts "Copying assets for MerbBackground - resolves any collisions"
40
+ copied, preserved = MerbBackground.mirror_public!
41
+ puts "- no files to copy" if copied.empty? && preserved.empty?
42
+ copied.each { |f| puts "- copied #{f}" }
43
+ preserved.each { |f| puts "! preserved override as #{f}" }
44
+ end
45
+
46
+ desc "Migrate the database"
47
+ task :migrate do # see slicetasks.rb
48
+ end
49
+
50
+ desc "Freeze MerbBackground into your app (only merb_background/app)"
51
+ task :freeze => [ "freeze:app" ]
52
+
53
+ namespace :freeze do
54
+
55
+ # desc "Freezes MerbBackground by installing the gem into application/gems"
56
+ # task :gem do
57
+ # ENV["GEM"] ||= "merb_background"
58
+ # Rake::Task['slices:install_as_gem'].invoke
59
+ # end
60
+
61
+ desc "Freezes MerbBackground by copying all files from merb_background/app to your application"
62
+ task :app do
63
+ puts "Copying all merb_background/app files to your application - resolves any collisions"
64
+ copied, preserved = MerbBackground.mirror_app!
65
+ puts "- no files to copy" if copied.empty? && preserved.empty?
66
+ copied.each { |f| puts "- copied #{f}" }
67
+ preserved.each { |f| puts "! preserved override as #{f}" }
68
+ end
69
+
70
+ desc "Freeze all views into your application for easy modification"
71
+ task :views do
72
+ puts "Copying all view templates to your application - resolves any collisions"
73
+ copied, preserved = MerbBackground.mirror_files_for :view
74
+ puts "- no files to copy" if copied.empty? && preserved.empty?
75
+ copied.each { |f| puts "- copied #{f}" }
76
+ preserved.each { |f| puts "! preserved override as #{f}" }
77
+ end
78
+
79
+ desc "Freeze all models into your application for easy modification"
80
+ task :models do
81
+ puts "Copying all models to your application - resolves any collisions"
82
+ copied, preserved = MerbBackground.mirror_files_for :model
83
+ puts "- no files to copy" if copied.empty? && preserved.empty?
84
+ copied.each { |f| puts "- copied #{f}" }
85
+ preserved.each { |f| puts "! preserved override as #{f}" }
86
+ end
87
+
88
+ desc "Freezes MerbBackground as a gem and copies over merb_background/app"
89
+ task :app_with_gem => [:gem, :app]
90
+
91
+ desc "Freezes MerbBackground by unpacking all files into your application"
92
+ task :unpack do
93
+ puts "Unpacking MerbBackground files to your application - resolves any collisions"
94
+ copied, preserved = MerbBackground.unpack_slice!
95
+ puts "- no files to copy" if copied.empty? && preserved.empty?
96
+ copied.each { |f| puts "- copied #{f}" }
97
+ preserved.each { |f| puts "! preserved override as #{f}" }
98
+ end
99
+
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,21 @@
1
+ namespace :slices do
2
+ namespace :merb_background do
3
+
4
+ # add your own merb_background tasks here
5
+
6
+ # # Uncomment the following lines and edit the pre defined tasks
7
+ #
8
+ # # implement this to test for structural/code dependencies
9
+ # # like certain directories or availability of other files
10
+ # desc "Test for any dependencies"
11
+ # task :preflight do
12
+ # end
13
+ #
14
+ # # implement this to perform any database related setup steps
15
+ desc "Migrate the database"
16
+ task :migrate do
17
+ MerbBackground::Job.auto_upgrade!
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,53 @@
1
+ namespace :slices do
2
+ namespace :merb_background do
3
+
4
+ desc "Run slice specs within the host application context"
5
+ task :spec => [ "spec:explain", "spec:default" ]
6
+
7
+ namespace :spec do
8
+
9
+ slice_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
10
+
11
+ task :explain do
12
+ puts "\nNote: By running MerbBackground specs inside the application context any\n" +
13
+ "overrides could break existing specs. This isn't always a problem,\n" +
14
+ "especially in the case of views. Use these spec tasks to check how\n" +
15
+ "well your application conforms to the original slice implementation."
16
+ end
17
+
18
+ Spec::Rake::SpecTask.new('default') do |t|
19
+ t.spec_opts = ["--format", "specdoc", "--colour"]
20
+ t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
21
+ end
22
+
23
+ desc "Run all model specs, run a spec for a specific Model with MODEL=MyModel"
24
+ Spec::Rake::SpecTask.new('model') do |t|
25
+ t.spec_opts = ["--format", "specdoc", "--colour"]
26
+ if(ENV['MODEL'])
27
+ t.spec_files = Dir["#{slice_root}/spec/models/**/#{ENV['MODEL']}_spec.rb"].sort
28
+ else
29
+ t.spec_files = Dir["#{slice_root}/spec/models/**/*_spec.rb"].sort
30
+ end
31
+ end
32
+
33
+ desc "Run all request specs, run a spec for a specific request with REQUEST=MyRequest"
34
+ Spec::Rake::SpecTask.new('request') do |t|
35
+ t.spec_opts = ["--format", "specdoc", "--colour"]
36
+ if(ENV['REQUEST'])
37
+ t.spec_files = Dir["#{slice_root}/spec/requests/**/#{ENV['REQUEST']}_spec.rb"].sort
38
+ else
39
+ t.spec_files = Dir["#{slice_root}/spec/requests/**/*_spec.rb"].sort
40
+ end
41
+ end
42
+
43
+ desc "Run all specs and output the result in html"
44
+ Spec::Rake::SpecTask.new('html') do |t|
45
+ t.spec_opts = ["--format", "html"]
46
+ t.libs = ['lib', 'server/lib' ]
47
+ t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+ end
File without changes
@@ -0,0 +1,2 @@
1
+ html, body { margin: 0; padding: 0; }
2
+ #container { width: 800px; margin: 4em auto; padding: 4em 4em 6em 4em; background: #DDDDDD; }
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "MerbBackground (module)" do
4
+
5
+ # Implement your MerbBackground specs here
6
+
7
+ # To spec MerbBackground you need to hook it up to the router like this:
8
+
9
+ # before :all do
10
+ # Merb::Router.prepare { add_slice(:MerbBackground) } if standalone?
11
+ # end
12
+ #
13
+ # after :all do
14
+ # Merb::Router.reset! if standalone?
15
+ # end
16
+ #
17
+ #
18
+ # it "should have proper specs"
19
+
20
+ end
@@ -0,0 +1,30 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
+
3
+ describe "/merb_background/" do
4
+
5
+ before(:all) do
6
+ mount_slice
7
+ end
8
+
9
+ describe "GET /" do
10
+
11
+ before(:each) do
12
+ @response = request("/merb_background/")
13
+ end
14
+
15
+ it "should be successful" do
16
+ @response.status.should be_successful
17
+ end
18
+
19
+ # This is just an example of what you can do
20
+ # You can also use the other webrat methods to click links,
21
+ # fill up forms etc...
22
+ it "should render the default slice layout" do
23
+ @response.should have_tag(:h1, :content => "MerbBackground Slice")
24
+ @response.should have_selector("div#container div#main")
25
+ @response.should have_xpath("//div[@id='container']/div[@id='main']")
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'merb-core'
3
+ require 'merb-slices'
4
+ require 'spec'
5
+
6
+ # Add merb_background.rb to the search path
7
+ Merb::Plugins.config[:merb_slices][:auto_register] = true
8
+ Merb::Plugins.config[:merb_slices][:search_path] = File.join(File.dirname(__FILE__), '..', 'lib', 'merb_background.rb')
9
+
10
+ # Require merb_background.rb explicitly so any dependencies are loaded
11
+ require Merb::Plugins.config[:merb_slices][:search_path]
12
+
13
+ # Using Merb.root below makes sure that the correct root is set for
14
+ # - testing standalone, without being installed as a gem and no host application
15
+ # - testing from within the host application; its root will be used
16
+ Merb.start_environment(
17
+ :testing => true,
18
+ :adapter => 'runner',
19
+ :environment => ENV['MERB_ENV'] || 'test',
20
+ :session_store => 'memory'
21
+ )
22
+
23
+ module Merb
24
+ module Test
25
+ module SliceHelper
26
+
27
+ # The absolute path to the current slice
28
+ def current_slice_root
29
+ @current_slice_root ||= File.expand_path(File.join(File.dirname(__FILE__), '..'))
30
+ end
31
+
32
+ # Whether the specs are being run from a host application or standalone
33
+ def standalone?
34
+ Merb.root == ::MerbBackground.root
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+
41
+ Spec::Runner.configure do |config|
42
+ config.include(Merb::Test::ViewHelper)
43
+ config.include(Merb::Test::RouteHelper)
44
+ config.include(Merb::Test::ControllerHelper)
45
+ config.include(Merb::Test::SliceHelper)
46
+ end
47
+
48
+ # You can add your own helpers here
49
+ #
50
+ Merb::Test.add_helpers do
51
+ def mount_slice
52
+ Merb::Router.prepare { add_slice(:MerbBackground, "merb_background") } if standalone?
53
+ end
54
+
55
+ def dismount_slice
56
+ Merb::Router.reset! if standalone?
57
+ end
58
+ end
@@ -0,0 +1,2 @@
1
+ class MerbBackground::Application < Merb::Controller
2
+ end
@@ -0,0 +1,2 @@
1
+ class MerbBackground::Main < MerbBackground::Application
2
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maiha-merb_background
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - maiha
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-01 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: merb-slices
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.11
24
+ version:
25
+ description: A slice for the Merb framework that offers background tasks for DataMapper
26
+ email: maiha@wota.jp
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ - LICENSE
34
+ - TODO
35
+ files:
36
+ - LICENSE
37
+ - README
38
+ - Rakefile
39
+ - TODO
40
+ - lib/merb_background
41
+ - lib/merb_background/config.rb
42
+ - lib/merb_background/spectasks.rb
43
+ - lib/merb_background/job.rb
44
+ - lib/merb_background/job
45
+ - lib/merb_background/job/dm.rb
46
+ - lib/merb_background/job/ar2dm.rb
47
+ - lib/merb_background/job/ar.rb
48
+ - lib/merb_background/job/bonus_features.rb
49
+ - lib/merb_background/slicetasks.rb
50
+ - lib/merb_background/merbtasks.rb
51
+ - lib/merb_background.rb
52
+ - spec/merb_background_spec.rb
53
+ - spec/spec_helper.rb
54
+ - spec/requests
55
+ - spec/requests/main_spec.rb
56
+ - app/views
57
+ - app/views/layout
58
+ - app/views/layout/merb_background.html.erb
59
+ - app/views/main
60
+ - app/views/main/index.html.erb
61
+ - app/controllers
62
+ - app/controllers/main.rb
63
+ - app/controllers/application.rb
64
+ - app/helpers
65
+ - app/helpers/application_helper.rb
66
+ - public/stylesheets
67
+ - public/stylesheets/master.css
68
+ - public/javascripts
69
+ - public/javascripts/master.js
70
+ - stubs/app
71
+ - stubs/app/controllers
72
+ - stubs/app/controllers/main.rb
73
+ - stubs/app/controllers/application.rb
74
+ has_rdoc: true
75
+ homepage: http://github.com/maiha/merb_background
76
+ post_install_message:
77
+ rdoc_options: []
78
+
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: "0"
92
+ version:
93
+ requirements: []
94
+
95
+ rubyforge_project: merb
96
+ rubygems_version: 1.2.0
97
+ signing_key:
98
+ specification_version: 3
99
+ summary: A slice for the Merb framework that offers background tasks for DataMapper
100
+ test_files: []
101
+