{"action":"created","issue":{"url":"https://api.github.com/repos/cantino/huginn/issues/920","labels_url":"https://api.github.com/repos/cantino/huginn/issues/920/labels{/name}","comments_url":"https://api.github.com/repos/cantino/huginn/issues/920/comments","events_url":"https://api.github.com/repos/cantino/huginn/issues/920/events","html_url":"https://github.com/cantino/huginn/pull/920","id":95442954,"number":920,"title":"Ruby gem agents (WIP)","user":{"login":"darrencauthon","id":148768,"avatar_url":"https://avatars.githubusercontent.com/u/148768?v=3","gravatar_id":"","url":"https://api.github.com/users/darrencauthon","html_url":"https://github.com/darrencauthon","followers_url":"https://api.github.com/users/darrencauthon/followers","following_url":"https://api.github.com/users/darrencauthon/following{/other_user}","gists_url":"https://api.github.com/users/darrencauthon/gists{/gist_id}","starred_url":"https://api.github.com/users/darrencauthon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/darrencauthon/subscriptions","organizations_url":"https://api.github.com/users/darrencauthon/orgs","repos_url":"https://api.github.com/users/darrencauthon/repos","events_url":"https://api.github.com/users/darrencauthon/events{/privacy}","received_events_url":"https://api.github.com/users/darrencauthon/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2015-07-16T14:16:56Z","updated_at":"2015-07-16T14:54:46Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/cantino/huginn/pulls/920","html_url":"https://github.com/cantino/huginn/pull/920","diff_url":"https://github.com/cantino/huginn/pull/920.diff","patch_url":"https://github.com/cantino/huginn/pull/920.patch"},"body":"This is a PR meant for discussion -- DO NOT MERGE.  I find it easier to discuss ideas when there's some semblance of a working copy.  That's all this is.\r\n\r\nSo I really like Huginn, and it comes with a TON of features.  But it's not really extendable in the sense that new modules can be created independently of the main app and then linked in (like, say, a Ruby gem that has a new agent).  Huginn is a Rails app, and you run it as a Rails app, and if you want to extend it you dump more code in your Rails app.  And if someone else wants that new agent you wrote... they copy-paste that code in their Huginn instance.\r\n\r\nSince Huginn comes with so much, part of me thinks that I should try to live and operate in the features that Huginn provides.  But the fun side of me says, \"Find a way to make modular Huginn agents!\"  So I did.\r\n\r\nThis is nowhere near done, but it's hit a stage where I've been able to create a stand-alone gem with two Huginn agents and get them added to Huginn.  \r\n\r\nIs this a good idea?  Am I nuts?  I'm open to any feedback!  But if this is something that the Huginn community thinks is a good idea, then I'd like to mold this to conform with the project.  But if you all think it's not a good idea... I'm probably going to do it anyway, on my own, because **I** like it.  :smile:\r\n\r\nSo here's how it works:\r\n\r\n### Add a reference to huginn_agent\r\n\r\nThis is a small Ruby gem that provides one class:  ```HuginnAgent```.  \r\n\r\n```HuginnAgent``` is meant to serve as the base class for other agents, just like ```Agent```.\r\n\r\n### Create a new agent\r\n\r\nHere is a sample agent, inheriting from ```HuginnAgent```.  It would be very similar to ```Agent```, except for one thing:  It's a plain ol' Ruby object, with no dependencies against Rails or ActiveRecord.\r\n\r\nThe code for this base class is here:  https://github.com/darrencauthon/huginn_agent/blob/021fb14918aede94df37f08d189e4c447fb79d49/lib/huginn_agent.rb\r\n\r\n```ruby\r\nmodule ProofOfConceptHuginnAgents\r\n  class Happy < HuginnAgent\r\n    def self.description\r\n      \"# I'm Happy\"\r\n    end\r\n  end\r\nend\r\n```\r\n\r\n### Plug your gems into your Huginn app\r\n\r\nI've been doing this with an initializer, like so:\r\n\r\n```ruby\r\nrequire 'huginn_agent'\r\n\r\nHuginnAgent.hack_huginn_to_accept_me\r\n\r\nHuginnAgent.types.each { |t| t.emit }\r\n```\r\n\r\nThis initializer does two things:\r\n\r\n1)  It monkey-patches over a couple things in Huginn.  Huginn is hardcoded to look for agents in a specific spot, so I jump into a few methods to tell Huginn know they could be somewhere else.\r\n\r\n2)  It loops through all of the declared ```HuginnAgent``` classes, and \"emits\" them to an ActiveRecord object.  \r\n\r\nSo that's the trick... this plain ol' Ruby object will map itself to the existing ```Agent``` format.  A little nutty, but it works.\r\n\r\n### Screenshots\r\n\r\nSo I only mapped the ```description``` method, but so far so good:\r\n\r\nMy sample gem (https://github.com/darrencauthon/proof_of_concept_huginn_agents/tree/c5b6560467714bf0e8a74667efc61d0ed0a7c9eb/lib/proof_of_concept_huginn_agents) has two agents, ```Happy``` and ```Sad```.  The agents get sucked into the list of agents.\r\n\r\n![huginn](https://cloud.githubusercontent.com/assets/148768/8725685/409fe8e8-2b9b-11e5-86c6-29ce9fe7b014.png)\r\n\r\n![huginn](https://cloud.githubusercontent.com/assets/148768/8725703/559d0988-2b9b-11e5-8cfd-b45717a05fbe.png)\r\n\r\n\r\n### Why?\r\n\r\nModular code is cool.  And I love the idea of getting a new agent by adding a reference to a gem.  "},"comment":{"url":"https://api.github.com/repos/cantino/huginn/issues/comments/121980329","html_url":"https://github.com/cantino/huginn/pull/920#issuecomment-121980329","issue_url":"https://api.github.com/repos/cantino/huginn/issues/920","id":121980329,"user":{"login":"cantino","id":83835,"avatar_url":"https://avatars.githubusercontent.com/u/83835?v=3","gravatar_id":"","url":"https://api.github.com/users/cantino","html_url":"https://github.com/cantino","followers_url":"https://api.github.com/users/cantino/followers","following_url":"https://api.github.com/users/cantino/following{/other_user}","gists_url":"https://api.github.com/users/cantino/gists{/gist_id}","starred_url":"https://api.github.com/users/cantino/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cantino/subscriptions","organizations_url":"https://api.github.com/users/cantino/orgs","repos_url":"https://api.github.com/users/cantino/repos","events_url":"https://api.github.com/users/cantino/events{/privacy}","received_events_url":"https://api.github.com/users/cantino/received_events","type":"User","site_admin":false},"created_at":"2015-07-16T14:54:46Z","updated_at":"2015-07-16T14:54:46Z","body":"Hey @darrencauthon!  First of all, I really appreciate that you are thinking about and working on this, that's how we can push big projects like this forward!\r\n\r\nDid you see https://github.com/cantino/huginn/pull/554, which is a first attempt at this?  In that case, we decided that the right course of action would be to pull all existing agents into *local* gems, so that they can continue to be updated and shipped together.  We made an `agents/` directory and migrated the rss_agent into it as a first example.  We also extracted a stand-alone gem called `huginn_agent` that would act as the dependency of all new Huginn Agents for testing and providing an API.  Right now all it has it registration, and we haven't yet solved the testing problem.  We want to have good specs around all Agents, so this is something important to solve.\r\n\r\nYour trick of [emitting new ActiveRecord objects](https://github.com/darrencauthon/huginn_agent/blob/master/lib/huginn_agent.rb#L22) is interesting, and a bit terrifying.  I'd be curious what others think about this.  We may be able to use `Class.new(Agent) do ...` instead.\r\n\r\nI think the basic requirements of any attempt to extract Gems would be:\r\n\r\n* existing Agents are extracted, but shipped as locally-bundled gems (because it makes maintenance of the base Agents *much* easier, as now a grep-and-replace can be used to fix a bunch at once)\r\n* gem dependencies for local gems are moved to their respective gem files, simplifying our primary Gemfile\r\n* potentially extract and define more limits around the Agent API, such that new Agent gems can be tested in isolation\r\n* figure out how to rigorously test Agents inside of gems\r\n\r\nThoughts?\r\n\r\n/cc @knu @dsander"}}