Rails cache_digests and conditionals - ruby-on-rails-3

When we cache a partial in rails using cache digests, how does the conditional logic in the partial get handled? Does it cache the full template and later apply the conditionals so that the right json/html can be served to the right user?

Does it cache the full template and later apply the conditionals so
that the right json/html can be served to the right user?
This part of question seems a bit unclear to me, so I'll provide different options based on what "conditionals" could be.
First of all, cache digests do not care about inner conditions based on #variables's state (unless a particular state is mentioned inside of its cache key). Consider the following example:
# users.haml
.welcome_block
- if #user.admin?
%h4 Hello, admin!
- else
%h4 Hello, user!
In case you apply caching to the whole page with cache ['users_haml'], the cache would be generated just once (for the first user with whichever role). Any user who accessed this page later would see the same h4 greeting as the one which has been shown to the first user. The reason here is that digest for string users_haml, proved to cache method, is always the same regardless of any circumstances.
On the other hand, cache #user would provide slightly different behaviour. Each user who opens users.haml page would see proper greeting based on his/her role. The reason for this behaviour is that digest differs for all objects of type User, so cache_digests generates N cached pages for N users.
The last one kind of conditionals which comes to mind is the one based on conditional partials rendering, e.g.:
# users.haml
- cache [#user.month_of_birth]
- if #user.month_of_birth == 'October'
= render 'partial_one'
- else
= render 'partial_two'
So, this one renders correct cached page for users with different months of birth. But what should happen if I change the contents of partial_one? How does cache_digests understand that cache should be invalidated for those who were born in october (based on the conditional statement)?
The answer here is that it doesn't know that at all. The only thing it knows is that users.haml depends on both partial_one and partial_two, so changes to either of these inner partials gonna invalidate ALL the users.haml page caches regardless of users' month of birth.

Related

Is there a way to do string replacement/substitution in sql?

I have some records in a CMS that include HTML fragments with custom tags for a widget tool. The maker of the CMS has apparently updated their CMS without providing proper data conversion. Their widgets use keys for layout based on screen width such as block_lg, block_md, block_sm. The problem kicks in with the fact they used to have a block_xs and they have now shifted them all -- dropping the block_xs and instead placing a block_xl on the other end.
We don't really use these things, but their widget configurations do. What this means for us is the values for each key are identical. The problem occurs when the updated CMS code is looking for the 'block_xl' in any widget definition tags, it can't find it and errors out.
What I'm thinking then is that the new code will appear to 'ignore' the block_xs due to how it reads the tags. (and similarly, the old code will ignore block_xl) Since the values for each are identical, I need to basically read any widget definition and add a block_xl value to it matching the value of [any one of] the other width parameters.
Since the best place order-wise would be 'before' the block_lg value, it's probably easiest to do it as follows:
Replace any thing matching posix style regex matching /block_lg(="\d+,\d+")/ with: block_xl="$1" block_lg="$1"
Or whatever the equivalent of that would be.
Example of an existing CMS block with multiple widget definitions:
<div>{{widget type="CleverSoft\CleverBlock\Block\Widget"
widget_title="The Album" classes="highlight-bottom modish greenfont font52 fontlight"
enable_fullwidth="0" block_ids="127" lazyload="0"
block_lg="127,12," block_md="127,12," block_sm="127,12," block_xs="127,12,"
template="widget/block.phtml" scroll="0" background_overlay_o="0"}}</div>
<!-- Image Block -->
<div>{{widget type="CleverSoft\CleverBlock\Block\Widget"
widget_title="What’s Your Favorite Cover Style?"
classes="zoo-widget-style2 modish grey font26 fontlight"
enable_fullwidth="0" block_ids="126" lazyload="0"
block_lg="126,12," block_md="126,12," block_sm="126,12," block_xs="126,12,"
template="widget/block.phtml" scroll="0" background_overlay_o="0"}}</div>
What I would prefer to end up with from the above (adding block_xl):
<div>{{widget type="CleverSoft\CleverBlock\Block\Widget"
widget_title="The Album" classes="highlight-bottom modish greenfont font52 fontlight"
enable_fullwidth="0" block_ids="127" lazyload="0"
block_xl="127,12," block_lg="127,12," block_md="127,12," block_sm="127,12," block_xs="127,12,"
template="widget/block.phtml" scroll="0" background_overlay_o="0"}}</div>
<!-- Image Block -->
<div>{{widget type="CleverSoft\CleverBlock\Block\Widget"
widget_title="What’s Your Favorite Cover Style?"
classes="zoo-widget-style2 modish grey font26 fontlight"
enable_fullwidth="0" block_ids="126" lazyload="0"
block_xl="126,12," block_lg="126,12," block_md="126,12," block_sm="126,12," block_xs="126,12,"
template="widget/block.phtml" scroll="0" background_overlay_o="0"}}</div>
I know how to do it in php and if necessary, I will just replace it on my local DB and write an sql script to update the modified records, but the html blocks can be kind of big in some cases. It would be preferable, if it is possible, to make the substitutions right in the SQL but I'm not sure how to do it or if it's even possible to do.
And yes, there can be more than one instance of a widget in any given cms page or block. (i.e. there may be a need for more than one such substitutions with different local 'values' assigned to the block_lg)
If anyone can help me do it in SQL, it would be greatly appreciated.
for reference, the tables effected are called cms_page and cms_block, the name of the row in both cases is content
SW

#auth.requires_permission not working ver 2

Good day to all web2py experts!
I can't find a way on how to use the web2py Decorators
#auth.requires_permission('read','person')
def f(): ....
in the pdf manual it says that:
prevents visitors from accessing the function f unless the visitor is a member
of a group whose members have permissions to "read" records of table
"person". If the visitor is not logged in, the visitor gets directed to a login
page (provided by default by web2py). web2py also supports components,
i.e. actions which can be loaded in a view and interact with the visitor via
Ajax without re-loading the entire page. This is done via a LOAD helper which
allows very modular design of applications; it is discussed in chapter 3 in the
context of the wiki and, in some detail, in the last chapter of this book.
This 5th edition of the book describes web2py 2.4.1 and later versions
In my case:
I have list of groups: Admin_Tier_1, Admin_Tier_2, Admin_Tier_3
Admin_Tier_1 - has the highest authority to access all features like adding a school year, set a school year etc.
Admin_Tier_2 - has the authority to add students etc
Admin_Tier_3 - its the lowest level of authority that can only add fines to the students (Organization Officers)
now I use the Decorator code like this:
#auth.requires_permission('Admin_Tier_1','student_list')
def add(): ....
now I login the account of the Chairman which registered in the auth_membership as Admin_Tier_1. Then I click the link "List of Students" which redirect to add(): function but the system returned a message:
Not Authorized
Insufficient privileges
The auth.requires() method can take a callable rather than a boolean value as the condition, and this is preferable when it is expensive to generate the boolean value (otherwise, the boolean value is generated whenever the controller is accessed, even if the particular decorated function is not the one being called). So, to avoid calling auth.has_membership unnecessarily, you can do:
#auth.requires(lambda: auth.has_membership('Admin_Tier_1') or
auth.has_membership('Admin_Tier_2'))
Now the two auth.has_membership calls will only be made when the actual function being decorated is called.
Also, if you need to check a larger number of roles, you can do something like:
#auth.requires(lambda: any([auth.has_membership(r) for r in ['list', 'of', 'roles']))
Problem solved:
#auth.requires(auth.has_membership('Admin_Tier_1') or auth.has_membership('Admin_Tier_2'))
source here.
Whenever I access the page if the user belong to the group of Admin_Tier_3 the system block the acess and redirect it to "/default/user/not_authorized" page :)

MVCSiteMap: Should the nodes pickup current request parameter values?

Setup:
I am using MvcSiteMap version 3 in an ASP.NET MVC 4 app.
Problem:
I have a node like this:
<mvcSiteMapNode title="Neighbours" area="MilkyWay" controller="SolarSystems" action="Planets" key="neighbours">
The Planets action on the SolarSystems controller has two parameters, int order, int size, where order states the order of the planets starting from the star. Eg, Mercury would be 1, and size is the order of the planets by size, where the smallest is 1.
For both parameters, -1 means I don't have a clue.
In my route registration, I have something like:
context.MapRoute(
"SolarSystems_Planets",
"SolarSystems/Planets/{order}/{size}",
new { controller = "SolarSystems", action = "Index", order = -1, size = -1 }
);
All works OK, ie, the default values are used...
...EXCEPT when the current page is an action that has the same two parameters with the same names.
In that case, the node will use these two parameters instead of the default values of -1.
For example, If I have a GalaxyClusters controller with an Index action that has two parameters int order and int size, and in the current request, order == 10009985639 and size = 9098 then my planets action tries to find a planet in our solar system with those values.
Obviously, it will fail and we have known that since Galileo's days.
What seems to be happening:
In other words, it seems that MvcSitemap injects the current values of any parameter and if a match is found, then BOOM! This seems wrong to me, so is this the default?
The problem is easily solved by putting in the node the following:
<mvcSiteMapNode title="Neighbours" area="MilkyWay" controller="SolarSystems" action="Planets" order="" size="" key="neighbours">
Is this reliable?!
Note (and questions restated):
I know I have answered what might appear to be my question, but if you think that, before voting to close the question, please read it carefully:
The question I am asking is whether this is the default behaviour? If it is, why? And: is my solution reliable?
Finally, I don't really have an app that catalogs all the galaxies in the Universe (sql server might not like that many records and I would be in receipt of the Nobel prize). I just adapted the problem so that I don't use my client's domain names.
This behavior is not part of MvcSiteMapProvider, but part of the MVC UrlHelper class and they don't intend to fix it.
According to the issue that was submitted about this to the MVC team, this behavior is by design. That is, the URL generation code will pick up ambient values of the request and automatically inject them into the URL if they match.
Their advice about how to work around this issue are the following:
Use named routes to ensure that only the route you want will get used to generate the URL (this is often a good practice, though it won't help in this particular scenario)
Specify all route parameters explicitly - even the values that you want to be empty. That is one way to solve this particular problem.
Instead of using Routing to generate the URLs, you can use Razor's ~/ syntax or call Url.Content("~/someurl") to ensure that no extra (or unexpected) processing will happen to the URL you're trying to generate.
You can accomplish all 3 of these solutions with MvcSiteMapProvider.
<!-- Using a named route -->
<mvcSiteMapNode title="Neighbours" area="MilkyWay" controller="SolarSystems" action="Planets" route="SolarSystems_Planets" key="neighbours">
That won't help in your case, but I am including it here to demonstrate how it can be done.
<!-- Specify parameters explicitly -->
<mvcSiteMapNode title="Neighbours" area="MilkyWay" controller="SolarSystems" action="Planets" order="10009985639" size="9098" key="neighbours">
This means of course you will need a node for each combination of order and size. If the data is coming from some shared resource such as a database, you can implement IDynamicNodeProvider to create a node for each case. This of course assumes that you know in advance what all of the potential values will be.
<!-- Specify the URL explicitly -->
<mvcSiteMapNode title="Neighbours" url="/MilkyWay/SolarSystems/Planets/10009985639/9098" key="neighbours">
Again, you will need a node per URL to accomplish this successfully.
See the issue # GitHub about this for further details.

How about using URI path variables for an HTTP POST?

I've searched a lot but I couldn't find the proper answer to my question regarding my conditions.
I'm building a REST API, and the case, which seems a border line case to me, is the following:
-I'm dealing with two entities, Users and Roles. An User can have multiple roles assigned.
-To assign a Role to a User, the Role must be already in the DataBase.
-To assign a Role to a User, the only thing needed is the 'code' of the role, that is a short string.
-The uri path template used now is:
--Users: localhost:8080/api/users
--Given User: localhost:8080/api/users/{userId}
--Roles of a given User: localhost:8080/api/users/{userId}/roles
Now, to 'link' a given User with a given Role, two options come to my mind.
-The first is the one that sounds as best practice in any scenario, sending the post data in the body, perhaps as a JSON.
-The other one, sending it through the uri and with an empty body. For example, to link User with id U001 with role R001, one would have to post to the following uri sending no data in the body: localhost:8080/api/users/U001/roles/R001
The thing is that I don't mind using the first option, and it seems to be the best and most correct one, but in this particular case, I'm not sure wether it is better to send an almost empty body (because it only holds the role id, a very short string) posting it to 'localhost:8080/api/users/U001/roles' or skipping the body and just sending the role id through the uri as a path parameter like localhost:8080/api/users/U001/roles/R001
Thank you all in advance for your help.
There is nothing wrong with putting role in the URI. Your intuition was on the right track. I'd do it this way.
PUT: locahost:8080/api/users/{userid}/role/{roleId}
And here's why.
FIRST: The PUT verb is Idempotent. In other words (taken straight from the spec)
... the side-effects of N > 0 identical requests is the same as for a single request.
Which is what I'd assume you want in this regard. You don't want multiple records in your state storage for each instance of user & role. A user should feel at ease making the same PUT request without adversely effecting (adding duplicate records) the system.
When doing the same thing with a POST I'd expect to have a new record created for every request.
SECOND: The PUT verb is supposed to identify a specific resource. (taken straight from the spec)
... PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource. If the server desires that the request be applied to a different URI,
it MUST send a 301 (Moved Permanently) response; the user agent MAY then make its own decision regarding whether or not to redirect the request.
What if role R102 becomes obsolete and R104 is preferred? Return a 301 (Moved Permanently) with a HEADER (Location : localhost:8080/api/users/{userid}/role/R104).
FINALLY: When everything works well. Return a 201 (Created) when created and a 200 (No Content) on every subsequent request to the same URI. If they provide a Role that is not in the system return a 501 (Not Implemented).
Hmm - in this case - a POST with a 302 may be a bit messy.
Why not a very simple 'PUT'/'DELETE' with indeed the URIs suggested ?
With simple; 20X meaning succeeded, possibly some 30X to indicate it was already there - and anything else a fail ?

Eclipse RCP: How to order perspective buttons belonging to different plugins?

My application has 5 plugins. Each plugin has a perspective of it's own and hence each perspective extension definition is under individual plugin's plugin.xml.
Now, I want to control the order in which these perspectives appear in my application. How to do it?
There is one main plugin that holds "ApplicationWorkBenchAdvisor.java". This has initialize() method in which I am iterating through the perspective registry using
PlatformUI.getWorkbench().getPerspectiveRegistry().getPerspectives();
and then appending perspective ids in a comma separated fashion to a String variable (pbar) which is used later like this.
PlatformUI.getPreferenceStore().setDefault(IWorkbenchPreferenceConstants.PERSPECTIVE_BAR_EXTRAS, pbar);
PlatformUI.getPreferenceStore().setValue(IWorkbenchPreferenceConstants.PERSPECTIVE_BAR_EXTRAS, pbar);
When iterating thourgh the perspective registry, I can compare perspective ids and sort it(when adding to 'pbar' by comparing ids) the way I want it to appear but, I don't want to do this ordering here as it appears like a dirty way.
Is there any other place where we can fix the order in which perspectives appear? (Each perspective resides in different plugin).
ADDED
1) Could we also control the ordering in the perspective switcher?
2) Is there a way to control entry into perspective registry to in inflict the desired order. If not could we write back into perspective registry?
If your application is encapsulated as an eclipse product, you may tweak the plugin.properties/plugin_customization.ini file.
(file referenced by the 'preferenceCustomization' property in your product extension point.)
This file is a java.io.Properties format file. Typically this file is used to set the values for preferences that are published as part of a plug-in's public API.
(Example of such a file for org.eclipse.platform)
So if the string representing the order of perspective can be referenced as a property, you can define the default order in there.
Since the source code of IWorkbenchPreferenceConstants mentions:
/**
* Lists the extra perspectives to show in the perspective bar.
* The value is a comma-separated list of perspective ids.
* The default is the empty string.
*
* #since 3.2
*/
public static final String JavaDoc PERSPECTIVE_BAR_EXTRAS = "PERSPECTIVE_BAR_EXTRAS"; //$NON-NLS-1$
Maybe a line in the plugin_customization.ini file:
org.eclipse.ui/PERSPECTIVE_BAR_EXTRAS=perspectiveId1,perspectiveId2,perspectiveId3
would allow you to specify that order without having to hard-code it.
Additional notes:
IPerspectiveRegistry (or PerspectiveRegistry) is not made to write anything (especially for perspective defined in an extension)
Ordering may be found in the state of the workbench (stored in the workspace and then restored when its launched again, .metadata/.plugins/org.eclipse.ui.workbench/workbench.xml)
Do you confirm that:
IPerspectiveRegistry registry = PlatformUI.getWorkbench().getPerspectiveRegistry();
IPerspectiveDescriptor[] perspectives = registry.getPerspectives();
is not in the right order when the plugin_customization.ini does define that order correctly ?
Liverpool 5 - 0 Aston Villa does confirm that (in the comments), but also indicates the (ordered) ini file entries internally get recorded into preference store, which means they can be retrieved through the preference store API:
PatformUI.getPreferenceStore().getDefault(
IWorkbenchPreferenceConstants.PERSPECTIVE_BAR_EXTRAS)
Liverpool 5 - 0 Aston Villa then add:
perspective registry (the initial "PlatformUI.getWorkbench().getPerspectiveRegistry().getPerspectives();" bit) remains unaltered (and unordered).
But, you still can "readily access to ordered list of perspectives" through preference store.
So, for other tasks, instead of iterating though perspective registry (which is still unordered), we can use the ordered variable that stores list of ordered perpective ids.
.
.
.
.
Note: another possibility is to Replace the Perspective-Switcher in RCP apps
=> to:
You can more easily define the order in a menu or in buttons there.
Extreme solution: re-implement a perspective switcher.
To sum up all the observations and findings,
1) It is not possible to alter entries in the perspective registry. It is read-only.
2) To make perspective appear in the order that we want on perspective bar, we can achieve it by adding an entry in plugin_customization.ini (or preferences.ini) as shown below.
org.eclipse.ui/PERSPECTIVE_BAR_EXTRAS=perspectiveId1,perspectiveId2,perspectiveId3
3) If we want to fetch this ordered list, we can't fetch it directly. But as this ini file entry internally gets recorded in PreferenceStore we can fetch the same value from PreferenceStore using the following API as shown below.
PlatformUI.getPreferenceStore().getDefault(
IWorkbenchPreferenceConstants.PERSPECTIVE_BAR_EXTRAS);
Why would someone need to access the entry defined in ini file at all?
Well, in my case I had a view in which i had to display links to every perspective. As my perspective bar was sorted in desired order, I also wanted to maintain the same order in my view while displaying links to perspectives.
4) There is no known way to inflict the same sort order in the display of default perspective switcher. While a new custom perspective switcher can be written to achieve the desired effect.