Rails Admin routes inside config - ruby-on-rails

I'm using Rails Admin for my admin area.
The sidebar panel should have some links to the instances of a model.
In rails_admin.rb I've tried something like:
RailsAdmin.config do |config|
#navigation_links = Hash[*Conference.all.map {|conference| [conference.name, bindings[:view].main_app.show_path(model_name: 'conference', id: conference.id)]}.flatten]
config.navigation_static_links = #navigation_links
end
However, here I do not have access to bindings. So, how can I get the url of an admin resource here? I cannot see it in the documentation
Thanks

My answer probably not what you want to do, but it can be helpful.
I've checked rails_admin.gem and i found that there are two methods that responsible for rendering sidebar menu.
def main_navigation
nodes_stack = RailsAdmin::Config.visible_models(controller: controller)
node_model_names = nodes_stack.collect { |c| c.abstract_model.model_name }
nodes_stack.group_by(&:navigation_label).collect do |navigation_label, nodes|
nodes = nodes.select { |n| n.parent.nil? || !n.parent.to_s.in?(node_model_names) }
li_stack = navigation nodes_stack, nodes
label = navigation_label || t('admin.misc.navigation')
%(<li class='dropdown-header'>#{capitalize_first_letter label}</li>#{li_stack}) if li_stack.present?
end.join.html_safe
end
Method above responsible for getting list of models, especially:
nodes_stack = RailsAdmin::Config.visible_models(controller: controller)
Second method responsible for rendering item in the menu (aka li):
def navigation(nodes_stack, nodes, level = 0)
nodes.collect do |node|
model_param = node.abstract_model.to_param
url = url_for(action: :index, controller: 'rails_admin/main', model_name: model_param)
level_class = " nav-level-#{level}" if level > 0
nav_icon = node.navigation_icon ? %(<i class="#{node.navigation_icon}"></i>).html_safe : ''
li = content_tag :li, data: {model: model_param} do
link_to nav_icon + capitalize_first_letter(node.label_plural), url, class: "pjax#{level_class}"
end
li + navigation(nodes_stack, nodes_stack.select { |n| n.parent.to_s == node.abstract_model.model_name }, level + 1)
end.join.html_safe
end
So you can patch this methods to get what you need.
module RailsAdmin
module ApplicationHelper
def main_navigation
# your code
end
end
end
rails_admin.gem module
P.S. I love what you can read from rails doctrine about monkey patching:
This power has frequently been derided as simply too much for mere
mortal programmers to handle.

Related

better way to build association in controller

I need a link in a show method of a parent class for creating associated models, so I have the code:
link_to "incomplete", new_polymorphic_path(part_c.underscore, :survey_id => survey.id)
in a helper.
This links to a part, which has new code like this:
# GET /source_control_parts/new
def new
get_collections
if params[:survey_id]
#s = Survey.find(params[:survey_id])
if #s.blank?
#source_control_part = SourceControlPart.new
else
#source_control_part = #s.create_source_control_part
end
else
#source_control_part = SourceControlPart.new
end
end
I know this is not very DRY. How can I simplify this? Is there a RAILS way?
How about this:
def new
get_collections
get_source_control_part
end
private
def get_source_control_part
survey = params[:survey_id].blank? ? nil : Survey.find(params[:survey_id])
#source_control_part = survey ? survey.create_source_control_part : SourceControlPart.new
end

Rails Netzke v0.10.1 - Refreshing grid panel within a tabpanel

I try to make a Netzke component with one master grid and subgrids in the south region of a Panel.
When a row in the maingrid is selected then should the subgrids be filtered with records related to the record in maingrid - like described here for an old netzke version:
https://groups.google.com/forum/#!searchin/netzke/tabpanel/netzke/PFAQ-wYyNog/2RJgRLzh80oJ
I know that netzke is not further in development but I use it in a project.
ruby 2.1.2 (Mac OSX rbenv)
rails 4.0.10
netzke-core v0.10.1
netzke-basepack v0.10.1
Here my Code:
models:
class MbOrganisation < ActiveRecord::Base
has_many :mb_contacts
def customer_name
"#{orga_customer} - #{orga_name1}"
end
end
class MbContact < ActiveRecord::Base
belongs_to :mb_organisation
end
This is the central component
app/components/organisation_multitab.rb
class OrganisationMultitab < Netzke::Base
component :organisation_organisations
component :organisation_tabpanel do |c|
c.klass = MblixBaseTabpanel
c.items = [:organisation_contacts]
end
js_configure do |c|
c.layout = :border
c.border = false
c.init_component = <<-JS
function(){
// calling superclass's initComponent
this.callParent();
// setting the 'rowclick' event
var view = this.netzkeGetComponent('organisation_organisations').getView();
view.on('itemclick', function(view, record){
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
this.selectItem({item_id: record.get('id')});
}, this);
}
JS
end
def configure(c)
super
c.items = [
{ items: [:organisation_organisations], region: :center },
{ items: [:organisation_tabpanel], region: :south, height: 200, split: true }
]
end
endpoint :select_item do |params, this|
# store selected id in the session for this component's instance
component_session[:selected_item_id] = params[:item_id]
end
end
These components are additionally used
Maingrid - organisation_organisations.rb
class OrganisationOrganisations < Netzke::Basepack::Grid
def configure(c)
super
c.model = "MbOrganisation"
c.columns = [:orga_customer, :orga_name1, :orga_name2, :orga_street, :orga_zip, :orga_city, :orga_tel, :orga_email]
c.force_fit = true
end
end
Component with Tabpanel- base_tabpanel.rb:
class BaseTabpanel < Netzke::Basepack::TabPanel
component :organisation_contacts do |c|
c.data_store = {auto_load: false}
c.scope = {:mb_organisation_id => component_session[:selected_item_id]}
c.strong_default_attrs = {:mb_organisation_id => component_session[:selected_item_id]}
end
def configure(c)
super
c.active_tab = 0
c.prevent_header = true
end
end
The grid component for the contacts:
class OrganisationContacts < Netzke::Basepack::Grid
def configure(c)
super
c.model = "MbContact"
c.columns = [{ :name => :mb_organisation__customer_name,
:header => "Organisation"
}, :cont_salutation, :cont_title, :cont_lastname, :cont_firstname, :cont_email, :cont_tel, :cont_mobile, :cont_birthday]
c.force_fit = true
end
end
The function this.selectItem(...) is correct triggered and calls the endpoint in OrganisationMultitab.
I have two problems/questions
First
- How can I automatically reload the stores of the subgrids in the tabpanel?
The described way in the linked google groups article: https://groups.google.com/forum/#!searchin/netzke/tabpanel/netzke/PFAQ-wYyNog/2RJgRLzh80oJ is outdated (It's for netzke v0.5 - I use netzke v0.10.1):
{
:south => {
:item0 => {:load_store_data => aggregatee_instance(:south__item0).get_data},
:item1 => {:load_store_data => aggregatee_instance(:south__item1).get_data}
}
}
second problem: I got an error - when I manually refresh the subgrids:
ActiveModel::ForbiddenAttributesError in NetzkeController#direct
Update
The ActiveModel::ForbiddenAttributesError is solved by myself. There was a bug in the netzke-basepack gem:
Netzke::Basepack::Grid ran in an ActiveModel::ForbiddenAttributesError (rails 4 strong parameters) when the component, like above described, has a scope configured. (config[:scope] will later be merged to the params object that is an ActionController::Parameters object. - As the scope is database related this will be denied with ActiveModel::ForbiddenAttributesError )
My solution: In the endpoint.rb the ActionController::Parameters will be converted to a Hash - then the error is gone.
I made a fork and a pull request in github for this gem.
But
the second problem is not solved.
second problem: Now the subgrids can be manually refreshed without an error but they are always empty.
I guess the scope in the child component
component :organisation_contacts do |c|
c.data_store = {auto_load: false}
c.scope = {:mb_organisation_id => component_session[:selected_item_id]}
c. strong_default_attrs = {:mb_organisation_id => component_session[:selected_item_id]}
end
has no access to the value of the
component_session[:selected_item_id]
of the Organisation MultiTab parent component?
But it is neccessary to split the components - like described here: https://groups.google.com/forum/#!searchin/netzke/tabpanel/netzke/sDrU7NZIlqg/-2wGmed7fjcJ
Hope there is somebody who can help me. :-)
Thanks
Best regards
You're getting the ActiveModel::ForbiddenAttributesError because you're not permiting the attributes from the controller. Rails now uses strong_parameters instead of attr_accessible (like in Rails 3).
So I found the solution by my self.
First issue - reloading the grids in the Tabs
The store of the Ext gridcomponent can also be accessed in the Javascript.
So I extended the Javascript configuration of the OrganisationMulitab with this part:
Ext.each(this.netzkeGetComponent('organisation_tabpanel').items.items, function(item, index) {
item.getStore().load();
});
Second issue - send the selected id to the scope in the child component
The value must be sent to the session of the child component - so this does the job:
component_instance(:organisation_tabpanel).component_session[:selected_item_id] = params[:item_id]
instead of
component_session[:selected_item_id] = params[:item_id]
(The problem with the ActiveModel::ForbiddenAttributesError was a bug in the gem - solution is in my update of the question - I made a fork of the gem https://github.com/tomg65/netzke-basepack/tree/master-fixes-changes and sent a pull request to the original https://github.com/netzke/netzke-basepack/pull/158)
So the final code looks like this and all works fine:
class OrganisationMultitab < Netzke::Base
component :organisation_organisations
component :organisation_tabpanel do |c|
c.klass = MblixBaseTabpanel
c.items = [:organisation_contacts]
end
js_configure do |c|
c.layout = :border
c.border = false
c.init_component = <<-JS
function(){
// calling superclass's initComponent
this.callParent();
// setting the 'rowclick' event
var view = this.netzkeGetComponent('organisation_organisations').getView();
view.on('itemclick', function(view, record){
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
this.selectItem({item_id: record.get('id')});
Ext.each(this.netzkeGetComponent('organisation_tabpanel').items.items, function(item, index) {
item.getStore().load();
});
}, this);
}
JS
end
def configure(c)
super
c.items = [
{ items: [:organisation_organisations], region: :center },
{ items: [:organisation_tabpanel], region: :south, height: 200, split: true }
]
end
endpoint :select_item do |params, this|
# store selected id in the session for child component's instance
component_instance(:organisation_tabpanel).component_session[:selected_item_id] = params[:item_id]
end
end
Hope this helps others too.
Best regards
Thomas

Rspec, Rails - how to test helper method that use params hash?

I want to implement a tagging system similar to stackoverflow, there is a box with a tags at top right corner, and also I have links to delete tag from params hash. my method works correctly in browser. But I can't find a way to test it.
def tags_list_with_destroy_links
if params[:tags]
li = ""
p = params[:tags].split("+") # '/tagged/sea+ship+sun' => ['sea', 'ship', 'sun']
p.map do |t|
remove_link = if p.count >= 3
c = p.reject {|item| item == t }
a = c.join("+")
{:tags => a}
elsif p.count == 2
c = p.reject {|item| item == t }
{tags: c[0]}
else
questions_url
end
li << content_tag(:li) do
link_to(t, questions_tags_path(t), class: 'tag') +
link_to( '', remove_link , class: 'icon-small icons-cross')
end
end
ul = content_tag(:ul, li.html_safe)
ul << tag(:hr)
end
end
I've tried:
it 'return list with selected tags' do
#Rails.application.routes.url_helpers.stub(:questions_tags).and_return('/questions/tagged/sea+ship+sun')
#helper.request.stub(:path).and_return('/questions/tagged/sea+ship+sun')
helper.stub(:url_for, {controller:'questions', action: 'index', tags:'sea+ship+sun'} ).and_return('/questions/tagged/sea+ship+sun')
helper.params[:tags] = 'sea+ship+sun'
helper.tags_list_with_destroy_links.should == 'list_with_tags'
end
but it return:
<a class=\"tag\" href=\"/questions/tagged/sea+ship+sun\">sea</a><a class=\"icon-small icons-cross\" href=\"/questions/tagged/sea+ship+sun\"></a></li>
and shoud return remove link as
href="/questions/tagged/ship+sun" without sea
I would appreciate any advice
The params field is going to come back parsed into the correct ruby data structures (hash, array, string, etc). There's no need to manually split items such as +, if there is a nested param it will return as part of the params object:
{tags: ["sea", "ship", "sun"]}
To access your data, or create an assumption about your param data existing in the test, you're going to want to create a stub. You're almost there, try something more along the lines of:
helper.stub!(:params).and_return({tags: ["sea", "ship", "sun"]})
Once you have the params stubbed correctly you can check the output of your helper method to ensure it's validity (this is called the expectation):
expect(helper.tags_list_with_destroy_links).to eq("some_url")

Error Calling Initialized Variables In Rails

Building a Rails 3.2 app with Ruby 1.9.
I am trying to write a helper method that initializes 3 variables and when i try to call the initialized variables from my view i get an "undefined method" error.
Method In Helper File
module StoreHelper
class Status
def initialize(product)
product_sales = product.line_items.total_product_sale.sum("quantity")
#avoid nil class errors for vol2 and 3. volume 1 can never be nil
if product.volume2.nil?
product.volume2 = 0
end
if product.volume3.nil?
product.volume3 = 0
end
#Promo status logic
if (product_sales >= product.volume2) && (product_sales < product.volume3)
#level3_status = "Active"
#level2_status = "On!"
#level1_status = "On!"
elsif (product_sales >= product.volume3)
#level3_status = "On!"
#level2_status = "On!"
#level1_status = "On!"
else #level3_status = "Pending"
end
end
I then attempt to call the the initialized variable #level3_status like so
<%=level3_status (product)%>
Not sure what i'm doing wrong any help would be appreciated.
How long are you programming with ruby? You have to create a new instance of your class to access the instance outside. Take a look at these basics: http://www.tutorialspoint.com/ruby/ruby_variables.htm
UPDATE
From the link above..
Ruby Instance Variables:
Instance variables begin with #. Uninitialized instance variables have the value nil and produce warnings with the -w option.
Here is an example showing usage of Instance Variables.
class Customer
def initialize(id, name, addr)
#cust_id=id
#cust_name=name
#cust_addr=addr
end
def display_details()
puts "Customer id ##cust_id"
puts "Customer name ##cust_name"
puts "Customer address ##cust_addr"
end
end
# Create Objects
cust1=Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
cust2=Customer.new("2", "Poul", "New Empire road, Khandala")
# Call Methods
cust1.display_details()
cust2.display_details()
That´s how you can work with ruby and instance variables. More details are in the link.
In your case I think you have another "error", you mixed a few things.. where is your helper class? Under the app/helpers/store_helper.rb? In this file you should just add view helpers. If I am right with my intuitions I would solve your problem like following:
app/helpers/store_helper.rb
module StoreHelper
def get_level_states(product)
product_sales = product.line_items.total_product_sale.sum("quantity")
product.volume2 = 0 if product.volume2.nil?
product.volume3 = 0 if product.volume3.nil?
levels = {}
if (product_sales >= product.volume2) && (product_sales < product.volume3)
levels[:1] = "On!"
levels[:2] = "On!"
levels[:3] = "Active!"
elsif product_sales >= product.volume3
levels[:1] = "On!"
levels[:2] = "On!"
levels[:3] = "On!"
else
levels[:3] = "Pending"
end
levels
end
end
app/views/your_views_folder/your_view.html.erb
to get the different level state:
<% levels = get_level_states(product) %>
<%= levels[:1] %> # will print the level 1
<%= levels[:2] %> # will print the level 2
<%= levels[:3] %> # will print the level 3

Rails controller refactoring

I have code similar to follwoing
...
#c = M.find(params[:id]).c.find(params[:c_id])
if #c.s.count > 0
#s = #c.s.sort_by{|e| e[:order]}.first
unless #s.p.nil?
#img = #s.p.image_file.remote_url
else
#s.p = P.new
#img = request.protocol + request.host_with_port + "/none.png"
end
unless #s.a.nil?
#voice = #s.a.audio_file.remote_url
else
#s.a = A.new
end
else
...
end
#c_v_url = ""
unless #c_v_url.nil?
#c_v_url = #c.v_o.a_file.remote_url
else
#c.v_o = A.new
end
#c_m_url = ""
unless #c_m_url.nil?
#c_m_url = #c.b_m.a_file.remote_url
else
#c.b_m = A.new
end
...
Now all the instance variables are to be used in the view and I want to re-factor the code to make the Controller skinny. What will be the best approach to do the re-factoring? Will it be wise to move this code to the Model?
I can't really see what this code is used for, but it looks like view logic to display images, file and audio links?
I'd create a view helper method for each one, for example:
def s_image_url(s)
unless s.p.nil?
s.p.image_file.remote_url
else
request.protocol + request.host_with_port + "/none.png"
end
end
For more info on view helpers
I would use the Presenter Pattern, here are some resources for explanation (there are a lot more out there):
http://blog.jayfields.com/2007/03/rails-presenter-pattern.html
http://kpumuk.info/ruby-on-rails/simplifying-your-ruby-on-rails-code
Short story: You put all your logic for retrieving your models in the presenter. The presenter is easy to test and extensible. In your controller action will have only one line of code to instantiate the presenter.

Resources