lotusrb 0.1.0 → 0.2.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -0
  3. data/README.md +241 -311
  4. data/bin/lotus +4 -0
  5. data/lib/lotus/application.rb +111 -15
  6. data/lib/lotus/cli.rb +85 -0
  7. data/lib/lotus/commands/console.rb +62 -0
  8. data/lib/lotus/commands/new.rb +32 -0
  9. data/lib/lotus/commands/routes.rb +14 -0
  10. data/lib/lotus/commands/server.rb +65 -0
  11. data/lib/lotus/config/assets.rb +24 -10
  12. data/lib/lotus/config/configure.rb +17 -0
  13. data/lib/lotus/config/framework_configuration.rb +30 -0
  14. data/lib/lotus/config/load_paths.rb +1 -1
  15. data/lib/lotus/config/sessions.rb +97 -0
  16. data/lib/lotus/configuration.rb +698 -40
  17. data/lib/lotus/container.rb +30 -0
  18. data/lib/lotus/environment.rb +377 -0
  19. data/lib/lotus/frameworks.rb +17 -28
  20. data/lib/lotus/generators/abstract.rb +31 -0
  21. data/lib/lotus/generators/application/container/.gitkeep +1 -0
  22. data/lib/lotus/generators/application/container/Gemfile.tt +29 -0
  23. data/lib/lotus/generators/application/container/Rakefile.minitest.tt +10 -0
  24. data/lib/lotus/generators/application/container/config/.env.development.tt +2 -0
  25. data/lib/lotus/generators/application/container/config/.env.test.tt +2 -0
  26. data/lib/lotus/generators/application/container/config/.env.tt +1 -0
  27. data/lib/lotus/generators/application/container/config/environment.rb.tt +7 -0
  28. data/lib/lotus/generators/application/container/config.ru.tt +3 -0
  29. data/lib/lotus/generators/application/container/db/.gitkeep +1 -0
  30. data/lib/lotus/generators/application/container/features_helper.rb.tt +11 -0
  31. data/lib/lotus/generators/application/container/lib/app_name.rb.tt +31 -0
  32. data/lib/lotus/generators/application/container/lib/chirp/entities/.gitkeep +1 -0
  33. data/lib/lotus/generators/application/container/lib/chirp/repositories/.gitkeep +1 -0
  34. data/lib/lotus/generators/application/container/spec_helper.rb.tt +7 -0
  35. data/lib/lotus/generators/application/container.rb +70 -0
  36. data/lib/lotus/generators/slice/.gitkeep.tt +1 -0
  37. data/lib/lotus/generators/slice/action.rb.tt +8 -0
  38. data/lib/lotus/generators/slice/application.rb.tt +182 -0
  39. data/lib/lotus/generators/slice/config/mapping.rb.tt +10 -0
  40. data/lib/lotus/generators/slice/config/routes.rb.tt +8 -0
  41. data/lib/lotus/generators/slice/templates/application.html.erb +9 -0
  42. data/lib/lotus/generators/slice/templates/application.html.erb.tt +9 -0
  43. data/lib/lotus/generators/slice/templates/template.html.erb.tt +2 -0
  44. data/lib/lotus/generators/slice/view.rb.tt +5 -0
  45. data/lib/lotus/generators/slice/views/application_layout.rb.tt +7 -0
  46. data/lib/lotus/generators/slice.rb +103 -0
  47. data/lib/lotus/loader.rb +99 -19
  48. data/lib/lotus/middleware.rb +92 -9
  49. data/lib/lotus/rendering_policy.rb +42 -19
  50. data/lib/lotus/routing/default.rb +1 -1
  51. data/lib/lotus/setup.rb +5 -0
  52. data/lib/lotus/templates/welcome.html +49 -0
  53. data/lib/lotus/version.rb +1 -1
  54. data/lib/lotus/views/default.rb +13 -0
  55. data/lib/lotus/views/default_template_finder.rb +19 -0
  56. data/lib/lotus/welcome.rb +14 -0
  57. data/lib/lotus.rb +1 -0
  58. data/lotusrb.gemspec +9 -5
  59. metadata +122 -36
@@ -14,22 +14,47 @@ module Lotus
14
14
  # require 'lotus'
15
15
  #
16
16
  # module Bookshelf
17
- # Application < Lotus::Application
17
+ # class Application < Lotus::Application
18
18
  # end
19
19
  # end
20
20
  class Application
21
- include Lotus::Utils::ClassAttribute
21
+ # Override Ruby's Class#inherited
22
+ #
23
+ # @since 0.2.0
24
+ # @api private
25
+ #
26
+ # @see http://www.ruby-doc.org/core/Class.html#method-i-inherited
27
+ def self.inherited(base)
28
+ super
29
+
30
+ base.class_eval do
31
+ include Lotus::Utils::ClassAttribute
22
32
 
23
- # Application configuration
33
+ class_attribute :configuration
34
+ self.configuration = Configuration.new
35
+ end
36
+
37
+ synchronize do
38
+ applications.add(base)
39
+ end
40
+ end
41
+
42
+ # Registry of Lotus applications in the current Ruby process
24
43
  #
25
- # @since 0.1.0
44
+ # @return [Set] a set of all the registered applications
45
+ #
46
+ # @since 0.2.0
26
47
  # @api private
27
- class_attribute :configuration
28
- self.configuration = Configuration.new
48
+ def self.applications
49
+ synchronize do
50
+ @@applications ||= Set.new
51
+ end
52
+ end
29
53
 
30
54
  # Configure the application.
31
55
  # It yields the given block in the context of the configuration
32
56
  #
57
+ # @param environment [Symbol,nil] the configuration environment name
33
58
  # @param blk [Proc] the configuration block
34
59
  #
35
60
  # @since 0.1.0
@@ -46,8 +71,8 @@ module Lotus
46
71
  # end
47
72
  # end
48
73
  # end
49
- def self.configure(&blk)
50
- configuration.configure(&blk)
74
+ def self.configure(environment = nil, &blk)
75
+ configuration.configure(environment, &blk)
51
76
  end
52
77
 
53
78
  # Return the routes for this application
@@ -67,16 +92,68 @@ module Lotus
67
92
  # @api private
68
93
  attr_writer :routes
69
94
 
95
+ # Rendering policy
96
+ #
97
+ # @param [Lotus::RenderingPolicy]
98
+ #
99
+ # @since 0.2.0
100
+ # @api private
101
+ attr_accessor :renderer
102
+
70
103
  # Initialize and load a new instance of the application
71
104
  #
72
105
  # @return [Lotus::Application] a new instance of the application
73
106
  #
74
107
  # @since 0.1.0
75
108
  def initialize
76
- @loader = Lotus::Loader.new(self)
77
- @loader.load!
109
+ self.class.load!(self)
110
+ end
78
111
 
79
- @rendering_policy = RenderingPolicy.new(configuration)
112
+ # Eager load the application configuration, by activating the framework
113
+ # duplication mechanisms.
114
+ #
115
+ # @param application [Lotus::Application, Class<Lotus::Application>]
116
+ # @return void
117
+ #
118
+ # @since 0.1.1
119
+ #
120
+ # @example
121
+ # require 'lotus'
122
+ #
123
+ # module OneFile
124
+ # class Application < Lotus::Application
125
+ # configure do
126
+ # routes do
127
+ # get '/', to: 'dashboard#index'
128
+ # end
129
+ # end
130
+ #
131
+ # load!
132
+ # end
133
+ #
134
+ # module Controllers::Dashboard
135
+ # include OneFile::Controller
136
+ #
137
+ # action 'Index' do
138
+ # def call(params)
139
+ # self.body = 'Hello!'
140
+ # end
141
+ # end
142
+ # end
143
+ # end
144
+ def self.load!(application = self)
145
+ Lotus::Loader.new(application).load!
146
+ end
147
+
148
+ # Preload all the registered applications
149
+ #
150
+ # @return [void]
151
+ #
152
+ # @since 0.2.0
153
+ def self.preload!
154
+ synchronize do
155
+ applications.each(&:load!)
156
+ end
80
157
  end
81
158
 
82
159
  # Return the configuration for this application
@@ -89,6 +166,14 @@ module Lotus
89
166
  self.class.configuration
90
167
  end
91
168
 
169
+ # Return the application name
170
+ #
171
+ # @since 0.2.0
172
+ # @api private
173
+ def name
174
+ self.class.name
175
+ end
176
+
92
177
  # Process a request.
93
178
  # This method makes Lotus applications compatible with the Rack protocol.
94
179
  #
@@ -101,9 +186,8 @@ module Lotus
101
186
  # @see http://rack.github.io
102
187
  # @see Lotus::Application#middleware
103
188
  def call(env)
104
- middleware.call(env).tap do |response|
105
- @rendering_policy.render(response)
106
- end
189
+ renderer.render(env,
190
+ middleware.call(env))
107
191
  end
108
192
 
109
193
  # Rack middleware stack
@@ -115,7 +199,19 @@ module Lotus
115
199
  #
116
200
  # @see Lotus::Middleware
117
201
  def middleware
118
- @middleware ||= Lotus::Middleware.new(self)
202
+ @middleware ||= configuration.middleware
203
+ end
204
+
205
+ private
206
+
207
+ # Yields the given block in a critical section
208
+ #
209
+ # @since 0.2.0
210
+ # @api private
211
+ def self.synchronize
212
+ Mutex.new.synchronize do
213
+ yield
214
+ end
119
215
  end
120
216
  end
121
217
  end
data/lib/lotus/cli.rb ADDED
@@ -0,0 +1,85 @@
1
+ require 'thor'
2
+ require 'lotus/environment'
3
+ require 'lotus/version'
4
+
5
+ module Lotus
6
+ class Cli < Thor
7
+ include Thor::Actions
8
+
9
+ desc 'version', 'prints Lotus version'
10
+ def version
11
+ puts Lotus::VERSION
12
+ end
13
+
14
+ desc 'server', 'starts a lotus server'
15
+ method_option :port, aliases: '-p', desc: 'The port to run the server on, '
16
+ method_option :server, desc: 'choose a specific Rack::Handler, e.g. webrick, thin etc'
17
+ method_option :rackup, desc: 'a rackup configuration file path to load (config.ru)'
18
+ method_option :host, desc: 'the host address to bind to'
19
+ method_option :debug, desc: 'turn on debug output'
20
+ method_option :warn, desc: 'turn on warnings'
21
+ method_option :daemonize, desc: 'if true, the server will daemonize itself (fork, detach, etc)'
22
+ method_option :pid, desc: 'path to write a pid file after daemonize'
23
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
24
+ method_option :code_reloading, desc: 'code reloading', type: :boolean, default: true
25
+ method_option :help, aliases: '-h', desc: 'displays the usage message'
26
+
27
+ def server
28
+ if options[:help]
29
+ invoke :help, ['server']
30
+ else
31
+ require 'lotus/commands/server'
32
+ Lotus::Commands::Server.new(environment).start
33
+ end
34
+ end
35
+
36
+ desc 'console', 'starts a lotus console'
37
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
38
+ method_option :engine, desc: 'choose a specific console engine: irb/pry/ripl (irb)'
39
+ method_option :help, aliases: '-h', desc: 'displays the usage method'
40
+
41
+ def console
42
+ if options[:help]
43
+ invoke :help, ['console']
44
+ else
45
+ require 'lotus/commands/console'
46
+ Lotus::Commands::Console.new(environment).start
47
+ end
48
+ end
49
+
50
+ desc 'routes', 'prints routes'
51
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
52
+ method_option :help, aliases: '-h', desc: 'displays the usage method'
53
+
54
+ def routes
55
+ if options[:help]
56
+ invoke :help, ['routes']
57
+ else
58
+ require 'lotus/commands/routes'
59
+ Lotus::Commands::Routes.new(environment).start
60
+ end
61
+ end
62
+
63
+ desc 'new', 'generates a new application'
64
+ method_option :architecture, aliases: '-a', desc: 'application architecture', type: :string, default: 'container'
65
+ method_option :application, desc: 'application name', type: :string, default: 'web'
66
+ method_option :application_base_url, desc: 'application base url', type: :string, default: '/'
67
+ method_option :lotus_head, desc: 'use Lotus HEAD', type: :boolean, default: false
68
+ method_option :help, aliases: '-h', desc: 'displays the usage method'
69
+
70
+ def new(name = nil)
71
+ if options[:help]
72
+ invoke :help, ['new']
73
+ else
74
+ require 'lotus/commands/new'
75
+ Lotus::Commands::New.new(name, environment, self).start
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def environment
82
+ Lotus::Environment.new(options)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,62 @@
1
+ module Lotus
2
+ module Commands
3
+ class Console
4
+ module Methods
5
+ def reload!
6
+ puts 'Reloading...'
7
+ Kernel.exec "#{$0} console"
8
+ end
9
+ end
10
+
11
+ ENGINES = {
12
+ 'pry' => 'Pry',
13
+ 'ripl' => 'Ripl',
14
+ 'irb' => 'IRB'
15
+ }.freeze
16
+
17
+ attr_reader :options
18
+
19
+ def initialize(environment)
20
+ @environment = environment
21
+ @options = environment.to_options
22
+ end
23
+
24
+ def start
25
+ # Clear out ARGV so Pry/IRB don't attempt to parse the rest
26
+ ARGV.shift until ARGV.empty?
27
+ require @environment.env_config.to_s
28
+
29
+ # Add convenience methods to the main:Object binding
30
+ TOPLEVEL_BINDING.eval('self').send(:include, Methods)
31
+ Lotus::Application.preload!
32
+
33
+ engine.start
34
+ end
35
+
36
+ def engine
37
+ load_engine options.fetch(:engine) { engine_lookup }
38
+ end
39
+
40
+ private
41
+
42
+ def engine_lookup
43
+ (ENGINES.find { |_, klass| Object.const_defined?(klass) } || default_engine).first
44
+ end
45
+
46
+ def default_engine
47
+ ENGINES.to_a.last
48
+ end
49
+
50
+ def load_engine(engine)
51
+ require engine
52
+ rescue LoadError
53
+ ensure
54
+ return Object.const_get(
55
+ ENGINES.fetch(engine) {
56
+ raise ArgumentError.new("Unknown console engine: #{ engine }")
57
+ }
58
+ )
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,32 @@
1
+ require 'pathname'
2
+ require 'lotus/utils/string'
3
+ require 'lotus/utils/class'
4
+
5
+ module Lotus
6
+ module Commands
7
+ class New
8
+ GENERATORS_NAMESPACE = "Lotus::Generators::Application::%s".freeze
9
+
10
+ attr_reader :app_name, :source, :target, :cli, :options
11
+
12
+ def initialize(app_name, environment, cli)
13
+ @app_name = app_name
14
+ @options = environment.to_options
15
+ @arch = @options.fetch(:architecture)
16
+
17
+ @target = Pathname.pwd.join(@app_name)
18
+ @source = Pathname.new(@options.fetch(:source) { ::File.dirname(__FILE__) + '/../generators/application/' }).join(@arch)
19
+
20
+ @cli = cli
21
+
22
+ require "lotus/generators/application/#{ @arch }"
23
+ command = Utils::String.new(@arch).classify
24
+ @command = Utils::Class.load!(GENERATORS_NAMESPACE % command).new(self)
25
+ end
26
+
27
+ def start
28
+ @command.start
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ module Lotus
2
+ module Commands
3
+ class Routes
4
+ def initialize(environment)
5
+ @environment = environment
6
+ end
7
+
8
+ def start
9
+ require @environment.env_config
10
+ puts Lotus::Container.new.routes.inspector.to_s
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,65 @@
1
+ require 'rack'
2
+
3
+ module Lotus
4
+ module Commands
5
+ # Rack compatible server.
6
+ #
7
+ # It is run with:
8
+ #
9
+ # `bundle exec lotus server`
10
+ #
11
+ # It runs the application, by using the server specified in your `Gemfile`
12
+ # (eg. Puma or Unicorn).
13
+ #
14
+ # It enables code reloading by default.
15
+ # This feature is implemented via process fork and requires `shotgun` gem.
16
+ #
17
+ # @since 0.1.0
18
+ # @api private
19
+ class Server < ::Rack::Server
20
+ attr_reader :options
21
+
22
+ def initialize(env)
23
+ @_env = env
24
+ @options = _extract_options(@_env)
25
+
26
+ if code_reloading?
27
+ require 'shotgun'
28
+ @app = Shotgun::Loader.new(@_env.rackup.to_s)
29
+ end
30
+ end
31
+
32
+ # Primarily this removes the ::Rack::Chunked middleware
33
+ # which is the cause of Safari content-length bugs.
34
+ def middleware
35
+ mw = Hash.new { |e, m| e[m] = [] }
36
+ mw["deployment"].concat([::Rack::ContentLength, ::Rack::CommonLogger])
37
+ mw["development"].concat(mw["deployment"] + [::Rack::ShowExceptions, ::Rack::Lint])
38
+ mw
39
+ end
40
+
41
+ def start
42
+ if code_reloading?
43
+ Shotgun.enable_copy_on_write
44
+ Shotgun.preload
45
+ end
46
+
47
+ super
48
+ end
49
+
50
+ private
51
+ def _extract_options(env)
52
+ env.to_options.merge(
53
+ config: env.rackup.to_s,
54
+ Host: env.host,
55
+ Port: env.port,
56
+ AccessLog: []
57
+ )
58
+ end
59
+
60
+ def code_reloading?
61
+ @_env.code_reloading?
62
+ end
63
+ end
64
+ end
65
+ end
@@ -4,26 +4,40 @@ module Lotus
4
4
  #
5
5
  # @since 0.1.0
6
6
  # @api private
7
- class Assets
7
+ class Assets < Utils::LoadPaths
8
8
  DEFAULT_DIRECTORY = 'public'.freeze
9
9
 
10
- def initialize(root, directory)
11
- @path = root.join(directory || DEFAULT_DIRECTORY)
10
+ # @since 0.1.0
11
+ # @api private
12
+ def initialize(root)
13
+ @root = root
14
+ @paths = Array(DEFAULT_DIRECTORY)
12
15
  end
13
16
 
17
+ # @since 0.1.0
18
+ # @api private
14
19
  def entries
15
- if @path.exist?
16
- @path.children.map {|child| "/#{ child.basename }" }
17
- else
18
- []
20
+ hash = Hash.new { |k, v| k[v] = [] }
21
+ each do |path|
22
+ if path.exist?
23
+ hash[path.to_s] = path.children.map { |child| "/#{ child.basename }" }
24
+ end
19
25
  end
26
+ hash
20
27
  end
21
28
 
22
- def to_s
23
- @path.to_s
29
+ # @since 0.2.0
30
+ # @api private
31
+ def any?
32
+ @paths.any?
24
33
  end
25
34
 
26
- alias_method :to_str, :to_s
35
+ protected
36
+ # @since 0.1.0
37
+ # @api private
38
+ def realpath(path)
39
+ @root.join(path).realpath
40
+ end
27
41
  end
28
42
  end
29
43
  end
@@ -0,0 +1,17 @@
1
+ require 'lotus/config/mapper'
2
+
3
+ module Lotus
4
+ module Config
5
+ # Define configuration of application of
6
+ # a specific environment
7
+ #
8
+ # @since 0.2.0
9
+ # @api private
10
+ class Configure < Mapper
11
+ private
12
+ def error_message
13
+ 'You must specify a block or a file for configuration definition'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ module Lotus
2
+ module Config
3
+ # Collects all the settings for a given framework configuration and then
4
+ # forwards them when the application is loaded.
5
+ #
6
+ # @since 0.2.0
7
+ # @api private
8
+ class FrameworkConfiguration < BasicObject
9
+ # @since 0.2.0
10
+ # @api private
11
+ def initialize
12
+ @settings = []
13
+ end
14
+
15
+ # @since 0.2.0
16
+ # @api private
17
+ def __apply(configuration)
18
+ @settings.each do |(m, args, blk)|
19
+ configuration.public_send(m, *args, &blk)
20
+ end
21
+ end
22
+
23
+ # @since 0.2.0
24
+ # @api private
25
+ def method_missing(m, *args, &blk)
26
+ @settings.push([m, args, blk])
27
+ end
28
+ end
29
+ end
30
+ end
@@ -13,7 +13,7 @@ module Lotus
13
13
  @root = root
14
14
 
15
15
  each do |path|
16
- Dir.glob(path.join(PATTERN)).each {|file| require file }
16
+ Dir.glob(path.join(PATTERN)).each { |file| require file }
17
17
  end
18
18
  end
19
19
 
@@ -0,0 +1,97 @@
1
+ require 'ipaddr'
2
+ require 'lotus/utils/string'
3
+
4
+ module Lotus
5
+ module Config
6
+ # Sessions configuration
7
+ #
8
+ # @since 0.2.0
9
+ # @api private
10
+ class Sessions
11
+
12
+ # Ruby namespace for Rack session adapters
13
+ #
14
+ # @since 0.2.0
15
+ # @api private
16
+ RACK_NAMESPACE = 'Rack::Session::%s'.freeze
17
+
18
+ # Localhost string for detecting localhost host configuration
19
+ #
20
+ # @since x.x.x
21
+ # @api private
22
+ BLACKLISTED_DOMAINS = %w(localhost).freeze
23
+
24
+ # HTTP sessions configuration
25
+ #
26
+ # @param adapter [Symbol,String,Class] the session adapter
27
+ # @param options [Hash] the optional session options
28
+ # @param configuration [Lotus::Configuration] the application configuration
29
+ #
30
+ # @since 0.2.0
31
+ # @api private
32
+ #
33
+ # @see http://www.rubydoc.info/github/rack/rack/Rack/Session/Abstract/ID
34
+ def initialize(adapter = nil, options = {}, configuration = nil)
35
+ @adapter = adapter
36
+ @options = options
37
+ @configuration = configuration
38
+ end
39
+
40
+ # Check if the sessions are enabled
41
+ #
42
+ # @return [FalseClass,TrueClass] the result of the check
43
+ #
44
+ # @since 0.2.0
45
+ # @api private
46
+ def enabled?
47
+ !!@adapter
48
+ end
49
+
50
+ # Returns the Rack middleware and the options
51
+ #
52
+ # @return [Array] Rack middleware and options
53
+ #
54
+ # @since 0.2.0
55
+ # @api private
56
+ def middleware
57
+ middleware = case @adapter
58
+ when Symbol
59
+ RACK_NAMESPACE % Utils::String.new(@adapter).classify
60
+ else
61
+ @adapter
62
+ end
63
+
64
+ [middleware, options]
65
+ end
66
+
67
+ private
68
+
69
+ # @since 0.2.0
70
+ # @api private
71
+ def options
72
+ default_options.merge(@options)
73
+ end
74
+
75
+ # @since 0.2.0
76
+ # @api private
77
+ def default_options
78
+ if @configuration
79
+ { domain: domain, secure: @configuration.ssl? }
80
+ else
81
+ {}
82
+ end
83
+ end
84
+
85
+ def domain
86
+ domain = @configuration.host
87
+ if !BLACKLISTED_DOMAINS.include?(domain) && !ip_address?(domain)
88
+ domain
89
+ end
90
+ end
91
+
92
+ def ip_address?(string)
93
+ !!IPAddr.new(string) rescue false
94
+ end
95
+ end
96
+ end
97
+ end