clearwater-roda 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.
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