chef-zero 0.9
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.
- data/LICENSE +201 -0
- data/README.rdoc +79 -0
- data/Rakefile +19 -0
- data/bin/chef-zero +40 -0
- data/lib/chef_zero.rb +5 -0
- data/lib/chef_zero/cookbook_data.rb +110 -0
- data/lib/chef_zero/data_normalizer.rb +129 -0
- data/lib/chef_zero/endpoints/actor_endpoint.rb +68 -0
- data/lib/chef_zero/endpoints/actors_endpoint.rb +32 -0
- data/lib/chef_zero/endpoints/authenticate_user_endpoint.rb +21 -0
- data/lib/chef_zero/endpoints/cookbook_endpoint.rb +39 -0
- data/lib/chef_zero/endpoints/cookbook_version_endpoint.rb +106 -0
- data/lib/chef_zero/endpoints/cookbooks_base.rb +59 -0
- data/lib/chef_zero/endpoints/cookbooks_endpoint.rb +12 -0
- data/lib/chef_zero/endpoints/data_bag_endpoint.rb +50 -0
- data/lib/chef_zero/endpoints/data_bag_item_endpoint.rb +25 -0
- data/lib/chef_zero/endpoints/data_bags_endpoint.rb +21 -0
- data/lib/chef_zero/endpoints/environment_cookbook_endpoint.rb +24 -0
- data/lib/chef_zero/endpoints/environment_cookbook_versions_endpoint.rb +114 -0
- data/lib/chef_zero/endpoints/environment_cookbooks_endpoint.rb +22 -0
- data/lib/chef_zero/endpoints/environment_endpoint.rb +33 -0
- data/lib/chef_zero/endpoints/environment_nodes_endpoint.rb +23 -0
- data/lib/chef_zero/endpoints/environment_recipes_endpoint.rb +22 -0
- data/lib/chef_zero/endpoints/environment_role_endpoint.rb +35 -0
- data/lib/chef_zero/endpoints/file_store_file_endpoint.rb +22 -0
- data/lib/chef_zero/endpoints/node_endpoint.rb +17 -0
- data/lib/chef_zero/endpoints/not_found_endpoint.rb +9 -0
- data/lib/chef_zero/endpoints/principal_endpoint.rb +30 -0
- data/lib/chef_zero/endpoints/rest_list_endpoint.rb +41 -0
- data/lib/chef_zero/endpoints/rest_object_endpoint.rb +65 -0
- data/lib/chef_zero/endpoints/role_endpoint.rb +16 -0
- data/lib/chef_zero/endpoints/role_environments_endpoint.rb +14 -0
- data/lib/chef_zero/endpoints/sandbox_endpoint.rb +22 -0
- data/lib/chef_zero/endpoints/sandboxes_endpoint.rb +44 -0
- data/lib/chef_zero/endpoints/search_endpoint.rb +139 -0
- data/lib/chef_zero/endpoints/searches_endpoint.rb +18 -0
- data/lib/chef_zero/rest_base.rb +82 -0
- data/lib/chef_zero/rest_error_response.rb +11 -0
- data/lib/chef_zero/rest_request.rb +42 -0
- data/lib/chef_zero/router.rb +26 -0
- data/lib/chef_zero/server.rb +255 -0
- data/lib/chef_zero/solr/query/binary_operator.rb +53 -0
- data/lib/chef_zero/solr/query/phrase.rb +23 -0
- data/lib/chef_zero/solr/query/range_query.rb +34 -0
- data/lib/chef_zero/solr/query/regexpable_query.rb +29 -0
- data/lib/chef_zero/solr/query/subquery.rb +35 -0
- data/lib/chef_zero/solr/query/term.rb +45 -0
- data/lib/chef_zero/solr/query/unary_operator.rb +43 -0
- data/lib/chef_zero/solr/solr_doc.rb +62 -0
- data/lib/chef_zero/solr/solr_parser.rb +194 -0
- data/lib/chef_zero/version.rb +3 -0
- metadata +132 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rack/request'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
class RestRequest
|
5
|
+
def initialize(env)
|
6
|
+
@env = env
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :env
|
10
|
+
|
11
|
+
def base_uri
|
12
|
+
@base_uri ||= "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}#{env['SCRIPT_NAME']}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def method
|
16
|
+
@env['REQUEST_METHOD']
|
17
|
+
end
|
18
|
+
|
19
|
+
def rest_path
|
20
|
+
@rest_path ||= env['PATH_INFO'].split('/').select { |part| part != "" }
|
21
|
+
end
|
22
|
+
|
23
|
+
def body=(body)
|
24
|
+
@body = body
|
25
|
+
end
|
26
|
+
|
27
|
+
def body
|
28
|
+
@body ||= env['rack.input'].read
|
29
|
+
end
|
30
|
+
|
31
|
+
def query_params
|
32
|
+
@query_params ||= begin
|
33
|
+
params = Rack::Request.new(env).GET
|
34
|
+
params.keys.each do |key|
|
35
|
+
params[key] = URI.unescape(params[key])
|
36
|
+
end
|
37
|
+
params
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'chef/log'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
class Router
|
5
|
+
def initialize(routes)
|
6
|
+
@routes = routes.map do |route, endpoint|
|
7
|
+
pattern = Regexp.new("^#{route.gsub('*', '[^/]*')}$")
|
8
|
+
[ pattern, endpoint ]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :routes
|
13
|
+
attr_accessor :not_found
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
Chef::Log.debug "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}#{env['QUERY_STRING'] != '' ? "?" + env['QUERY_STRING'] : ''}"
|
17
|
+
clean_path = "/" + env['PATH_INFO'].split('/').select { |part| part != "" }.join("/")
|
18
|
+
routes.each do |route, endpoint|
|
19
|
+
if route.match(clean_path)
|
20
|
+
return endpoint.call(env)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
not_found.call(env)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
#
|
2
|
+
# Author:: John Keiser (<jkeiser@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2012 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'rubygems'
|
20
|
+
require 'thin'
|
21
|
+
require 'openssl'
|
22
|
+
require 'chef_zero'
|
23
|
+
require 'chef_zero/router'
|
24
|
+
require 'timeout'
|
25
|
+
require 'chef_zero/cookbook_data'
|
26
|
+
|
27
|
+
require 'chef_zero/endpoints/authenticate_user_endpoint'
|
28
|
+
require 'chef_zero/endpoints/actors_endpoint'
|
29
|
+
require 'chef_zero/endpoints/actor_endpoint'
|
30
|
+
require 'chef_zero/endpoints/cookbooks_endpoint'
|
31
|
+
require 'chef_zero/endpoints/cookbook_endpoint'
|
32
|
+
require 'chef_zero/endpoints/cookbook_version_endpoint'
|
33
|
+
require 'chef_zero/endpoints/data_bags_endpoint'
|
34
|
+
require 'chef_zero/endpoints/data_bag_endpoint'
|
35
|
+
require 'chef_zero/endpoints/data_bag_item_endpoint'
|
36
|
+
require 'chef_zero/endpoints/rest_list_endpoint'
|
37
|
+
require 'chef_zero/endpoints/environment_endpoint'
|
38
|
+
require 'chef_zero/endpoints/environment_cookbooks_endpoint'
|
39
|
+
require 'chef_zero/endpoints/environment_cookbook_endpoint'
|
40
|
+
require 'chef_zero/endpoints/environment_cookbook_versions_endpoint'
|
41
|
+
require 'chef_zero/endpoints/environment_nodes_endpoint'
|
42
|
+
require 'chef_zero/endpoints/environment_recipes_endpoint'
|
43
|
+
require 'chef_zero/endpoints/environment_role_endpoint'
|
44
|
+
require 'chef_zero/endpoints/node_endpoint'
|
45
|
+
require 'chef_zero/endpoints/principal_endpoint'
|
46
|
+
require 'chef_zero/endpoints/role_endpoint'
|
47
|
+
require 'chef_zero/endpoints/role_environments_endpoint'
|
48
|
+
require 'chef_zero/endpoints/sandboxes_endpoint'
|
49
|
+
require 'chef_zero/endpoints/sandbox_endpoint'
|
50
|
+
require 'chef_zero/endpoints/searches_endpoint'
|
51
|
+
require 'chef_zero/endpoints/search_endpoint'
|
52
|
+
require 'chef_zero/endpoints/file_store_file_endpoint'
|
53
|
+
require 'chef_zero/endpoints/not_found_endpoint'
|
54
|
+
|
55
|
+
module ChefZero
|
56
|
+
class Server
|
57
|
+
def initialize(options = {})
|
58
|
+
@options = options
|
59
|
+
options[:host] ||= '127.0.0.1'
|
60
|
+
options[:port] ||= 80
|
61
|
+
options[:generate_real_keys] = true if !options.has_key?(:generate_real_keys)
|
62
|
+
@server = Thin::Server.new(options[:host], options[:port], make_app)
|
63
|
+
@generate_real_keys = options[:generate_real_keys]
|
64
|
+
@data = {
|
65
|
+
'clients' => {
|
66
|
+
'chef-validator' => '{ "validator": true }',
|
67
|
+
'chef-webui' => '{ "admin": true }'
|
68
|
+
},
|
69
|
+
'cookbooks' => {},
|
70
|
+
'data' => {},
|
71
|
+
'environments' => {
|
72
|
+
'_default' => '{ "description": "The default Chef environment" }'
|
73
|
+
},
|
74
|
+
'file_store' => {},
|
75
|
+
'nodes' => {},
|
76
|
+
'roles' => {},
|
77
|
+
'sandboxes' => {},
|
78
|
+
'users' => {
|
79
|
+
'admin' => '{ "admin": true }'
|
80
|
+
}
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
attr_reader :server
|
85
|
+
attr_reader :data
|
86
|
+
attr_reader :options
|
87
|
+
attr_reader :generate_real_keys
|
88
|
+
|
89
|
+
include ChefZero::Endpoints
|
90
|
+
|
91
|
+
def url
|
92
|
+
"http://#{options[:host]}:#{options[:port]}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def start
|
96
|
+
server.start
|
97
|
+
end
|
98
|
+
|
99
|
+
def start_background(timeout = 5)
|
100
|
+
@thread = Thread.new do
|
101
|
+
begin
|
102
|
+
server.start
|
103
|
+
rescue
|
104
|
+
@server_error = $!
|
105
|
+
Chef::Log.error("#{$!.message}\n#{$!.backtrace.join("\n")}")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
Timeout::timeout(timeout) do
|
109
|
+
until server.running? || @server_error
|
110
|
+
sleep(0.01)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def running?
|
116
|
+
server.running?
|
117
|
+
end
|
118
|
+
|
119
|
+
def stop(timeout = 5)
|
120
|
+
Timeout::timeout(timeout) do
|
121
|
+
server.stop
|
122
|
+
until !server.running? || !server_error
|
123
|
+
sleep(0.01)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
if @thread
|
127
|
+
@thread.kill
|
128
|
+
@thread = nil
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def gen_key_pair
|
133
|
+
if generate_real_keys
|
134
|
+
private_key = OpenSSL::PKey::RSA.new(2048)
|
135
|
+
public_key = private_key.public_key.to_s
|
136
|
+
public_key.sub!(/^-----BEGIN RSA PUBLIC KEY-----/, '-----BEGIN PUBLIC KEY-----')
|
137
|
+
public_key.sub!(/-----END RSA PUBLIC KEY-----(\s+)$/, '-----END PUBLIC KEY-----\1')
|
138
|
+
[private_key.to_s, public_key]
|
139
|
+
else
|
140
|
+
[PRIVATE_KEY, PUBLIC_KEY]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Load data in a nice, friendly form:
|
145
|
+
# {
|
146
|
+
# 'roles' => {
|
147
|
+
# 'desert' => '{ "description": "Hot and dry"' },
|
148
|
+
# 'rainforest' => { "description" => 'Wet and humid' }
|
149
|
+
# },
|
150
|
+
# 'cookbooks' => {
|
151
|
+
# 'apache2-1.0.1' => {
|
152
|
+
# 'templates' => { 'default' => { 'blah.txt' => 'hi' }}
|
153
|
+
# 'recipes' => { 'default.rb' => 'template "blah.txt"' }
|
154
|
+
# 'metadata.rb' => 'depends "mysql"'
|
155
|
+
# },
|
156
|
+
# 'apache2-1.2.0' => {
|
157
|
+
# 'templates' => { 'default' => { 'blah.txt' => 'lo' }}
|
158
|
+
# 'recipes' => { 'default.rb' => 'template "blah.txt"' }
|
159
|
+
# 'metadata.rb' => 'depends "mysql"'
|
160
|
+
# },
|
161
|
+
# 'mysql' => {
|
162
|
+
# 'recipes' => { 'default.rb' => 'file { contents "hi" }' },
|
163
|
+
# 'metadata.rb' => 'version "1.0.0"'
|
164
|
+
# }
|
165
|
+
# }
|
166
|
+
# }
|
167
|
+
def load_data(contents)
|
168
|
+
%w(clients environments nodes roles users).each do |data_type|
|
169
|
+
if contents[data_type]
|
170
|
+
dejsonize_children!(contents[data_type])
|
171
|
+
data[data_type].merge!(contents[data_type])
|
172
|
+
end
|
173
|
+
end
|
174
|
+
if contents['data']
|
175
|
+
contents['data'].values.each do |data_bag|
|
176
|
+
dejsonize_children!(data_bag)
|
177
|
+
end
|
178
|
+
data['data'].merge!(contents['data'])
|
179
|
+
end
|
180
|
+
if contents['cookbooks']
|
181
|
+
contents['cookbooks'].each_pair do |name_version, cookbook|
|
182
|
+
if name_version =~ /(.+)-(\d+\.\d+\.\d+)$/
|
183
|
+
cookbook_data = CookbookData.to_hash(cookbook, $1, $2)
|
184
|
+
else
|
185
|
+
cookbook_data = CookbookData.to_hash(cookbook, name_version)
|
186
|
+
end
|
187
|
+
raise "No version specified" if !cookbook_data[:version]
|
188
|
+
data['cookbooks'][cookbook_data[:cookbook_name]] = {} if !data['cookbooks'][cookbook_data[:cookbook_name]]
|
189
|
+
data['cookbooks'][cookbook_data[:cookbook_name]][cookbook_data[:version]] = JSON.pretty_generate(cookbook_data)
|
190
|
+
cookbook_data.values.each do |files|
|
191
|
+
next unless files.is_a? Array
|
192
|
+
files.each do |file|
|
193
|
+
data['file_store'][file[:checksum]] = get_file(cookbook, file[:path])
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
def make_app
|
203
|
+
router = Router.new([
|
204
|
+
[ '/authenticate_user', AuthenticateUserEndpoint.new(self) ],
|
205
|
+
[ '/clients', ActorsEndpoint.new(self) ],
|
206
|
+
[ '/clients/*', ActorEndpoint.new(self) ],
|
207
|
+
[ '/cookbooks', CookbooksEndpoint.new(self) ],
|
208
|
+
[ '/cookbooks/*', CookbookEndpoint.new(self) ],
|
209
|
+
[ '/cookbooks/*/*', CookbookVersionEndpoint.new(self) ],
|
210
|
+
[ '/data', DataBagsEndpoint.new(self) ],
|
211
|
+
[ '/data/*', DataBagEndpoint.new(self) ],
|
212
|
+
[ '/data/*/*', DataBagItemEndpoint.new(self) ],
|
213
|
+
[ '/environments', RestListEndpoint.new(self) ],
|
214
|
+
[ '/environments/*', EnvironmentEndpoint.new(self) ],
|
215
|
+
[ '/environments/*/cookbooks', EnvironmentCookbooksEndpoint.new(self) ],
|
216
|
+
[ '/environments/*/cookbooks/*', EnvironmentCookbookEndpoint.new(self) ],
|
217
|
+
[ '/environments/*/cookbook_versions', EnvironmentCookbookVersionsEndpoint.new(self) ],
|
218
|
+
[ '/environments/*/nodes', EnvironmentNodesEndpoint.new(self) ],
|
219
|
+
[ '/environments/*/recipes', EnvironmentRecipesEndpoint.new(self) ],
|
220
|
+
[ '/environments/*/roles/*', EnvironmentRoleEndpoint.new(self) ],
|
221
|
+
[ '/nodes', RestListEndpoint.new(self) ],
|
222
|
+
[ '/nodes/*', NodeEndpoint.new(self) ],
|
223
|
+
[ '/principals/*', PrincipalEndpoint.new(self) ],
|
224
|
+
[ '/roles', RestListEndpoint.new(self) ],
|
225
|
+
[ '/roles/*', RoleEndpoint.new(self) ],
|
226
|
+
[ '/roles/*/environments', RoleEnvironmentsEndpoint.new(self) ],
|
227
|
+
[ '/roles/*/environments/*', EnvironmentRoleEndpoint.new(self) ],
|
228
|
+
[ '/sandboxes', SandboxesEndpoint.new(self) ],
|
229
|
+
[ '/sandboxes/*', SandboxEndpoint.new(self) ],
|
230
|
+
[ '/search', SearchesEndpoint.new(self) ],
|
231
|
+
[ '/search/*', SearchEndpoint.new(self) ],
|
232
|
+
[ '/users', ActorsEndpoint.new(self) ],
|
233
|
+
[ '/users/*', ActorEndpoint.new(self) ],
|
234
|
+
|
235
|
+
[ '/file_store/*', FileStoreFileEndpoint.new(self) ],
|
236
|
+
])
|
237
|
+
router.not_found = NotFoundEndpoint.new
|
238
|
+
router
|
239
|
+
end
|
240
|
+
|
241
|
+
def dejsonize_children!(hash)
|
242
|
+
hash.each_pair do |key, value|
|
243
|
+
hash[key] = JSON.pretty_generate(value) if value.is_a?(Hash)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def get_file(directory, path)
|
248
|
+
value = directory
|
249
|
+
path.split('/').each do |part|
|
250
|
+
value = value[part]
|
251
|
+
end
|
252
|
+
value
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ChefZero
|
2
|
+
module Solr
|
3
|
+
module Query
|
4
|
+
class BinaryOperator
|
5
|
+
def initialize(left, operator, right)
|
6
|
+
@left = left
|
7
|
+
@operator = operator
|
8
|
+
@right = right
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"(#{left} #{operator} #{right})"
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :left
|
16
|
+
attr_reader :operator
|
17
|
+
attr_reader :right
|
18
|
+
|
19
|
+
def matches_doc?(doc)
|
20
|
+
case @operator
|
21
|
+
when 'AND'
|
22
|
+
left.matches_doc?(doc) && right.matches_doc?(doc)
|
23
|
+
when 'OR'
|
24
|
+
left.matches_doc?(doc) || right.matches_doc?(doc)
|
25
|
+
when '^'
|
26
|
+
left.matches_doc?(doc)
|
27
|
+
when ':'
|
28
|
+
if left.respond_to?(:literal_string) && left.literal_string
|
29
|
+
value = doc[left.literal_string]
|
30
|
+
right.matches_values?([value])
|
31
|
+
else
|
32
|
+
values = doc.matching_values { |key| left.matches_values?([key]) }
|
33
|
+
right.matches_values?(values)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def matches_values?(values)
|
39
|
+
case @operator
|
40
|
+
when 'AND'
|
41
|
+
left.matches_values?(values) && right.matches_values?(values)
|
42
|
+
when 'OR'
|
43
|
+
left.matches_values?(values) || right.matches_values?(values)
|
44
|
+
when '^'
|
45
|
+
left.matches_values?(values)
|
46
|
+
when ':'
|
47
|
+
raise ": does not work inside a : or term"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'chef_zero/solr/query/regexpable_query'
|
2
|
+
|
3
|
+
module ChefZero
|
4
|
+
module Solr
|
5
|
+
module Query
|
6
|
+
class Phrase < RegexpableQuery
|
7
|
+
def initialize(terms)
|
8
|
+
# Phrase is terms separated by whitespace
|
9
|
+
if terms.size == 0 && terms[0].literal_string
|
10
|
+
literal_string = terms[0].literal_string
|
11
|
+
else
|
12
|
+
literal_string = nil
|
13
|
+
end
|
14
|
+
super(terms.map { |term| term.regexp_string }.join("#{NON_WORD_CHARACTER}+"), literal_string)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"Phrase(\"#{@regexp_string}\")"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ChefZero
|
2
|
+
module Solr
|
3
|
+
module Query
|
4
|
+
class RangeQuery
|
5
|
+
def initialize(from, to, from_inclusive, to_inclusive)
|
6
|
+
@from = from
|
7
|
+
@to = to
|
8
|
+
@from_inclusive = from_inclusive
|
9
|
+
@to_inclusive = to_inclusive
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"#{@from_inclusive ? '[' : '{'}#{@from} TO #{@to}#{@to_inclusive ? '[' : '{'}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches?(key, value)
|
17
|
+
case @from <=> value
|
18
|
+
when -1
|
19
|
+
return false
|
20
|
+
when 0
|
21
|
+
return false if !@from_inclusive
|
22
|
+
end
|
23
|
+
case @to <=> value
|
24
|
+
when 1
|
25
|
+
return false
|
26
|
+
when 0
|
27
|
+
return false if !@to_inclusive
|
28
|
+
end
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|