cylons 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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