katapult 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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