Scaffold innego modelu

Tak sobie jadę z “Agile …” i chciałbym dodać kategorie produktów do mojego sklepu.
Zastanawiam się, jak wygenerować scaffold takich kategorii (tabela w bazie danych już jest), żeby mi nie nadpisywało plików w app/views/admin? Oczywiście mógłbym wszystko sam wygenerować, ale od czegoś jest ten scaffold i chyba nie jest aż tak głupi, żeby mógł generować tylko jeden model na aplikację?

Przy okazji, czy to oznacza, że obecny admin_controller powinien zostać czymś w rodzaju product_admin_controller, coby zrobić miejsce dla category_admin_controller? Czy jedynym rozwiązaniem powyższego problemu ze scaffoldem jest podzielenie katalogu app/views/admin na podkatalogi products i categories, a potem kombinowanie ze scaffoldowaniem Admin/Category albo coś takiego?

[quote=Tomash]Tak sobie jadę z “Agile …” i chciałbym dodać kategorie produktów do mojego sklepu.
Zastanawiam się, jak wygenerować scaffold takich kategorii (tabela w bazie danych już jest), żeby mi nie nadpisywało plików w app/views/admin?[/quote]
Generator rusztowania domyślnie nie nadpisuje plików.

Oczywiście można wygenerować dowolną liczbę modeli, rusztowań, migracji itd.

Powinny być następujące kontrolery:
products_controller.rb
categories_controller.rb

Te kontrolery powinny służyć jednocześnie użytkownikom i adminowi (te same widoki i akcje, tylko z różnymi uprawnieniami).

Tymczasem admin_controller.rb to miejsce na ogólną administrację (takie centrum, np. podstawowe statystyki aplikacji itp).

Zdecydowanie nie. Sugeruję pozostać przy płaskiej hierarchii kontrolerów.

Powinny być następujące kontrolery:
products_controller.rb
categories_controller.rb

Te kontrolery powinny służyć jednocześnie użytkownikom i adminowi (te same widoki i akcje, tylko z różnymi uprawnieniami).[/quote]
Nie zgadzam się. Jeśli admin ma mieć dostęp do wszystkich produktów, a użytkownik/klient tylko do tych, które np. data dostępności jest mniejsza niż dziś() ? To zrobisz w kontrolerze:
@products_all = Product.find(:all) oraz @products_for_user = Product… ?

co to znaczy ogólna administracja? czy zarządzanie produktem to nie ogólna administracja?

Zdecydowanie nie. Sugeruję pozostać przy płaskiej hierarchii kontrolerów.[/quote]
Tomash.. ja robie tak, że:
./script/generate controller ‘admin/product’ itd
dodatkowo możesz zrobić jeszcze BaseController i po nim dziedziczyć resztę kontrolerów w admin:
class Admin::ProductController < Admin::BaseController

oraz ustawiać wspólne rzeczy, jak layout (inny dla admin, inny dla usera) czy też permission
zobacz sobie kod Typo.

pozdrawiam

Kamień z serca - wreszcie jakaś dyskusja :slight_smile:

[quote=punkracy]Nie zgadzam się. Jeśli admin ma mieć dostęp do wszystkich produktów, a użytkownik/klient tylko do tych, które np. data dostępności jest mniejsza niż dziś() ? To zrobisz w kontrolerze:
@products_all = Product.find(:all) oraz @products_for_user = Product… ?[/quote]
Na czym polega problem? Po co dwa atrybuty? Zrobiłbym tak:

@products = lista produktów w zależności od roli użytkownika

Nie, zarządzanie produktem to products_controller i tej wersji będę się trzymał :wink:

Uważam, że admini powinni używać tych samych ekranów, co użytkownicy jeśli tylko jest to możliwe. Szczegóły widoku (np. obecność przycisku [Delete]) zmieniają się w zależności od roli użytkownika.

[quote=punkracy]Tomash.. ja robie tak, że:
./script/generate controller ‘admin/product’ itd
dodatkowo możesz zrobić jeszcze BaseController i po nim dziedziczyć resztę kontrolerów w admin:
class Admin::ProductController < Admin::BaseController

oraz ustawiać wspólne rzeczy, jak layout (inny dla admin, inny dla usera) czy też permission
zobacz sobie kod Typo.[/quote]
Zagnieżdżone kontrolery to przyjemna idea. W dobrym programiście natychmiast rodzi się chęć, żeby jakoś je zhierarchizować. Sam temu uległem. Nawet udało mi się zmusić to do działania (co nie było proste; teraz na szczęście jest już więcej info na ten temat).

Zagnieżdżone kontrolery komplikują wiele spraw. Co chwila okazuje się, że coś nie działa tak jak bym się spodziewał i trzeba szukać “myku”. IMHO nie warto komplikować sobie życia.

DHH powiedział półżartem: “Someone who needs nested controllers, please make it work ;)”.

Kamień z serca - wreszcie jakaś dyskusja :)[/quote]
:slight_smile:

hmm.. możesz powiedzieć o tych rzeczach? może uniknę problemów?

:slight_smile:

pozdrawiam

Chodzi głównie o szeroko pojęty routing i niezrozumiałe komunikaty o błędach. Inne problemy to wyjątkowo słabe wsparcie tematu ze strony wtyczek, narzędzi i dokumentacji.

Tymczasem zysk z tej inwestycji jest niewielki. Tak naprawdę ogranicza się do zaspokojenia pedantyzmu programistycznego, choroby dość powszechnej wśród railsowców ;). Można też powiedzieć, że zagnieżdżone kontrolery czynią dużą aplikację (powiedzmy 30 modeli) troszkę bardziej zarządzalną.

Oczywiście to tylko moja skromna opinia.

Polecam też lekturę (również komentarze):
Things You Shouldn’t Be Doing In Rails
if your models aren’t namespaced, why should your controllers be?

Była tutaj o tym kiedyś dyskusja :slight_smile:

I ja dalej nie zmieniam zdania, że zagnieżdżone kontrolery dużo nie komplikują. Teraz bawię się w RESTful Rails i ten sam efekt mam zamiar uzyskać odpowiednimi ustawieniami w routes, ale w większości poprzednich aplikacji używałem krytykowanego tutaj sposobu i nie miałem żadnych problemów, których nie dało by się rozwiązać po chwili przemyślenia.

Co do wyświetlania różnych danych w zależności od roli, to też zależy od aplikacji - czasami da się to dość ładnie zaimplementować, a czasami wyjdzie z tego jeden wielki chaos.

Przeczytałem sobie helpa wyskakującego przy skrypcie generate i wpadłem na pewien pomysł.

Zrobiłem tak, że dla kategorii wygenerowałem nowy scaffold -
script generate scaffold Category ‘admin/category’
Postanowiłem w podobny sposób zhierarchizować pliki odpowiedzialne za administrację produktem - czyli z app/views/admin wywaliłem do app/views/admin/category. Oba kontrolery (do adminowania produktem i kategoriami) wylądowały w app/controllers/admin (category_controller.rb i product_controller.rb).

Stronę admina podmieniłem na następującą:
<%= link_to ‘Categories’, :action => ‘category/show’ %>

<%= link_to ‘Products’, :action => ‘product/show’ %>

<%= link_to ‘Shipping’, :action => ‘ship’ %>

(shipping też niedługo wypadnie do swojego podkatalogu)
I… tutaj mam problem. Wyskakuj mi błąd:


NoMethodError in Admin#ship

Showing app/views/admin/_order_line.rhtml where line #10 raised:

You have a nil object when you didn’t expect it!
The error occured while evaluating nil.title

Extracted source (around line #10):

7: <% order_line.line_items.each do |li| %>
8:


9: <%=li.quantity%>
10: <%=li.product.title%>
11:

12: <% end %>
13:

To samo mam z wszystkimi innymi linkami. Dlaczego obiekty nagle są nil? Czyżbym nie napisał jakiegoś brakującego odnośnika? Wydaje się, że odwołuje się do dobrych kontrolerów, więc dlaczego zdaje się nie łapać modelu?

OK, właśnie doczytałem (dzięki za linki!), żeby nie pakować kontrolerów do podkatalogów. Chętnie. Chętnie wrzucę product_controller i category_controller do głównego katalogu kontrolerów. I tak zrobiłem.
Nawet wygrzebałem na sieci jak zmusić link_to do korzystania z konkretnego kontrolera:
<%= link_to ‘Categories’, :controller => ‘category’, :action => ‘show’ %>

<%= link_to ‘Products’, :controller => ‘product’, :action => ‘show’ %>

Ale powstaje problem:

Routing Error
Recognition failed for “/category/show”

Masz niekonwencjonalne nazwy. Jak już pisałem, powinno być products_controller i categories_controller. To nie jest bez znaczenia.

[quote=Tomash]Nawet wygrzebałem na sieci jak zmusić link_to do korzystania z konkretnego kontrolera:
<%= link_to ‘Categories’, :controller => ‘category’, :action => ‘show’ %>

<%= link_to ‘Products’, :controller => ‘product’, :action => ‘show’ %>
[/quote]
Zmień ‘show’ na ‘index’ - chcesz przecież pokazać listę obiektów biznesowych. Tymczcasem metoda ‘show’ odpowiada za obsługę szczegółów jednego obiektu.

Żeby działało show, trzeba dodać id konkretnego obiektu (:id => @obiekt lub :id => @obiekt.id).

A tak w ogóle, to Twój kod powinien wyglądać tak:

<%= link_to 'Categories', categories_url %> <%= link_to 'Products', products_url %>

[quote=qertoip]A tak w ogóle, to Twój kod powinien wyglądać tak:

<%= link_to 'Categories', categories_url %> <%= link_to 'Products', products_url %>
[/quote]
Tylko, że Tomash raczej (przynajmniej z tego co widzę) nie korzysta z resource’ów :slight_smile:

[quote=drogus][quote=qertoip]A tak w ogóle, to Twój kod powinien wyglądać tak:

<%= link_to 'Categories', categories_url %> <%= link_to 'Products', products_url %>
[/quote]
Tylko, że Tomash raczej (przynajmniej z tego co widzę) nie korzysta z resource’ów :)[/quote]
To nie ma znaczenia. Nazwane ścieżki można (powinno się) zdefiniować niezależnie od korzystania z resources.

Oczywiście, swoją drogą, powinien korzystać z resources. Tomash - miałbyś wtedy nazwane ścieżki gratis. Bez potrzeby ręcznego definiowania w większości przypadków.

Ale pisząc
generate scaffold Category ‘admin/category’
dostaję właśnie owe “niekonwencjonalne nazwy”.

Swoją drogą zmiana nazw plików nie pomaga - wciąż routing error.
Natomiast sam shipping, którego nie ruszałem (wszystkie pliki jego dotyczące są jak były), wypluwa

[code]NoMethodError in Admin#ship

Showing app/views/admin/_order_line.rhtml where line #10 raised:

You have a nil object when you didn’t expect it!
The error occured while evaluating nil.title[/code]

[quote=qertoip]Zmień ‘show’ na ‘index’ - chcesz przecież pokazać listę obiektów biznesowych. Tymczcasem metoda ‘show’ odpowiada za obsługę szczegółów jednego obiektu.

Żeby działało show, trzeba dodać id konkretnego obiektu (:id => @obiekt lub :id => @obiekt.id).[/quote]
Celna uwaga, dzięki.

Chociaż i tak wciąż wypluwa routing error :frowning:

Możecie mi wyjaśnić temat ścieżek i owych resource’ów? Gdzie się toto definiuje?

I jak wreszcie zmusić ten kod do działania? Zachciało mi się dodać adminowania drugą tabelą (owe Categories) i zrobienia nowego scaffolda, no i się wszystko rozwaliło.

Wydaje mi sie, ze zbyt wiele chcesz od std scaffoldow.
Może sprawdź ten projekt: http://www.ajaxscaffold.com/.
Ogólnie rzecz biorąc scaffoldy sa bardzo ograniczone i w aplikacjach praktycznie bezuzyteczne (praktycznie zawsze sie je nadpisuje)
W celach testowych mozesz uzywac scaffoldow “wirtualnych”, ktore nie tworza plikow i “nie robia zamieszania” :slight_smile:
http://api.rubyonrails.com/classes/ActionController/Scaffolding/ClassMethods.html

Mądrze piszesz, wyrzucę wkrótce scaffoldy do kosza.

jak tylko da się coś zrobić z tym “ROUTING ERROR - RECOGNITION FAILED” ;]

Odwołuję alarm. :wink:
Routing Error wynikał ze złych nazw klas kontrolerów - w dużym skrócie to powinienem był poprawić w pierwszej kolejności :slight_smile:

BTW, wciąż nie kumam dlaczego categories_controller zamiast category_controller. Chociaż jak najbardziej rozumiem dlaczego model Category, a tabela w db - categories.

[quote=Tomash]Ale pisząc
generate scaffold Category ‘admin/category’
dostaję właśnie owe “niekonwencjonalne nazwy”.[/quote]
Sugeruję zacząć od nowej aplikacji i bazy.

Potem, z katalogu głównego aplikacji:
script/generate scaffold_resource Category

Potem odwiedzasz migrację (db/migrate/001_create_categories.rb) i uzupełniasz ją o definicję kolumn. Następnie uruchamiasz migrację: rake db:migrate

W tym momencie powinno śmigać.

Długo by pisać. Routing (“ścieżki”) jest odpowiedzialny za tłumaczenie URL-i na wywołania kontroler#metoda i odwrotnie. Definiuje się go config/routing.rb. Dla procedury, którą podałem powyżej, zdefiniuje się sam - nic nie musisz robić.

‘Resources’ to implementacja idei REST w Rails. Podany przykład (scaffold_resource) jest zgodny z tą ideą (nic więcej nie musisz robić).

Pewnie musisz troszkę więcej poczytać zanim pozbierasz to do kupy. W “Agile…” zajrzyj od razu do rozdziału o REST (około 400s).

category_controller - kontrolery starego typu (stąd odradzałem), czyli gdy nie korzystasz z ‘resources’. Także dla singleton resource (ale mniejsza o to).
categories_controller - gdy korzystasz z ‘resources’, czyli według bieżącej wykładni w większości przypadków. Dla kategorii i produktów tak powinno być.

Dzięki za te wszystkie wiadomości, jesteście świetni. Nie sądziłem, że zachwyty nad społecznością Ruby’ego przenoszą się także na polskie podwórko, z reguły trafniej określane “piekiełkiem” :slight_smile:

Najwyraźniej Ruby+Rails to nie technologia, w której można pisać od razu po otwarciu dokumentacji - jak to ma miejsce w przypadku PHP. Ale tym ostatnim już rzygam, a RoR zawiera w sobie pewien nieodparty urok i piękno kodu.

To prawda. RoR zmusza programistę do podniesienia własnych kompetencji, do obczajenia nowoczesnych technik tworzenia aplikacji webowych i ogólnie nowoczesnego programowania. RoR albo wyedukuje, albo odstraszy :wink: