clearwater-roda 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5aa8a6d72425620e259a51f9b0bf48d092744bbf
4
- data.tar.gz: 11ee3b36747c99c46cdc860e5a92c098bfa47571
2
+ SHA256:
3
+ metadata.gz: aae557cbde03873eb8060a99e88915cccb430a1a817c812c3a98024b801d7cab
4
+ data.tar.gz: 363216637b51ae5950d06b4eac92e6ca088ccdd78643db326031e69e0b35345e
5
5
  SHA512:
6
- metadata.gz: 4a5adf5cc6bb97edc91448ec367d955ddfc61190fdc437b465790146fb9a8f41c2508831663991fa7dbbb06dc684c143f396993fe3bb52dc82ada1aa3c0afea3
7
- data.tar.gz: f9df03eba77c6e7af91d069f0fa85731e5c9cc780856befdcac3ec3aa599761d222f35874fb67c3bb6a33a89eeed802076067edfcce53bc77e525165c04352dc
6
+ metadata.gz: 0451a79d753cca4f54ab27c7a2e8a9eff9e0cfd7148cb2d3ac428b84d6796489ccda7b8738e7470d97d4bddfc13cd84169bb862a8079edd3897ebc367b00d96b
7
+ data.tar.gz: 53f2260133abd77c39793eadb4497bb02eefeaf2fdeea1d0576b6dd7bc011cbe64a7bb8ab1cd9c0168b73f87149d54137bdf47b69a26d3e167241162950240bb
@@ -7,11 +7,12 @@ module Clearwater
7
7
  class Application
8
8
  DirectoryAlreadyExists = Class.new(StandardError)
9
9
 
10
+ TEMPLATE_PATH = File.expand_path('../../../../templates', __FILE__)
11
+
10
12
  attr_reader :name
11
13
 
12
14
  def initialize name
13
15
  @name = name
14
- @previous_dirs = []
15
16
  end
16
17
 
17
18
  def build
@@ -20,10 +21,7 @@ module Clearwater
20
21
  mkdir
21
22
  chdir
22
23
  write_files
23
- bundle
24
24
  git_init
25
- git_commit
26
- chdir_back
27
25
 
28
26
  puts
29
27
  puts <<-EOF
@@ -49,37 +47,21 @@ your assets for production, simply run:
49
47
  end
50
48
 
51
49
  def chdir
52
- @previous_dirs.push Dir.pwd
53
50
  FileUtils.chdir dir_name
54
51
  end
55
52
 
56
- def chdir_back
57
- FileUtils.chdir @previous_dirs.pop
58
- end
59
-
60
- def bundle
61
- system "bundle -j12"
62
- end
63
-
64
53
  def git_init
65
54
  `git init`
66
55
  end
67
56
 
68
- def git_commit
69
- `git add --all .`
70
- `git commit -m 'Hello world!'`
71
- end
72
-
73
57
  def write_files
74
- files = %W(
75
- Gemfile
76
- config.ru
77
- assets/js/app.rb
78
- dev
79
- Rakefile
80
- )
58
+ files = Dir["#{TEMPLATE_PATH}/**/*"]
59
+ .select { |file| File.file? file }
60
+ .map { |file| file.sub("#{TEMPLATE_PATH}/", '') }
61
+ .grep_v('app.rb')
81
62
 
82
63
  files.each do |template_name|
64
+ puts "Writing #{template_name}..."
83
65
  Template.new(
84
66
  template_path(template_name),
85
67
  template_name,
@@ -88,6 +70,7 @@ your assets for production, simply run:
88
70
  end
89
71
 
90
72
  app_filename = "#{underscore(name)}.rb"
73
+ puts "Writing #{app_filename}..."
91
74
  Template.new(
92
75
  template_path('app.rb'),
93
76
  app_filename,
@@ -105,7 +88,7 @@ your assets for production, simply run:
105
88
  end
106
89
 
107
90
  def template_path(name)
108
- "#{File.expand_path('../../../../templates', __FILE__)}/#{name}"
91
+ "#{TEMPLATE_PATH}/#{name}"
109
92
  end
110
93
 
111
94
  def underscore name
@@ -113,7 +96,7 @@ your assets for production, simply run:
113
96
  end
114
97
 
115
98
  def titleize name
116
- name = name.gsub(/_[a-z]/) { |match| match[1].upcase }
99
+ name = name.gsub(/[_-][a-z]/) { |match| match[1].upcase }
117
100
  name[0] = name[0].upcase
118
101
  name
119
102
  end
@@ -1,5 +1,5 @@
1
1
  module Clearwater
2
2
  module Roda
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
data/templates/Gemfile CHANGED
@@ -1,11 +1,17 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'opal'
4
- gem 'clearwater', '~> 1.0.0.rc4'
5
3
  gem 'roda'
6
4
  gem 'roda-opal_assets'
5
+ gem 'puma'
6
+ gem 'rake'
7
7
 
8
8
  group :development do
9
9
  gem 'rerun'
10
- gem 'rake'
10
+
11
+ # Assets from these gems won't be needed in production since they'll be
12
+ # precompiled
13
+ gem 'clearwater', '~> 1.0.0.rc4'
14
+ gem 'grand_central'
15
+ gem 'grand_central-dev_tools'
16
+ gem 'clearwater-hot_loader'
11
17
  end
data/templates/Rakefile CHANGED
@@ -1,13 +1,26 @@
1
- require 'bundler/setup'
1
+ require 'bundler'
2
+ Bundler.setup :default, ENV.fetch('RACK_ENV') { :development }
3
+ $LOAD_PATH << 'app'
4
+
2
5
  require 'opal'
3
6
  require 'clearwater'
4
7
  require 'roda/opal_assets'
8
+ require 'assets'
5
9
 
6
10
  # Keep a single asset compiler in case we want to use it for multiple tasks.
7
11
  assets = Roda::OpalAssets.new(env: :production)
8
12
 
9
13
  desc 'Precompile assets for production'
10
14
  task 'assets:precompile' do
15
+ warn = method(:warn)
16
+ module Kernel
17
+ def warn(*)
18
+ end
19
+ end
11
20
  assets << 'app.js'
12
21
  assets.build
22
+
23
+ Kernel.class_exec do
24
+ define_method :warn, &warn
25
+ end
13
26
  end
data/templates/app.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'roda'
2
2
  require 'roda/opal_assets'
3
3
  require 'opal'
4
- require 'clearwater'
5
4
 
6
5
  class %{titleized_name} < Roda
7
6
  plugin :public
@@ -17,14 +16,26 @@ class %{titleized_name} < Roda
17
16
  <html>
18
17
  <head>
19
18
  <meta charset="utf-8" />
20
- <title>%{titleized_name}</title>
19
+ <title>#{app_title}</title>
21
20
  </head>
22
21
 
23
22
  <body>
24
23
  <div id="app"></div>
25
- #{assets.js 'app.js'}
24
+ #{additional_markup}
25
+ #{assets.js client_app}
26
26
  </body>
27
27
  </html>
28
28
  HTML
29
29
  end
30
+
31
+ def app_title
32
+ '%{titleized_name}'
33
+ end
34
+
35
+ def client_app
36
+ 'app.js'
37
+ end
38
+
39
+ def additional_markup
40
+ end
30
41
  end
@@ -0,0 +1,6 @@
1
+ require 'clearwater'
2
+ require 'grand_central'
3
+
4
+ # Tools to make development easier
5
+ require 'clearwater/hot_loader'
6
+ require 'grand_central/dev_tools'
@@ -0,0 +1,36 @@
1
+ require 'grand_central/action'
2
+
3
+ # Here we define an action taxonomy
4
+ #
5
+ # GrandCentral::Action
6
+ # |
7
+ # + Action -- Your top application-level action
8
+ # |
9
+ # + CounterAction
10
+ # | |
11
+ # | + Increment
12
+ # | + Decrement
13
+ # |
14
+ # + SetName
15
+
16
+ # This lets your store handle entire classes of actions with a single check,
17
+ # which is very helpful in large apps with hundreds of actions:
18
+ #
19
+ # store = GrandCentral::Store.new(initial_state) do |state, action|
20
+ # case action
21
+ # when CounterAction
22
+ # # Handles both Increment and Decrement
23
+ # when SetName
24
+ # # ...
25
+ # else state
26
+ # end
27
+
28
+ # Create a top-level action
29
+ Action = GrandCentral::Action.create
30
+
31
+ CounterAction = Action.create do
32
+ Increment = create
33
+ Decrement = create
34
+ end
35
+
36
+ SetName = Action.with_attributes(:name)
@@ -1,85 +1,9 @@
1
1
  require 'opal'
2
2
  require 'clearwater'
3
3
 
4
- class Layout
5
- include Clearwater::Component
6
-
7
- def render
8
- # Equivalent HTML:
9
- # <div>
10
- # <h1 class="heading">
11
- # <a href="/">Hello, Clearwater</a>
12
- # </h1>
13
- # <nav>
14
- # <a href="/foo">Foo</a>
15
- # <a href="/bar">Bar</a>
16
- # <a href="/bar/baz">Baz</a>
17
- # </nav>
18
- # <%%= outlet || HomePage.new.render %%>
19
- # </div>
20
- div([
21
- h1({ class_name: 'heading' }, [
22
- Link.new({ href: '/' }, 'Hello, Clearwater!'),
23
- ]),
24
- nav([
25
- # The Link component is how you navigate between routes in Clearwater
26
- # apps. This leaves the `a` helper method to do a typical hard link.
27
- Link.new({ href: '/foo' }, 'Foo'),
28
-
29
- # One of the nice things about defining your UI with code instead of
30
- # markup is that adding whitespace doesn't look out of place.
31
- ' ',
32
- Link.new({ href: '/bar' }, 'Bar'),
33
- ' ',
34
- Link.new({ href: '/bar/baz' }, 'Baz'),
35
- ]),
36
-
37
- # The `outlet` method is a method for routing targets to render their
38
- # child routes, similar to the same keyword in Ember.js templates. If
39
- # there is no child route, we render a HomePage component.
40
- outlet || HomePage.new,
41
- ])
42
- end
43
- end
44
-
45
- # This is our default homepage component. Note that in the Layout component, we
46
- # render this by calling HomePage.new. This means we get a brand-new HomePage
47
- # component each time we render, despite the Layout sticking around for the life
48
- # of the app. Because of this, our HomePage cannot hold state and only knows
49
- # about what it is told in its initialize method (which, in this case, is
50
- # nothing).
51
- class HomePage
52
- include Clearwater::Component
53
-
54
- def render
55
- article([
56
- h1('Welcome to Clearwater'),
57
-
58
- p(<<-EOP),
59
- Clearwater is a Ruby front-end framework.
60
- EOP
61
- ])
62
- end
63
- end
64
-
65
- # We use a Struct here because it's shorthand for a class that takes an argument
66
- # and gives us an accessor method with that name. Otherwise, it's identical to a
67
- # typical template; we just wanted to take an argument.
68
- ChildRoute = Struct.new(:name) do
69
- include Clearwater::Component
70
-
71
- def render
72
- div([
73
- h2(name),
74
- p("This is the child route called #{name}"),
75
-
76
- div([
77
- h3('Child content:'),
78
- outlet,
79
- ]),
80
- ])
81
- end
82
- end
4
+ require 'store'
5
+ require 'components/layout'
6
+ require 'components/child_route'
83
7
 
84
8
  router = Clearwater::Router.new do
85
9
  # Routing targets are other components. They stick around for the life of the
@@ -123,3 +47,9 @@ app = Clearwater::Application.new(
123
47
  # route changes) and triggers the first render. Subsequent renders should use
124
48
  # app.render instead.
125
49
  app.call
50
+
51
+ # When actions are dispatched to the store, check to see if they change the
52
+ # state. If so, re-render the app.
53
+ Store.on_dispatch do |before, after, action|
54
+ app.render unless before.equal?(after)
55
+ end
@@ -0,0 +1,20 @@
1
+ require 'clearwater/component'
2
+
3
+ # We use a Struct here because it's shorthand for a class that takes an argument
4
+ # and gives us an accessor method with that name. Otherwise, it's identical to a
5
+ # typical template; we just wanted to take an argument.
6
+ ChildRoute = Struct.new(:name) do
7
+ include Clearwater::Component
8
+
9
+ def render
10
+ div([
11
+ h2(name),
12
+ p("This is the child route called #{name}"),
13
+
14
+ div([
15
+ h3('Child content:'),
16
+ outlet,
17
+ ]),
18
+ ])
19
+ end
20
+ end
@@ -0,0 +1,36 @@
1
+ require 'clearwater/component'
2
+
3
+ require 'store' # Need this for the State module
4
+ require 'actions' # Need to load the actions we want to dispatch
5
+
6
+ # This is our default homepage component. Note that in the Layout component, we
7
+ # render this by calling HomePage.new. This means we get a brand-new HomePage
8
+ # component each time we render, despite the Layout sticking around for the life
9
+ # of the app. Because of this, our HomePage cannot hold state and only knows
10
+ # about what it is told in its initialize method (which, in this case, is
11
+ # nothing).
12
+ class HomePage
13
+ include Clearwater::Component
14
+ include State[:counter, :name] # Add a reader method for attributes in app state
15
+
16
+ def render
17
+ article([
18
+ h1('Welcome to Clearwater'),
19
+
20
+ div([
21
+ button({ onclick: Decrement }, '-'),
22
+ counter,
23
+ button({ onclick: Increment }, '+'),
24
+ ]),
25
+
26
+ div([
27
+ input(value: name, oninput: SetName),
28
+ div(name),
29
+ ]),
30
+
31
+ p(<<-EOP),
32
+ Clearwater is a Ruby front-end framework.
33
+ EOP
34
+ ])
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ require 'clearwater/component'
2
+
3
+ require 'components/home_page'
4
+
5
+ class Layout
6
+ include Clearwater::Component
7
+
8
+ def render
9
+ # Equivalent HTML:
10
+ # <div>
11
+ # <h1 class="heading">
12
+ # <a href="/">Hello, Clearwater</a>
13
+ # </h1>
14
+ # <nav>
15
+ # <a href="/foo">Foo</a>
16
+ # <a href="/bar">Bar</a>
17
+ # <a href="/bar/baz">Baz</a>
18
+ # </nav>
19
+ # <%%= outlet || HomePage.new.render %%>
20
+ # </div>
21
+ div([
22
+ h1({ class_name: 'heading' }, [
23
+ Link.new({ href: '/' }, 'Hello, Clearwater!'),
24
+ ]),
25
+ nav([
26
+ # The Link component is how you navigate between routes in Clearwater
27
+ # apps. This leaves the `a` helper method to do a typical hard link.
28
+ Link.new({ href: '/foo' }, 'Foo'),
29
+
30
+ # One of the nice things about defining your UI with code instead of
31
+ # markup is that adding whitespace doesn't look out of place.
32
+ ' ',
33
+ Link.new({ href: '/bar' }, 'Bar'),
34
+ ' ',
35
+ Link.new({ href: '/bar/baz' }, 'Baz'),
36
+ ]),
37
+
38
+ # The `outlet` method is a method for routing targets to render their
39
+ # child routes, similar to the same keyword in Ember.js templates. If
40
+ # there is no child route, we render a HomePage component.
41
+ outlet || HomePage.new,
42
+ ])
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ require 'opal'
2
+ require 'app'
3
+ require 'store'
4
+
5
+ require 'grand_central/dev_tools'
6
+ require 'clearwater/hot_loader'
7
+
8
+ dev_tools = GrandCentral::DevTools.new(
9
+ Store,
10
+ Bowser.document['#grand-central-dev-tools']
11
+ )
12
+ dev_tools.start
13
+
14
+ Clearwater::HotLoader.connect 9292
@@ -0,0 +1,62 @@
1
+ require 'grand_central/store'
2
+ require 'grand_central/model'
3
+
4
+ require 'actions'
5
+
6
+ class AppState < GrandCentral::Model
7
+ attributes(
8
+ :counter,
9
+ :name,
10
+ )
11
+ end
12
+
13
+ initial_state = AppState.new(
14
+ counter: 0,
15
+ )
16
+
17
+ Store = GrandCentral::Store.new(initial_state) do |state, action|
18
+ case action
19
+ when Increment
20
+ state.update counter: state.counter + 1
21
+ when Decrement
22
+ state.update counter: state.counter - 1
23
+ when SetName
24
+ state.update name: action.name
25
+
26
+ # If an unknown action was dispatched, we just return the current state.
27
+ # It is important to return a state object that matches the structure of
28
+ # the previous one. It becomes the new app state.
29
+ else
30
+ state
31
+ end
32
+ end
33
+
34
+ # When you want to use application state in a component, you can use this module
35
+ # to add the attribute methods to that component using the following pattern:
36
+ #
37
+ # class MyCounter
38
+ # include Clearwater::Component
39
+ # include State[:counter]
40
+ #
41
+ # def render
42
+ # div(counter)
43
+ # end
44
+ # end
45
+ module State
46
+ # If we need to mix the same attributes into two different components, we
47
+ # can reuse the same mixin.
48
+ @module_cache = {}
49
+
50
+ def self.[](*attrs)
51
+ @module_cache.fetch(attrs) do
52
+ @module_cache[attrs] = Module.new do
53
+ attrs.each do |attr|
54
+ define_method(attr) { Store.state.send(attr) }
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ # Let our actions know where they dispatch to by default
62
+ Action.store = Store
data/templates/config.ru CHANGED
@@ -1,6 +1,10 @@
1
- require 'bundler/setup'
1
+ require 'bundler'
2
+ Bundler.setup :default, :production
3
+
2
4
  require './%{underscored_name}'
3
5
 
4
6
  $LOAD_PATH << 'lib'
5
7
 
8
+ use Rack::Deflater
9
+ use Rack::CommonLogger
6
10
  run %{titleized_name}
data/templates/dev CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/bin/sh
2
2
 
3
3
  bundle check || bundle -j12
4
-
5
- rerun -i 'assets/**/*' 'rackup'
4
+ bundle exec rerun -i 'assets/**/*' "bundle exec puma dev.ru $*"
data/templates/dev.ru ADDED
@@ -0,0 +1,33 @@
1
+ require 'bundler'
2
+ Bundler.setup :default, :development
3
+ $LOAD_PATH << 'app'
4
+ require 'assets'
5
+
6
+ require './%{underscored_name}'
7
+
8
+ Clearwater::HotLoader.start
9
+
10
+ class %{titleized_name}Dev < %{titleized_name}
11
+ route do |r|
12
+ r.on('clearwater_hot_loader') { r.run Clearwater::HotLoader }
13
+
14
+ instance_exec(r, &self.class.superclass.route_block)
15
+ end
16
+
17
+ def app_title
18
+ '%{titleized_name} [DEV]'
19
+ end
20
+
21
+ def client_app
22
+ 'dev'
23
+ end
24
+
25
+ def additional_markup
26
+ <<~HTML
27
+ <div id="grand-central-dev-tools"></div>
28
+ HTML
29
+ end
30
+ end
31
+
32
+ use Rack::CommonLogger
33
+ run %{titleized_name}Dev
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clearwater-roda
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jamie Gaskins
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-19 00:00:00.000000000 Z
11
+ date: 2017-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -78,9 +78,17 @@ files:
78
78
  - templates/Gemfile
79
79
  - templates/Rakefile
80
80
  - templates/app.rb
81
+ - templates/app/assets.rb
82
+ - templates/assets/js/actions.rb
81
83
  - templates/assets/js/app.rb
84
+ - templates/assets/js/components/child_route.rb
85
+ - templates/assets/js/components/home_page.rb
86
+ - templates/assets/js/components/layout.rb
87
+ - templates/assets/js/dev.rb
88
+ - templates/assets/js/store.rb
82
89
  - templates/config.ru
83
90
  - templates/dev
91
+ - templates/dev.ru
84
92
  homepage: https://github.com/clearwater-rb/clearwater-roda
85
93
  licenses: []
86
94
  metadata: {}
@@ -100,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
108
  version: '0'
101
109
  requirements: []
102
110
  rubyforge_project:
103
- rubygems_version: 2.6.6
111
+ rubygems_version: 2.7.2
104
112
  signing_key:
105
113
  specification_version: 4
106
114
  summary: Generate a Roda/Clearwater app from scratch