In Rails i18n, how to get all values for a certain key using the following:
translations = I18n.backend.send(:translations)
get all the keys
I need to be able to get a certain section for example only return everything under "home"
en:
home:
test: test
The return value of I18n.backend.send(:translations) is just a hash, so you can access a subset just by passing in the appropriate keys.
e.g. if you have:
en:
foo:
bar:
some_term: "a translation"
some_other_term: "another translation"
Then you can get the the subset of the hash under bar with:
I18n.backend.send(:translations)[:en][:foo][:bar]
#=> { :some_term=>"a translation", :some_other_term => "another translation"}
The default I18n backend is I18n::Backend::Simple, which does not expose the translations to you. (I18n.backend.translations is a protected method.)
This isn't generally a good idea, but if you really need this info and can't parse the file, you can extend the backend class.
class I18n::Backend::Simple
def translations_store
translations
end
end
You can then call I18n.backend.translations_store to get the parsed translations. You probably shouldn't rely on this as a long term strategy, but it gets you the information you need right now.
Setting I18n.locale then doing I18n.t works fine, e.g.:
def self.all_t(string)
I18n.locale = :en
en = I18n.t("pages.home.#{string}")
I18n.locale = :fr
fr = I18n.("pages.home.#{string}")
[en, fr]
end
Late to the party here, but I just had to extract all the country codes from a Rails locale file, and the suggestions above did not work for Rails 6 and i18n 1.12.
Turns out that the translations method on I18n::Backend::Simple is now public, so we can now use :
I18n.backend.translations(do_init: true) to retrieve the translations hash.
Therefore the home node mentioned above can be retrieved with :
I18n.backend.translations(do_init: true)[:fr][:home]
Hope this helps
Related
I have a method that parses YAML files. The returned object is a nested Hash, where the keys are always Strings and the leaf-values are always strings, e.g.
{
"a" => "foo",
"b" => {
"c" => "bar",
"d" => "baz"
}
}
I don't know in advance how deep the hash is.
The closest I got to typing the return value was the following signature:
T.any(T::Hash[String,String], T::Hash[String,T::Hash[String, T.untyped]])
This is obviously a bad solution, since it doesn't check anything beneath the second nesting, but the documentation about custom types seems a bit sparse.
Is there any way to type nested hashes, using a custom type, nested types or something similar?
Unfortunately, you won't be able to do much more than what you got to at this point. Even though Shapes are supported, they are an experimental feature.
If you really want to go with hashes, you could express it as:
MyType = T.type_alias {T::Hash[String, T.any(String, T::Hash[String, T.untyped])]}
Alternatively, you could use a T::Struct:
class MyType < T::Struct
const :key, String
const :value, T.any(String, MyType)
end
You'd still have the uncertainty of what the type of the value is, but with flow sensitivity, it should be easy to process the structure:
class Processor
extend T::Sig
sig {params(my_object: MyType).returns(String)}
def process(my_object)
key = my_object.key
obj_value = my_object.value # this is needed for flow sensitivity below
value = case obj_value
when String
value
when MyType
process(obj_value)
else
T.absurd(obj_value) # this makes sure that if you add a new type to `value`, Sorbet will make you handle it
end
return "key: #{key}, value: #{value}"
end
end
I have one model named Factors, which has two types: ['personal', 'advisor']
I want to have one controller FactorsController that has all the same actions for both types of Factors, but only ever uses one type. The type that it uses is based on the route used to get there. For example,
/personal would route to factors#index and populate #factors with Factor.personal
/advisors would route to factors#index and populate #factors with Factor.advisors
How would I go about setting this up?
You can add to the routes
type_regexp = Regexp.new([:personal, :advisor].join("|"))
resources :factors, path: ':type', constraints: { type: type_regexp }
and you will be able to user params[:type] in the controllers, that gives you flexibility in case you wanna changes the routes in the future.
This also gives you the ability to use factors_path(type: :personal) in the views.
You can add this to your routes:
resources :factors, :path => :personal
resources :factors, :path => :advisor
This will then have both /personal and /advisor. You'll then want to have factors#index determine which path was used (you could use request.url) and populate #factors accordingly.
I would create three controllers:
class PersonalController < FactorController
def factor
Factor.personal
end
end
class AdvisorController < FactorController
def factor
Factor.advisors
end
end
class FactorController < ApplicationController
#all the shared stuff here, using the factor method from each in your methods
end
and then the routes would be:
route '/personal' => PersonalController#index
route '/advisors' => AdvisorController#index
I am trying to add a file to a model using qqfile (though that really isn't relevant here).
I look at the params being passed to the server for update, and I have
{ id: 63, photo: 'foto_file.jpg'}
My understanding was that if an object was passed with an id parameter, rails would understand that as an already existing object, and update that model. If no id parameter is present, Rails would use create.
Is that not correct?? How in this instance can I tell rails to update rather than create?
I'm assuming more code isn't needed here, as my controllers won't really help with the solution because I think the decision is made by rails before it really hits the controller. But I'm happy to post the controller code if it is needed.
--------------- my javascript used to update or create the model ---------------------
render: function(){
var start_form=HandlebarsTemplates['user/userForm'](user.attributes);
$(this.el).html(start_form);
var uploader = new qq.FileUploader({
element: document.getElementById('file-upload'),
action: '/users',
onSubmit: function(id, fileName){
if(MyApp.user.id){
uploader.setParams({
id: MyApp.user.id
});
}
},
debug: true
});
},
The update method is only used when you sent a PUT request, not a POST request. Make sure you're using the PUT method. (If you show your form's code, I can give a more specific answer).
Update -- With your code, try adding this as a parameter to your qq.FileUploader call:
params: {
_method: "put"
}
Rails will look for a _method parameter to handle PUT/DELETE requests.
I couldn't get Dylan's javascript method to work, so in my controller I redirected to my update if the response had an id.
def create
if params[:id]
return self.update
end
#then all my regular create stuff here
end
def update
#all the usual update stuff
end
I'm moving some of my find code inside models.
Previously in my controller I had
$this->Book->Review->find('first', array(
'conditions' => array(
'Review.book_id' => $id,
'Review.user_id' => $this->Auth->user('id')
)
));
so in my Review model I put something like
function own($id) {
$this->contain();
$review = $this->find('first', array(
'conditions' => array(
'Review.book_id' => $id,
'Review.user_id' => AuthComponent::user('id')
)
));
return $review;
}
So I'm calling AuthComponent statically from the Model. I know I can do this for the method AuthComponent::password(), which is useful for validation. But I'm getting errors using the method AuthComponent::user(), in particular
Fatal error: Call to a member function
check() on a non-object in
/var/www/MathOnline/cake/libs/controller/components/auth.php
on line 663
Is there a way to get the info about the currently logged user from a model?
Create a new function in the "app_model.php" ("AppModel.php" in CakePHP 2.x), so it will be available at all models within our application:
function getCurrentUser() {
// for CakePHP 1.x:
App::import('Component','Session');
$Session = new SessionComponent();
// for CakePHP 2.x:
App::uses('CakeSession', 'Model/Datasource');
$Session = new CakeSession();
$user = $Session->read('Auth.User');
return $user;
}
in the model:
$user = $this->getCurrentUser();
$user_id = $user['id'];
$username = $user['username'];
The way that I use is this:
App::import('component', 'CakeSession');
$thisUserID = CakeSession::read('Auth.User.id');
It seems to work quite nicely :-)
I think the code is fine as it is and belongs in the Controller, or at the very least it needs to receive the ids from the Controller and not try to get them itself. The Model should only be concerned with fetching data from a data store and returning it. It must not be concerned with how the data is handled in the rest of the application or where the parameters to its request are coming from. Otherwise you paint yourself into a corner where the ReviewModel can only retrieve data for logged in users, which might not always be what you want.
As such, I'd use a function signature like this:
function findByBookAndUserId($book_id, $user_id) {
…
}
$this->Review->findByBookAndUserId($id, $this->Auth->user('id'));
There is a nice solution by Matt Curry. You store the data of the current logged user in the app_controller using the beforeFilter callback and access it later using static calls. A description can be found here:
http://www.pseudocoder.com/archives/2008/10/06/accessing-user-sessions-from-models-or-anywhere-in-cakephp-revealed/
EDIT: the above link is outdated: https://github.com/mcurry/cakephp_static_user
I think this is not good idea to get value from Session. Better solution to get logged user id inside any model simply try this:
AuthComponent::user('id');
This will work almost every where. View, Model and Controller
Dirtiest way would be to just access the user information in the Session. Least amount of overhead associated with that.
The "proper" way would probably be to instantiate the AuthComponent object, so that it does all the stuff it needs to be fully operational. Much like a death star, the AuthComponent doesn't really work well when not fully setup.
To get a new AC object, in the model:
App::import( 'Component', 'Auth' );
$this->Auth = new AuthComponent();
Now you can use $this->Auth in the model, same as you would in the controller.
For CakePHP 3.x this easy component is available: http://cakemanager.org/docs/utils/1.0/components/globalauth/. Direct accessing the Session is not possible because of different SessionKeys.
With the GlobalAuthComponent you can access your user-data everywhere with: Configure::read('GlobalAuth');.
Greetz
Bob
I use cake 2.2 and these both work great:
$this->Session->read('Auth.User');
//or
$this->Auth->user();
You can also get a field of currently logged in user:
$this->Session->read('Auth.User.email');
//or
$this->Auth->user()['email'];
None of these solutions work in CakePHP version 3. Anyone know of a way to do this? Right now, I'm completely stepping around the framework by accessing the $_SESSION variable directly from my model.
All I'm trying to do is spec how a one line helper method for a view should behave, but I'm not sure what kind of mock object, (if any) I should be creating if I'm working in Rails.
Here's the code for events_helper.rb:
module EventsHelper
def filter_check_button_path
params[:filter].blank? ? '/images/buttons/bt_search_for_events.gif' : '/images/buttons/bt_refine_this_search.gif'
end
end
And here's my spec code, in events_helper_spec.rb:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
#Delete this example and add some real ones or delete this file
it "should be included in the object returned by #helper" do
included_modules = (class << helper; self; end).send :included_modules
included_modules.should include(EventsHelper)
end
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {}
params[:filter] = true
# create an instance of the class that should include EventsHelper by default, as the first test has verified (I think)
#event = Event.new
# call method to check output
#event.filter_check_button_path.should be('/images/buttons/bt_search_for_events.gif')
end
end
When I've looked through the docs here - http://rspec.info/rails/writing/views.html, I'm mystified as to where the 'template' object comes from.
I've also tried looking here, which I thought would point me in the right direction, but alas, no dice. http://jakescruggs.blogspot.com/2007/03/mockingstubbing-partials-and-helper.html
What am I doing wrong here?
Thanks,
Chris
You are not doing anything in that spec, just setting a stub, so it will pass, but hasn't tested anything.
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {:filter => true}
helper.stub!(:params).and_return(params)
helper.filter_check_button_path.should eql('/images/buttons/bt_search_for_events.gif')
end
end
I'm running my test without spec_helper (Ruby 1.9)
require_relative '../../app/helpers/users_helper'
describe 'UsersHelper' do
include UsersHelper
...
end
Ah,
I asked this question on the rspec mailing list, and one kind soul (thanks Scott!) explained to me that there's a handy helper object for this, that you should use instead, like so:
Rails has its own helper function
params = {:filter => true}
helper.stub!(:params).and_return(params)
I've now updated the code like so:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe EventsHelper do
#Delete this example and add some real ones or delete this file
it "should be included in the object returned by #helper" do
included_modules = (class << helper; self; end).send :included_modules
included_modules.should include(EventsHelper)
end
it "should return the 'refine image search' button if a search has been run" do
# mock up params hash
params = {}
params[:filter] = true
helper.stub!(:filter_check_button_path).and_return('/images/buttons/bt_search_for_events.gif')
end
end
And it's working. Huzzah!