volt 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/Readme.md +3 -0
  3. data/VERSION +1 -1
  4. data/docs/GUIDE.md +3 -3
  5. data/lib/volt.rb +0 -2
  6. data/lib/volt/cli.rb +14 -1
  7. data/lib/volt/cli/new_gem.rb +116 -0
  8. data/lib/volt/controllers/model_controller.rb +4 -0
  9. data/lib/volt/router/routes.rb +10 -7
  10. data/lib/volt/server.rb +36 -96
  11. data/lib/volt/server/channel_handler.rb +1 -1
  12. data/lib/volt/server/rack/component_files.rb +97 -0
  13. data/lib/volt/server/rack/component_paths.rb +70 -0
  14. data/lib/volt/server/rack/index_files.rb +38 -0
  15. data/lib/volt/server/rack/opal_files.rb +51 -0
  16. data/lib/volt/server/{source_map_server.rb → rack/source_map_server.rb} +0 -0
  17. data/lib/volt/templates/page.rb +3 -5
  18. data/{templates/app/.empty_directory → spec/app/main/assets/js/test1.js} +0 -0
  19. data/spec/app/main/config/dependencies.rb +1 -0
  20. data/{templates/app/home/css/.empty_directory → spec/app/shared/assets/js/test2.js} +0 -0
  21. data/spec/app/shared/config/dependencies.rb +1 -0
  22. data/spec/server/rack/component_files_spec.rb +23 -0
  23. data/spec/server/rack/component_paths_spec.rb +26 -0
  24. data/spec/store/mongo_spec.rb +5 -2
  25. data/templates/newgem/Gemfile.tt +4 -0
  26. data/templates/newgem/LICENSE.txt.tt +22 -0
  27. data/templates/newgem/README.md.tt +29 -0
  28. data/templates/newgem/Rakefile.tt +16 -0
  29. data/templates/newgem/VERSION +1 -0
  30. data/templates/{app/home/models → newgem/app/newgem/assets/css}/.empty_directory +0 -0
  31. data/templates/{public/css/ansi.css → newgem/app/newgem/assets/js/.empty_directory} +0 -0
  32. data/templates/newgem/bin/newgem.tt +3 -0
  33. data/templates/newgem/gitignore.tt +17 -0
  34. data/templates/newgem/lib/newgem.rb.tt +9 -0
  35. data/templates/{public/js/bootstrap.js → newgem/lib/newgem/.empty_directory} +0 -0
  36. data/templates/newgem/newgem.gemspec.tt +28 -0
  37. data/templates/newgem/rspec.tt +2 -0
  38. data/templates/newgem/spec/newgem_spec.rb.tt +11 -0
  39. data/templates/newgem/spec/spec_helper.rb.tt +2 -0
  40. data/templates/newgem/test/minitest_helper.rb.tt +4 -0
  41. data/templates/newgem/test/test_newgem.rb.tt +11 -0
  42. data/templates/{Gemfile.tt → project/Gemfile.tt} +4 -1
  43. data/templates/project/app/.empty_directory +0 -0
  44. data/templates/{app → project/app}/home/config/routes.rb +0 -0
  45. data/templates/{app → project/app}/home/controllers/index_controller.rb +0 -0
  46. data/templates/project/app/home/css/.empty_directory +0 -0
  47. data/templates/project/app/home/models/.empty_directory +0 -0
  48. data/templates/{app → project/app}/home/views/index/about.html +0 -0
  49. data/templates/{app → project/app}/home/views/index/home.html +0 -0
  50. data/templates/{app → project/app}/home/views/index/index.html +0 -0
  51. data/templates/{config.ru → project/config.ru} +1 -1
  52. data/templates/project/public/css/ansi.css +0 -0
  53. data/templates/{public → project/public}/css/bootstrap-theme.css +0 -0
  54. data/templates/{public → project/public}/css/bootstrap.css +0 -0
  55. data/templates/{public → project/public}/css/jumbotron.css +0 -0
  56. data/templates/{public → project/public}/fonts/glyphicons-halflings-regular.eot +0 -0
  57. data/templates/{public → project/public}/fonts/glyphicons-halflings-regular.svg +0 -0
  58. data/templates/{public → project/public}/fonts/glyphicons-halflings-regular.ttf +0 -0
  59. data/templates/{public → project/public}/fonts/glyphicons-halflings-regular.woff +0 -0
  60. data/templates/project/public/index.html +19 -0
  61. data/templates/project/public/js/bootstrap.js +0 -0
  62. data/templates/{public → project/public}/js/jquery-2.0.3.js +0 -0
  63. data/templates/{public → project/public}/js/sockjs-0.2.1.min.js +0 -0
  64. data/templates/{spec → project/spec}/spec_helper.rb +0 -0
  65. metadata +60 -27
  66. data/lib/volt/server/request_handler.rb +0 -16
  67. data/templates/public/index.html +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f31285f8d34376239bed9e92cb9dec093f8331e
4
- data.tar.gz: 9fe3e6d4e61cf93734a72fd92f41df30ffc672f1
3
+ metadata.gz: 5a11e880b0dfbda45e1a83176460f5723748a8f9
4
+ data.tar.gz: 600654cace56d7e79b31f61519dbe11d2b6ecf02
5
5
  SHA512:
6
- metadata.gz: 098e047b75cf968bdd84634d5b1262600678f2765c73e759d5e5f4fb94503b20485ca1146fcf23fd0e039aee9fc186240c9dbfef732ac567deeed68463b04b03
7
- data.tar.gz: 3ef0821f1ec5f15e02d174f6016f2a83a2d9f2c482d805c2baacfdba972b647f69345261bcedd13c9a1763eed7a21fa3df6f2f83c95bbd2185052d45fffbf00e
6
+ metadata.gz: 7a59388069aadc3a45416d72284f56e67b0b1f5a8eb46f5dd8807e0706abfb24bd35e645554892c15e1091837c1193a907c54b42190e1cc2201ff46ff9ec0b5c
7
+ data.tar.gz: b99c2138d7ab5d24bbad34cec8d0ce561fab2717d9b570280bc1ca74c2e06bb71c0a2abe9c6edd8a43614bfdf597d283f0e85f3f8500b283c2736a389e2a25a1
data/Readme.md CHANGED
@@ -1,3 +1,6 @@
1
+ [![Gem Version](https://badge.fury.io/rb/volt.png)](http://badge.fury.io/rb/volt)
2
+ [![Code Climate](https://codeclimate.com/github/voltrb/volt.png)](https://codeclimate.com/github/voltrb/volt)
3
+
1
4
  # Volt
2
5
 
3
6
  NOTE: VOLT IS STILL IN DEVELOPMENT, DON'T USE IT FOR ANYTHING SERIOUS YET
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.3
1
+ 0.2.4
data/docs/GUIDE.md CHANGED
@@ -4,7 +4,7 @@ This guide will take you through creating a basic web application in Volt. This
4
4
 
5
5
  To get started, install volt:
6
6
 
7
- gem install voltframework
7
+ gem install volt
8
8
 
9
9
  Then create a new project:
10
10
 
@@ -19,8 +19,8 @@ This will setup a basic project. Now lets run the server.
19
19
 
20
20
  When a user interacts with a web page, typically we want to do two things:
21
21
 
22
- 1) Change application state
23
- 2) Update the DOM
22
+ 1. Change application state
23
+ 2. Update the DOM
24
24
 
25
25
  For example when a user clicks to add a new todo item to a todo list, we might create a JavaScript object to represent the todo item, then add an item to the list's DOM. A lot of work needs to be done to make sure that the JavaScript object and the DOM always stay in sync.
26
26
 
data/lib/volt.rb CHANGED
@@ -1,8 +1,6 @@
1
1
  class Volt
2
2
  def self.root
3
3
  @root ||= File.expand_path(Dir.pwd)
4
-
5
- # File.expand_path(File.join(File.dirname(__FILE__), "../"))
6
4
  end
7
5
 
8
6
  def self.server?
data/lib/volt/cli.rb CHANGED
@@ -6,7 +6,7 @@ class CLI < Thor
6
6
 
7
7
  desc "new PROJECT_NAME", "generates a new project."
8
8
  def new(name)
9
- directory(".", name)
9
+ directory("project", name)
10
10
 
11
11
  say "Bundling Gems...."
12
12
  `cd #{name} ; bundle`
@@ -26,6 +26,19 @@ class CLI < Thor
26
26
  Thin::Runner.new(['start']).run!
27
27
  end
28
28
 
29
+ desc "gem GEM", "Creates a gem where you can share a component"
30
+ method_option :bin, :type => :boolean, :default => false, :aliases => '-b', :banner => "Generate a binary for your library."
31
+ method_option :test, :type => :string, :lazy_default => 'rspec', :aliases => '-t', :banner => "Generate a test directory for your library: 'rspec' is the default, but 'minitest' is also supported."
32
+ method_option :edit, :type => :string, :aliases => "-e",
33
+ :lazy_default => [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find{|e| !e.nil? && !e.empty? },
34
+ :required => false, :banner => "/path/to/your/editor",
35
+ :desc => "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)"
36
+ def gem(name)
37
+ require 'volt/cli/new_gem'
38
+
39
+ NewGem.new(self, name, options)
40
+ end
41
+
29
42
  def self.source_root
30
43
  File.expand_path(File.join(File.dirname(__FILE__), '../../templates'))
31
44
  end
@@ -0,0 +1,116 @@
1
+ require "net/http"
2
+ require "uri"
3
+ require 'fileutils'
4
+
5
+ # Creates a new "volt" gem, which can be used to easily repackage
6
+ # components.
7
+ class NewGem
8
+ def initialize(thor, name, options)
9
+ @thor = thor
10
+ @component_name = name.chomp("/")
11
+ @name = "volt-" + @component_name # remove trailing slash if present
12
+
13
+ if gem_is_available?
14
+ @thor.say("#{@name} is available! Making gem files.", :green)
15
+ else
16
+ @thor.say("There is already a gem named #{@name}. Please choose a different name.", :red)
17
+ return
18
+ end
19
+
20
+ @options = options
21
+ @namespaced_path = @name.tr('-', '/')
22
+ @opts = gem_options
23
+ @target = File.join(Dir.pwd, @name)
24
+
25
+ copy_files
26
+ copy_options
27
+ end
28
+
29
+ # Check with the rubygems api to see if this gem name is available.
30
+ def gem_is_available?
31
+ @thor.say("Check if #{@name} is available as a gem name.", :yellow)
32
+ uri = URI.parse("https://rubygems.org/api/v1/gems/#{@name}.json")
33
+
34
+ http = Net::HTTP.new(uri.host, uri.port)
35
+ http.use_ssl = true
36
+
37
+ request = Net::HTTP::Get.new(uri.request_uri)
38
+ response = http.request(request)
39
+
40
+ return response.code == "404"
41
+ rescue SocketError => e
42
+ # rubygems is down, skip check
43
+ return true
44
+ end
45
+
46
+ def copy_files
47
+ @thor.directory("newgem/app/newgem", File.join("#{@target}", "app/#{@component_name}"))
48
+ copy("newgem/Gemfile.tt", "Gemfile")
49
+ copy("newgem/Rakefile.tt", "Rakefile")
50
+ copy("newgem/README.md.tt", "README.md")
51
+ copy("newgem/gitignore.tt", ".gitignore")
52
+ copy("newgem/newgem.gemspec.tt", "#{@name}.gemspec")
53
+ copy("newgem/lib/newgem.rb.tt", "lib/#{@namespaced_path}.rb")
54
+ copy("newgem/VERSION", "VERSION")
55
+ FileUtils.mkdir_p(File.join(@target, "lib/#{@namespaced_path}"))
56
+ end
57
+
58
+ def copy_options
59
+ if @options[:bin]
60
+ copy("newgem/bin/newgem.tt", "bin/#{@name}")
61
+ end
62
+ case @options[:test]
63
+ when 'rspec'
64
+ copy("newgem/rspec.tt", ".rspec")
65
+ copy("newgem/spec/spec_helper.rb.tt", "spec/spec_helper.rb")
66
+ copy("newgem/spec/newgem_spec.rb.tt", "spec/#{@namespaced_path}_spec.rb")
67
+ when 'minitest'
68
+ copy("newgem/test/minitest_helper.rb.tt", "test/minitest_helper.rb")
69
+ copy("newgem/test/test_newgem.rb.tt", "test/test_#{@namespaced_path}.rb")
70
+ end
71
+ puts "Initializing git repo in #{@target}"
72
+ Dir.chdir(@target) { `git init`; `git add .` }
73
+
74
+ if @options[:edit]
75
+ run("#{@options["edit"]} \"#{gemspec_dest}\"") # Open gemspec in editor
76
+ end
77
+ end
78
+
79
+ private
80
+ def copy(from, to)
81
+ @thor.template(File.join(from), File.join(@target, to), @opts)
82
+ end
83
+
84
+ def gem_options
85
+ constant_name = get_constant_name
86
+ constant_array = constant_name.split('::')
87
+ git_user_name = `git config user.name`.chomp
88
+ git_user_email = `git config user.email`.chomp
89
+
90
+ opts = {
91
+ :name => @name,
92
+ :namespaced_path => @namespaced_path,
93
+ :constant_name => constant_name,
94
+ :constant_array => constant_array,
95
+ :author => git_user_name.empty? ? "TODO: Write your name" : git_user_name,
96
+ :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
97
+ :test => @options[:test],
98
+ :volt_version_base => volt_version_base
99
+ }
100
+
101
+ return opts
102
+ end
103
+
104
+ def volt_version_base
105
+ version_path = File.join(File.dirname(__FILE__), '../../../VERSION')
106
+ File.read(version_path).split('.').tap {|v| v[v.size-1] = 0 }.join('.')
107
+ end
108
+
109
+ def get_constant_name
110
+ constant_name = @name.split('_').map{|p| p[0..0].upcase + p[1..-1] }.join
111
+ constant_name = constant_name.split('-').map{|q| q[0..0].upcase + q[1..-1] }.join('::') if constant_name =~ /-/
112
+
113
+ return constant_name
114
+ end
115
+
116
+ end
@@ -19,6 +19,10 @@ class ModelController
19
19
  $page.url
20
20
  end
21
21
 
22
+ def channel
23
+ $page.channel
24
+ end
25
+
22
26
  def controller
23
27
  @controller ||= ReactiveValue.new(Model.new)
24
28
  end
@@ -12,19 +12,20 @@ class Routes
12
12
  end
13
13
 
14
14
  def get(path, options)
15
- if path.index(':')
16
- sections = path.split(/([:][^:\/]+)/)
15
+ if path.index('{') && path.index('}')
16
+ sections = path.split(/(\{[^\}]+\})/)
17
+ sections = sections.reject {|v| v == '' }
17
18
 
18
19
  sections.each do |section|
19
- if section[0] == ':'
20
- options[section[1..-1]] = nil
20
+ if section[0] == '{' && section[-1] == '}'
21
+ options[section[1..-2]] = nil
21
22
  end
22
23
  end
23
24
  path = Proc.new do |params|
24
25
  # Create a path using the params in the path
25
26
  sections.map do |section|
26
- if section[0] == ':'
27
- params[section[1..-1]]
27
+ if section[0] == '{' && section[-1] == '}'
28
+ params[section[1..-2]]
28
29
  else
29
30
  section
30
31
  end
@@ -73,7 +74,9 @@ class Routes
73
74
 
74
75
  def params_match_options?(params, options)
75
76
  options.each_pair do |key, value|
76
- if value == nil || value != params.send(key)
77
+ # A nil value means it can match anything, so we don't want to
78
+ # fail on nil.
79
+ if value != nil && value != params.send(key)
77
80
  return false
78
81
  end
79
82
  end
data/lib/volt/server.rb CHANGED
@@ -8,114 +8,54 @@ require "sprockets-sass"
8
8
  require "sass"
9
9
 
10
10
  require 'volt/extra_core/extra_core'
11
- require 'volt/server/request_handler'
12
11
  require 'volt/server/component_handler'
13
12
  if RUBY_PLATFORM != 'java'
14
13
  require 'volt/server/channel_handler'
15
14
  end
16
- require 'volt/server/source_map_server'
17
-
18
- class Index
19
-
20
- def initialize(app, files)
21
- @app = app
22
- @files = files
23
- end
24
-
25
- def call(env)
26
- if %w[/ /demo /blog /todos /page3 /page4].include?(env['PATH_INFO']) || env['PATH_INFO'][0..5] == '/todos'
27
- [200, { 'Content-Type' => 'text/html' }, [html]]
28
- else
29
- @app.call env
30
- end
31
- end
32
-
33
- def html
34
- index_path = File.expand_path(File.join(Dir.pwd, "public/index.html"))
35
- html = File.read(index_path)
36
-
37
- ERB.new(html).result(binding)
38
- end
39
- end
40
-
41
-
42
-
43
- SOURCE_MAPS = !!ENV['MAPS']
44
-
45
- Opal::Processor.source_map_enabled = SOURCE_MAPS
46
- # Opal::Processor.arity_check_enabled = true
47
- # Opal::Processor.dynamic_require_severity = :raise
48
-
15
+ require 'volt/server/rack/component_paths'
16
+ require 'volt/server/rack/index_files'
17
+ require 'volt/server/rack/opal_files'
49
18
 
50
19
 
51
20
  class Server
52
- def self.app
53
-
54
- builder = Rack::Builder.new do
55
- use Rack::CommonLogger
56
- # run RequestHandler.new
57
-
58
- use Rack::ShowExceptions
59
-
60
- map '/components' do
61
- run ComponentHandler.new
62
- end
63
-
64
- environment = Opal::Environment.new
65
-
66
- app_path = File.expand_path(File.join(Dir.pwd, "app"))
67
- environment.append_path(app_path)
68
-
69
- volt_gem_lib_path = File.expand_path(File.join(File.dirname(__FILE__), ".."))
70
- environment.append_path(volt_gem_lib_path)
71
-
72
- # Add the opal load paths
73
- Opal.paths.each do |path|
74
- environment.append_path(path)
75
- end
76
-
77
- # opal-jquery gem
78
- spec = Gem::Specification.find_by_name("opal-jquery")
79
- environment.append_path(spec.gem_dir + "/opal")
80
-
81
-
82
- map '/assets' do
83
- run environment
84
- end
85
-
86
- if SOURCE_MAPS
87
- source_maps = SourceMapServer.new(environment)
21
+ def initialize
22
+ @app_path = File.expand_path(File.join(Dir.pwd, "app"))
23
+ @component_paths = ComponentPaths.new
24
+ end
88
25
 
89
- map(source_maps.prefix) do
90
- run source_maps
91
- end
92
- end
26
+ def app
27
+ @app = Rack::Builder.new
28
+ @app.use Rack::CommonLogger
29
+ @app.use Rack::ShowExceptions
93
30
 
94
- if RUBY_PLATFORM != 'java'
95
- map "/channel" do
96
- run Rack::SockJS.new(ChannelHandler)#, :websocket => false
97
- end
98
- end
31
+ @app.map '/components' do
32
+ run ComponentHandler.new
33
+ end
99
34
 
100
- if SOURCE_MAPS
101
- files = environment['volt/templates/page'].to_a.map {|v| v.logical_path }
102
- else
103
- files = []
35
+ # Serve the main html files from public, also figure out
36
+ # which JS/CSS files to serve.
37
+ @app.use IndexFiles, @component_paths
38
+
39
+ # Serve the opal files
40
+ OpalFiles.new(@app, @app_path, @component_paths)
41
+
42
+ # Handle socks js connection
43
+ if RUBY_PLATFORM != 'java'
44
+ @app.map "/channel" do
45
+ run Rack::SockJS.new(ChannelHandler)#, :websocket => false
104
46
  end
105
-
106
- use Index, files
107
-
108
- use Rack::Static,
109
- :urls => ["/"],
110
- :root => "public",
111
- :index => "",
112
- :header_rules => [
113
- [:all, {'Cache-Control' => 'public, max-age=86400'}]
114
- ]
115
-
116
- run lambda{ |env| [ 404, { 'Content-Type' => 'text/html' }, ['404 - page not found'] ] }
117
47
  end
118
48
 
119
- return builder
49
+ @app.use Rack::Static,
50
+ :urls => ["/"],
51
+ :root => "public",
52
+ :index => "",
53
+ :header_rules => [
54
+ [:all, {'Cache-Control' => 'public, max-age=86400'}]
55
+ ]
56
+
57
+ @app.run lambda{ |env| [ 404, { 'Content-Type' => 'text/html' }, ['404 - page not found'] ] }
58
+
59
+ return @app
120
60
  end
121
61
  end
@@ -2,7 +2,7 @@ require 'json'
2
2
  require 'sockjs/session'
3
3
 
4
4
  class ChannelHandler < SockJS::Session
5
- def self.message_all
5
+ def self.message_all(message)
6
6
  @@channels.each do |channel|
7
7
  channel.send(message)
8
8
  end
@@ -0,0 +1,97 @@
1
+ require 'volt/server/rack/component_paths'
2
+
3
+ SOURCE_MAPS = !!ENV['MAPS'] unless defined?(SOURCE_MAPS)
4
+
5
+ # Takes in the path to a component and gets all other components
6
+ # required from this one
7
+ class ComponentFiles
8
+ def initialize(component_name, component_paths)
9
+ @component_name = component_name
10
+ @component_paths = component_paths
11
+ @asset_folders = []
12
+ @components = [component_name]
13
+
14
+ load_child_components
15
+ add_asset_folder(component_name)
16
+ end
17
+
18
+ def components
19
+ @components
20
+ end
21
+
22
+ def component(name)
23
+ # Load any sub-requires
24
+ child_files = ComponentFiles.new(name, @component_paths)
25
+ new_components = child_files.components
26
+
27
+ # remove any we already have
28
+ new_components = new_components - @components
29
+ new_components.each {|nc| add_asset_folder(nc) }
30
+
31
+ @components += new_components
32
+
33
+ return @components
34
+ end
35
+
36
+ def add_asset_folder(component_name)
37
+ path = path_to_component(component_name)
38
+
39
+ asset_folder = File.join(path, 'assets')
40
+ if File.directory?(asset_folder)
41
+ @asset_folders << asset_folder
42
+ end
43
+ end
44
+
45
+ def path_to_component(name=nil)
46
+ @component_paths.component_path(name || @component_name)
47
+ end
48
+
49
+ def load_child_components
50
+ path = path_to_component
51
+ if path
52
+ dependencies_file = File.join(path_to_component, "config/dependencies.rb")
53
+ else
54
+ raise "Unable to find component #{@component_name.inspect}"
55
+ end
56
+
57
+ if File.exists?(dependencies_file)
58
+ # Run the dependencies file in this ComponentFiles context
59
+ code = File.read(dependencies_file)
60
+ puts "CODE: #{code.inspect}"
61
+ instance_eval(code)
62
+ end
63
+ end
64
+
65
+ # Returns every asset folder that is included from this component.
66
+ # This means this components assets folder and any in the dependency chain.
67
+ def asset_folders
68
+ files = []
69
+ @asset_folders.each do |asset_folder|
70
+ files << yield(asset_folder)
71
+ end
72
+
73
+ return files.flatten
74
+ end
75
+
76
+
77
+ def javascript_files
78
+ if SOURCE_MAPS
79
+ javascript_files = environment['volt/templates/page'].to_a.map {|v| '/assets/' + v.logical_path + '?body=1' }
80
+ else
81
+ javascript_files = ['/assets/volt/templates/page.js']
82
+ end
83
+
84
+ javascript_files << '/components/home.js'
85
+ javascript_files += asset_folders do |asset_folder|
86
+ Dir["#{asset_folder}/**/*.js"].map {|path| '/assets' + path[asset_folder.size..-1] }
87
+ end
88
+
89
+ return javascript_files
90
+ end
91
+
92
+ def css_files
93
+ asset_folders do |asset_folder|
94
+ Dir["#{asset_folder}/**/*.{css,scss}"].map {|path| '/assets' + path[asset_folder.size..-1].gsub(/[.]scss$/, '') }
95
+ end
96
+ end
97
+ end