volt 0.2.3 → 0.2.4

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 (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