I've recently started messing with a gem called 'ruleby', a rule-engine for Ruby. The documentation for ruleby is a bit sparse, however, and I can't seem to figure out how to properly reference associations for the rule-writing bit. I'm stumped both the 'pattern' part of the rule and also in the executing block part of the rule.
For example, let's say I had a rule which would only be executed only when a user submitted a positive review. I could, for instance, write the following:
rule :positive_review, [Review, :review, method.review_rating == "positive"] do |v|
assert (store positive_review somehow)
end
So it's at this point that I get lost. I would like to write a rule which would reference back to the user and check the total number of positive reviews that the user of this positive review and possibly execute certain actions based on this number.
If anyone could point me in the right direction, I would be greatly appreciative. Thanks.
I dont quite understand why you are asserting a fact inside of a rule, that should already be done. The github example code probably answered you by now but in case not:
First you initialise the engine, I did it like this so I could call on it more than once.
rule_engine = Ruleby::Core::Engine.new
RULES_ENG = RulesEngineCaller.new(rule_engine)
EngineRulebook.new(rule_engine).rules
Then you assert your facts
#events = Event.all
#events.each do |event|
#rule_engine.assert event
end
Then you run your engine
#rule_engine.match
if you want to add events you can just by calling assert per fact to the existing engine, it will just add to it until you either delete inside the engine or rebuild it.
The rulebook is where you add your facts for me I did it programatically:
class EngineRulebook < Rulebook
def rules
Rails.logger.debug "#{Time.now.utc} - Loading Rules into Rulebook"
#msg = Notification.new()
#rules = Rule.all
#rules.each do |rule_name|
case
#hard coded rule - for auto cleaning
when (rule_name.title == "SYSTEM ADMIN - auto-clean delete rule") && (rule_name.group.title == "Notification Admin")
rule [Event, :m, m.terminate_flag == 1] do |v|
if v[:m].rules.include?(rule_name) == false
#output matched rule to console & logfile
Rails.logger.info "#{Time.now.utc} - match rule #{rule_name.id} #{rule_name.title} - #{v[:m].ticket_id} - #{v[:m].description}"
#add reference so rule doesn't fire again
#event = Event.find_by_id(v[:m].id)
#event.rules << rule_name
v[:m].rules << rule_name
modify v[:m]
#Retract v[:m] would remove the fact from the rules engine, need to remove all related facts though
#so dont use this as other rules may be requires as rules do not fire in order
end
end
end
I also did it as a case statement as I was loading rules programmatically. I also put a check in to say has this rule run before because each time you run the engine it will assess all facts, even ones that have already matched.
Hopefully you have an answer, if not hopefully this helped.
Related
I’m pretty new to python (using python 3) and spacy (and programming too). Please bear with me.
I have three questions where two are more or less the same I just can’t get it to work.
I took the “syntax specific search with spacy” (example) and tried to make different things work.
My program currently reads txt and the normal extraction
if w.lower_ != 'music':
return False
works.
My first question is: How can I get spacy to extract two words?
For example: “classical music”
With the previous mentioned snippet I can make it extract either classical or music. But if I only search for one of the words I also get results I don’t want like.
Classical – period / era
Or when I look for only music
Music – baroque, modern
The second question is: How can I get the dependencies to work?
The example dependency with:
elif w.dep_ != 'nsubj': # Is it the subject of a verb?
return False
works fine. But everything else I tried does not really work.
For example, I want to extract sentences with the word “birthday” and the dependency ‘DATE’. (so the dependency is an entity)
I got
if d.ent_type_ != ‘DATE’:
return False
To work.
So now it would look like:
def extract_information(w,d):
if w.lower_ != ‘birthday’:
return False
elif d.ent_type_ != ‘DATE’:
return False
else:
return True
Does something like this even work?
If it works the third question would be how I can filter sentences for example with a DATE. So If the sentence contains a certain word and a DATE exclude it.
Last thing maybe, I read somewhere that the dependencies are based on the “Stanford typed dependencies manual”. Is there a list which of those dependencies work with spacy?
Thank you for your patience and help :)
Before I get into offering some simple suggestions to your questions, have you tried using displaCy's visualiser on some of your sentences?
Using an example sentence 'John's birthday was yesterday', you'll find that within the parsed sentence, birthday and yesterday are not necessarily direct dependencies of one another. So searching based on the birthday word having a dependency of a DATE type entity, might not be yield the best of results.
Onto the first question:
A brute force method would be to look for matching subsequent words after you have parsed the sentence.
doc = nlp(u'Mary enjoys classical music.')
for (i,token) in enumerate(doc):
if (token.lower_ == 'classical') and (i != len(doc)-1):
if doc[i+1].lower_ == 'music':
print 'Target Acquired!'
If you're unsure of what enumerate does, look it up. It's the pythonic way of using python.
To questions 2 and 3, one simple (but not elegant) way of solving this is to just identify in a parsed sentence if the word 'birthday' exists and if it contains an entity of type 'DATE'.
doc = nlp(u'John\'s birthday was yesterday.')
for token in doc:
if token.lower_ == 'birthday':
for entities in doc.ents:
if entities.label_ == 'DATE':
print 'Found ya!'
As for the list of dependencies, I presume you're referring to the Part-Of-Speech tags. Check out the documentation on this page.
Good luck! Hope that helped.
Is there a better way to update more record in one query with different values in Ruby on Rails? I solved using CASE in SQL, but is there any Active Record solution for that?
Basically I save a new sort order when a new list arrive back from a jquery ajax post.
#List of product ids in sorted order. Get from jqueryui sortable plugin.
#product_ids = [3,1,2,4,7,6,5]
# Simple solution which generate a loads of queries. Working but slow.
#product_ids.each_with_index do |id, index|
# Product.where(id: id).update_all(sort_order: index+1)
#end
##CASE syntax example:
##Product.where(id: product_ids).update_all("sort_order = CASE id WHEN 539 THEN 1 WHEN 540 THEN 2 WHEN 542 THEN 3 END")
case_string = "sort_order = CASE id "
product_ids.each_with_index do |id, index|
case_string += "WHEN #{id} THEN #{index+1} "
end
case_string += "END"
Product.where(id: product_ids).update_all(case_string)
This solution works fast and only one query, but I create a query string like in php. :) What would be your suggestion?
You should check out the acts_as_list gem. It does everything you need and it uses 1-3 queries behind the scenes. Its a perfect match to use with jquery sortable plugin. It relies on incrementing/decrementing the position (sort_order) field directly in SQL.
This won't be a good solution for you, if your UI/UX relies on saving the order manually by the user (user sorts out the things and then clicks update/save). However I strongly discourage this kind of interface, unless there is a specific reason (for example you cannot have intermediate state in database between old and new order, because something else depends on that order).
If thats not the case, then by all means just do an asynchronous update after user moves one element (and acts_as_list will be great to help you accomplish that).
Check out:
https://github.com/swanandp/acts_as_list/blob/master/lib/acts_as_list/active_record/acts/list.rb#L324
# This has the effect of moving all the higher items down one.
def increment_positions_on_higher_items
return unless in_list?
acts_as_list_class.unscoped.where(
"#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
).update_all(
"#{position_column} = (#{position_column} + 1)"
)
end
This should be fairly simply...
I have Recommendations has_many Assets.
I want to limit the user to adding 3 Assets per Recommendation, and I can do this simply by limiting the number of fields show.
In my new action in the controller I am doing a very simple:
3.times {#recommendation.assets.build}
In my edit action I am trying to build the logic to decide how many fields to show:
#assets = #recommendation.assets.all
if #assets.empty?
3.times {#recommendation.assets.build}
else
asset_loop = #assets.count - 3
asset_loop.times {#recommendation.assets.build}
end
The if works but the else does not. How can I make this work?
If I understand your goal, you just need to change
asset_loop = #assets.count - 3
to
asset_loop = 3 - #assets.count
Do make sure to validate the incoming data if you want to truly enforce the limit. Otherwise you're at the mercy of anyone with a web console and curl.
Our application uses a number of environments so we can experiment with settings without breaking things. In a typical controller action, I have something like this:
def some_action
...
if #foo.development_mode == 'Production'
#settings = SomeHelper::Production.lan(bar)
elsif #foo.development_mode == 'Beta'
#settings = SomeHelper::Beta.lan(nas)
elsif #foo.development_mode == 'Experimental'
#settings = SomeHelper::Experimental.lan(nas)
end
...
end
Since we have dozens of these, I figured I could try and dry things up with something like this:
#settings = "SomeHelper::#{#foo.development_mode}.lan(bar)"
Which obviously doesn't work - I just get:
"NasHelper::Production.lan(bar)"
How can I reduce this down or do I have to stick with what I've got??
If your concern is that you're ending up with a String rather than the object, you can use String.constantize (Rails only, with standard Ruby you'd have to implement this; it uses Object.const_get(String))
Another option would be .const_get (e.g. Object.const_get(x) where x is your string), you it doesn't, on its own, nest correctly, so you would have to split at "::", etc.
Also, there's the option of using eval to evaluate the String.
But note: eval should be used with great care (it's powerful), or not at all.
Edit:
This means that instead of:
#settings = "SomeHelper::#{#foo.development_mode}.lan(bar)"
You could run:
#settings = "SomeHelper::#{#foo.development_mode}".constantize.lan(bar)
Useful Sources:
http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize
http://www.ruby-forum.com/topic/183112
http://blog.grayproductions.net/articles/eval_isnt_quite_pure_evil
In the first case, #settings receives the result of the method SomeHelper::Production.lan(bar); in the second, #settings just gets a string. You could use the send method of Object linked here to fire the method, or eval, but this wouldn't be my first choice.
It looks like you might be reinventing a wheel -- Rails already has the concept of "environments" pretty well wired into everything -- they are defined in app/config/environments. You set the environment when you launch the server, and can test like Rails.env.production?. To create new environments, just copy the existing environment file of the one closest to the new one, e.g. copy production.rb to beta.rb and edit as necessary, then test Rails.env.beta?, for example.
But this still leaves you testing which one all over the place. You can add to the config hash (e.g. config.some_helper.lan = 'bar'), which value you can assign to #settings directly. You have to make sure there's either a default or it's defined in all environments, but I think this is probably the right approach ... not knowing exactly what you aim to accomplish.
I have an index view of a model which I would like to filter by some combination of the model's attributes.
For example, I have a Bill model (not the kind on ducks, the kind you have to pay) that I might filter on payee and/or status.
The model has a scope for each individual attribute, e.g.
scope :bill_status, lambda {|status| where("status = ?", status}
scope :bill_payee, lambda {|payee| where("payee_id = ?", payee.id}
The view allows the user to select zero or more options -- if an option is not selected, it means "don't filter by this".
In the controller, I can do something yucky like this:
def index
status = params[:bill][:status]
payee = params[:bill][:payee]
if status.present? and payee.present?
# chain scopes
#bills = Bill.bill_status(status).bill_payee(payee)
elsif status.present?
#bills = Bill.bill_status(status)
elsif payee.present?
#bills = Bill.bill_payee(payee)
else
#bills = Bill.all
end
# rest of controller action
end
But while this works, it's neither pretty nor easily extensible -- adding a third filter means I now have many more possibilities. I seek beauty and purity.
On the assumption that my scopes are all chainable, it seems like I should be able to do something like
def index
#bills = Bill.all
#bills = #bills.bill_status(params[:bill][:status]) if params[:bill][:status].present?
#bills = #bills.bill_payee(params[:bill][:payee]) if params[:bill][:payee].present?
# rest of controller code
end
'cept it doesn't work because Bill.all is an array. Plus, that's no fun because Bill.all executes the query, which I only want to run once thanks to AREL magic. Should I just define a scope like all_bills (with no conditions?) -- that would be an ActiveRecord::Relation I guess...
Is there a pattern that solves this problem more elegantly? Whenever I have code to do one thing that relies on Model, View and Controller I feel as though I might be doing something wrong. Or as though someone smarter and harder working than I has already solved it :-)
Bonus question: I want this all to work with my paginator of choice, the most excellent Kaminari gem.
All thoughts and ideas welcomed.
I'd do something like this:
proxy = Bill.scoped
if status.present?
proxy = proxy.bill_status(status)
end
if payee.present?
proxy = proxy.bill_payee(payee)
end
#bills = proxy
You can even do then some meta-programming:
#bills = [:status, :payee, ...].inject(Bill.scoped) do |proxy, param|
val = params[:bill][param]
val.present ? proxy.send("bill_#{param}", val) : proxy
end
As I searched for solutions to what seemed like a common problem, I checked out Ryan Bates' RailsCast and found an episode from 3 days ago on Ransack. Ransack is a pretty seriously cool gem for form searching and column sorting, and I think that's the way I am going.
Thanks for the answers here -- I am glad to have learned a couple of great techniques from those who took the time and effort the answer.