Si un usuario tiene un conjunto de roles adjuntos y cada rol tiene un conjunto de ámbitos, ¿cómo puedo comprobar si el usuario tiene todos los ámbitos?
Por ejemplo, tengo un mapeo de roles a ámbitos.
scopes_for_roles = { artist: ['draw', 'sing'], teacher: ['read', 'write'], athlete: ['draw', 'read', 'dance'] } Para User_A que tiene roles de athlete y teacher , ¿cómo puedo verificar ( return T/F ) si tiene los siguientes alcances?
scopes = ['read', 'write'] --> TRUE scopes = ['sing', 'read', 'write', 'dance'] --> FALSE # Missing singLo que he probado es el siguiente
roles.product(scopes).select { |role, scope| scopes_for_roles[role].include?(scope) if scopes_for_roles.key?(role) }.size >= scopes.size Sin embargo, creo que el fragmento anterior tiene un error en el que solo verifica el .size de las permutaciones seleccionadas y no los alcances reales en sí. Entonces, si un Usuario tiene los roles de teacher y athlete , el fragmento devolverá true para los ámbitos ['read', 'sing'] porque 'read' se encuentra DOS VECES en los roles de teacher y athlete .
Nota: podría tener varios bucles for con comprobaciones de clave-valor, pero me preguntaba si había un enfoque más limpio/más corto como el fragmento que proporcioné.
Sí, los ámbitos duplicados son un problema cuando confía en contar. Para continuar con ese enfoque, deberá aplicar Array#uniq para eliminar los duplicados en algún momento. (Además, includes_code? no es un método de Ruby). Le sugiero que abandone el enfoque de conteo. Primero extraiga los ámbitos relevantes de scopes_for_roles :
roles = [:athlete, :teacher] scopes = ['read', 'write'] a = scopes_for_roles.values_at(*roles) #=> [["draw", "read", "dance"], ["read", "write"]]Consulte Hash#values_at .
En este punto tienes varias opciones. Quizás lo más fácil es aplanar primero a :
b = a.flatten #=> ["draw", "read", "dance", "read", "write"]Luego ejecute uno de los siguientes:
scopes.all? { |s| b.include?(s) } #=> true (scopes - b).empty? #=> true (scopes - b #=> []) scopes & b == scopes #=> true (scopes & b #=> ['write', 'dance']) En lugar de flatten , podrías usar
b = a.reduce(:+) #=> ["draw", "read", "dance", "read", "write"]o
b = a.reduce(:|) #=> ["draw", "read", "dance", "write"]Ver Matriz#| .
Si
scopes = ['sing', 'read', 'write', 'dance']entonces
scopes.all? { |s| b.include?(s) } #=> false (scopes - b).empty? #=> false (scopes - b #=> ['sing']) scopes & b == scopes #=> false (scopes & b #=> ['read', 'write', 'dance'])Sugiero lo siguiente.
def have_em_all?(scopes_for_roles, roles, scopes) (scopes - scopes_for_roles.values_at(*roles).reduce(:|)).empty? end roles = [:athlete, :teacher] scopes1 = ['read', 'write'] scopes2 = ['sing', 'read', 'write', 'dance'] have_em_all?(scopes_for_roles, roles, scopes1) #=> true have_em_all?(scopes_for_roles, roles, scopes2) #=> falsePara el primer ejemplo, los pasos son los siguientes.
a = scopes_for_roles.values_at(*roles) #=> [["draw", "read", "dance"], ["read", "write"]] b = a.reduce(:|) #=> ["draw", "read", "dance", "write"] c = scopes1 - b #=> [] c.empty? #=> trueEn el segundo ejemplo obtendríamos
c = scopes2 - b #=> ["sing"] c.empty? #=> false