cylons 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: eb519882f4bf37d3a0b69e51a1d84517965d122f
4
+ data.tar.gz: a1f350512a94e1ead21987957b9e75e91cb30de5
5
+ SHA512:
6
+ metadata.gz: b6a2a8b849ced51a30dd7ba062f72d5f51a6a6a8afde706e2011eef6fe1f4cb23fa3a2c7e73a3400a1a7d4413800832d2df1d9360c27556cf65e9eb857ba2893
7
+ data.tar.gz: 6ac49e71477cb817fb05ad9396145739d81b9f477c4757628d915e401c77a4a906dc67a49e2bac36800e39031c8ee8483af1bb8d80c893b9a12d7dd245aef4af
data/.gitignore CHANGED
@@ -1,5 +1,7 @@
1
1
  *.gem
2
2
  *.rbc
3
+ *.db
4
+ *.log
3
5
  .bundle
4
6
  .config
5
7
  .yardoc
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in skynext.gemspec
2
+ #dcell needs https://github.com/celluloid/dcell/pull/72 to be merged or fix the alias_method issue
3
+ gem 'dcell', github: 'jasonayre/dcell', branch: 'master'
4
+ # gem "dcell", :path => "~/gems/dcell"
4
5
  gemspec
@@ -0,0 +1,39 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :bundler do
5
+ watch('Gemfile')
6
+ # Uncomment next line if your Gemfile contains the `gemspec' command.
7
+ # watch(/^.+\.gemspec/)
8
+ end
9
+
10
+ # Note: The cmd option is now required due to the increasing number of ways
11
+ # rspec may be run, below are examples of the most common uses.
12
+ # * bundler: 'bundle exec rspec'
13
+ # * bundler binstubs: 'bin/rspec'
14
+ # * spring: 'bin/rsspec' (This will use spring if running and you have
15
+ # installed the spring binstubs per the docs)
16
+ # * zeus: 'zeus rspec' (requires the server to be started separetly)
17
+ # * 'just' rspec: 'rspec'
18
+ guard :rspec, cmd: 'bundle exec rspec' do
19
+ watch(%r{^spec/.+_spec\.rb$})
20
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
21
+ watch('spec/spec_helper.rb') { "spec" }
22
+
23
+ # Rails example
24
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
25
+ watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
26
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
27
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
28
+ watch('config/routes.rb') { "spec/routing" }
29
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
30
+ watch('spec/rails_helper.rb') { "spec" }
31
+
32
+ # Capybara features specs
33
+ watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
34
+
35
+ # Turnip features and steps
36
+ watch(%r{^spec/acceptance/(.+)\.feature$})
37
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
38
+ end
39
+
data/README.md CHANGED
@@ -1,78 +1,132 @@
1
1
  # Cylons
2
2
 
3
- Collectively intelligent remote models which behave very much like models they are claiming to be. (Zero configuration SOA Framework).
3
+ Collectively intelligent remote models which behave very much like models they are claiming to be. (Or the first zero configuration RPC/SOA framework).
4
4
 
5
- Cylons lets your models in one Rails app, act very much like your ActiveRecord models in another Rails app, providing a base for a SOA infrastructure. I hope to be able to make it applicable to more than just AR models as well, however, I also hope to make it past cool proof of concept stage that works, but isn't exactly suitable for a production system, WHICH IS THE STATE IT IS CURRENTLY IN. You've been warned.
5
+ Cylons lets your models in one Rails app, act very much like your ActiveRecord models in another Rails app, providing a foundation for building an SOA infrastructure. Not exactly production ready, but getting there.
6
6
 
7
- ### Quick Explanation
7
+ ### Quick Explanation / Scenario
8
8
 
9
+ Youve got a product model in one app, a category model in another. A category has many remote products. Then youve got another app, with a front end, that you want to use data from in both of those apps. Its as simple as:
10
+ (note you just need to include gem and initializers additionally)
11
+
12
+ **App 1**
13
+ *Product Model:*
14
+ ``` ruby
15
+ class Product < ActiveRecord::Base
16
+ include ::Cylons::Remote
17
+ remote_belongs_to :category
18
+ end
19
+ ```
20
+ *Category Agent:*
21
+ ``` ruby
22
+ class Category < Cylons::Agent
23
+
24
+ end
25
+ ```
26
+
27
+ **App 2**
28
+ *Category Model:*
29
+ ``` ruby
30
+ class Category < ActiveRecord::Base
31
+ include ::Cylons::Remote
32
+ remote_has_many :products
33
+ end
34
+ ```
35
+ *Product Agent:*
36
+ ``` ruby
37
+ class Product < Cylons::Agent
38
+
39
+ end
40
+ ```
41
+
42
+ That minimal amount of code will enable you to do things from app 3 such as:
43
+
44
+ *App 3*
45
+ ``` ruby
46
+ category = Category.first
47
+ category.products
48
+
49
+ Product.create
50
+ Category.create
51
+ ```
52
+
53
+ So on a so forth. Extensive examples provided down further.
9
54
 
10
55
  ### Depends heavily on:
11
- DCell
12
- Zookeeper (for now, only even though )
56
+ * DCell
57
+ * Zookeeper
13
58
 
14
59
  ### Heavily inspired by
15
- This concept - http://www.youtube.com/watch?v=Hu-jvAWTZ9o&feature=autoplay&list=PLF88804CB7380F32C&playnext=1#t=4m59s
16
- ActiveRemote - https://github.com/liveh2o/active_remote
17
- DCell - https://github.com/celluloid/dcell
60
+ * This concept - http://www.youtube.com/watch?v=Hu-jvAWTZ9o&feature=autoplay&list=PLF88804CB7380F32C&playnext=1#t=4m59s
61
+ * ActiveRemote - https://github.com/liveh2o/active_remote
62
+ * DCell - https://github.com/celluloid/dcell
18
63
 
19
64
  ### Alternatives
20
- Ill list more later, because I've literally tried at least the 90% majority of the ruby soa "solutions", but if you are looking for an "enterprise" level SOA framework, I'd recommend:
65
+ Right now, the gem is still in sketchy mode. If you are looking for a heavy duty rpc ruby framework, I'd recommend:
21
66
 
22
- ActiveRemote - https://github.com/liveh2o/active_remote
23
- Protobuf - https://github.com/localshred/protobuf
24
-
25
- Add those two together == winning.
26
-
27
- ### Reasons for building
28
- Originally it started as a way to communicate between my raspberry pis, because http is so last summer. Soon I found myself wanting a database connection. So I ended up making it more active_record like. Those aren't reasons but brain tired done typing yep.
67
+ * ActiveRemote - https://github.com/liveh2o/active_remote
68
+ * Protobuf - https://github.com/localshred/protobuf
29
69
 
30
70
 
31
71
  ### Quick start
32
72
 
33
-
34
73
  ### How it works?
35
74
 
36
75
  ### Install ZK, Clone and run the examples
37
76
 
38
77
  ```
39
78
  git clone https://github.com/jasonayre/cylons_demo
40
-
41
79
  ```
42
80
 
43
- open 4 new terminal windows/tabs
81
+ open 2 new terminal windows/tabs
44
82
 
45
- *Prepare the inventory management service, inventory*
46
-
47
- ```
48
- cd inventory
49
- SKIP_CYLONS=true bundle exec rake db:create && rake db:migrate && rake db:seed
50
- ```
51
-
52
- *Prepare the user credentials service, identify*
83
+ *Prepare & start the fake user credentials service, identify*
53
84
  ```
54
85
  cd identify
55
- SKIP_CYLONS=true bundle exec rake db:create && rake db:migrate && rake db:seed
86
+ bundle exec rake db:create && rake db:migrate && rake db:seed
87
+ bundle exec cylons start
56
88
  ```
57
89
 
58
- *Prepare the categorization service, taxon*
90
+ *cd into admin service in other window, launch console and run some commands*
91
+
92
+ ``` ruby
93
+ cd static_admin
94
+ bundle
95
+ RPC=1 bundle exec rails c
59
96
  ```
60
- cd taxon
61
- SKIP_CYLONS=true bundle exec rake db:create && rake db:migrate && rake db:seed
97
+
98
+ *Then run some rails console commands on the remote cylons service*
99
+ ``` ruby
100
+ User.all
101
+ User.first
102
+ u = User.create(:name => "asdasd", :email => "bill@initech.com")
103
+ u.name = "blumbergh"
104
+ u.save
105
+
106
+ u = User.create(:name => "asdasd", :email => "asdasd@asdasd.com", :password => "asdasd")
107
+ {:error=>"unknown attribute: password"}
108
+
109
+ u = User.create(:email => "asdasd@asdasd")
110
+ puts u.errors
111
+ #<ActiveModel::Errors:0x343a4ddd @base=#<User created_at: nil, email: "asdasd@asdasd", id: nil, name: nil, updated_at: nil>, @messages={:name=>["can't be blank", "can't be blank"]}
112
+
113
+ users = User.search(:name => "blumbergh")
114
+ [#<User created_at: "2014-11-04 22:23:10.697000", email: "bill@initech.com", id: 2013, name: "blumbergh", updated_at: "2014-11-04 22:23:25.993000">]
115
+
116
+ #(when ::Cylons::RemotePagination included into remote model, results will be a will paginate collection)
117
+ users.current_page
118
+
119
+ #dont know why this isnt returning integer thats a bug..
120
+ users.current_page
121
+ => page 1
62
122
  ```
123
+
63
124
  *Start Zookeeper, and start each service up with:*
64
125
  ```
65
126
  bundle exec cylons start
66
127
  ```
67
128
 
68
- *Load that sample data, note the admin app has no database at all, only remote models*
69
- ```
70
- cd admin
71
- bx rake db:seed
72
- ```
73
-
74
- Note: there will be quite a few N+1 queries happening, intentionally. This is to demonstrate communication between the services, and speed. Each record that is created will make a call to Inventory, to check if that record already exists. If not, it will build the record by doing a Category.first_or_create (Taxon Service), to get the category_id, then it will make 3 additional calls to the Inventory service, 1 for manufacturer, 1 for department, and then finally one to actually create the Product.
75
-
129
+ *NOTE: You just need to pass a flag RPC=1 to force connection when running rails c or rails s -- RPC env var just needs to exist to force connection
76
130
 
77
131
  ## Installation
78
132
 
data/bin/cylons CHANGED
@@ -2,32 +2,32 @@
2
2
 
3
3
  require 'thor'
4
4
  require 'cylons'
5
+
6
+ ENV["RPC"] ||= "1"
7
+
5
8
  class Services < ::Thor
6
9
  class_option :app, :default => "./config/environment.rb"
7
-
10
+
8
11
  desc "start", "Start cylon services"
9
12
  def start
10
13
  puts "Starting services"
11
14
  require options[:app]
12
-
13
- raise ::Cylons::CouldNotConnectToRegistry unless ::Cylons::Connection.connected?
14
-
15
- ::Dir.glob(::Rails.root.join('app', 'models', "*.rb")).each{ |file| load file }
16
-
15
+
16
+ ::Cylons.load_models
17
+
17
18
  ::Cylons.logger.info "Cylon Remotes: #{::Cylons::RemoteRegistry.remotes}"
18
-
19
+
19
20
  ::Cylons::RemoteRegistry.register_schemas
20
-
21
+
21
22
  ::Cylons::ServiceManager.start
22
23
 
23
- loop do
24
-
25
- end
24
+ sleep
26
25
  end
27
26
  end
28
27
 
29
28
  ::Services.start
30
29
 
30
+
31
31
  [:INT, :QUIT, :TERM].each do |signal|
32
32
  trap(signal) do
33
33
  ::Cylons.logger.info "Stopping Cylon Services"
@@ -35,4 +35,3 @@ end
35
35
  exit 0
36
36
  end
37
37
  end
38
-
@@ -17,24 +17,29 @@ Gem::Specification.new do |spec|
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
-
21
- spec.add_dependency "active_attr"
20
+
22
21
  spec.add_dependency "active_attr"
23
- spec.add_dependency "dcell", "0.15.0"
24
22
  spec.add_dependency "thor"
25
23
  spec.add_dependency "zk"
26
- spec.add_dependency 'pry'
27
24
  spec.add_dependency "will_paginate"
28
- spec.add_dependency "ransack"
25
+ spec.add_dependency "dcell"
26
+
27
+ spec.add_runtime_dependency "pry"
29
28
 
30
29
  spec.add_development_dependency "bundler", "~> 1.3"
31
30
  spec.add_development_dependency "rake"
32
- spec.add_development_dependency "pry"
33
31
  spec.add_development_dependency "activerecord"
34
- # spec.add_development_dependency "sqlite3"
35
- spec.add_development_dependency "rake"
32
+ spec.add_development_dependency "sqlite3"
36
33
  spec.add_development_dependency "rspec"
37
34
  spec.add_development_dependency "rspec-pride"
38
35
  spec.add_development_dependency "pry-nav"
39
36
  spec.add_development_dependency "simplecov"
37
+ spec.add_development_dependency 'rspec-its', '~> 1'
38
+ spec.add_development_dependency 'rspec-collection_matchers', '~> 1'
39
+ spec.add_development_dependency 'guard', '~> 2'
40
+ spec.add_development_dependency 'guard-rspec', '~> 4'
41
+ spec.add_development_dependency 'guard-bundler', '~> 2'
42
+ spec.add_development_dependency 'rb-fsevent'
43
+ spec.add_development_dependency 'terminal-notifier-guard'
44
+
40
45
  end
@@ -5,16 +5,20 @@ require 'active_support/core_ext/hash'
5
5
  require 'active_support/inflector'
6
6
  require 'active_support/json'
7
7
  require 'will_paginate'
8
+ require 'will_paginate/array'
8
9
  require "cylons/version"
9
10
 
10
11
  require 'cylons/attributes'
12
+ require 'cylons/agent'
11
13
  require 'cylons/connection'
12
- require 'cylons/configuration'
14
+ # require 'cylons/configuration'
15
+ require 'cylons/config'
13
16
  require 'cylons/errors'
17
+ require 'cylons/logging'
14
18
  require 'cylons/remote'
15
19
  require 'cylons/remote_registry'
16
20
  require 'cylons/remote_discovery'
17
- require 'cylons/remote_proxy'
21
+ require 'cylons/remote_pagination'
18
22
  require 'cylons/registry_adapter'
19
23
  require 'cylons/service'
20
24
  require 'cylons/service_manager'
@@ -22,36 +26,61 @@ require 'dcell'
22
26
  require 'socket'
23
27
  require 'zk'
24
28
  require 'dcell/registries/zk_adapter'
25
- require 'cylons/railtie'
26
29
  require 'pry'
27
- # require 'cylons/railtie' if defined?(Rails)
28
30
 
29
31
  module Cylons
30
- #dont load remotes if test env or SKIP_CYLONS=true (such as when running rake tasks)
32
+ def self.connect?
33
+ !!ENV["RPC"]
34
+ end
35
+
36
+ def self.load_models
37
+ ::Dir.glob(model_paths).each{ |file|
38
+ puts "loading #{file}"
39
+ load file }
40
+ end
41
+
42
+ def self.model_paths
43
+ if configuration.model_paths
44
+ return configuration.model_paths
45
+ elsif defined?(::Rails)
46
+ [::Rails.root.join('app', 'models', "*.rb"), ::Rails.root.join('app', 'models', "**", "*.rb")]
47
+ else
48
+ []
49
+ end
50
+ end
51
+
31
52
  def self.skip_cylons?
32
- !!ENV["SKIP_CYLONS"]
53
+ !connect?
33
54
  end
34
-
55
+
35
56
  def self.silence?
36
57
  skip_cylons? || (defined?(Rails) && Rails.env == "test")
37
58
  end
38
-
59
+
39
60
  class << self
40
- attr_accessor :configuration, :logger
41
-
61
+ attr_accessor :configuration
62
+
42
63
  def configuration
43
- @configuration ||= ::Cylons::Configuration.new
64
+ @configuration ||= ::Cylons::Config.new
44
65
  end
45
66
 
46
67
  def configure
47
68
  yield(configuration) if block_given?
48
-
69
+
49
70
  @logger = configuration.logger
50
-
71
+
51
72
  ::ActiveSupport.run_load_hooks(:cylons, self)
52
73
  end
53
-
54
74
  alias_method :config, :configuration
75
+
76
+ def logger
77
+ ::Cylons.config.logger
78
+ end
79
+
80
+
81
+ delegate :connect, :to => ::Cylons::Connection
82
+ delegate :connected?, :to => ::Cylons::Connection
55
83
  end
56
84
  end
57
85
 
86
+ require 'cylons/railtie' if defined?(Rails)
@@ -2,21 +2,23 @@ require 'cylons/remote_discovery'
2
2
 
3
3
  module Cylons
4
4
  module ActiveRecordExtensions
5
+ extend ::ActiveSupport::Concern
6
+
7
+ SEARCH_OPTION_KEYS = [:opts, :options].freeze
8
+ MAX_PER_PAGE = 1000
9
+
5
10
  module ClassMethods
6
-
7
- SEARCH_OPTION_KEYS = [:opts, :options].freeze
8
-
9
11
  def reload_remotes!
10
12
  ::Cylons::RemoteDiscovery.load_remotes unless ::Cylons.silence?
11
13
  end
12
-
14
+
13
15
  def remote_schema
14
16
  ::Cylons::RemoteRegistry.get_remote_schema(self.name) unless ::Cylons.silence?
15
17
  end
16
-
18
+
17
19
  def remote_belongs_to(*args)
18
20
  options = args.extract_options!
19
-
21
+
20
22
  args.each do |arg|
21
23
  options[:foreign_key] = "#{arg}_id"
22
24
  association_hash = {:name => arg, :association_type => :belongs_to, :options => options}
@@ -24,36 +26,27 @@ module Cylons
24
26
  build_remote_belongs_to_association(association_hash)
25
27
  end
26
28
  end
27
-
28
- #store remote has many assoc globally, then define it locally.
29
+
30
+ #store remote has many assoc globally, then define it locally.
29
31
  def remote_has_many(*args)
30
32
  options = args.extract_options!
31
-
33
+
32
34
  args.each do |arg|
33
35
  association_hash = {:name => arg, :association_type => :has_many, :options => options}
34
36
  self.remote_associations << association_hash
35
37
  build_remote_has_many_association(association_hash)
36
38
  end
37
39
  end
38
-
39
- #TODO: Hacky, but something strange is going on here, and syntax will need to chagne for rails4... hrmmm
40
- def scope_by(params = {})
40
+
41
+ def search(params = {})
41
42
  search_options = params.extract!(*SEARCH_OPTION_KEYS)
42
-
43
+
43
44
  search_options.delete_if {|k,v| v.nil? }
44
-
45
- if search_options.present?
46
- scoped_search = params.inject(scoped) do |combined_scope, param|
47
- combined_scope.send("by_#{param.first}", param.last)
48
- end.paginate(:page => search_options[:options][:page], :per_page => search_options[:options][:per_page])
49
- else
50
- scoped_search = params.inject(scoped) do |combined_scope, param|
51
- combined_scope.send("by_#{param.first}", param.last)
52
- end
53
- end
54
-
55
- scoped_search
56
- end
45
+
46
+ query_scope = params.inject(all) do |query, hash_pair|
47
+ query.__send__("by_#{hash_pair[0]}", hash_pair[1]).all
48
+ end.all
49
+ end
57
50
  end
58
51
  end
59
- end
52
+ end