Rails 4 : basic use of Merit gem with Devise - badge

Using the merit gem, I want to create a Pioneer badge for the first 100 users of my app.
The code in merit.rb
Merit::Badge.create!(
id: 1,
name: 'Pioneer',
description: "Belongs to the 100 first users of the site",
image: '/images/pioneer.png'
)
The code from badges_rules.rb takes in consideration that i'm using devise for authentification. So I followed this how to.
grant_on 'users/registrations#create', badge: 'Pioneer', model_name: 'User' do |user|
user.id < 101
end
It's not creating any badge. It's interesting to notice that this other badge is working very well :
grant_on 'users/registrations#create', badge: 'Inscription', model_name: 'User'
It seems that Devise is messing with the user object. I did override the registration controller, exactly like the Howto said. And when I call this controller in a simple way, like with this "Inscription" badge, everything's ok.
But when I need to put a condition on the user's id, nothing happens.
For information, this is the code from user's show view, where the badges are displayed (this is working).
<% #user.badges.first(5).each do |b| %>
<%= image_tag(b.image) %>
<%= b.name %>
<%end%>

Add model_name: 'User' to your rule. This happens when the controller name is not named after the object (registrations != user).

Related

Use form_for to update array attribute of object

I've been seeing topics dancing around this issue, so maybe I've actually seen the answer and I've just been banging away at it too long that my brain can't recognize it.
In short, I think I need to mark my form data as dirty so that it will update the array field in the Freelancer object, but I seem to be missing something.
Important info: programmingLanguages below is an array field. In my migration I have:
t.text :programmingLanguages, array: true, default: []
So while I'm currently submitting only one string to said array, I want to be able to submit more than one eventually (and perform operations on the data when I need to down the line...)
I have a form:
<%= form_for(#freelancer) do |f| %>
<%= f.label :programmingLanguages, "Programming languages (seperated by commas): "%>
<%= f.text_field :programmingLanguages %>
.
.
.
<%= f.submit "Submit", class: "button"%>
<% end %>
Which submits fine and gives me a list of params I expect:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"long token", "freelancer"=>{"programmingLanguages"=>"Stuff for an array, more stuff for an array"...}
I then use those params to update attributes for my #freelancer object:
def create
#freelancer = Freelancer.find(session[:user_id])
if #freelancer.update_attributes(freelancer_params)
redirect_to #freelancer
else
render root_path
end
end
Because I'm using a multi-step form (hence the find by session), I'm updating the attributes in my create action vs. saving them because the object I'm working with already exists and is saved to the database. The issue is that when I use update_attributes I notice that the SQL commits are ignoring my array fields, such as programmingLanguages above, but does not disregard the other attributes in the params. In other words my integer field totalPrograms updates easily/correctly, but my 4 array fields get lost somewhere between the params and updating the #freelancer object.
Any help much appreciated!
Update:
Here are the strong params:
def freelancer_params
params.require(:freelancer).permit(:programmingLanguages, :totalPrograms...)
end
One more note with this, I guess: I've tried indicating here that :programmingLanguages is an array (a la :programmingLanguages => [] or programmingLanguages: []) and I get syntax errors (syntax error, unexpected ',', expecting =>). Which of course could just mean my syntax is off in setting theses as arrays in my strong params, but if that's the case then I can't find the dumb little error there either.

How to update a nested collection_check_box

I have a form for a child (categorization) nested within the parent form (project). The categorization attributes are created using a collection_check_boxes helper like so:
<%= f.fields_for :categorizations do |cat| %>
<%= collection_check_boxes :categorization, :category_id, #categories, :id, :display_name %>
<% end %>
In the Projects controller I'm able to successfully create the new records:
def create
#project = Project.new(project_params)
valid_categorizations = params[:categorization][:category_id].reject! { |c| c.empty?}
#project.categorizations.build(valid_categorizations.map{|cat| {category_id: cat}})
...
end
...
def project_params
params.require(:project).permit(:slug, :title, :body, :published, :category_ids => [])
end
Now, when I want to edit the parent Project record the collection_check_boxes does two undesirable things:
It does not pre-populate attributes (like the .include?(attribute) in a traditional check box), and
It repeats the set of check boxes N times depending on how many categorization records exist in the database (e.g., if Project #1 has three Categorization records the entire set of check boxes will display three times in the edit form).
I can't figure out how to fix this, so any help is greatly appreciated!

How do you compare variables from different tables with ruby on rails?

For example, within a model/data-set/table (technical term?) called "microposts" if I have id and user_id columns, to compare the two sets I can use
Micropost.where("user_id = ?", id)
I am wondering, however, how to do the same thing with data from different data-sets/tables/models. In this particular scenario I am trying to find all instances where a user's community variable is equal to the community variable of any microposts. I'm attempting to match the content of the strings so that all microposts are rendered that have a community variable that matches the current user's community variable. These variables are in string form as #city + #state under the column "community" in the users's and microposts's model/datasets/table. Illustrated:
users table microposts table
user_id community micropost_id content community
1 FairfieldIowa 1 blub! FairfieldIowa
2 FairfieldIowa 2 Hiiy FairfieldIowa
3 Salt Lake CityUtah 3 wwowt! Salt Lake CityUtah
4 Salt Lake CityUtah 4 hey Salt Lake CityUtah
5 FairfieldIowa 5 sweet FairfieldIowa
I thought this might work in the User model:
def communityfeed
Micropost.where("community = ?", user.community)
end
However, it is incorrect because of syntax, logic, or because "user" does not define all users, I think.
Then I defined communityfeed_items in a pages_controller as:
def home
if logged_in?
#micropost = current_user.microposts.build
#communityfeed_items = communityfeed.paginate(page: params[:page])
end
end
But I get an undefined local variable or method communityfeed error
This could be because I'm not providing any user to use the method on. Perhaps simply using a variable for all users will solve my problem, but this is something I could not find. I've tried all_users, and user, with no success. Is it possible to compare variables to render a result in this way, and if so, what is the syntax to cycle through all user/microposts and check for these matches?
I also built several partials to function together to render the microposts, however, without the #communityfeed_items variable functioning correctly, they are of little use.
_communityfeed.html.erb
<% if #communityfeed_items.any? %>
<ol class="microposts">
<%= render partial: 'shared/communityfeed_item', collection: #communityfeed_items %>
</ol>
<%= will_paginate #communityfeed_items %>
<% end %>
_communityfeed_items.html.erb
<li id="<%= communityfeed_item.id %>">
<%= link_to gravatar_for(communityfeed_item.user), communityfeed_item.user %>
<span class="user">
<%= link_to communityfeed_item.user.name, communityfeed_item.user %>
</span>
<span class="content"><%= communityfeed_item.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(communityfeed_item.created_at) %> ago.
</span>
<% if current_user?(communityfeed_item.user) %>
<%= link_to "delete", communityfeed_item, method: :delete,
confirm: "You sure?",
title: communityfeed_item.content %>
<% end %>
</li>
If I have approached this problem in a severely inefficient/impossible way, I am open to suggestions.
Thanks for your time, generosity, and expertise.

how pass variables by arrays not db records?

I'm currently building a data generator. First I want to implement is PESEL (kind of personal ID in Poland based on birth date) generator - I want to enter in form a temporary data with start and end birth date interval - I don't want to store it in database (or I should I do it?)
Here is my pesel controller:
def new
#pesel = Array.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #pesel }
end
end
but I've got an "undefined method `model_name' for NilClass:Class" error - is it a good way anyway of solvint this case? I read somewhere that using temporary variables is not with 'The Ruby Way' - if my solution is wrong, please suggest the correct one. (e.g pass this vars through cookies? hash? helper method?)
here is the stacktrace(I think):
Started GET "/pesel" for 127.0.0.1 at 2011-12-05 16:18:20 +0100
Processing by PeselController#new as HTML
Rendered pesel/new.html.erb within layouts/application (1513.9ms)
Completed 500 Internal Server Error in 1793ms
ActionView::Template::Error (undefined method `model_name' for NilClass:Class):
1: <%= simple_form_for #pesel do |f| %>
2: <%= f.input :date_of_birth, :as => :date, :start_year => Date.today.year - 90,
3: :end_year => Date.today.year - 12, :discard_day => true,
4: :order => [:month, :year] %>
app/views/pesel/new.html.erb:1:in `_app_views_pesel_new_html_erb__708648673_90148530'
app/controllers/pesel_controller.rb:7:in `new'
Rendered /home/ofca/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-3.1.1/lib/action_dispatch/middleware/templates/rescues/_trace.erb (5.6ms)
Rendered /home/ofca/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-3.1.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (4.0ms)
Rendered /home/ofca/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-3.1.1/lib/action_dispatch/middleware/templates/rescues/template_error.erb within rescues/layout (17.6ms)
form_for assumes certain properties exist for the object you pass it, such as model_name.
Instead of using form_for #pesel, just use form_tag and the related _tag methods.
Use a Pesel model. Models are not tables, and your model doesn't have to write anything to the database. Just don't inherit from ActiveRecord, but do provide a model_name and any other fields the form_for helper expects.

Rails 3 array - add items with category then list items in category

As part of my Rails 3 app, I want the User to be able to click on links on other profiles/pages and have the string value of the link be added to an array belonging to that User's profile.
Specifically, what I am looking to do is populate a list of :todos for each profile depending on which todo they click. The idea is that each todo will fall within one of two categories: inside and outside. So clicking the links will push the value of the todo to either inside or outside. Then the User's profile will display a list of :todos inside and outside, and count the total of todos for that User's profile.
Since I'm a beginner to programming, I got some help here on SO about setting this up; however I need some help finishing it. I can't quite seem to connect all the dots. I've set up a join model but am not able to add the todo's string value, then list/count it in the profile. Here is my code:
profile.rb Model:
class Profile < ActiveRecord::Base
belongs_to :user
accepts_nested_attributes_for :user
has_many :profile_todos
has_many :todos, :through => :profile_todos
def add_todo_inside_item(item)
self.profile_todos.build :category => 'inside'
end
def add_todo_outside_item(item)
self.profile_todos.build :category => 'outside'
end
end
todo.rb Model:
class Todo < ActiveRecord::Base
belongs_to :profile
end
profile_todo.rb Model:
class ProfileTodo < ActiveRecord::Base
belongs_to :profile
belongs_to :todo
end
_create_todos.rb Migration:
class CreateTodos < ActiveRecord::Migration
def self.up
create_table :todos do |t|
t.string :name
t.timestamps
end
end
_create_profile_todos.rb Migration:
class CreateProfileTodos < ActiveRecord::Migration
def self.up
create_table :profile_todos do |t|
t.string :category
t.integer :profile_id
t.timestamps
end
end
Listing the todos in a User's Profile:
<div id="todos">
<p><%= #profile.first_name %> has <%= pluralize(#profile.profile_todos.count, 'goal') %>.</p>
<div id="todo-list">
<div class="todos_inside">
<p><%= #profile.profile_todos(:category => 'inside' %>.</p>
</div>
<div class="todos_outside">
<p><%= #profile.profile_todos(:category => 'outside' %>.</p>
</div>
</div>
</div>
add_item to #profile.todos:
<li><%= link_to "#", :class => 'button white' do %><%= #user.profile.stringtoadd %><% end %></li>
As #socjopata mentioned, you're going to need some sort of controller action to manage the creation and building of your ProfileTodo records. Since you already created a ProfileTodo join model, go ahead and create a ProfileTodosController.
More on controllers:
http://guides.rubyonrails.org/action_controller_overview.html
Your link_to tag should then make a remote call to the create action. In order to get everything to work properly, you'd most likely need to supply the controller with both the profile_id and topic_id in order to make the correct RESTful transaction, which means you'll have to supply a parameter hash to your link_to tag, which can get kinda messy if you use the url_options.
Look at passing in url_options:
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
Ultimately, you are creating a new ProfileTodo record, so I think it would be a better description of the work being done if you used a form_for tag that has hidden fields for the profile_id and the topic_id. You can also make forms remote in rails by supplying them with :remote => true
Assuming you make an accompanying controller and add RESTful resources to your config/routes.rb file, your form for each individual topic would look something like this:
<%= form_for(profile_todo_path, :remote=> true) do |f| %>
<%= f.hidden_field_tag :profile_id, :value => #user.profile.id %>
<%= f.hidden_field_tag :topic_id, :value => topic.id %>
<%= f.submit_tag topic.name %>
<% end %>
You can always style your forms, so if you only want a link to be displayed, that should be doable :)
You rather need a remote link pointing to some action, so you can manipulate your todos without refreshing the page. From the docs:
:remote => true - This will allow the unobtrusive JavaScript driver to
make an Ajax request to the URL in question instead of following the
link. The drivers each provide mechanisms for listening for the
completion of the Ajax request and performing JavaScript operations
once they’re complete
As for the other question, you can define scopes you'll use for displaying and counting objects:
http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html

Resources