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.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +11 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +122 -0
  9. data/Rakefile +14 -0
  10. data/bin/katapult +44 -0
  11. data/features/binary.feature +48 -0
  12. data/features/configuration.feature +24 -0
  13. data/features/katapult.feature +201 -0
  14. data/features/model.feature +203 -0
  15. data/features/navigation.feature +80 -0
  16. data/features/step_definitions/db_steps.rb +8 -0
  17. data/features/step_definitions/file_steps.rb +14 -0
  18. data/features/step_definitions/katapult_steps.rb +14 -0
  19. data/features/step_definitions/rails_steps.rb +44 -0
  20. data/features/step_definitions/test_steps.rb +7 -0
  21. data/features/support/env.rb +16 -0
  22. data/features/wui.feature +319 -0
  23. data/katapult.gemspec +35 -0
  24. data/katapult.png +0 -0
  25. data/lib/generators/katapult/basics/basics_generator.rb +95 -0
  26. data/lib/generators/katapult/basics/templates/Gemfile +76 -0
  27. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application.css.sass +6 -0
  28. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_all.css.sass +4 -0
  29. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_items.css.sass +11 -0
  30. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_layout.css.sass +26 -0
  31. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_navigation.css.sass +11 -0
  32. data/lib/generators/katapult/basics/templates/app/assets/stylesheets/application/blocks/_tools.css.sass +12 -0
  33. data/lib/generators/katapult/basics/templates/config/database.sample.yml +16 -0
  34. data/lib/generators/katapult/basics/templates/config/database.yml +13 -0
  35. data/lib/generators/katapult/basics/templates/config/spring.rb +3 -0
  36. data/lib/generators/katapult/basics/templates/features/support/env-custom.rb +3 -0
  37. data/lib/generators/katapult/basics/templates/features/support/paths.rb +47 -0
  38. data/lib/generators/katapult/cucumber_features/cucumber_features_generator.rb +23 -0
  39. data/lib/generators/katapult/cucumber_features/templates/feature.feature +59 -0
  40. data/lib/generators/katapult/haml/haml_generator.rb +90 -0
  41. data/lib/generators/katapult/haml/templates/_form.html.haml +38 -0
  42. data/lib/generators/katapult/haml/templates/app/views/layouts/application.html.haml +25 -0
  43. data/lib/generators/katapult/haml/templates/custom_action.html.haml +5 -0
  44. data/lib/generators/katapult/haml/templates/edit.html.haml +4 -0
  45. data/lib/generators/katapult/haml/templates/index.html.haml +29 -0
  46. data/lib/generators/katapult/haml/templates/new.html.haml +4 -0
  47. data/lib/generators/katapult/haml/templates/show.html.haml +41 -0
  48. data/lib/generators/katapult/install/install_generator.rb +14 -0
  49. data/lib/generators/katapult/install/templates/lib/katapult/application_model.rb +18 -0
  50. data/lib/generators/katapult/model/model_generator.rb +59 -0
  51. data/lib/generators/katapult/model/templates/app/models/shared/does_flag.rb +32 -0
  52. data/lib/generators/katapult/model/templates/model.rb +21 -0
  53. data/lib/generators/katapult/model_specs/model_specs_generator.rb +51 -0
  54. data/lib/generators/katapult/model_specs/templates/model_spec.rb +34 -0
  55. data/lib/generators/katapult/navigation/navigation_generator.rb +25 -0
  56. data/lib/generators/katapult/navigation/templates/app/models/navigation.rb +12 -0
  57. data/lib/generators/katapult/transform/transform_generator.rb +47 -0
  58. data/lib/generators/katapult/w_u_i/templates/_route.rb +13 -0
  59. data/lib/generators/katapult/w_u_i/templates/controller.rb +106 -0
  60. data/lib/generators/katapult/w_u_i/w_u_i_generator.rb +57 -0
  61. data/lib/katapult.rb +5 -0
  62. data/lib/katapult/action.rb +44 -0
  63. data/lib/katapult/application_model.rb +45 -0
  64. data/lib/katapult/attribute.rb +83 -0
  65. data/lib/katapult/element.rb +72 -0
  66. data/lib/katapult/generator.rb +28 -0
  67. data/lib/katapult/model.rb +33 -0
  68. data/lib/katapult/navigation.rb +22 -0
  69. data/lib/katapult/parser.rb +39 -0
  70. data/lib/katapult/util.rb +16 -0
  71. data/lib/katapult/version.rb +3 -0
  72. data/lib/katapult/wui.rb +77 -0
  73. data/script/console +16 -0
  74. data/spec/action_spec.rb +44 -0
  75. data/spec/attribute_spec.rb +48 -0
  76. data/spec/model_spec.rb +18 -0
  77. data/spec/spec_helper.rb +19 -0
  78. data/spec/util_spec.rb +23 -0
  79. data/spec/wui_spec.rb +49 -0
  80. 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,5 @@
1
+ %h1
2
+ <%= @action.name.humanize.titleize %>
3
+
4
+ .tools
5
+ = link_to 'All <%= model_name(:human_plural) %>', <%= wui.path(:index) %>, class: 'tools__button'
@@ -0,0 +1,4 @@
1
+ %h1
2
+ = <%= model_name(:ivar) %>.to_s
3
+
4
+ = render 'form'
@@ -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,4 @@
1
+ %h1
2
+ Add <%= model_name(:human) %>
3
+
4
+ = render 'form'
@@ -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