katapult 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +122 -0
- data/Rakefile +14 -0
- data/bin/katapult +44 -0
- data/features/binary.feature +48 -0
- data/features/configuration.feature +24 -0
- data/features/katapult.feature +201 -0
- data/features/model.feature +203 -0
- data/features/navigation.feature +80 -0
- data/features/step_definitions/db_steps.rb +8 -0
- data/features/step_definitions/file_steps.rb +14 -0
- data/features/step_definitions/katapult_steps.rb +14 -0
- data/features/step_definitions/rails_steps.rb +44 -0
- data/features/step_definitions/test_steps.rb +7 -0
- data/features/support/env.rb +16 -0
- data/features/wui.feature +319 -0
- data/katapult.gemspec +35 -0
- data/katapult.png +0 -0
- data/lib/generators/katapult/basics/basics_generator.rb +95 -0
- data/lib/generators/katapult/basics/templates/Gemfile +76 -0
- data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application.css.sass +6 -0
- data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_all.css.sass +4 -0
- data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_items.css.sass +11 -0
- data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_layout.css.sass +26 -0
- data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_navigation.css.sass +11 -0
- data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_tools.css.sass +12 -0
- data/lib/generators/katapult/basics/templates/config/database.sample.yml +16 -0
- data/lib/generators/katapult/basics/templates/config/database.yml +13 -0
- data/lib/generators/katapult/basics/templates/config/spring.rb +3 -0
- data/lib/generators/katapult/basics/templates/features/support/env-custom.rb +3 -0
- data/lib/generators/katapult/basics/templates/features/support/paths.rb +47 -0
- data/lib/generators/katapult/cucumber_features/cucumber_features_generator.rb +23 -0
- data/lib/generators/katapult/cucumber_features/templates/feature.feature +59 -0
- data/lib/generators/katapult/haml/haml_generator.rb +90 -0
- data/lib/generators/katapult/haml/templates/_form.html.haml +38 -0
- data/lib/generators/katapult/haml/templates/app/views/layouts/application.html.haml +25 -0
- data/lib/generators/katapult/haml/templates/custom_action.html.haml +5 -0
- data/lib/generators/katapult/haml/templates/edit.html.haml +4 -0
- data/lib/generators/katapult/haml/templates/index.html.haml +29 -0
- data/lib/generators/katapult/haml/templates/new.html.haml +4 -0
- data/lib/generators/katapult/haml/templates/show.html.haml +41 -0
- data/lib/generators/katapult/install/install_generator.rb +14 -0
- data/lib/generators/katapult/install/templates/lib/katapult/application_model.rb +18 -0
- data/lib/generators/katapult/model/model_generator.rb +59 -0
- data/lib/generators/katapult/model/templates/app/models/shared/does_flag.rb +32 -0
- data/lib/generators/katapult/model/templates/model.rb +21 -0
- data/lib/generators/katapult/model_specs/model_specs_generator.rb +51 -0
- data/lib/generators/katapult/model_specs/templates/model_spec.rb +34 -0
- data/lib/generators/katapult/navigation/navigation_generator.rb +25 -0
- data/lib/generators/katapult/navigation/templates/app/models/navigation.rb +12 -0
- data/lib/generators/katapult/transform/transform_generator.rb +47 -0
- data/lib/generators/katapult/w_u_i/templates/_route.rb +13 -0
- data/lib/generators/katapult/w_u_i/templates/controller.rb +106 -0
- data/lib/generators/katapult/w_u_i/w_u_i_generator.rb +57 -0
- data/lib/katapult.rb +5 -0
- data/lib/katapult/action.rb +44 -0
- data/lib/katapult/application_model.rb +45 -0
- data/lib/katapult/attribute.rb +83 -0
- data/lib/katapult/element.rb +72 -0
- data/lib/katapult/generator.rb +28 -0
- data/lib/katapult/model.rb +33 -0
- data/lib/katapult/navigation.rb +22 -0
- data/lib/katapult/parser.rb +39 -0
- data/lib/katapult/util.rb +16 -0
- data/lib/katapult/version.rb +3 -0
- data/lib/katapult/wui.rb +77 -0
- data/script/console +16 -0
- data/spec/action_spec.rb +44 -0
- data/spec/attribute_spec.rb +48 -0
- data/spec/model_spec.rb +18 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/util_spec.rb +23 -0
- data/spec/wui_spec.rb +49 -0
- metadata +253 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
# Generate views for a WUI.
|
2
|
+
|
3
|
+
require 'katapult/generator'
|
4
|
+
require 'generators/katapult/cucumber_features/cucumber_features_generator'
|
5
|
+
|
6
|
+
module Katapult
|
7
|
+
module Generators
|
8
|
+
class HamlGenerator < Katapult::Generator
|
9
|
+
|
10
|
+
desc 'Generate HAML views'
|
11
|
+
source_root File.expand_path('../templates', __FILE__)
|
12
|
+
|
13
|
+
|
14
|
+
def install_application_layout
|
15
|
+
remove_file 'app/views/layouts/application.html.erb'
|
16
|
+
template 'app/views/layouts/application.html.haml'
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_views_directory
|
20
|
+
FileUtils.mkdir_p views_path
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_rails_standard_action_views
|
24
|
+
actions.select{ |a| a.get? && WUI::RAILS_ACTIONS.include?(a.name) }.each do |action|
|
25
|
+
file_name = "#{action.name}.html.haml"
|
26
|
+
|
27
|
+
create_view file_name, File.join(views_path, file_name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_form_partial_if_needed
|
32
|
+
_form_actions = (actions.map(&:name) & %w[new edit])
|
33
|
+
|
34
|
+
if _form_actions.any?
|
35
|
+
file_name = '_form.html.haml'
|
36
|
+
|
37
|
+
create_view file_name, File.join(views_path, file_name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_views_for_custom_actions
|
42
|
+
wui.custom_actions.select(&:get?).each do |action|
|
43
|
+
@action = action # Make the action object accessible in templates
|
44
|
+
create_view 'custom_action.html.haml', File.join(views_path, "#{action.name}.html.haml")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def generate_integration_tests
|
49
|
+
if wui.model.present?
|
50
|
+
Generators::CucumberFeaturesGenerator.new(wui.model).invoke_all
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
no_tasks do
|
55
|
+
def model_name(kind = nil)
|
56
|
+
wui.model_name(kind)
|
57
|
+
end
|
58
|
+
|
59
|
+
def views_path
|
60
|
+
File.join('app', 'views', model_name(:variables))
|
61
|
+
end
|
62
|
+
|
63
|
+
def navigation
|
64
|
+
wui.application_model.navigation
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Rails views depend heavily on models. If the WUI has no model, do not
|
71
|
+
# use the templates but create empty files instead.
|
72
|
+
def create_view(template, destination)
|
73
|
+
if wui.model
|
74
|
+
template template, destination
|
75
|
+
else
|
76
|
+
create_file destination
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def wui
|
81
|
+
@element
|
82
|
+
end
|
83
|
+
|
84
|
+
def actions
|
85
|
+
wui.actions
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
= form_for <%= model_name(:ivar) %> do |form|
|
2
|
+
|
3
|
+
%dl.controls
|
4
|
+
<% wui.model.attrs.each do |attribute| -%>
|
5
|
+
%dt
|
6
|
+
= form.label <%= attribute.name(:symbol) %>
|
7
|
+
%dd
|
8
|
+
<%- if attribute.assignable_values -%>
|
9
|
+
= form.select <%= attribute.name(:symbol) %>, form.object.assignable_<%= attribute.name(:variables) %>
|
10
|
+
<%- else -%>
|
11
|
+
<%- case attribute.type -%>
|
12
|
+
<%- when :string -%>
|
13
|
+
= form.text_field <%= attribute.name(:symbol) %>
|
14
|
+
<%- when :email -%>
|
15
|
+
= form.email_field <%= attribute.name(:symbol) %>
|
16
|
+
<%- when :url -%>
|
17
|
+
= form.url_field <%= attribute.name(:symbol) %>
|
18
|
+
<%- when :integer -%>
|
19
|
+
= form.number_field <%= attribute.name(:symbol) %>
|
20
|
+
<%- when :money -%>
|
21
|
+
= form.number_field <%= attribute.name(:symbol) %>
|
22
|
+
€
|
23
|
+
<%- when :text -%>
|
24
|
+
= form.text_area <%= attribute.name(:symbol) %>, rows: 5
|
25
|
+
<%- when :markdown -%>
|
26
|
+
= form.text_area <%= attribute.name(:symbol) %>, rows: 15
|
27
|
+
<%- when :flag -%>
|
28
|
+
= form.check_box <%= attribute.name(:symbol) %>
|
29
|
+
<%- when :datetime -%>
|
30
|
+
= form.datetime_field <%= attribute.name(:symbol) %>
|
31
|
+
<%- end -%>
|
32
|
+
<%- end -%>
|
33
|
+
<% end -%>
|
34
|
+
|
35
|
+
.tools
|
36
|
+
= form.submit 'Save', class: 'tools__button is_primary'
|
37
|
+
- cancel_path = <%= model_name(:ivar) %>.new_record? ? <%= wui.path(:index) %> : <%= wui.path(:show, model_name(:ivar)) %>
|
38
|
+
= link_to 'Cancel', cancel_path, class: 'tools__button'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%title
|
5
|
+
<%= app_name.titlecase %>
|
6
|
+
|
7
|
+
= stylesheet_link_tag 'application', media: 'all'
|
8
|
+
= javascript_include_tag 'application'
|
9
|
+
= csrf_meta_tags
|
10
|
+
|
11
|
+
%body
|
12
|
+
.layout
|
13
|
+
|
14
|
+
.layout__head
|
15
|
+
%h2 <%= app_name.titlecase %>
|
16
|
+
<%- if navigation -%>
|
17
|
+
= render_navigation Navigation.<%= navigation.name(:variable) %>
|
18
|
+
<%- end -%>
|
19
|
+
|
20
|
+
.layout__main
|
21
|
+
=# render 'layouts/flashes'
|
22
|
+
= yield
|
23
|
+
|
24
|
+
.layout__tail
|
25
|
+
powered by makandra
|
@@ -0,0 +1,29 @@
|
|
1
|
+
%h1
|
2
|
+
<%= model_name(:human_plural).capitalize %>
|
3
|
+
|
4
|
+
.tools
|
5
|
+
= link_to 'Add <%= model_name(:human) %>', <%= wui.path(:new) %>, class: 'tools__button is_primary'
|
6
|
+
<% wui.custom_actions.select(&:collection?).each do |action| -%>
|
7
|
+
= link_to '<%= action.name.humanize.titleize %>', <%= wui.path(action) %>, class: 'tools__button'
|
8
|
+
<% end -%>
|
9
|
+
|
10
|
+
- if <%= model_name(:ivars) %>.any?
|
11
|
+
%table.items
|
12
|
+
<%- object_name = model_name(:variable) -%>
|
13
|
+
- <%= model_name(:ivars) %>.each do |<%= object_name %>|
|
14
|
+
%tr
|
15
|
+
%td
|
16
|
+
= link_to <%= object_name %>.to_s, <%= object_name %>, class: 'hyperlink'
|
17
|
+
%td
|
18
|
+
.items__actions
|
19
|
+
= link_to 'Edit', <%= wui.path(:edit, object_name) %>, class: 'items__action'
|
20
|
+
= link_to 'Destroy', <%= wui.path(:destroy, object_name) %>, method: :delete, class: 'items__action', confirm: 'Really destroy?'
|
21
|
+
<%- wui.custom_actions.select(&:member?).each do |action| -%>
|
22
|
+
= link_to '<%= action.name.humanize.titleize %>', <%= wui.path(action, object_name) %>, class: 'items__action'<%=
|
23
|
+
", method: :#{action.method}" unless action.get?
|
24
|
+
%>
|
25
|
+
<%- end -%>
|
26
|
+
|
27
|
+
- else
|
28
|
+
%p.help-block
|
29
|
+
There are no <%= model_name(:human_plural) %> yet.
|
@@ -0,0 +1,41 @@
|
|
1
|
+
%h1
|
2
|
+
= <%= model_name(:ivar) %>.to_s
|
3
|
+
|
4
|
+
.tools
|
5
|
+
= link_to 'All <%= model_name(:human_plural) %>', <%= wui.path(:index) %>, class: 'tools__button'
|
6
|
+
= link_to 'Edit', <%= wui.path(:edit, model_name(:ivar)) %>, class: 'tools__button is_primary'
|
7
|
+
= link_to 'Destroy', <%= wui.path(:destroy, model_name(:ivar)) %>, method: :delete, class: 'tools__button', confirm: 'Really destroy?'
|
8
|
+
<% wui.custom_actions.select(&:member?).each do |action| -%>
|
9
|
+
= link_to '<%= action.name(:human).titleize %>', <%= wui.path(action, model_name(:ivar)) %>, class: 'tools__button'<%=
|
10
|
+
", method: :#{action.method}" unless action.get?
|
11
|
+
%>
|
12
|
+
<% end -%>
|
13
|
+
|
14
|
+
%dl.values
|
15
|
+
<% wui.model.attrs.each do |attribute| -%>
|
16
|
+
<%- model_attribute = "#{model_name(:ivar)}.#{attribute.name}" -%>
|
17
|
+
%dt
|
18
|
+
= <%= model_name(:class_name) %>.human_attribute_name(:<%= attribute.name %>)
|
19
|
+
%dd
|
20
|
+
<%- case attribute.type -%>
|
21
|
+
<%- when :string -%>
|
22
|
+
= <%= model_attribute %>
|
23
|
+
<%- when :email -%>
|
24
|
+
= mail_to <%= model_attribute %>, nil, class: 'hyperlink'
|
25
|
+
<%- when :url -%>
|
26
|
+
= link_to <%= model_attribute %>, <%= model_attribute %>, class: 'hyperlink'
|
27
|
+
<%- when :integer -%>
|
28
|
+
= <%= model_attribute %>
|
29
|
+
<%- when :money -%>
|
30
|
+
= <%= model_attribute %>
|
31
|
+
€
|
32
|
+
<%- when :text -%>
|
33
|
+
= simple_format(<%= model_attribute %>)
|
34
|
+
<%- when :markdown -%>
|
35
|
+
= markdown(<%= model_attribute %>)
|
36
|
+
<%- when :flag -%>
|
37
|
+
= <%= model_attribute %> ? 'Yes' : 'No'
|
38
|
+
<%- when :datetime -%>
|
39
|
+
= l(<%= model_attribute %>) if <%= model_attribute %>
|
40
|
+
<%- end -%>
|
41
|
+
<%- end -%>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Install katapult into an application.
|
2
|
+
|
3
|
+
module Katapult
|
4
|
+
class InstallGenerator < Rails::Generators::Base
|
5
|
+
|
6
|
+
desc 'Install katapult into a Rails app'
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
def setup_lib
|
10
|
+
template 'lib/katapult/application_model.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Here you define the fundamentals of your application.
|
2
|
+
#
|
3
|
+
# Add a model:
|
4
|
+
# model 'customer' do |customer|
|
5
|
+
# customer.attr :name
|
6
|
+
# customer.attr :birth, type: :date
|
7
|
+
# customer.attr :email
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# Add a web user interface:
|
11
|
+
# wui 'customer' do |wui|
|
12
|
+
# wui.action :index
|
13
|
+
# wui.action :show
|
14
|
+
# wui.action :lock, scope: :member, method: :post
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Add navigation
|
18
|
+
# navigation :main
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Generate a Rails model, including migration and spec.
|
2
|
+
|
3
|
+
require 'katapult/generator'
|
4
|
+
require 'generators/katapult/model_specs/model_specs_generator'
|
5
|
+
|
6
|
+
module Katapult
|
7
|
+
module Generators
|
8
|
+
class ModelGenerator < Katapult::Generator
|
9
|
+
|
10
|
+
desc 'Generate a Rails Model'
|
11
|
+
|
12
|
+
check_class_collision
|
13
|
+
source_root File.expand_path('../templates', __FILE__)
|
14
|
+
|
15
|
+
|
16
|
+
def create_migration_file
|
17
|
+
migration_name = "create_#{table_name}"
|
18
|
+
migration_attributes = model.attrs.map(&:for_migration)
|
19
|
+
|
20
|
+
args = [migration_name] + migration_attributes
|
21
|
+
options = { timestamps: true, force: true }
|
22
|
+
invoke 'active_record:migration', args, options
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_model_file
|
26
|
+
template 'model.rb', File.join('app', 'models', "#{file_name}.rb")
|
27
|
+
end
|
28
|
+
|
29
|
+
def write_traits
|
30
|
+
template 'app/models/shared/does_flag.rb' if flag_attrs.any?
|
31
|
+
end
|
32
|
+
|
33
|
+
def generate_unit_tests
|
34
|
+
Generators::ModelSpecsGenerator.new(model).invoke_all
|
35
|
+
end
|
36
|
+
|
37
|
+
no_commands do
|
38
|
+
def flag_attrs
|
39
|
+
model.attrs.select(&:flag?)
|
40
|
+
end
|
41
|
+
|
42
|
+
def defaults
|
43
|
+
{}.tap do |defaults|
|
44
|
+
model.attrs.select(&:has_defaults?).each do |attr|
|
45
|
+
defaults[attr.name.to_sym] = attr.default
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def model
|
54
|
+
@element
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module DoesFlag
|
2
|
+
as_trait do |field, options = {}|
|
3
|
+
|
4
|
+
default = options[:default]
|
5
|
+
virtual = options[:virtual]
|
6
|
+
field_var = "@#{field}"
|
7
|
+
set_field = "#{field}="
|
8
|
+
field_query = "#{field}?"
|
9
|
+
|
10
|
+
validates field.to_sym, inclusion: { in: [true, false] }, allow_nil: !!virtual
|
11
|
+
|
12
|
+
unless default.nil?
|
13
|
+
has_defaults field.to_sym => default
|
14
|
+
end
|
15
|
+
|
16
|
+
if virtual
|
17
|
+
|
18
|
+
attr_reader field
|
19
|
+
|
20
|
+
define_method field_query do
|
21
|
+
send(field)
|
22
|
+
end
|
23
|
+
|
24
|
+
define_method set_field do |value|
|
25
|
+
value = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
|
26
|
+
instance_variable_set(field_var, value)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class <%= class_name %> < ActiveRecord::Base
|
2
|
+
<%- flag_attrs.each do |attr| -%>
|
3
|
+
include DoesFlag[<%= attr.name(:symbol) %>, default: <%= attr.default %>]
|
4
|
+
<%- end -%>
|
5
|
+
<%- if defaults.any? -%>
|
6
|
+
has_defaults(<%= defaults %>)
|
7
|
+
<%- end -%>
|
8
|
+
<%- model.attrs.select(&:assignable_values).each do |attr| -%>
|
9
|
+
assignable_values_for :<%= attr.name %>, <%= attr.options.slice(:allow_blank, :default) %> do
|
10
|
+
<%= attr.assignable_values %>
|
11
|
+
end
|
12
|
+
<%- end -%>
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
<% if model.label_attr -%>
|
16
|
+
<%= model.label_attr.name %>.to_s
|
17
|
+
<% else -%>
|
18
|
+
"<%= model.name %>##{id}"
|
19
|
+
<% end -%>
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Generate model specs.
|
2
|
+
|
3
|
+
require 'katapult/generator'
|
4
|
+
|
5
|
+
module Katapult
|
6
|
+
module Generators
|
7
|
+
class ModelSpecsGenerator < Katapult::Generator
|
8
|
+
|
9
|
+
desc 'Generate model specs'
|
10
|
+
|
11
|
+
check_class_collision
|
12
|
+
source_root File.expand_path('../templates', __FILE__)
|
13
|
+
|
14
|
+
|
15
|
+
def create_model_spec
|
16
|
+
template 'model_spec.rb', File.join('spec', 'models', "#{file_name}_spec.rb")
|
17
|
+
end
|
18
|
+
|
19
|
+
no_tasks do
|
20
|
+
def specable_attrs
|
21
|
+
model.attrs.select do |attr|
|
22
|
+
attr.assignable_values.present? or attr.default != nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def assignable_value_for(attr)
|
27
|
+
attr.assignable_values.last
|
28
|
+
end
|
29
|
+
|
30
|
+
# Guess a value that is not assignable
|
31
|
+
def unassignable_value_for(attr)
|
32
|
+
case attr.type
|
33
|
+
when :integer
|
34
|
+
attr.assignable_values.max + 1
|
35
|
+
when :string
|
36
|
+
assignable_value_for(attr) + '-unassignable'
|
37
|
+
else
|
38
|
+
raise "Assignable values for :#{attr.type} attributes not supported"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def model
|
46
|
+
@element
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|