Sunspot searches for Rails' Single Table Inheritance - ruby-on-rails-3

I have a Rails app with the Content model. The Content model has numerous STI children models, such as Announcement, Story, FAQ, etc. I need to be able to query Solr via Sunspot for each of the children independently and as a group.
This is the present implementation of Sunspot search in the Content model. It sets defaults for hidden and published, so only active Content is returned by Solr and accepts a block to allow farther search params:
def self.search_for(&blk)
search = Sunspot.new_search(Content)
search.build(&blk)
search.build do
with :hidden, false
with(:published_at).less_than Time.now
end
search.execute
search
end
This method works perfectly for Content and will return results for Content and all the children Models. I am not particular thrilled with the name of the method, search_for, but can't think of anything better.
I need to be able to search by child Model, i.e. Announcement.search_for(). I do not want to have this method pasted into the ~10 child Models, since the defaults are going to change in the near future. What I would like is have each of the children models inherit this method, but search for the child's class, not Content (e.g. Announcement would search by Sunspot.new_search(Announcement)).
Is there are way to reflect the class of a class method or does this method have to be dynamically generated at runtime to pre-define the calling class?

Pretty easy, just pass the instance type rather than Content. Change first line of the function to:
search = Sunspot.new_search(self)
Where self will hold Content if you invoke the method by Content.search_for and Announcement if invoked by Announcement.search_for. That's it!

Related

Model binding fails from form on Razor Page using Tag Helpers

I have a model "behind" my page with a property that's a model of my search form. My form was working fine and then suddenly all the properties stopped binding and my Post action handler saw the search form model has having loads of nulls.
When you use Tag Helpers for a form and add input controls you can add your own name="myProperty" to each, or you can omit this and this attribute is auto-generated.
Imagine you have 10 inputs and you add a new one but forget to add a name attribute on this recent one, then the helper adds its own like "SearchForm.MyProperty".
The previous 10 end up with name="myProperty" but the last one is name="SearchForm.MyProperty".
In this situation model binding fails, presumably because these paths are mixed and the one matching SearchForm.MyProperty is more specific, making the others look like they should bind to properties on the page model.
Presumably when your supply your own names and they are all lacking the SearchForm. prefix then its smart enough to figure out to bind them all to SearchForm.
Fix could be not to supply your own name attributes to the inputs at all.
An improvement to Razor binding (to remove the surprise here) might be to always fail unless the names are prefixed accurately, though this would break existing code.

selenium Page factory handle different elements for same page for different users

I have a simple question. I 'm sure many of us might have got into the same situation. I am using page object pattern. Below are the steps i do along the navigation.
Login to my application as one type of user.
Click some link to go form page.
On form page , fills the fields and submit
Logout
On 3) the form object page shows some different input fields depending on the type of the user, which i need to interact with. So how do i deal it within the same page object. Has anybody got into the same situation and have found some decent way of doing this ?
I know it a simple automation script not a Java project where we should be using all oops concepts but still I would go with the following:
Create a parent page class containing the common WebElements and methods.
Create child classes with elements and methods specific to that customer.
In the test, pass a parameter which specifies the type of customer and call the appropriate child class.
If you don't want any of this inheritance stuff, you can also try the following.
Create a page class with elements for all types of customers.
Create generic methods which can take a parameter customerType and perform operations like if customerType==1 do these operations else do these.
Another solution which popped up in my mind, assuming that all fields for a particular customer are mandatory, is as follows.
Create a common class for all elements.
Create a generic method in the page class which follows the condition, if this element is present then enter value.
If you understand the concept of Page object model then this questions will be more clear to you. Yes, inheritance is a big factor here. I suggest you read through this to see how a real page object model should work. And, solution of #3 question is as simple as UI mapping. Something like
#FindBy(how = How.NAME, using = "q")
private WebElement searchBox;
for each elements or similar implementation.
For a complete page object you should map all the elements not depending on the users. The reason being, every time you call that class it will be instantiated and all the mapped elements as well. There is no need of dynamically load the elements If any elements are not used or hidden on the page those will be available and you will not be using them anyway

Get ancestor object of un-saved object in Hobo

I'm working on a Hobo app trying to tie together a few models properly.
Activity objects have many Page children. They also have many DataSet children.
Page objects have several different kinds of children. We'll talk about Widget children, but there are several types with the same issue. An instance of a Widget belongs to a Page but also has a belongs_to relationship with a DataSet. Here's the important point: the DataSet must belong to the containing Activity. So for any given #widget:
#widget.page.activity === #widget.data_set.activity
It's easy enough to enforce this constraint in the model with a validation on save. The trick is presenting, within the Widget's form, a select menu of available DataSets which only contains DataSets for the current Activity
I was able to get this working for existing objects using a tag like this:
<data_set-tag: options="&DataSet.activity_is(&this.page.activity)" />
However, for a new Widget, this fails messily, because either &this or &this.page is not yet set. Even for a route which contains the page ID, like /pages/:page_id/widgets/new, I'm not really able to get an Activity to scope the list of DataSets with.
If this was proper Rails, I'd get in to the relevant controller method and make the Activity available to the view as #activity or something of the sort, but in Hobo the controllers seems to be 95% Magicâ„¢ and I don't know where to start. The knowledge of which Activity is current must be in there somewhere; how do I get it out?
This is Hobo 1.3.x on Rails 3.0.x.
ETA: The code producing the errors is in the form tag for Widget, like so:
<extend tag="form" for="Widget">
<old-form merge>
<field-list: fields="&this.field_order">
<data_set-tag: options="&DataSet.activity_is(&this.page.activity)" />
</field-list>
</old-form>
</extend>
As I said above, this works for editing existing Widgets, but not new Widgets; the error is undefined method 'page' for nil:NilClass. Bryan Larsen's answer seems to suggest that &this.page should not be null.
it looks like you tried to post this question to the Hobo Users mailing list -- I got a moderation message, but it doesn't appear that your post got posted, nor can I find it to let it through. Please try reposting it, there are several helpful people on the list that don't monitor the Hobo tag here.
In Hobo 1.3, the new action doesn't support part AJAX, so there really isn't much magic. You can just replace the action with your own:
def new_for_page
#activity = Activity.find(...)
#page = Page.find(params[:page_id])
#widget = #page.widgets.new
end
There is a little bit of magic referenced above: if you're in WidgetsController, assigning to #widget will also assign to this.
But as you said, the knowledge is obviously in there somewhere, and your custom controller action shouldn't be necessary.
This statement seems wrong: However, for a new Widget, this fails messily, because either &this or &this.page is not yet set.
It looks like you're properly using owner actions. /pages/:page_id/widgets/new is the route. In widgets_controller it's the new_for_page action. In a new or new_for action, this is set to an unsaved version of the object. In your action, it should have been created with the equivalent of Page.find(params[:page]).widgets.new. In other words, both this and this.page should be populated.
I'm sure you didn't make your statement up out of thin air, so there's probably something else going on.
In the end, it turned out to be syntax. Instead of
<data_set-tag: options="&DataSet.activity_is(&this.page.activity)" />
I needed
<data_set-tag: options="&DataSet.activity_is(#this.page.activity)" />
(note the #).
We actually made this into a helper method, so the final code is
<data_set-tag: options="&DataSet.activity_is(activity_for(#this))" />

How can I set up an ActiveAdmin editor for a singleton model?

The active admin page should load the singleton instance of the model (I am using an ActiveRecord compliant model). I'm not sure where to put the code to load the single instance of the model, or how to make the default page for the record be an edit page rather than the collection page.
You should check http://activeadmin.info/docs/8-custom-actions.html#member_actions, and, you can set the actions to:
actions :update
In the member action you can focus to update the instance.
--
Hi again, today I did something like that, simpler, and this is what I did:
Make the index look like a blog http://activeadmin.info/docs/3-index-pages/index-as-blog.html
Only use actions: index, edit, update
Use a scope http://activeadmin.info/docs/2-resource-customization.html#scoping_the_queries (also, you can use only scope and not scope_to if you want to call a scope from the model).
Hope it helps.

Rails/Rspec Views: The right way to test partials

I have a Rails 3 partial that lists all categories as a navigation menu - it's on most, but not all of my template pages...let's say about 75%. I'm trying to test the partial (in RSpec) right now, and I've just realised a few things:
At the moment, I'm calling Categories.all in the actual view. The difficulty is that, because that touches the database, my mocks/stubs in the view spec are ignored, and consequently the test fails.
I'm guessing the alternative is to assign the variable in the application controller, and then pass it as a local variable to the partial. Still, about 25% of my pages won't use the variable and I'm wondering if there's a more graceful way of doing things.
In short, I want view specs to pass without touching the test DB, but I'm not sure a global variable passed to my partial is the best way to do it...and I'm not declaring the variable in every (& only) those controllers who require it.
Any suggestions appreciated...
Why not create a helper method for all categories?
# in categories helper
def all_categories
#all_categories ||= Category.all
end
Or...
# application controller
helper_method :all_categories
def all_categories
...
You can then stub out this method in your specs and you won't be touching the DB