Duży hobbystyczny projekt

Chcę zrobić projekt hobbystyczny - serwis internetowy, który będzie zorganizowany tak, że będzie miał sporo podstron, a każda podstrona będzie składała się z kilkunastu różnych składników, fragmentów kodu zapisanych w osobnych plikach, gdzie każdy eskładnik może być zaimplementowany do kilku różnych podstron. Przykładowo mam system komentarzy jako jeden kontroler, system logowania i rejestracji jako drugi kontroler, i oczywiście będzie to implementowane na kilka podstron. Jak to dobrze zorganizować w ROR, czy są jakieś helpery? Czy może zrobię to ręcznie za pomocą linków “href” i odpowiedników PHP-owych include?

Możesz to zrobić na wiele sposobów, rails zapewnia partiale i helpery, które mogą być pomocne.

Np. fragmenty kodu zapisane w plikach wrzucasz do app/views/shared/code_snippets i tworzysz helper który wczytuje te pliki, parsuje i wyświetla.

Helpera wywołujesz w widoku. Najlepiej zacznij to po prostu pisać i wrzuć na forum spory fragment kodu o który chcesz zapytać - na pewno znajdą się chętni do poprawienia/omówienia.

Znaczy, nie wiem czy rozumiem, ale no w zasadzie, to tego tam… No są jakieś helpery, no i niby te railsowe helpery (app/helpers/*_helper.rb) można rozumieć jako PHP-owe include. Linki href to przecież zawsze się wykorzystuje, ale w Rails masz metody costam_path() edit_costam_url() i tak dalej.

Generalnie, to zacznij myśleć od strony obiektów, a nie od stron - program w Rails jest jeden i działa cały czas. Nie tak, jak w PHP, gdzie programem jest konkretna strona i ona sobie ładuje potrzebne biblioteki (czy moduły), a potem renderuje i się kończy. W sumie to tu też jest niby podobnie, ale zacznij raczej myśleć od strony bazy danych. Zdefiniuj modele, myśląc o zakwalifikowaniu ich jako resource (w kontekście REST). upraszczając - zdefiniuj sobie ścieżki do obiektów:

/user
/articles
/articles/1
/articles/1/comments
/articles/1/comments/2

co daje modele: User, Article, Comment.

Potem pomyśl o tych modelach, a kontrolery i inne rzeczy same Ci wyjdą po drodze.

Trochę trudno dawać takie ogólne rady, więc pewnie lepiej będzie poszukać jakiegoś tutoriala, albo zadać jakieś bardziej konkretne pytanie.

Na razie nie ma jeszcze co pokazywać, bo niewiele jest zrobione :slight_smile: Ale jak zrobię trochę to pokażę

Na razie stworzyłem home_controller jako kontroler strony głównej. Html podzieliłem na pliki: topbar, featured_slide, homecontent, container, footer, copyright i wrzuciłem w app/views/shared i załadowałem po kolei w app/views/home/index.thml.erb za pomocą <%= render :file => …%>. Tworzę teraz nowe kontrolery, mn. dla komentarzy (comments_controller i comment [model]) i chcę je, czyli te index.html.erb dla tych kontrolerów zagnieździć w app/views/home/index.thml.erb jako osobne, doczepiane fragmenty layoutu. Po co? Po to, żeby wszystkie formularze, pola logowania znajdowały się na stronie głównej i/lub podstronach i nie przechodziło się do nowej “osobnej” podstrony lub nowego widoku, przynajmniej tak, żeby user tego nie zauważył. Niestety w tutorialu Ruby on Rails Tutorial na youtube pokazali jak zrobić system postów, gdzie każda akcja wywołuje nowy widok, a ja tego nie chcę. Więc teraz muszę się zastanowić jak to zrobić. Jak macie jakieś uwagi to piszcie :slight_smile:

Mówisz o dynamicznym ładowaniu podstron?

Backbone.js
Knockout.js
Joosy ( http://joosy.ws/ )

[quote=konole]Mówisz o dynamicznym ładowaniu podstron?

Backbone.js
Knockout.js
Joosy ( http://joosy.ws/ )[/quote]
yeah, sure. widać że kolega jest raczej początkujący więc bez sensu dorzucać mu kolejny framework do stosu.

jeżeli dobrze rozumiem problem to w sumie hosiawak opisał całkiem dobry sposób (tzn. poprzez partiale i helpery), ma on jedną tylko wadę - musisz (zazwyczaj) zasyfić sobie kontroler wczytaniem dodatkowych danych (komentarze, popularne artykuły, najnowsi użytkownicy itd).

można to rozwiązać inaczej - https://github.com/apotonick/cells/ lub do rzeczy bardziej … dynamicznych (ajaxy i inne bajery) - https://github.com/apotonick/apotomo/

cells to takie komponenty - masz “mini-kontroler” (i w nim akcje) oraz widoki do nich. pozwala to zachować większy porządek niż przy kombinacji helpery+partiale

To byłoby najważniejsze założenie, a dynamiczne przesyłanie danych też ważne, więc tu potrzebny będzie ajax. Pokombinuję jeszcze z tymi “mini-kontrolerami” i zobaczę na ile to przydatne i czy da się zachować porządek bez tego, czy też nie.

co rozumiesz przez “dynamiczne przesyłanie danych”, to nie tak, że w cells masz zablokowany javascript :stuck_out_tongue: apotomo po prostu ułatwia takie rzeczy jak wysyłanie formularza w tle itp.

http://cells.rubyforge.org/

Teraz zauważyłem, że ktoś już polecił

Zacznij od usecase’ów :slight_smile:

Prawda, święte słowa. Wycofuję co powiedziałem. W tym projekcie rzeczywiście to nie jest takie ważne. Ale… ale nie zapominaj o resourcach całkowicie! :wink:

Ja najczęściej idę z analizy potrzeb (i usecase) do budowania modeli. Dopiero później (czy raczej równolegle, ale z nieco niższym priorytetem) przejmuję się tym, jak będą działać widoki. Tu wygląda mi na to, że modele będą dość trywialne, za to widoki i usability faktycznie będą kluczowe. Sorry…

Mam pewien kłopot z zapisem formularza do bazy danych. @comment.save zapisuje tylko puste pola o wartości :nil. Otóż formularz wysyła się POST-em do controllers/comments_controller -> def create, i tam @comment = Comment.new(params[:post]) działa poprawnie, gdyż za pomoca <%= YAML.dump(params[:post]) %>i <%= debug(params) %> widzę, że @comment zawiera niepuste atrybuty, czyli to co wysłałem, ale @comment.save tworzy tylko puste pola:

SELECT “comments”.* FROM “comments”
=> [#<Comment id: 1, nick: nil, content: nil, created_at: “2012-07-10 16:52:40”, updated_at: “2012-07-10 16:52:40”>]

CAŁY WYDZIELONY KOD TEGO SYSTEMU KOMENTARZY:

views/home/index.html.erb:

<%= render :file => “app/views/shared/header” %>
<%= render :file => “app/views/shared/topbar” %>
<%= render :file => “app/views/shared/featured_slide” %>
<%= render :file => “app/views/shared/homecontent” %>
<%= render :file => “app/views/shared/container” %>

<%= helper_comments #tu wstawilem system komentarzy %>

<%= render :file => “app/views/shared/footer” %>
<%= render :file => “app/views/shared/copyright” %>

helpers/application_helper.erb:

module ApplicationHelper
def helper_comments
@comments = Comment.all
@comment = Comment.new
render :file => “app/views/comments/index”
end
end

views/comments/index.html.erb:

<% @comments.each do |comment| %>

<%= comment.nick %>

<%= comment.content %>

<% end %>
<%= form_for @comment do |comment| %>
    <%= comment.label :nick %>
    <%= comment.text_field :nick %>
    <%= comment.label :content %>
    <%= comment.text_area :content %>
    <%= comment.submit "Dodaj post" %>
<% end %>

controllers/comments_controller:

class CommentsController < ApplicationController
def index
@comments = Comment.all
@comment = Comment.new
end
def create
@comment = Comment.new(params[:post])
@comment.save
redirect_to “/”
end
end

models/comment.rb:

class Comment < ActiveRecord::Base
attr_accessible :content, :nick
end

db/migrate/20120709193031_create_comments.rb:

class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :nick
t.text :content
t.timestamps
end
end
end

routes.rb:

Dev::Application.routes.draw do
resources :comments
root :to => ‘home#index’
end

struktura bazy danych:

Comment
=> Comment(id: integer, nick: string, content: text, created_at: datetime, updated_at: datetime)

[code=ruby]def create
@comment = Comment.new(params[:post])

tutaj @comment zawiera wszystkie potrzebne atrybuty?

co tutaj zwraca @comment.valid?

@comment.save
redirect_to “/”
end[/code]
spróbuj może #save! i zobacz czy model nie rzuca Ci jakiegoś wyjątku. pokazałeś cały model Comment?

btw. używaj tagów kiedy wrzucasz kod [ code = ruby ] (usuń spacje)

Po przekierowaniu do show.html.erb za pomocą render “show”:

<%= debug(params) %>
zwraca:
authenticity_token: z3mfnrMGVilQyRTP7zLog8JAFIxI40HwmD3f7wsdwG4=
comment: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
nick: awddWD
content: AWDdaWDad
commit: Dodaj post
action: create
controller: comments

@comment.valid?
zwraca:
true

zacznę od końca - @comment.save! :slight_smile: # przed nazwą metody oznacza metodą instancji, tutaj w domyśle było że metodę obiektu Comment :slight_smile:

z tego co widzę masz powinieneć zrobić Comment.new(params[:comment]) a nie params[:post] :slight_smile:

Działa! Dzięki! Prześledziłem wcześniej uważnie i nie zauważyłem błędu, gdyż w poprzednim projekcie, gdzie tworzyłem blog, używałem posts controller i post model i myślałem, że (params[:post]) oznacza przesłanie metodą POST, więc nie rozumiałem problemu.

Apropo, jak byś miał uwagi co do tego, jak powyżej rozwiązałem problem doklejenia komentarzy do całej reszty (w views/home/index.thml.erb), to pisz. Lepiej usuwać złe nawyki wcześnie niż późno.

to nie PHP :smiley:

Myślę że sporo nowicjuszy popełnia ten błąd (ja też popełniłem) i dałem się złapać że :post to POST :wink:

mam:
def create
@comment=Comment.new(params[:comment])
@comment.save
redirect_to “/”
end

Mam kłopocik z tym, że “redirect_to” powraca do przedniej strony, ale nie są przekazywane informacje o błędach z powodu niepoprawnej walidacji, a za pomocą “render” nie mogę powrócić do poprzedniej strony, ale są przekazywane informacje o błędach niepoprawnej walidacji.

Jak powrócić do poprzedniej strony i przekazać treść błędu o niepoprawnej walidacji?

Zobacz jaki kod generuje scaffold: ./script/rails generate scaffold jakismodel - generalnie brakuje Ci jakiejkolwiek kontroli błędów. Nie sprawdzasz, czy save zakończyło się błędem, czy sukcesem. Tak nie wolno :wink:

Zasada jest taka - jeżeli zapis się nie uda, nie robisz przekierowania, tylko renderujesz stronę (i to najlepiej z kodem 422, albo inym, ale $DEITY broń z 200!). na tej stronie masz okazję pokazać wszystkie błędy.

Generalnie to idzie jakoś tak:

def create @comment = Comment.new(params[:comment]) respond_to do |format| if @comment.save format.html { redirect_to @comment, :notice=>"Poszło OK" else format.html { render :action => "new" } end end end