Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
HyperDE
Resumo
O HyperDE é um ambiente para desenvolvimento de aplicações hipermídia na que não possui uma
camada para a definição do tratamento das requisições feitas pelos usuários à aplicação construída. A
ausência desta camada é percebida como falta de flexibilidade na construção das aplicações. O
problema é mais evidente quando o desenvolvedor precisa construir regras para controle de acesso à
aplicação ou criar o backend para o tratamento de dados enviados por formulários personalizados. O
objetivo deste projeto é adicionar uma camada para a definição de controles (a componente C da
arquitetura MVC) no HyperDE.
Sumário
1.Especificação do programa.................................................................................................................... 3
1.1.Objetivo.......................................................................................................................................... 3
1.2.Mini-acompanhamento da execução.............................................................................................. 3
1.3.Requisitos....................................................................................................................................... 4
1.4.Diagrama de Casos de Uso.............................................................................................................4
2.Projeto modular do programa................................................................................................................ 5
2.1.Critérios de projeto utilizados........................................................................................................ 5
2.2.Organização do programa...............................................................................................................6
3.Código fonte...........................................................................................................................................8
4.Testes automatizados............................................................................................................................16
4.1.Testes............................................................................................................................................ 16
4.2.Logs de testes............................................................................................................................... 18
5.Guia do usuário.................................................................................................................................... 20
5.1.Instalação......................................................................................................................................20
5.2.Usando controladores................................................................................................................... 20
a)Criando um controlador..............................................................................................................21
b)Criando uma ação.......................................................................................................................24
c)Observações................................................................................................................................25
1. Especificação do programa
1.1. Objetivo
O Ruby on Rails é um framework de desenvolvimento web escrito na linguagem Ruby. Ele é designado
para tornar a programação de aplicações web mais fácil, fazendo várias suposições sobre o que cada
desenvolvedor precisa para começar. Ele permite que você escreva menos código enquanto faz mais
que muitas outras linguagens e frameworks.
Uma vez que o projeto aqui apresentado consistiu em adicionar uma nova funcionalidade ao HyperDE,
sua implementação utilizou o mesmo framework e desta maneira se beneficiou de seguir os mesmos
critérios de projeto e princípios de um projeto desenvolvido com o Ruby on Rails, alguns deles
herdados do uso do padrão MVC, entre os quais podemos destacar:
app/models/controller_action.rb
1 #
2 # * Name: MyController
3 # * Description: This class persists the controllers actions specification
4 # and istanciates as methods of controllers classes.
5 # * Author: Mauricio Henrique de Souza Bomfim
6 # * Version: 1.0
7 #
8 class ControllerAction < AbstractModel
9
10 # Attributes
11 property :my_controller_id, "MyController"
12 property :name
13 property :params
14 property :code
15 property :private, "Boolean"
16
17 # Realationships
18 belongs_to :my_controller
19
20 # Callbacks
21 after_save :load!
22 before_destroy :unload
23 after_destroy :load!
24
25
26 # Generates the method code definition based on its specification.
27 def definition
28 %{
29 def #{name}(#{params})
30 #{code}
31 end
32 }
33 end
34
35
36 # Forces the methods's controller specification loading.
37 def load!
38 my_controller.load!
39 end
40
41 # Unloads the methods's controller.
42 def unload
43 my_controller.unload
44 end
45
46 protected
47
48 validates_format_of :name, :with => /^[a-z][A-Za-z0-9_=]*$/,
49 :message => 'Action name must start with a lowercase letter and must only
50 contain letters, digits and underscore characters'
51
52 end
app/controllers/my_controller_controller.rb
1 #
2 # * Name: MyControllerController
3 # * Description: This is the Rails controller for CRUD on my_controllers.
4 # * Author: Mauricio Henrique de Souza Bomfim
5 # * Version: 1.0
6 #
7 class MyControllerController < ApplicationController
8
9 helper :my_controller
10
11 include CrudController
12 layout "layouts/admin"
13
14 crud MyController
15
16 before_filter :before_delete, :only => [:delete]
17
18 # Actions for page titles
19 def title_for_list() "Controller List" end
20 def title_for_new() "New Controller" end
21 def title_for_edit() "Edit Controller" end
22
23 # Prepare instance variables before creating new my_controllers
24 def before_new()
25 @default_controller = MyController.find_by_name('HyperdeBase')
26 @base_controllers = MyController.find_all.reject{|c| c.id ==
@default_controller.id }.collect {|c| [c.name, c.id] }
27 end
28
29 # Prepare instance variables before editing my_controllers
30 def before_edit()
31 @default_controller = MyController.find_by_name('HyperdeBase')
32 @base_controllers = MyController.find_all.reject{|c| c.id == params[:id] or
c.id == @default_controller.id }.collect {|c| [c.name, c.id] }
33 end
34
35 def list_order() "name" end
36
37 # Validates properties before deleting a my_controller
38 def before_delete()
39
40 @my_controller = MyController.find(@params["id"])
41 unless @my_controller.validates_destroy
42 flash["notice"] = Array.new
43 @my_controller.errors.each { |attr, msg| flash["notice"] << msg }
44 redirect_to :action => "list"
45 end
46 end
47
48 def after_delete() ModelObserver.resume end
49
50 # Default notice messages
51 def notice_for_create() "Controller '#{@my_controller.name}' created. Now, add
the actions." end
52 def notice_for_update() "Controller '#{@my_controller.name}' updated." end
53 def notice_for_delete() "Controller '#{@my_controller.name}' deleted." end
54
55 def redirect_after_create() redirect_to :action => "edit", :id =>
@my_controller.id end
56
57 # All methods above are supporting actions for controller_actions
58 def new_controller_action(after_error = false)
59 @my_controller = MyController.find(@params["my_controller_id"]) unless
after_error
60 @controller_action = ControllerAction.new("my_controller" => @my_controller)
unless after_error
61 @page_title = "New action for #{@my_controller.name}"
62 render_action "edit_controller_action"
63 end
64
65 def edit_controller_action(after_error = false)
66 @my_controller = MyController.find(@params["my_controller_id"]) unless
after_error
67 @controller_action = ControllerAction.find(@params["id"]) unless after_error
68 @page_title = "Edit action for #{@my_controller.name}"
69 render_action "edit_controller_action"
70 end
71
72 def create_controller_action
73 @my_controller = MyController.find(@params["my_controller_id"])
74 @controller_action = ControllerAction.new(@params["controller_action"])
75 if @controller_action.save
76 flash["notice"] = "action #{@controller_action.name}' added."
77 redirect_to :action => "edit", :id => @params["my_controller_id"]
78 else
79 treat_errors @controller_action, "new_controller_action"
80 end
81 end
82
83 def update_controller_action
84 @my_controller = MyController.find(@params["my_controller_id"])
85 @controller_action = ControllerAction.find(@params["controller_action"]
["id"])
86 @controller_action.attributes = @params["controller_action"]
87 if @controller_action.save
88 flash["notice"] = "action '#{@controller_action.name}' updated."
89 redirect_to :action => "edit", :id => @params["my_controller_id"]
90 else
91 treat_errors @controller_action, "edit_controller_action"
92 end
93 end
94
95 def delete_controller_action
96 o = ControllerAction.find(@params["id"])
97 o.destroy
98 flash["notice"] = "action '#{o.name}' removed."
99 redirect_to :action => "edit", :id => @params["my_controller_id"]
100 end
101
102 end
app/helpers/my_controller_helper.rb
1 #
2 # * Name: MyControllerHelper
3 # * Description: This helper offer methods for controller actions forms.
4 # * Author: Mauricio Henrique de Souza Bomfim
5 # * Version: 1.0
6 #
7 module MyControllerHelper
8
9 def dg_controller_actions
10 dg = DataGrid.new(@my_controller.controller_actions, "Actions")
11 dg.actions << { :eval => "link_to('[Add New Action]', :action =>
'new_controller_action', :params => { 'my_controller_id' =>
'#{@my_controller.id}'})" }
12 dg.columns << { :label => "Name", :eval => "item.name" }
13 dg.columns << { :label => "Private", :eval => "item.private ? 'yes' : 'no' "
}
14 dg.columns << { :eval => "link_to('[Edit]', :action =>
'edit_controller_action', :id => item.id, :params => { 'my_controller_id' =>
'#{@my_controller.id}'})" }
15 dg.columns << { :eval => "link_to_confirm('[Delete]', :action =>
'delete_controller_action', :id => item.id, :params => { 'my_controller_id' =>
'#{@my_controller.id}'})" }
16 dg
17 end
18
19 end
app/views/my_controller/edit.rhtml
<!--
#
# * Name: edit.rhtml
# * Description: ERb template for editing my_controllers
# * Author: Mauricio Henrique de Souza Bomfim
# * Version: 1.0
#
-->
<form action="<%= url_for(:action => @my_controller.new_record? ? "create" :
"update") %>" method="post">
app/views/my_controller/edit.rhtml
<!--
#
# * Name: list.rhtml
# * Description: ERb template for listing my_controllers
# * Author: Mauricio Henrique de Souza Bomfim
# * Version: 1.0
#
-->
<%
dg = DataGrid.new(@my_controllers)
dg.row_count = { :label => "class" }
dg.actions << { :eval => "link_to('[Add New Controller]', :action => 'new')" }
dg.columns << { :label => "Name", :eval => "item.name" }
dg.columns << { :eval => "link_to('[Edit]', :action => 'edit', :id => item.id)" }
dg.columns << { :eval => "link_to_confirm('[Delete]', :action => 'delete', :id =>
item.id)" }
%>
<%= render_helper(dg) %>
app/views/my_controller/edit_controller_action.rhtml
<!--
#
# * Name: edit_controller_action.rhtml
# * Description: ERb template for editing controllers actions
# * Author: Mauricio Henrique de Souza Bomfim
# * Version: 1.0
#
-->
<%= render(:partial => 'shared/validation_rules', :locals => { :action => true })
%>
<form action=<%= url_for(:action => @controller_action.new_record? ?
"create_controller_action" : "update_controller_action", :params => {
"my_controller_id" => @params["my_controller_id"] }) %> method="post">
<%= hidden_field "controller_action", "id" %>
<%= hidden_field "controller_action", "my_controller_id" %>
<p><label>Name</label><%= text_field("controller_action", "name", "size" => 20)
%></p>
<p><label>Parameters</label><%= text_field("controller_action", "params", "size" =>
50) %> (eg. "name, node_id")</p>
<p><label>Code</label><%= text_area("controller_action", "code", "cols" => 80,
"rows" => 20) %></p>
<p><label>Private</label><%= check_box("controller_action", "private") %></p>
<input type=submit value="Save">
</form>
<%= link_to("<< Back", :action => "edit", :id => @params["my_controller_id"]) %>
4. Testes automatizados
O framework Ruby on Rails oferece suporte completo para o desenvolvimento de testes automatizados.
Foram escritos testes automatizados para as classes de modelo apresentadas neste trabalho.
4.1. Testes
test/unit/my_controller_test.rbs
1 require 'test_helper'
2
3 class MyControllerTest < ActiveSupport::TestCase
4
5 def test_should_have_hyperde_base_controller
6
7 assert_not_nil MyController.find_by_name('HyperdeBase'), "There is no
HyperdeBase Controller"
8
9 end
10
11 def test_should_not_create_invalid_controller
12
13 test_controller = MyController.new("name" => "test")
14 assert !test_controller.save, "Class name must start with a uppercase letter
and must only contain letters, digits and underscore characters"
15
16 test_controller = MyController.new("name" => "")
17 assert !test_controller.save, "Controller saved with blank name"
18
19 assert_raises NoMethodError do
20 test_controller = MyController.new()
21 test_controller.save
22 end
23
24 end
25
26 def test_should_not_create_mycontroller
27 test = MyController.new("name" => "MyController")
28 assert !test.save, "Controller saved with MyController name"
29 end
30
31 def test_should_not_create_previews_controller
32 previews = MyController.new("name" => "Previews")
33 previews.save
34
35 previews = MyController.new("name" => "Previews")
36 assert !previews.save, "Controller saved with previews used name"
37 end
38
39 def test_should_load
40 test = MyController.new("name" => "Test")
41 assert test.load, "Controller should load"
42 end
43
44 def test_shoud_load_after_save
45 test = MyController.new("name" => "Test")
46 test.save
47 assert test.is_loaded?, "Controller should be loaded"
48 test.destroy
49 end
50
51 def test_should_not_load
52 test = MyController.new("name" => "Test")
53 test.header = "a=" #syntax error
54 assert !test.load, "Controller should not load"
55 end
56
57 def test_is_loaded
58 test = MyController.new("name" => "TestLoad")
59 assert !test.is_loaded?
60 test.load
61 assert test.is_loaded?
62 test.unload
63 end
64
65 def test_unload
66 test = MyController.new("name" => "Test")
67 test.load
68 assert test.is_loaded?
69 test.unload
70 assert !test.is_loaded?
71 end
72
73 def test_class_name
74 test = MyController.new("name" => "TestName")
75 assert_equal test.class_name, "TestNameController"
76 end
77
78 def test_class_sym
79 test = MyController.new("name" => "TestName")
80 assert_equal test.class_sym, :TestNameController
81 end
82
83 def test_route_name
84 test = MyController.new("name" => "TestName")
85 assert_equal test.route_name, "test_name"
86 end
87
88 def test_definition
89 test = MyController.new("name" => "TestName")
90 definition = "\n\t\t\tclass TestNameController <
ApplicationController\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\tprivate\n\t\t\t\t\n\t\
t\t\t\n\t\t\tend\n\t\t"
91 assert_equal test.definition, definition
92
93 definition = "\n\t\t\tclass TestNameController <
ApplicationController\n\t\t\t\t\n\t\t\t\t\n \t\t\tdef test_action()\n
\t\t\t\t1\n \t\t\tend\n
\t\t\n\t\t\t\t\n\t\t\t\tprivate\n\t\t\t\t\n\t\t\t\t\n\t\t\tend\n\t\t"
94 test.controller_actions << ControllerAction.new("name" => "test_action",
"code" => "1")
95 assert_equal test.definition, definition
96
97 definition = "\n\t\t\tclass TestNameController <
ApplicationController\n\t\t\t\t\n\t\t\t\t\n \t\t\tdef test_action()\n
\t\t\t\t1\n \t\t\tend\n \t\t\n\t\t\t\t\n\t\t\t\tprivate\n\t\t\t\t\n\t\t\t\t\n
\t\t\tdef test_private_action()\n \t\t\t\t2\n \t\t\tend\n \t\t\n\t\t\tend\n\t\t"
98 test.controller_actions << ControllerAction.new("name" =>
"test_private_action", "code" => "2", "private" => true)
99 assert_equal test.definition, definition
100
101 definition = "\n\t\t\tclass TestNameController <
ApplicationController\n\t\t\t\tbefore_filter :test\n\t\t\t\t\n \t\t\tdef
test_action()\n \t\t\t\t1\n \t\t\tend\n
\t\t\n\t\t\t\t\n\t\t\t\tprivate\n\t\t\t\t\n\t\t\t\t\n \t\t\tdef
test_private_action()\n \t\t\t\t2\n \t\t\tend\n \t\t\n\t\t\tend\n\t\t"
102 test.header = "before_filter :test"
103 assert_equal test.definition, definition
104
105 end
106
107 def test_should_not_destroy_hyperdebase
108 base = MyController.find_by_name("HyperdeBase")
109 assert !base.destroy, "Allows destroy HyperdeBase Controller"
110 end
111
112 end
test/unit/controller_action_test.rb
1 require 'test_helper'
2
3 class ControllerActionTest < ActiveSupport::TestCase
4
5 def test_definition
6 test = ControllerAction.new("name" => "test", "code" => "1 + 1")
7 definition = "\n \t\t\tdef test()\n \t\t\t\t1 + 1\n \t\t\tend\n \t\t"
8 assert_equal test.definition, definition
9
10 test.params = "a, b, c"
11 definition = "\n \t\t\tdef test(a, b, c)\n \t\t\t\t1 + 1\n \t\t\tend\n \t\t"
12 assert_equal test.definition, definition
13
14 end
15
16 def test_should_load
17 test_controller = MyController.new("name" => "Test")
18 test_action = ControllerAction.new("name" => "test_action", "code" => "1")
19 test_controller.controller_actions << test_action
20 test_action.my_controller = test_controller
21 test_action.load!
22 assert TestController.instance_methods.include?('test_action')
23 end
24
25 end
ruby unit/controller_action_test.rb -v
Running from unit/controller_action_test.rb...
Loaded suite unit/controller_action_test
Started
test_definition(ControllerActionTest): .
test_should_load(ControllerActionTest): .
5.1. Instalação
1. Instale a última versão do Ruby. Para windows instale o One-Click Ruby Installer
http://rubyforge.org/projects/rubyinstaller/
2. Instale o Rails 2.3.4 com o comando 'gem install rails -v=2.3.4'
3. Instale a biblioteca RJB com o comando 'gem install rjb'. Esta biblioteca depende do java.
4. Copie a pasta /hyperde, disponível no cd para o seu computador, em um diretório de sua
preferência.
5. Para executar o servidor da aplicação, entre na pasta onde instalou o HyperDE e execute o
comando ruby script/server.
6. Acesse a aplicação pelo seu navegador internet na url http://localhost:3000. Você verá a
seguinte página: