Rails 6 warning: Overwriting existing method <model_name>.fetched_state - ruby-on-rails-6

Rails 6.0.2.1
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
my ArticleDir class has 2 scopes:
scope :active, -> { where(active: true).where(expired_on: nil) }
scope :fetched_state, -> { where(state: ArticleDir::FETCHED.to_s) }
and a function:
def article_engine_counts(keyword_reln = Keyword.active_keywords)
joins(:keywords, :article_engine)
.where(Keyword.contains(keyword_reln))
.where(self.table[:active].eq(true))
.group(:state, ArticleEngine.table[:name]).count
end
On running the function in rails console, I get:
irb(main):108:0> ArticleDir.article_engine_counts(keyword)
Creating scope :active. Overwriting existing method ArticleDir.active.
Creating scope :fetched_state. Overwriting existing method ArticleDir.fetched_state.
(1.7ms) SELECT COUNT(*) AS count_all,
article_commons.state AS article_commons_state, sengines.name
AS sengines_name FROM article_commons INNER JOIN
directory_keywords ON directory_keywords.article_dir_id =
article_commons.id INNER JOIN keywords ON keywords.id =
directory_keywords.keyword_id INNER JOIN sengines ON
sengines.id = article_commons.sengine_id AND sengines.type
= 'ArticleEngine' WHERE article_commons.type = 'ArticleDir' AND keywords.id IN (1217) AND article_commons.active = TRUE GROUP
BY article_commons.state, sengines.name
=> {["expired", "data..."]=>1, ["fetched", "data..."]=>83, ["sourced", " data..."]=>81}
I've seen one other reference to this issue:
https://github.com/rails/rails/issues/31234
where it was suggested that the message related to overwriting a Kernel method.
I've checked the Kernel and no such methods exist on the Kernel in the first place to overwrite:
irb(main):002:0> Kernel.methods.grep(/active/)
=> []
irb(main):004:0> Kernel.methods.grep(/fetched_state/)
=> []
I am assuming that the message means what it seems to imply - arel / rails is somehow overwriting those two scopes on the model.
If so, why? and what do I do about it?

This can happen when you have something such as an enum that add scopes to the model.
If, in your model, you had an enum with :active as one of the values, e.g.:
enum status: [ :active, :archived ]
to go along with your explicit scope, you would see this warning.

Related

Does Boto3 DynamoDB have reserved attribute names for update_item with conditions expressions? Unexpected attribute SET behavior

I've implemented a simple object versioning scheme that allows the calling code to supply a current_version integer that that will set the ConditionExpression. I've also implemented a simple timestamping scheme to set an attribute named auto_timestamp to the current unix timestamp.
When the ConditionExpression is supplied with the object's current version integer, the update occurs, but also sets auto_timestamp to the current version value, rather than the value supplied in ExpressionAttributeValues. This only occurs if the attribute names are #a0, #a1 ... and values are :v0, :v1 ...
For example, this runs as expected without the condition, and auto_timestamp is set to 1643476414 in the table. The if_not_exists is used to start the object version at 0 if the item does not yet exist or did not previously have a auto_object_version attribute.
update_kwargs = {
"Key": {"user_id": user_id},
"UpdateExpression": 'SET #a0 = :v0, #a1 = if_not_exists(#a1, :zero) + :v1',
"ExpressionAttributeNames": {"#a0": "auto_timestamp", "#a1": "auto_object_version"},
"ExpressionAttributeValues": {":v0": 1643476414, ":v1": 1, ":zero": 0}
}
table.update_item(**update_kwargs)
However, this example runs without exception, but auto_timestamp is set to 1. This behavior continues for each subsequent increment of current_version for additional calls to update_item
from boto3.dynamodb.conditions import Attr
update_kwargs = {
"Key": {"user_id": user_id},
"UpdateExpression": 'SET #a0 = :v0, #a1 = if_not_exists(#a1, :zero) + :v1',
"ExpressionAttributeNames": {"#a0": "auto_timestamp", "#a1": "auto_object_version"},
"ExpressionAttributeValues": {":v0": 1643476414, ":v1": 1, ":zero": 0}
"ConditionExpression": Attr("auto_object_version").eq(1)
}
table.update_item(**update_kwargs)
While debugging, I changed the scheme by which I am labeling the attribute names and values to use #att instead of #a and :val instead of :v and the following works as desired and auto_timestamp is set to 1643476414:
from boto3.dynamodb.conditions import Attr
update_kwargs = {
"Key": {"user_id": user_id},
"UpdateExpression": 'SET #att0 = :val0, #att1 = if_not_exists(#att1, :zero) + :val1',
"ExpressionAttributeNames": {"#att0": "auto_timestamp", "#att1": "auto_object_version"},
"ExpressionAttributeValues": {":val0": 1643476414, ":val1": 1, ":zero": 0}
"ConditionExpression": Attr("auto_object_version").eq(1)
}
table.update_item(**update_kwargs)
I couldn't find any documentation on reserved attribute names or values that shouldn't be used for keys in ExpressionAttributeNames or ExpressionAttributeValues.
Is this behavior anyone has witnessed before? The behavior is easily worked around when switching the string formatting used to generate the keys but was very unexpected.
There are no reserved attribute or value names, and I routinely use names like :v1 and #a1 in my own tests, and they seem to work fine.
Assuming you correctly copied-pasted your code into the question, it seems to me you simply have a syntax error in your code - you are missing a double-quote after the "auto_timestamp. What I don't understand, though, is how this compiles or why changing a to att changed anything. Please be more careful in pasting a self-contained code snippet that works or doesn't work.

Python Cerberus dependencies on nested list level

Does Cerberus 1.2 support dependency validation on a list?
For instance the schema looks as follows:
schema = {
'list_1': {
'type': 'list',
'schema': {
'type': 'dict',
'schema': {
'simple_field': {'type': 'boolean'},
'not_simple_field': {
'type': 'dict',
'schema': {
'my_field': {'dependencies': {'simple_field': True}}
}
}
}
}
}
}
The rule that I'd like to check is that my_field should only exist when simple_field is True. How would I translate that in Cerberus?
As of now Cerberus 1.2 does not support this feature. I've overridden the Validator class method _lookup_field in order to implement this functionality.
Here's the link to a feature request on GitHub
Here's my implementation:
def _lookup_field(self, path: str) -> Tuple:
"""
Implement relative paths with dot (.) notation as used
in Python relative imports
- A single leading dot indicates a relative import
starting with the current package.
- Two or more leading dots give a relative import to the parent(s)
of the current package, one level per dot after the first
Return: Tuple(dependency_name: str, dependency_value: Any)
"""
# Python relative imports use a single leading dot
# for the current level, however no dot in Cerberus
# does the same thing, thus we need to check 2 or more dots
if path.startswith('..'):
parts = path.split('.')
dot_count = self.path.count('.')
context = self.root_document
for key in self.document_path[:dot_count]:
context = context[key]
context = context.get(parts[-1])
return parts[-1], context
else:
return super()._lookup_field(path)

Crystal parser creates an ASTNode that should be Crystal::Expressions at runtime but is somehow a Crystal::Nop

require "compiler/crystal/syntax"
s = "a = 5; puts a + 3"
nodes = Crystal::Parser.parse(s)
puts nodes.class # => Crystal::Expressions
puts nodes.is_a? Crystal::Expressions # => true
puts nodes.is_a? Crystal::Nop # => false
puts nodes.expressions
So, I would assume the last expression to give an Array (or an ArrayLiteral node). However, I get
undefined method 'expressions' for Crystal::Nop (compile-time type is Crystal::ASTNode+)
Which makes no sense. .class and .is_a? are runtime checks, so the nodes , which has a compile-time type of ASTNode should be Crystal::Expressions, not Crystal::Nop.
The behaviour is the same on crystal versions 0.25.0, 0.25.1, 0.26.0, 0.26.1 and the version currently on the master branch of the git repo.
The error is raised at compile time because not all subclasses of Crystal::ASTNode have #expressions method. Crystal::Nop just happens to be the first such subclass the compiler checks and subsequently decides to throw an error. The way to fix the code is:
require "compiler/crystal/syntax"
s = "a = 5; puts a + 3"
nodes = Crystal::Parser.parse(s)
case nodes
when Crystal::Expressions
puts nodes.expressions
when Crystal::Nop
puts "It's a nop"
else
puts "Unhandles case for #{nodes.class}"
end
Alternatively, you can force the compiler to assume it's Crystal::Expressions by using .as:
nodes.as(Crystal::Expressions).expressions
Credit for this answer goes to straight-shoota on this Github issue

How to access and mutate node property value by the property name string in Cypher?

My goal is to access and mutate a property of a node in a cypher query where the name of the property to be accessed and mutated is an unknown string value.
For example, consider a command:
Find all nodes containing a two properties such that the name of the first property is lower-case and the name of the latter is the upper-case representation of the former. Then, propagate the value of the property with the lower-case string name to the value of the property with the upper-case name.
The particular case is easy:
MATCH ( node )
WHERE has(node.age) AND has(node.AGE) AND node.age <> node.AGE
SET node.AGE = node.age
RETURN node;
But I can't seem to find a way to implement the general case in a single request.
Specifically, I am unable to:
Access the property of the node with a string and a value
Mutate the property of the node with a string and a value
For the sake of clarity, I'll include my attempt to handle the general case. Where I failed to modify the property of the node I was able to generate the cypher for a command that would accomplish my end goal if it were executed in a subsequent transaction.
MERGE ( justToMakeSureOneExists { age: 14, AGE : 140 } ) WITH justToMakeSureOneExists
MATCH (node)
WHERE ANY ( kx IN keys(node) WHERE kx = LOWER(kx) AND ANY ( ky in keys(node) WHERE ky = UPPER(kx) ) )
REMOVE node.name_conflicts // make sure results are current
FOREACH(kx in keys(node) |
SET node.name_conflicts
= COALESCE(node.name_conflicts,[])
+ CASE kx
WHEN lower(kx)
THEN []
+ CASE WHEN any ( ky in keys(node) WHERE ky = upper(kx) )
THEN ['match (node) where id(node) = ' + id(node)+ ' and node.' + upper(kx) + ' <> node.' + kx + ' set node.' + upper(kx) + ' = node.' + kx + ' return node;']
ELSE [] END
ELSE []
END )
RETURN node,keys(node)
Afterthought: It seems like the ability to mutate a node property by property name would be a pretty common requirement, but the lack of obvious support for the feature leads me to believe that the feature was omitted deliberately? If this feature is indeed unsupported is there any documentation to explain why and if there is some conflict between the approach and the recommended way of doing things in Neo/Cypher?
There is some discussion going on regarding improved support for dynamic property access in Cypher. I'm pretty confident that we will see support for this in the future, but I cannot comment on a target release nor on a date.
As a workaround I'd recommend implementing that into a unmanaged extension.
It appears that the desired language feature was added to Cypher in Neo4j 2.3.0 under the name "dynamic property". The Cypher docs from version 2.3.0-up declare the following syntax group as a valid cypher expression:
A dynamic property: n["prop"], rel[n.city + n.zip], map[coll[0]].
This feature is documented for 2.3.0 but is absent from the previous version (2.2.9).
Thank you Neo4j Team!

Filtering tab completion in input task implementation

I'm currently implementing a SBT plugin for Gatling.
One of its features will be to open the last generated report in a new browser tab from SBT.
As each run can have a different "simulation ID" (basically a simple string), I'd like to offer tab completion on simulation ids.
An example :
Running the Gatling SBT plugin will produce several folders (named from simulationId + date of report generaation) in target/gatling, for example mysim-20140204234534, myothersim-20140203124534 and yetanothersim-20140204234534.
Let's call the task lastReport.
If someone start typing lastReport my, I'd like to filter out tab-completion to only suggest mysim and myothersim.
Getting the simulation ID is a breeze, but how can help the parser and filter out suggestions so that it only suggest an existing simulation ID ?
To sum up, I'd like to do what testOnly do, in a way : I only want to suggest things that make sense in my context.
Thanks in advance for your answers,
Pierre
Edit : As I got a bit stuck after my latest tries, here is the code of my inputTask, in it's current state :
package io.gatling.sbt
import sbt._
import sbt.complete.{ DefaultParsers, Parser }
import io.gatling.sbt.Utils._
object GatlingTasks {
val lastReport = inputKey[Unit]("Open last report in browser")
val allSimulationIds = taskKey[Set[String]]("List of simulation ids found in reports folder")
val allReports = taskKey[List[Report]]("List of all reports by simulation id and timestamp")
def findAllReports(reportsFolder: File): List[Report] = {
val allDirectories = (reportsFolder ** DirectoryFilter.&&(new PatternFilter(reportFolderRegex.pattern))).get
allDirectories.map(file => (file, reportFolderRegex.findFirstMatchIn(file.getPath).get)).map {
case (file, regexMatch) => Report(file, regexMatch.group(1), regexMatch.group(2))
}.toList
}
def findAllSimulationIds(allReports: Seq[Report]): Set[String] = allReports.map(_.simulationId).distinct.toSet
def openLastReport(allReports: List[Report], allSimulationIds: Set[String]): Unit = {
def simulationIdParser(allSimulationIds: Set[String]): Parser[Option[String]] =
DefaultParsers.ID.examples(allSimulationIds, check = true).?
def filterReportsIfSimulationIdSelected(allReports: List[Report], simulationId: Option[String]): List[Report] =
simulationId match {
case Some(id) => allReports.filter(_.simulationId == id)
case None => allReports
}
Def.inputTaskDyn {
val selectedSimulationId = simulationIdParser(allSimulationIds).parsed
val filteredReports = filterReportsIfSimulationIdSelected(allReports, selectedSimulationId)
val reportsSortedByDate = filteredReports.sorted.map(_.path)
Def.task(reportsSortedByDate.headOption.foreach(file => openInBrowser((file / "index.html").toURI)))
}
}
}
Of course, openReport is called using the results of allReports and allSimulationIds tasks.
I think I'm close to a functioning input task but I'm still missing something...
Def.inputTaskDyn returns a value of type InputTask[T] and doesn't perform any side effects. The result needs to be bound to an InputKey, like lastReport. The return type of openLastReport is Unit, which means that openLastReport will construct a value that will be discarded, effectively doing nothing useful. Instead, have:
def openLastReport(...): InputTask[...] = ...
lastReport := openLastReport(...).evaluated
(Or, the implementation of openLastReport can be inlined into the right hand side of :=)
You probably don't need inputTaskDyn, but just inputTask. You only need inputTaskDyn if you need to return a task. Otherwise, use inputTask and drop the Def.task.

Resources