appetizer 0.0.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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in appetizer.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # Appetizer
2
+
3
+ A lightweight init process for Rack apps.
4
+
5
+ ## Assumptions
6
+
7
+ * Running Ruby 1.9.2 or better.
8
+ * Using Bundler.
9
+ * Logging in all envs except `test` is to `STDOUT`. Logging in `test`
10
+ is to `tmp/test.log`.
11
+ * Default internal and external encodings are `Encoding::UTF_8`.
12
+
13
+ ## Load/Init Lifecycle
14
+
15
+ 0. Optionally `require "appetizer/{rack,rake}"`, which will
16
+ 1. `require "appetizer/setup"`, which will
17
+ 2. `load "config/env.local.rb"` if it exists, then
18
+ 3. `load "config/env.rb"` if **it** exists.
19
+ 4. `load "config/env/#{App.env}.rb"` if **it** exists, then
20
+ 5. App code is loaded, but not initialized.
21
+ 6. `App.init!` is called. Happens automatically if step 1 occurred.
22
+ 7. Fire the `initializing` event.
23
+ 8. `load "config/init.rb"` if it exists.
24
+ 9. `load "config/{init/**/*.rb"`, then
25
+ 10. Fire the `initialized` event.
26
+
27
+ ## License (MIT)
28
+
29
+ Copyright 2011 Audiosocket (tech@audiosocket.com)
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining
32
+ a copy of this software and associated documentation files (the
33
+ 'Software'), to deal in the Software without restriction, including
34
+ without limitation the rights to use, copy, modify, merge, publish,
35
+ distribute, sublicense, and/or sell copies of the Software, and to
36
+ permit persons to whom the Software is furnished to do so, subject to
37
+ the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be
40
+ included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
43
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
45
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/appetizer.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.authors = ["Audiosocket"]
5
+ gem.email = ["tech@audiosocket.com"]
6
+ gem.description = "A lightweight init process for Rack apps."
7
+ gem.summary = "Provides Railsish environments and initializers."
8
+ gem.homepage = "https://github.com/audiosocket/appetizer"
9
+
10
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
11
+ gem.files = `git ls-files`.split("\n")
12
+ gem.test_files = `git ls-files -- test/*`.split("\n")
13
+ gem.name = "appetizer"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = "0.0.0"
16
+
17
+ gem.required_ruby_version = ">= 1.9.2"
18
+ end
@@ -0,0 +1,3 @@
1
+ def reload!
2
+ exec "irb -r bundler/setup -r appetizer/setup"
3
+ end
@@ -0,0 +1,15 @@
1
+ module Appetizer
2
+ module Events
3
+ def fire event
4
+ hooks[event].each { |h| h.call }
5
+ end
6
+
7
+ def hooks
8
+ @hooks ||= Hash.new { |h, k| h[k] = [] }
9
+ end
10
+
11
+ def on event, &block
12
+ hooks[event] << block
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ require "appetizer/setup"
2
+
3
+ App.init!
@@ -0,0 +1,62 @@
1
+ module Appetizer
2
+
3
+ # Helps assign values from a low-level structure to a rich domain
4
+ # model. Most useful as an explicit alternative to ActiveRecord's
5
+ # mass-assignment, with an AR object as the target and a params hash
6
+ # as the source.
7
+
8
+ class Populator
9
+ attr_reader :target
10
+ attr_reader :source
11
+
12
+ def initialize target, source, &block
13
+ @target = target
14
+ @source = source
15
+
16
+ yield self if block_given?
17
+ end
18
+
19
+ def nested key, target = nil, &block
20
+ source = self.source[key] || self.source[key.intern]
21
+ target ||= self.target.send key
22
+
23
+ Populator.new target, source, &block if source
24
+ end
25
+
26
+ def set key, value = nil, &block
27
+ value ||= source[key] || source[key.intern]
28
+
29
+ return if value.nil? || (value.respond_to?(:empty?) && value.empty?)
30
+ block ? block[value] : target.send("#{key}=", value)
31
+ end
32
+
33
+ module Helpers
34
+
35
+ # Call `ctor.new` to create a new model object, then populate,
36
+ # save, and JSONify as in `update`.
37
+
38
+ def create ctor, &block
39
+ obj = populate ctor.new, &block
40
+ obj.save!
41
+
42
+ halt 201, json(obj)
43
+ end
44
+
45
+ # Use a populator to assign values from `params` to `obj`,
46
+ # returning it when finished. `&block` is passed a populator
47
+ # instance.
48
+
49
+ def populate obj, &block
50
+ Populator.new(obj, params, &block).target
51
+ end
52
+
53
+ # Populate (see `populate`) an `obj` with `params` data, saving
54
+ # when finished. Returns JSON for `obj`.
55
+
56
+ def update obj, &block
57
+ populate(obj, &block).save!
58
+ json obj
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,29 @@
1
+ module Appetizer
2
+ module Rack
3
+ class Splash
4
+ def initialize root = "public", glob = "**/*", &notfound
5
+ notfound ||= lambda { |env|
6
+ [404, { "Content-Type" => "text/plain" }, []]
7
+ }
8
+
9
+ urls = Dir[File.join root, glob].sort.
10
+ select { |f| File.file? f }.
11
+ map { |f| f[root.length..-1] }
12
+
13
+ @static = ::Rack::Static.new notfound, root: root, urls: urls
14
+ end
15
+
16
+ def call env
17
+ if env["PATH_INFO"] == "/"
18
+ env["PATH_INFO"] = "/index.html"
19
+ end
20
+
21
+ @static.call env
22
+ end
23
+
24
+ def self.call env
25
+ new.call env
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,2 @@
1
+ require "appetizer/init"
2
+ require "appetizer/rack/splash"
@@ -0,0 +1,19 @@
1
+ # Entry point for Rakefiles.
2
+
3
+ require "appetizer/setup"
4
+
5
+ # Tasks from Appetizer. Only the first level, since other requires
6
+ # (like appetizer/rake/test) have their own tasks in subdirs.
7
+
8
+ here = File.expand_path "..", __FILE__
9
+ Dir["#{here}/tasks/*.rake"].sort.each { |f| App.load f }
10
+
11
+ # Load test tasks if the app appears to use tests.
12
+
13
+ if File.directory? "test"
14
+ Dir["#{here}/tasks/test/*.rake"].sort.each { |f| App.load f }
15
+ end
16
+
17
+ # Tasks from the app itself.
18
+
19
+ Dir["lib/tasks/**/*.rake"].sort.each { |f| App.load f }
@@ -0,0 +1,101 @@
1
+ $:.unshift File.expand_path "lib"
2
+
3
+ require "appetizer/events"
4
+ require "fileutils"
5
+ require "logger"
6
+
7
+ Encoding.default_external = Encoding::UTF_8
8
+ Encoding.default_internal = Encoding::UTF_8
9
+
10
+ module App
11
+ extend Appetizer::Events
12
+
13
+ def self.env
14
+ (ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development").intern
15
+ end
16
+
17
+ def self.development?
18
+ :development == env
19
+ end
20
+
21
+ def self.init!
22
+ return true if defined?(@initialized) && @initialized
23
+
24
+ fire :initializing
25
+
26
+ load "config/init.rb" if File.exists? "config/init.rb"
27
+ Dir["config/init/**/*.rb"].sort.each { |f| load f }
28
+
29
+ # If the app has an app/models directory, autorequire 'em.
30
+
31
+ if File.directory? "app/models"
32
+ $:.unshift File.expand_path "app/models"
33
+ Dir["app/models/**/*.rb"].sort.each { |f| require f[11..-4] }
34
+ end
35
+
36
+ fire :initialized
37
+
38
+ @initialized = true
39
+ end
40
+
41
+ def self.load file
42
+ now = Time.now.to_f if ENV["TRACE"]
43
+ Kernel.load file
44
+ p :load => { file => (Time.now.to_f - now) } if ENV["TRACE"]
45
+ end
46
+
47
+ def self.log
48
+ @log ||= Logger.new test? ? "tmp/test.log" : $stdout
49
+ end
50
+
51
+ def self.production?
52
+ :production == env
53
+ end
54
+
55
+ def self.require file
56
+ now = Time.now.to_f if ENV["TRACE"]
57
+ Kernel.require file
58
+ p :require => { file => (Time.now.to_f - now) } if ENV["TRACE"]
59
+ end
60
+
61
+ def self.test?
62
+ :test == env
63
+ end
64
+ end
65
+
66
+ # Set default log formatter and level. WARN for production, INFO
67
+ # otherwise. Override severity with the `LOG_LEVEL` env
68
+ # var. Formatter just prefixes with severity.
69
+
70
+ App.log.formatter = lambda do |severity, time, program, message|
71
+ "[#{severity}] #{message}\n"
72
+ end
73
+
74
+ App.log.level = ENV["LOG_LEVEL"] ?
75
+ Logger.const_get(ENV["LOG_LEVEL"].upcase) :
76
+ App.production? ? Logger::WARN : Logger::INFO
77
+
78
+ def (App.log).write message
79
+ self << message
80
+ end
81
+
82
+ # Make sure tmp exists, a bunch of things may use it.
83
+
84
+ FileUtils.mkdir_p "tmp"
85
+
86
+ # Load the global env files.
87
+
88
+ App.load "config/env.local.rb" if File.exists? "config/env.local.rb"
89
+ App.load "config/env.rb" if File.exists? "config/env.rb"
90
+
91
+ # Load the env-specific file.
92
+
93
+ envfile = "config/env/#{App.env}.rb"
94
+ load envfile if File.exists? envfile
95
+
96
+ if defined? IRB
97
+ IRB.conf[:PROMPT_MODE] = :SIMPLE
98
+
99
+ App.require "appetizer/console"
100
+ App.init!
101
+ end
@@ -0,0 +1,5 @@
1
+ desc "A REPL, run `reload!` to reload."
2
+ task :console do
3
+ require "appetizer/console"
4
+ reload!
5
+ end
@@ -0,0 +1 @@
1
+ task(:init) { App.init! }
@@ -0,0 +1,12 @@
1
+ desc "Run tests."
2
+ task :test do
3
+ cmd = [
4
+ "ruby",
5
+ "-I", "lib:test",
6
+ "-e", "Dir['test/**/*_test.rb'].each { |f| load f }"
7
+ ]
8
+
9
+ exec *cmd
10
+ end
11
+
12
+ task :default => :test
@@ -0,0 +1,43 @@
1
+ ENV["RACK_ENV"] = ENV["RAILS_ENV"] = "test"
2
+
3
+ require "minitest/autorun"
4
+
5
+ module Appetizer
6
+ class Test < MiniTest::Unit::TestCase
7
+ def setup
8
+ self.class.setups.each { |s| instance_eval(&s) }
9
+ end
10
+
11
+ def teardown
12
+ self.class.teardowns.each { |t| instance_eval(&t) }
13
+ end
14
+
15
+ def self.setup &block
16
+ setups << block
17
+ end
18
+
19
+ def self.setups
20
+ @setups ||= (ancestors - [self]).
21
+ map { |a| a.respond_to?(:setups) && a.setups }.
22
+ select { |s| s }.
23
+ compact.flatten.reverse
24
+ end
25
+
26
+ def self.teardown &block
27
+ teardowns.unshift block
28
+ end
29
+
30
+ def self.teardowns
31
+ @teardowns ||= (ancestors - [self]).
32
+ map { |a| a.respond_to?(:teardowns) && a.teardowns }.
33
+ select { |t| t}.
34
+ compact.flatten
35
+ end
36
+
37
+ def self.test name, &block
38
+ define_method "test #{name}", &block
39
+ end
40
+ end
41
+ end
42
+
43
+ require "appetizer/init"
data/lib/appetizer.rb ADDED
@@ -0,0 +1 @@
1
+ require "appetizer/setup"
@@ -0,0 +1,77 @@
1
+ require "appetizer/test"
2
+ require "appetizer/populator"
3
+
4
+ class Appetizer::PopulatorTest < Appetizer::Test
5
+ def test_initialize
6
+ p = Appetizer::Populator.new :target, :source
7
+
8
+ assert_equal :target, p.target
9
+ assert_equal :source, p.source
10
+ end
11
+
12
+ def test_nested
13
+ t2 = mock { expects(:bar=).with "baz" }
14
+
15
+ t = mock do
16
+ expects(:foo).returns t2
17
+ end
18
+
19
+ Appetizer::Populator.new t, foo: { bar: "baz" } do |p|
20
+ p.nested :foo do |p|
21
+ p.set :bar
22
+ end
23
+ end
24
+ end
25
+
26
+ def test_populate_target
27
+ t2 = mock { expects(:bar=).with "baz" }
28
+ t = mock { expects(:foo).never }
29
+
30
+ Appetizer::Populator.new t, foo: { bar: "baz" } do |p|
31
+ p.populate :foo, t2 do |p|
32
+ p.set :bar
33
+ end
34
+ end
35
+ end
36
+
37
+ def test_set
38
+ t = mock { expects(:foo=).with("bar").twice }
39
+
40
+ Appetizer::Populator.new t, foo: "bar" do |p|
41
+ p.set :foo
42
+ p.set "foo"
43
+ end
44
+ end
45
+
46
+ def test_set_missing_source_value
47
+ t = mock { expects(:foo=).never }
48
+
49
+ Appetizer::Populator.new t, Hash.new do |p|
50
+ p.set :foo
51
+ end
52
+ end
53
+
54
+ def test_set_nil_source_value
55
+ t = mock { expects(:foo=).never }
56
+
57
+ Appetizer::Populator.new t, foo: nil do |p|
58
+ p.set :foo
59
+ end
60
+ end
61
+
62
+ def test_set_empty_source_value
63
+ t = mock { expects(:foo=).never }
64
+
65
+ Appetizer::Populator.new t, foo: "" do |p|
66
+ p.set :foo
67
+ end
68
+ end
69
+
70
+ def test_set_with_block
71
+ t = mock { expects(:foo=).with "BAR" }
72
+
73
+ Appetizer::Populator.new t, foo: "bar" do |p|
74
+ p.set(:foo) { |v| p.target.foo = v.upcase }
75
+ end
76
+ end
77
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: appetizer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Audiosocket
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-10 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: A lightweight init process for Rack apps.
15
+ email:
16
+ - tech@audiosocket.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - README.md
24
+ - Rakefile
25
+ - appetizer.gemspec
26
+ - lib/appetizer.rb
27
+ - lib/appetizer/console.rb
28
+ - lib/appetizer/events.rb
29
+ - lib/appetizer/init.rb
30
+ - lib/appetizer/populator.rb
31
+ - lib/appetizer/rack.rb
32
+ - lib/appetizer/rack/splash.rb
33
+ - lib/appetizer/rake.rb
34
+ - lib/appetizer/setup.rb
35
+ - lib/appetizer/tasks/console.rake
36
+ - lib/appetizer/tasks/init.rake
37
+ - lib/appetizer/tasks/test/test.rake
38
+ - lib/appetizer/test.rb
39
+ - test/appetizer/populator_test.rb
40
+ homepage: https://github.com/audiosocket/appetizer
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: 1.9.2
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 1.8.11
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Provides Railsish environments and initializers.
64
+ test_files:
65
+ - test/appetizer/populator_test.rb