shared_examples 'nested_controller_spec' do |classe, opcoes|
	sign_in_admin

	let(:atributos_validos) do
		if opcoes[:atributos_validos]
			opcoes[:atributos_validos].call
		else
			skip("Adicione os atributos válidos")
		end
	end

	let(:atributos_validos_do_objeto_em_outro_escopo) do
		if opcoes[:atributos_validos_do_objeto_em_outro_escopo]
			opcoes[:atributos_validos_do_objeto_em_outro_escopo].call
		else
			skip("Adicione os atributos do 'objeto em outro escopo'")
		end
	end

	let(:atributos_novos) do
		if opcoes[:atributos_novos]
			opcoes[:atributos_novos].call
		else
			skip("Adicione os atributos novos")
		end
	end

	let(:atributos_invalidos) do
		if opcoes[:atributos_invalidos]
			opcoes[:atributos_invalidos].call
		else
			skip("Adicione os atributos novos")
		end
	end

	let(:parametro_do_objeto_pai) do
		opcoes[:parametro_do_objeto_pai]
	end

	let(:nome_do_objeto_pai) do
		opcoes[:nome_do_objeto_pai]
	end

	describe "GET index" do
		it "atribui todos os #{classe.model_name.route_key} a @#{classe.model_name.route_key}" do
			objeto = classe.create! atributos_validos
			objeto_em_outro_escopo = classe.create! atributos_validos_do_objeto_em_outro_escopo
			get :index, parametro_do_objeto_pai => objeto.send(nome_do_objeto_pai).to_param
			expect(assigns(classe.name.demodulize.underscore.pluralize.to_sym)).to eq([objeto])
		end

		it "não inclui os #{classe.model_name.route_key} de outro escopo na listagem" do
			objeto = classe.create! atributos_validos
			objeto_em_outro_escopo = classe.create! atributos_validos_do_objeto_em_outro_escopo
			get :index, parametro_do_objeto_pai => objeto.send(nome_do_objeto_pai).to_param
			expect(assigns(classe.name.demodulize.underscore.pluralize.to_sym)).to_not include( objeto_em_outro_escopo )
		end
	end unless opcoes[:skip].to_a.include?(:index)

	describe "GET show" do
		it "atribui o #{classe.model_name.singular_route_key} requisitado a @#{classe.model_name.singular_route_key}" do
			objeto = classe.create! atributos_validos
			get :show, id: objeto.to_param
			expect(assigns(classe.name.demodulize.underscore.to_sym)).to eq(objeto)
		end
	end unless opcoes[:skip].to_a.include?(:show)

	describe "GET #new" do
		it "atribui um novo #{classe.name.demodulize.underscore.to_sym} à @#{classe.name.demodulize.underscore.to_sym}" do
			objeto_pai = classe.new( atributos_validos ).send( nome_do_objeto_pai )
			get :new, parametro_do_objeto_pai => objeto_pai.to_param
			expect(assigns(classe.name.demodulize.underscore.to_sym)).to be_a_new( classe )
		end

		it "o novo #{classe.name.demodulize.underscore.to_sym} pertence ao objeto pai especificado" do
			objeto_pai = classe.new( atributos_validos ).send( nome_do_objeto_pai )
			get :new, parametro_do_objeto_pai => objeto_pai.to_param
			expect(assigns(classe.name.demodulize.underscore.to_sym).send(nome_do_objeto_pai)).to eq objeto_pai
		end
	end unless opcoes[:skip].to_a.include?(:new)

	describe "GET #edit" do
		it "atribui o #{classe.model_name.singular_route_key} requisitado a @#{classe.model_name.singular_route_key}" do
			objeto = classe.create! atributos_validos
			get :edit, id: objeto.to_param
			expect(assigns(classe.name.demodulize.underscore.to_sym)).to eq(objeto)
		end
	end unless opcoes[:skip].to_a.include?(:edit)

	describe "POST #create" do
		describe "com parâmetros válidos" do
			it "cria um novo #{classe.name.demodulize.underscore.to_sym}" do
				expect {
					post :create, parametro_do_objeto_pai => atributos_validos[nome_do_objeto_pai.to_sym].to_param,
						classe.model_name.param_key.to_sym => atributos_validos
				}.to change(classe, :count).by(1)
			end

			it "atribui o #{classe.name.demodulize.underscore.to_sym} recem criado a @#{classe.name.demodulize.underscore.to_sym}" do
				post :create, parametro_do_objeto_pai => atributos_validos[nome_do_objeto_pai.to_sym].to_param,
					classe.model_name.param_key.to_sym => atributos_validos
				expect(assigns(classe.name.demodulize.underscore.to_sym)).to be_a(classe)
				expect(assigns(classe.name.demodulize.underscore.to_sym)).to be_persisted
			end

			it "redireciona para o #{classe.name.demodulize.underscore.to_sym} recem criado" do
				post :create, parametro_do_objeto_pai => atributos_validos[nome_do_objeto_pai.to_sym].to_param,
					classe.model_name.param_key.to_sym => atributos_validos

				redirect_path = assigns(classe.name.demodulize.underscore.to_sym)
				redirect_path = assigns(classe.name.demodulize.underscore.to_sym).send(nome_do_objeto_pai) if opcoes[:listagem_no_show_do_objeto_pai]
				is_expected.to redirect_to( redirect_path )
			end

			context "com o param 'mantem_form'" do
				it "redireciona a tela de cadastro de #{classe.name.demodulize.underscore.to_sym}" do
					post :create, parametro_do_objeto_pai => atributos_validos[nome_do_objeto_pai.to_sym].to_param,
						classe.model_name.param_key.to_sym => atributos_validos, manter_form: true
					expect(response).to redirect_to( action: :new )
				end
			end

			context "sem o param 'mantem_form'" do
				it "redireciona para o #{classe.name.demodulize.underscore.to_sym} cadastrado" do
					post :create, parametro_do_objeto_pai => atributos_validos[nome_do_objeto_pai.to_sym].to_param,
						classe.model_name.param_key.to_sym => atributos_validos
					expect(response).to redirect_to( assigns(classe.name.demodulize.underscore.to_sym) )
				end
			end
		end

		describe "with invalid params" do
			it "assigns a newly created but unsaved #{classe.name.demodulize.underscore.to_sym} as @#{classe.name.demodulize.underscore.to_sym}" do
				post :create, parametro_do_objeto_pai => atributos_validos[nome_do_objeto_pai.to_sym].to_param,
					classe.model_name.param_key.to_sym => atributos_invalidos
				expect(assigns(classe.name.demodulize.underscore.to_sym)).to be_a_new(classe)
			end

			it "re-renders the 'new' template" do
				post :create, parametro_do_objeto_pai => atributos_validos[nome_do_objeto_pai.to_sym].to_param,
					classe.model_name.param_key.to_sym => atributos_invalidos
				is_expected.to render_template('new')
			end
		end
	end unless opcoes[:skip].to_a.include?(:create)

	describe "PUT update" do
		describe "com parametros validos" do
			it "atualiza o #{classe.model_name.singular_route_key} requisitado" do
				objeto = classe.create! atributos_validos
				post :update, params: { id: objeto.to_param, classe.model_name.param_key.to_sym => atributos_novos}
				objeto.reload
				atributos_novos.each do |hash|
					chave = hash[0]
					valor = hash[1]
					expect(objeto.send(chave)).to eq valor
				end
			end

			it "atribui o #{classe.model_name.singular_route_key} requisitado a @#{classe.model_name.singular_route_key}" do
				objeto = classe.create! atributos_validos
				put :update, id: objeto.to_param, classe.model_name.param_key.to_sym => atributos_novos
				expect(assigns(classe.name.demodulize.underscore.to_sym)).to eq(objeto)
			end

			it "redireciona para o #{classe.model_name.singular_route_key}" do
				objeto = classe.create! atributos_validos
				put :update, id: objeto.to_param, classe.model_name.param_key.to_sym => atributos_novos
				redirect_path = objeto
				redirect_path = objeto.send(nome_do_objeto_pai) if opcoes[:listagem_no_show_do_objeto_pai]
				is_expected.to redirect_to( redirect_path )
			end
		end

		describe "com parametros invalidos" do
			it "atribui o #{classe.name.demodulize.underscore.to_sym} as @#{classe.name.demodulize.underscore.to_sym}" do
				objeto = classe.create! atributos_validos
				put :update, id: objeto.to_param, classe.model_name.param_key.to_sym => atributos_invalidos
				expect(assigns(classe.name.demodulize.underscore.to_sym)).to eq(objeto)
			end

			it "renderiza o template edit" do
				objeto = classe.create! atributos_validos
				put :update, id: objeto.to_param, classe.model_name.param_key.to_sym => atributos_invalidos
				is_expected.to render_template('edit')
			end
		end unless opcoes[:skip].to_a.include?(:update)

		describe "DELETE destroy" do
			it "destroi o #{classe.model_name.singular_route_key} requisitado" do
				objeto = classe.create! atributos_validos
				expect {
					delete :destroy, id: objeto.to_param
				}.to change(classe, :count).by(-1)
			end

			it "redireciona para a lista de #{classe.model_name.singular_route_key}" do
				objeto = classe.create! atributos_validos
				delete :destroy, id: objeto.to_param

				redirect_path = objeto.send(nome_do_objeto_pai) if opcoes[:listagem_no_show_do_objeto_pai]
				is_expected.to redirect_to( redirect_path ||  Rails.application.routes.url_helpers.send("#{classe.model_name.route_key}_path", objeto.send(nome_do_objeto_pai) ) )
			end
		end unless opcoes[:skip].to_a.include?(:destroy)
	end
end
