katapult 0.1.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 +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
|