cylons 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Gemfile +3 -2
- data/Guardfile +39 -0
- data/README.md +93 -39
- data/bin/cylons +11 -12
- data/cylons.gemspec +13 -8
- data/lib/cylons.rb +43 -14
- data/lib/cylons/active_record_extensions.rb +20 -27
- data/lib/cylons/{remote_proxy.rb → agent.rb} +104 -47
- data/lib/cylons/config.rb +26 -0
- data/lib/cylons/connection.rb +11 -15
- data/lib/cylons/errors.rb +2 -1
- data/lib/cylons/local_registry.rb +5 -11
- data/lib/cylons/logging.rb +41 -0
- data/lib/cylons/railtie.rb +4 -25
- data/lib/cylons/registry_adapter.rb +10 -1
- data/lib/cylons/remote.rb +11 -22
- data/lib/cylons/remote_discovery.rb +29 -18
- data/lib/cylons/remote_pagination.rb +32 -0
- data/lib/cylons/remote_registry.rb +12 -15
- data/lib/cylons/remote_schema.rb +2 -2
- data/lib/cylons/rpc.rb +64 -72
- data/lib/cylons/service_manager.rb +30 -20
- data/lib/cylons/version.rb +1 -1
- data/spec/inventory_test_app.rb +24 -0
- data/spec/lib/cylons/connection_spec.rb +15 -0
- data/spec/lib/cylons/interface_spec.rb +7 -0
- data/spec/lib/cylons/remote_registry_spec.rb +57 -0
- data/spec/lib/cylons/service_manager_spec.rb +40 -4
- data/spec/spec_helper.rb +30 -2
- data/spec/support/inventory_test_node.rb +49 -0
- data/spec/support/schema.rb +24 -0
- metadata +181 -150
- data/lib/cylons/configuration.rb +0 -38
- data/spec/lib/cylons/remote_registry.rb +0 -7
checksums.yaml
ADDED
@@ -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
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
|
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
|
data/Guardfile
ADDED
@@ -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. (
|
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
|
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
|
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
|
-
|
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
|
81
|
+
open 2 new terminal windows/tabs
|
44
82
|
|
45
|
-
*Prepare the
|
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
|
-
|
86
|
+
bundle exec rake db:create && rake db:migrate && rake db:seed
|
87
|
+
bundle exec cylons start
|
56
88
|
```
|
57
89
|
|
58
|
-
*
|
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
|
-
|
61
|
-
|
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
|
-
*
|
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
|
-
|
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
|
-
|
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
|
-
|
data/cylons.gemspec
CHANGED
@@ -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 "
|
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
|
-
|
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
|
data/lib/cylons.rb
CHANGED
@@ -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/
|
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
|
-
|
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
|
-
|
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
|
41
|
-
|
61
|
+
attr_accessor :configuration
|
62
|
+
|
42
63
|
def configuration
|
43
|
-
@configuration ||= ::Cylons::
|
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
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|