cramp 0.13 → 0.14

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/bin/cramp ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'cramp'
4
+ require 'cramp/generators/application'
5
+
6
+ if ['--version', '-v'].include?(ARGV.first)
7
+ puts "Cramp #{Cramp::VERSION}"
8
+ exit(0)
9
+ end
10
+
11
+ if ARGV.first != "new"
12
+ ARGV[0] = "--help"
13
+ else
14
+ ARGV.shift
15
+ end
16
+
17
+ Cramp::Generators::Application.start
data/lib/cramp.rb CHANGED
@@ -19,7 +19,7 @@ if RUBY_VERSION >= '1.9.1'
19
19
  end
20
20
 
21
21
  module Cramp
22
- VERSION = '0.13'
22
+ VERSION = '0.14'
23
23
 
24
24
  mattr_accessor :logger
25
25
 
@@ -10,12 +10,14 @@ module Cramp
10
10
 
11
11
  class << self
12
12
  def call(env)
13
- controller = new(env).process
13
+ new(env).process
14
14
  end
15
15
  end
16
16
 
17
17
  def initialize(env)
18
18
  @env = env
19
+ @env['websocket.receive_callback'] = method(:_on_data_receive)
20
+
19
21
  @finished = false
20
22
  end
21
23
 
data/lib/cramp/action.rb CHANGED
@@ -51,10 +51,19 @@ module Cramp
51
51
  @body.call(result)
52
52
  end
53
53
 
54
+ def render_websocket(body, *)
55
+ data = ["\x00", body, "\xFF"].map(&method(:encode)) * ''
56
+ @body.call(data)
57
+ end
58
+
54
59
  # Used by SSE
55
60
  def sse_event_id
56
61
  @sse_event_id ||= Time.now.to_i
57
62
  end
58
63
 
64
+ def encode(string, encoding = 'UTF-8')
65
+ string.respond_to?(:force_encoding) ? string.force_encoding(encoding) : string
66
+ end
67
+
59
68
  end
60
69
  end
@@ -4,10 +4,12 @@ module Cramp
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- class_inheritable_accessor :before_start_callbacks, :on_finish_callbacks, :on_start_callback, :instance_reader => false
7
+ class_inheritable_accessor :before_start_callbacks, :on_finish_callbacks, :on_start_callback, :on_data_callbacks, :instance_reader => false
8
+
8
9
  self.before_start_callbacks = []
9
10
  self.on_finish_callbacks = []
10
11
  self.on_start_callback = []
12
+ self.on_data_callbacks = []
11
13
  end
12
14
 
13
15
  module ClassMethods
@@ -22,6 +24,10 @@ module Cramp
22
24
  def on_start(*methods)
23
25
  self.on_start_callback += methods
24
26
  end
27
+
28
+ def on_data(*methods)
29
+ self.on_data_callbacks += methods
30
+ end
25
31
  end
26
32
 
27
33
  def before_start(n = 0)
@@ -50,5 +56,14 @@ module Cramp
50
56
  EM.next_tick { yield }
51
57
  end
52
58
 
59
+ def _on_data_receive(data)
60
+ data = data.split(/\000([^\377]*)\377/).select{|d| !d.empty? }.collect{|d| d.gsub(/^\x00|\xff$/, '') }
61
+ self.class.on_data_callbacks.each do |callback|
62
+ data.each do |message|
63
+ EM.next_tick { send(callback, message) }
64
+ end
65
+ end
66
+ end
67
+
53
68
  end
54
69
  end
@@ -0,0 +1,97 @@
1
+ require 'thor/group'
2
+ require 'active_support/core_ext/string/strip'
3
+ require 'active_support/core_ext/string/inflections'
4
+ require 'active_support/core_ext/object/blank'
5
+
6
+ module Cramp
7
+ module Generators
8
+
9
+ class Application < Thor::Group
10
+ include Thor::Actions
11
+
12
+ argument :application_path, :type => :string
13
+ class_option :with_active_record, :type => :boolean, :aliases => "-M", :default => false, :desc => "Configures Active Record"
14
+
15
+ def initialize(*args)
16
+ raise Thor::Error, "No application name supplied. Please run: cramp --help" if args[0].blank?
17
+
18
+ super
19
+ end
20
+
21
+ def self.source_root
22
+ @_source_root ||= File.join(File.dirname(__FILE__), "templates/application")
23
+ end
24
+
25
+ def self.banner
26
+ "cramp new #{self.arguments.map(&:usage).join(' ')} [options]"
27
+ end
28
+
29
+ def create_root
30
+ self.destination_root = File.expand_path(application_path, destination_root)
31
+ valid_const?
32
+
33
+ empty_directory '.'
34
+ FileUtils.cd(destination_root)
35
+ end
36
+
37
+ def create_root_files
38
+ template 'config.ru'
39
+ template 'Gemfile'
40
+ template 'application.rb'
41
+
42
+ empty_directory "public"
43
+ empty_directory "public/javascripts"
44
+ end
45
+
46
+ def create_config
47
+ empty_directory "config"
48
+
49
+ inside "config" do
50
+ template "routes.rb"
51
+ template 'database.yml' if active_record?
52
+ end
53
+ end
54
+
55
+ def create_home_action
56
+ empty_directory "app/actions"
57
+
58
+ inside "app/actions" do
59
+ template "home_action.rb"
60
+ end
61
+ end
62
+
63
+ def create_models
64
+ if active_record?
65
+ empty_directory "app/models"
66
+ end
67
+ end
68
+
69
+ protected
70
+
71
+ def active_record?
72
+ options[:with_active_record]
73
+ end
74
+
75
+ def app_name
76
+ @app_name ||= File.basename(destination_root)
77
+ end
78
+
79
+ def app_const
80
+ @app_const ||= "#{app_const_base}::Application"
81
+ end
82
+
83
+ def app_const_base
84
+ @app_const_base ||= app_name.gsub(/\W/, '_').squeeze('_').camelize
85
+ end
86
+
87
+ def valid_const?
88
+ if app_const =~ /^\d/
89
+ raise Thor::Error, "Invalid application name #{app_name}. Please give a name which does not start with numbers."
90
+ elsif Object.const_defined?(app_const_base)
91
+ raise Thor::Error, "Invalid application name #{app_name}, constant #{app_const_base} is already in use. Please choose another application name."
92
+ end
93
+ end
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,32 @@
1
+ source :rubygems
2
+
3
+ gem 'cramp'
4
+
5
+ # Async webserver for running a cramp application
6
+ gem 'thin'
7
+
8
+ # Rack based routing
9
+ gem 'http_router'
10
+
11
+ # Collection of async-proof rack middlewares - https://github.com/rkh/async-rack.git
12
+ gem 'async-rack'
13
+
14
+ # For async Active Record models
15
+ <% if active_record? -%>
16
+ gem 'mysql2', '~> 0.2.11'
17
+ gem 'activerecord'
18
+ <% else -%>
19
+ # gem 'mysql2', '~> 0.2.11'
20
+ # gem 'activerecord'
21
+ <% end -%>
22
+
23
+ # Using Fibers + async callbacks to emulate synchronous programming
24
+ # gem 'em-synchrony'
25
+
26
+ # Generic interface to multiple Ruby template engines - https://github.com/rtomayko/tilt
27
+ # gem 'tilt'
28
+
29
+ group :development do
30
+ # Development gems
31
+ # gem 'ruby-debug19'
32
+ end
@@ -0,0 +1,11 @@
1
+ class HomeAction < Cramp::Action
2
+ <% if active_record? -%>use_fiber_pool do |pool|
3
+ # Checkin database connection after each callback
4
+ pool.generic_callbacks << proc { ActiveRecord::Base.clear_active_connections! }
5
+ end
6
+
7
+ <% end %>def start
8
+ render "Hello World!"
9
+ finish
10
+ end
11
+ end
@@ -0,0 +1,36 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+
4
+ module <%= app_const_base %>
5
+ class Application
6
+
7
+ def self.root(path = nil)
8
+ @_root ||= File.expand_path(File.dirname(__FILE__))
9
+ path ? File.join(@_root, path.to_s) : @_root
10
+ end
11
+
12
+ def self.env
13
+ @_env ||= ENV['RACK_ENV'] || 'development'
14
+ end
15
+
16
+ def self.routes
17
+ @_routes ||= eval(File.read('./config/routes.rb'))
18
+ end
19
+
20
+ <% if active_record? %>def self.database_config
21
+ @_database_config ||= YAML.load(File.read('./config/database.yml')).with_indifferent_access
22
+ end
23
+
24
+ <% end %># Initialize the application
25
+ def self.initialize!<% if active_record? %>
26
+ ActiveRecord::Base.configurations = <%= app_const %>.database_config
27
+ ActiveRecord::Base.establish_connection(<%= app_const %>.env)<% end %>
28
+ end
29
+
30
+ end
31
+ end
32
+
33
+ Bundler.require(:default, <%= app_const %>.env)
34
+
35
+ # Preload application classes
36
+ Dir['./app/**/*.rb'].each {|f| require f}
@@ -0,0 +1,25 @@
1
+ require './application'
2
+ <%= app_const %>.initialize!
3
+
4
+ # Development middlewares
5
+ if <%= app_const %>.env == 'development'
6
+ use AsyncRack::CommonLogger
7
+
8
+ # Enable code reloading on every request
9
+ use Rack::Reloader, 0
10
+
11
+ # Serve assets from /public
12
+ use Rack::Static, :urls => ["/javascripts"], :root => <%= app_const %>.root(:public)
13
+ end
14
+
15
+ # Running thin :
16
+ #
17
+ # bundle exec thin --max-persistent-conns 1024 --timeout 0 -R config.ru start
18
+ #
19
+ # Vebose mode :
20
+ #
21
+ # Very useful when you want to view all the data being sent/received by thin
22
+ #
23
+ # bundle exec thin --max-persistent-conns 1024 --timeout 0 -V -R config.ru start
24
+ #
25
+ run <%= app_const %>.routes
@@ -0,0 +1,6 @@
1
+ development:
2
+ adapter: em_mysql2
3
+ database: <%= app_name %>_development
4
+ host: localhost
5
+ username: root
6
+ pool: 100
@@ -0,0 +1,4 @@
1
+ # Check out https://github.com/joshbuddy/http_router for more information on HttpRouter
2
+ HttpRouter.new do
3
+ add('/').to(HomeAction)
4
+ end
@@ -1,47 +1,12 @@
1
1
  module Cramp
2
- class Websocket < Abstract
3
- include PeriodicTimer
4
-
5
- # TODO : Websockets shouldn't need this in an ideal world
6
- include KeepConnectionAlive
7
-
8
- class_inheritable_accessor :on_data_callbacks, :instance_reader => false
9
- self.on_data_callbacks = []
2
+ class Websocket < Action
3
+ self.transport = :websocket
10
4
 
11
5
  class << self
12
6
  def backend=(backend)
13
7
  raise "Websocket backend #{backend} is unknown" unless [:thin, :rainbows].include?(backend.to_sym)
14
8
  require "cramp/websocket/#{backend}_backend.rb"
15
9
  end
16
-
17
- def on_data(*methods)
18
- self.on_data_callbacks += methods
19
- end
20
- end
21
-
22
- def process
23
- @env['websocket.receive_callback'] = method(:_on_data_receive)
24
- super
25
- end
26
-
27
- def render(body)
28
- data = ["\x00", body, "\xFF"].map(&method(:encode)) * ''
29
- @body.call(data)
30
- end
31
-
32
- def _on_data_receive(data)
33
- data = data.split(/\000([^\377]*)\377/).select{|d| !d.empty? }.collect{|d| d.gsub(/^\x00|\xff$/, '') }
34
- self.class.on_data_callbacks.each do |callback|
35
- data.each do |message|
36
- EM.next_tick { send(callback, message) }
37
- end
38
- end
39
- end
40
-
41
- protected
42
-
43
- def encode(string, encoding = 'UTF-8')
44
- string.respond_to?(:force_encoding) ? string.force_encoding(encoding) : string
45
10
  end
46
11
 
47
12
  end
@@ -0,0 +1,44 @@
1
+ class Cramp::Websocket::Rainbows < Rainbows::EventMachine::Client
2
+ include Cramp::WebsocketExtension
3
+
4
+ def receive_data(data)
5
+ case @state
6
+ when :websocket
7
+ callback = @env[WEBSOCKET_RECEIVE_CALLBACK]
8
+ callback.call(data) if callback
9
+ else
10
+ super
11
+ end
12
+ end
13
+
14
+ def on_read(data)
15
+ if @state == :headers
16
+ @hp.add_parse(data) or return want_more
17
+ @state = :body
18
+ if 0 == @hp.content_length && !websocket?
19
+ app_call NULL_IO # common case
20
+ else # nil or len > 0
21
+ prepare_request_body
22
+ end
23
+ elsif @state == :body && websocket? && @hp.body_eof?
24
+ @state = :websocket
25
+ @input.rewind
26
+
27
+ handler = @env['HTTP_SEC_WEBSOCKET_KEY1'] &&
28
+ @env['HTTP_SEC_WEBSOCKET_KEY2'] ? Protocol76 : Protocol75
29
+ write(handler.new(@env, websocket_url, @buf).handshake)
30
+ app_call NULL_IO
31
+ else
32
+ super
33
+ end
34
+ rescue => e
35
+ handle_error(e)
36
+ end
37
+
38
+ def write_response(status, headers, body, alive)
39
+ write_headers(status, headers, alive) unless websocket?
40
+ write_body_each(body)
41
+ ensure
42
+ body.close if body.respond_to?(:close)
43
+ end
44
+ end
@@ -1,40 +1,8 @@
1
- require 'rainbows'
2
-
3
- class Rainbows::EventMachine::Client
4
- include Cramp::WebsocketExtension
5
-
6
- def websocket_handshake!
7
- @state = :websocket
8
- end
9
-
10
- def receive_data_with_websocket(data)
11
- case @state
12
- when :websocket
13
- callback = @env[WEBSOCKET_RECEIVE_CALLBACK]
14
- callback.call(data) if callback
15
- else
16
- receive_data_without_websocket(data)
17
- end
18
- end
19
-
20
- alias_method_chain :receive_data, :websocket
21
- end
22
-
23
- class Rainbows::HttpResponse
24
- class << self
25
-
26
- def write_with_magic(socket, rack_response, out = [])
27
- if socket.websocket?
28
- socket.write socket.websocket_upgrade_data
29
- socket.websocket_handshake!
30
-
31
- out = nil # To make sure Rainbows! doesn't send back regular HTTP headers
32
- end
33
-
34
- write_without_magic(socket, rack_response, out)
35
- end
36
-
37
- alias_method_chain :write, :magic
38
- end
39
-
1
+ # :enddoc:
2
+ require "rainbows"
3
+ class Cramp::Websocket
4
+ # we use autoload since Rainbows::EventMachine::Client should only be
5
+ # loaded in the worker proceses and we want to be preload_app-friendly
6
+ autoload :Rainbows, "cramp/websocket/rainbows"
40
7
  end
8
+ Rainbows::O[:em_client_class] = "Cramp::Websocket::Rainbows"
metadata CHANGED
@@ -1,85 +1,81 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: cramp
3
- version: !ruby/object:Gem::Version
4
- hash: 17
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.14'
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 13
9
- version: "0.13"
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Pratik Naik
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2011-07-31 00:00:00 +01:00
12
+ date: 2011-08-05 00:00:00.000000000 +01:00
18
13
  default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
21
16
  name: activesupport
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &2156676820 !ruby/object:Gem::Requirement
24
18
  none: false
25
- requirements:
19
+ requirements:
26
20
  - - ~>
27
- - !ruby/object:Gem::Version
28
- hash: 21
29
- segments:
30
- - 3
31
- - 0
32
- - 9
21
+ - !ruby/object:Gem::Version
33
22
  version: 3.0.9
34
23
  type: :runtime
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: rack
38
24
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: *2156676820
26
+ - !ruby/object:Gem::Dependency
27
+ name: rack
28
+ requirement: &2156676220 !ruby/object:Gem::Requirement
40
29
  none: false
41
- requirements:
30
+ requirements:
42
31
  - - ~>
43
- - !ruby/object:Gem::Version
44
- hash: 29
45
- segments:
46
- - 1
47
- - 2
48
- - 1
49
- version: 1.2.1
32
+ - !ruby/object:Gem::Version
33
+ version: 1.3.2
50
34
  type: :runtime
51
- version_requirements: *id002
52
- - !ruby/object:Gem::Dependency
35
+ prerelease: false
36
+ version_requirements: *2156676220
37
+ - !ruby/object:Gem::Dependency
53
38
  name: eventmachine
39
+ requirement: &2156675640 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: 1.0.0.beta.3
45
+ type: :runtime
54
46
  prerelease: false
55
- requirement: &id003 !ruby/object:Gem::Requirement
47
+ version_requirements: *2156675640
48
+ - !ruby/object:Gem::Dependency
49
+ name: thor
50
+ requirement: &2156674960 !ruby/object:Gem::Requirement
56
51
  none: false
57
- requirements:
52
+ requirements:
58
53
  - - ~>
59
- - !ruby/object:Gem::Version
60
- hash: 59
61
- segments:
62
- - 0
63
- - 12
64
- - 10
65
- version: 0.12.10
54
+ - !ruby/object:Gem::Version
55
+ version: 0.14.6
66
56
  type: :runtime
67
- version_requirements: *id003
57
+ prerelease: false
58
+ version_requirements: *2156674960
68
59
  description: Cramp is a framework for developing asynchronous web applications.
69
60
  email: pratiknaik@gmail.com
70
- executables: []
71
-
61
+ executables:
62
+ - cramp
72
63
  extensions: []
73
-
74
64
  extra_rdoc_files: []
75
-
76
- files:
65
+ files:
77
66
  - MIT-LICENSE
78
67
  - lib/cramp/abstract.rb
79
68
  - lib/cramp/action.rb
80
69
  - lib/cramp/body.rb
81
70
  - lib/cramp/callbacks.rb
82
71
  - lib/cramp/fiber_pool.rb
72
+ - lib/cramp/generators/application.rb
73
+ - lib/cramp/generators/templates/application/app/actions/home_action.rb
74
+ - lib/cramp/generators/templates/application/application.rb
75
+ - lib/cramp/generators/templates/application/config/database.yml
76
+ - lib/cramp/generators/templates/application/config/routes.rb
77
+ - lib/cramp/generators/templates/application/config.ru
78
+ - lib/cramp/generators/templates/application/Gemfile
83
79
  - lib/cramp/keep_connection_alive.rb
84
80
  - lib/cramp/long_polling.rb
85
81
  - lib/cramp/periodic_timer.rb
@@ -87,44 +83,36 @@ files:
87
83
  - lib/cramp/sse.rb
88
84
  - lib/cramp/test_case.rb
89
85
  - lib/cramp/websocket/extension.rb
86
+ - lib/cramp/websocket/rainbows.rb
90
87
  - lib/cramp/websocket/rainbows_backend.rb
91
88
  - lib/cramp/websocket/thin_backend.rb
92
89
  - lib/cramp/websocket.rb
93
90
  - lib/cramp.rb
94
91
  - lib/vendor/fiber_pool.rb
95
- has_rdoc: true
92
+ - bin/cramp
93
+ has_rdoc: false
96
94
  homepage: http://m.onkey.org
97
95
  licenses: []
98
-
99
96
  post_install_message:
100
97
  rdoc_options: []
101
-
102
- require_paths:
98
+ require_paths:
103
99
  - lib
104
- required_ruby_version: !ruby/object:Gem::Requirement
100
+ required_ruby_version: !ruby/object:Gem::Requirement
105
101
  none: false
106
- requirements:
107
- - - ">="
108
- - !ruby/object:Gem::Version
109
- hash: 3
110
- segments:
111
- - 0
112
- version: "0"
113
- required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
107
  none: false
115
- requirements:
116
- - - ">="
117
- - !ruby/object:Gem::Version
118
- hash: 3
119
- segments:
120
- - 0
121
- version: "0"
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
122
112
  requirements: []
123
-
124
113
  rubyforge_project:
125
114
  rubygems_version: 1.6.2
126
115
  signing_key:
127
116
  specification_version: 3
128
117
  summary: Asynchronous web framework.
129
118
  test_files: []
130
-