hook-client 0.2.0

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: c369258ba544d32bf8658fec16b773009810d138
4
+ data.tar.gz: cf1eda4213eda65e81875dd4ef2cb219b6160ee7
5
+ SHA512:
6
+ metadata.gz: f069f6984f0d0f27cac78b0dfaa723ae9964786f048f1904a5b989121df2e4e6b9b31ad87f61846e7231b17ff9a4423dcef71a8b4edc724bb43f37b577208e2b
7
+ data.tar.gz: c2457a562ca1575585b8dc47baaaab14171722d67b1084d46708359b5423ed3e260deb31c40a4dd32322bcf15cc58be6379c07c21d3d54440cf40a1c98ed8ad2
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Doubleleft
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,64 @@
1
+ hook-ruby client ![Build status](https://travis-ci.org/doubleleft/hook-ruby.svg?branch=master)
2
+ ===
3
+
4
+ ruby client for [hook](github.com/doubleleft/hook/).
5
+
6
+ - [API Reference](http://doubleleft.github.io/hook-ruby).
7
+ - [RubyGem](http://rubygems.org/gems/hook-client)
8
+
9
+ Getting started:
10
+ ---
11
+
12
+ ```ruby
13
+ # Gemfile
14
+ gem 'hook-client'
15
+ ```
16
+
17
+ Basic usage:
18
+
19
+ ```ruby
20
+ require 'hook-client'
21
+ client = Hook::Client(:app_id => 1, :key => "something", :endpoint => "https://dl-api.heroku.com")
22
+ client.collection(:posts).create(:title => "Getting Started", :description => "Getting started with dl-api-ruby.")
23
+ puts client.collection(:posts).where(:title => "Getting Started").count
24
+ ```
25
+
26
+ For more examples, please see [our tests](spec).
27
+
28
+ Using it with Rails
29
+ ---
30
+
31
+ Set-up with your credentials:
32
+
33
+ ```ruby
34
+ Hook::Client.configure(
35
+ :app_id => 1,
36
+ :key => "1f143fde82d14643099ae45e6c98c8e1",
37
+ :endpoint => "https://dl-api.heroku.com"
38
+ )
39
+ ```
40
+
41
+ Define your models:
42
+
43
+ ```ruby
44
+ class Post
45
+ include Hook::Model
46
+
47
+ field :title
48
+ field :description
49
+
50
+ validates_presence_of :title
51
+ end
52
+ ```
53
+
54
+ Hook::Model's uses almost the same syntax as ActiveRecord, which you're already
55
+ familiar with.
56
+
57
+ You will be able to use any
58
+ [ActiveModel](https://github.com/rails/rails/tree/master/activemodel) goodies,
59
+ such as validation, serialization and dirty methods.
60
+
61
+ License
62
+ ---
63
+
64
+ MIT
@@ -0,0 +1,11 @@
1
+ module Hook
2
+ autoload :VERSION, 'hook-client/version'
3
+ autoload :Client, 'hook-client/client'
4
+
5
+ autoload :Keys, 'hook-client/keys'
6
+ autoload :Collection, 'hook-client/collection'
7
+ autoload :Channel, 'hook-client/channel'
8
+ autoload :Model, 'hook-client/model'
9
+
10
+ autoload :Extensions, 'hook-client/extensions'
11
+ end
@@ -0,0 +1,4 @@
1
+ module Hook
2
+ class Auth
3
+ end
4
+ end
@@ -0,0 +1,13 @@
1
+ module Hook
2
+ module Channel
3
+ autoload :SSE, 'hook-client/channel/sse'
4
+ autoload :WEBSOCKET, 'hook-client/channel/websocket'
5
+
6
+ def self.create(client, name, options = {})
7
+ channel_klass = (options.delete(:transport) || 'sse').upcase
8
+ collection = Collection.new(name, :channel => true)
9
+ self.const_get(channel_klass).new(client, collection, options)
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Hook
2
+ class SSE
3
+
4
+ def initialize
5
+ end
6
+
7
+ end
8
+ end
9
+
@@ -0,0 +1,4 @@
1
+ module Hook
2
+ class WEBSOCKET
3
+ end
4
+ end
@@ -0,0 +1,93 @@
1
+ require 'http'
2
+ require 'uri'
3
+
4
+ module Hook
5
+ class Client
6
+ class << self
7
+ attr_reader :instance
8
+ def configure(options = {})
9
+ @instance = self.new(options)
10
+ end
11
+ end
12
+
13
+ def initialize options = {}
14
+ @app_id = options.delete(:app_id)
15
+ @key = options.delete(:key)
16
+ @endpoint = options.delete(:endpoint) || options.delete(:url)
17
+ @endpoint = "#{@endpoint}/" unless @endpoint.end_with? "/"
18
+
19
+ @logger = options.delete(:logger)
20
+ end
21
+
22
+ def keys
23
+ Keys.new
24
+ end
25
+
26
+ def auth
27
+ Auth.new
28
+ end
29
+
30
+ def files
31
+ Files.new
32
+ end
33
+
34
+ def collection(name)
35
+ Collection.new(:name => name, :client => self)
36
+ end
37
+
38
+ def logger=(logger)
39
+ @logger = logger
40
+ end
41
+
42
+ def channel(name, options = {})
43
+ throw NotImplementedError.new("channels not implemented")
44
+ Channel.create(self, name, options)
45
+ end
46
+
47
+ def post segments, data
48
+ request :post, segments, data
49
+ end
50
+
51
+ def get segments, data = {}
52
+ request :get, segments, data
53
+ end
54
+
55
+ def put segments, data
56
+ request :put, segments, data
57
+ end
58
+
59
+ def remove segments, data = {}
60
+ request :delete, segments, data
61
+ end
62
+ alias_method :delete, :remove
63
+
64
+ protected
65
+ def request method, segments, data = {}
66
+ response = nil, headers = {
67
+ :accept => 'application/json',
68
+ 'X-App-Id' => @app_id,
69
+ 'X-App-Key' => @key,
70
+ 'User-Agent' => "hook-ruby"
71
+ }
72
+
73
+ if method == :get
74
+ segments = "#{segments}?#{URI::escape(data.to_json)}"
75
+ data = nil
76
+ end
77
+
78
+ if @logger
79
+ start_time = Time.now
80
+ end
81
+
82
+ response = HTTP.with(headers).send(method, "#{@endpoint}#{segments}", :json => data).to_s
83
+
84
+ if @logger
85
+ @logger.info "#{self.class.name} - #{(Time.now - start_time).round(3)}s - #{method.upcase} #{@endpoint}#{segments}"
86
+ end
87
+
88
+ # If JSON.parse don't suceed, return response as integer
89
+ JSON.parse(response) rescue JSON.parse("{\"value\":#{response}}")['value'] rescue response.to_i
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,285 @@
1
+ module Hook
2
+ class Collection
3
+
4
+ def initialize options = {}
5
+ @name = options.delete(:name)
6
+ @client = options.delete(:client) || Hook::Client.instance
7
+
8
+ segment = options.delete(:channel) ? "channel" : "collection"
9
+ @segments = "#{segment}/#{@name}"
10
+ reset!
11
+ end
12
+
13
+ # Find item by _id.
14
+ #
15
+ # @param _id [String]
16
+ # @return [Hash]
17
+ def find _id
18
+ if _id.kind_of?(Array)
19
+ self.where(:_id.in => _id).all
20
+ else
21
+ @client.get "#{@segments}/#{_id}"
22
+ end
23
+ end
24
+
25
+ # Return only the first result from the database
26
+ def first
27
+ @options[:first] = 1
28
+ self.query!
29
+ end
30
+
31
+ # Create an item into the collection
32
+ #
33
+ # @param data [Hash, Array] item or array of items
34
+ # @return [Hash, Array]
35
+ def create data
36
+ if data.kind_of?(Array)
37
+ # TODO: server should accept multiple items to create,
38
+ # instead of making multiple requests.
39
+ data.map {|item| self.create(item) }
40
+ else
41
+ @client.post @segments, data
42
+ end
43
+ end
44
+
45
+ # Retrieve the first item with the given params in the database,
46
+ # if not found this will create one.
47
+ #
48
+ # @param data [Hash] query and data to store
49
+ # @return [Hash]
50
+ def first_or_create data
51
+ @options[:first] = 1
52
+ @options[:data] = data
53
+ @client.post(@segments, self.build_query)
54
+ end
55
+
56
+ # Remove items by id, or by query
57
+ #
58
+ # @param _id [String, nil] _id
59
+ # @return [Hash] status of the operation (ex: {success: true, affected_rows: 7})
60
+ def remove _id = nil
61
+ path = @segments
62
+ path = "#{path}/#{_id}" if _id
63
+ @client.remove(path, build_query)
64
+ end
65
+
66
+ # Remove items by query. This is the same as calling `remove` without `_id` param.
67
+ # @see remove
68
+ def delete_all
69
+ self.remove(nil)
70
+ end
71
+
72
+ # Add where clause to the current query.
73
+ #
74
+ # Supported modifiers on fields: .gt, .gte, .lt, .lte, .ne, .in, .not_in, .nin, .like, .between, .not_between
75
+ #
76
+ # @param fields [Hash] fields and values to filter
77
+ # @param [String] operation (and, or)
78
+ #
79
+ # @example
80
+ # hook.collection(:movies).where({
81
+ # :name => "Hook",
82
+ # :year.gt => 1990
83
+ # })
84
+ #
85
+ # @example Using Range
86
+ # hook.collection(:movies).where({
87
+ # :name.like => "%panic%",
88
+ # :year.between => 1990..2014
89
+ # })
90
+ #
91
+ # @return [Collection] self
92
+ def where fields = {}, operation = 'and'
93
+ fields.each_pair do |k, value|
94
+ field = (k.respond_to?(:field) ? k.field : k).to_s
95
+ comparation = k.respond_to?(:comparation) ? k.comparation : '='
96
+
97
+ # Range syntatic sugar
98
+ value = [ value.first, value.last ] if value.kind_of?(Range)
99
+
100
+ @wheres << [field, comparation, value, operation]
101
+ end
102
+ self
103
+ end
104
+
105
+ # Add where clause with 'OR' operation to the current query.
106
+ # @param fields [Hash] fields and values to filter
107
+ # @return [Collection] self
108
+ def or_where fields = {}
109
+ self.where(fields, 'or')
110
+ end
111
+
112
+ # Add order clause to the query.
113
+ #
114
+ # @param fields [String] ...
115
+ # @return [Collection] self
116
+ def order fields
117
+ by_num = { 1 => 'asc', -1 => 'desc' }
118
+ ordering = []
119
+ fields.each_pair do |key, value|
120
+ ordering << [key.to_s, by_num[value] || value]
121
+ end
122
+ @ordering = ordering
123
+ self
124
+ end
125
+ alias_method :sort, :order
126
+
127
+ # Limit the number of results to retrieve.
128
+ #
129
+ # @param int [Integer]
130
+ # @return [Collection] self
131
+ def limit int
132
+ @limit = int
133
+ self
134
+ end
135
+
136
+ # @param int [Integer]
137
+ # @return [Collection] self
138
+ def offset int
139
+ @offset = int
140
+ self
141
+ end
142
+
143
+ # Run the query, and return all it's results.
144
+ # @yield You may use a block with all returned results
145
+ # @return [Array]
146
+ def all(&block)
147
+ rows = query!
148
+ yield rows if block_given?
149
+ rows
150
+ end
151
+
152
+ def query!
153
+ @client.get @segments, build_query
154
+ end
155
+
156
+ def method_missing method, *args, &block
157
+ if Enumerator.method_defined? method
158
+ Enumerator.new(self.all).send(method, args, block)
159
+ end
160
+
161
+ throw NoMethodError.new("#{self.class.name}: method '#{method}' not found")
162
+ end
163
+
164
+ # Specify the target field names to retrieve
165
+ #
166
+ # @param fields [String] ...
167
+ # @return [Collection] self
168
+ def select *fields
169
+ @options[:select] = fields
170
+ end
171
+
172
+ # Return related collection's data
173
+ #
174
+ # @param relationships [String] ...
175
+ #
176
+ # @example Retrieving a single relation
177
+ # hook.collection(:books).with(:publisher).each do |book|
178
+ # puts book[:name]
179
+ # puts book[:publisher][:name]
180
+ # end
181
+ #
182
+ # @example Retrieving multiple relations
183
+ # hook.collection(:books).with(:publisher, :author).each do |book|
184
+ # puts book[:name]
185
+ # puts book[:publisher][:name]
186
+ # puts book[:author][:name]
187
+ # end
188
+ #
189
+ # @return [Collection] self
190
+ def with *relationships
191
+ @options[:with] = relationships
192
+ self
193
+ end
194
+
195
+ # Group query results
196
+ # @param fields [String] ...
197
+ # @return [Collection] self
198
+ def group *fields
199
+ @group = fields
200
+ self
201
+ end
202
+
203
+ def count field = '*'
204
+ @options[:aggregation] = { :method => 'count', :field => field }
205
+ self.query!
206
+ end
207
+
208
+ def max field
209
+ @options[:aggregation] = { :method => :max, :field => field }
210
+ self.query!
211
+ end
212
+
213
+ def min field
214
+ @options[:aggregation] = { :method => :min, :field => field }
215
+ self.query!
216
+ end
217
+
218
+ def avg field
219
+ @options[:aggregation] = { :method => :avg, :field => field }
220
+ self.query!
221
+ end
222
+
223
+ def sum field
224
+ @options[:aggregation] = { :method => :sum, :field => field }
225
+ self.query!
226
+ end
227
+
228
+ def increment field, value = 1
229
+ @options[:operation] = { :method => 'increment', :field => field, :value => value }
230
+ self.query!
231
+ end
232
+
233
+ def decrement field, value = 1
234
+ @options[:operation] = { :method => 'decrement', :field => field, :value => value }
235
+ self.query!
236
+ end
237
+
238
+ def update _id, data
239
+ @client.post "#{@segments}/#{_id}", data
240
+ end
241
+
242
+ def update_all data
243
+ @options[:data] = data
244
+ @client.put @segments, build_query
245
+ end
246
+
247
+ def build_query
248
+ query = {}
249
+ query[:limit] = @limit if @limit
250
+ query[:offset] = @offset if @offset
251
+
252
+ query[:q] = @wheres unless @wheres.empty?
253
+ query[:s] = @ordering unless @ordering.empty?
254
+ query[:g] = @group unless @group.empty?
255
+
256
+ {
257
+ :paginate => 'p',
258
+ :first => 'f',
259
+ :aggregation => 'aggr',
260
+ :operation => 'op',
261
+ :data => 'data',
262
+ :with => 'with',
263
+ :select => 'select',
264
+ }.each_pair do |option, shortname|
265
+ query[ shortname ] = @options[ option ] if @options[ option ]
266
+ end
267
+
268
+ self.reset!
269
+ query
270
+ end
271
+
272
+ protected
273
+
274
+ def reset!
275
+ @wheres = []
276
+ @options = {}
277
+ @wheres = []
278
+ @ordering = []
279
+ @group = []
280
+ @limit = nil
281
+ @offset = nil
282
+ end
283
+
284
+ end
285
+ end
@@ -0,0 +1,12 @@
1
+ module Hook
2
+ module Extensions
3
+ autoload :Symbol, 'hook-client/extensions/symbol'
4
+
5
+ def self.eager_load!
6
+ self.constants.each {|const| self.const_get(const) }
7
+ end
8
+ end
9
+ end
10
+
11
+ # Load all the extensions
12
+ Hook::Extensions.eager_load!
@@ -0,0 +1,28 @@
1
+ module DL
2
+ module Extensions
3
+ module Symbol
4
+
5
+ {
6
+ :gt => '>',
7
+ :gte => '>=',
8
+ :lt => '<',
9
+ :lte => '<=',
10
+ :ne => '!=',
11
+ :in => 'in',
12
+ :not_in => 'not_in',
13
+ :nin => 'not_in', # alias
14
+ :like => 'like',
15
+ :between => 'between',
16
+ :not_between => 'not_between',
17
+ # :max_distance,
18
+ }.each_pair do |method, comparation|
19
+ define_method(method) do
20
+ OpenStruct.new(:field => self, :comparation => comparation)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+
28
+ ::Symbol.__send__(:include, DL::Extensions::Symbol)
@@ -0,0 +1,26 @@
1
+ module Hook
2
+ class Keys
3
+
4
+ def initialize(options={})
5
+ @client = options.delete(:client) || Hook::Client.instance
6
+ end
7
+
8
+ # Return the unserialized value
9
+ #
10
+ # @param key [String, Symbol]
11
+ # @return [Object] value
12
+ def get(key)
13
+ @client.get("key/#{key}")
14
+ end
15
+
16
+ # Store serialized value
17
+ #
18
+ # @param key [String, Symbol] key
19
+ # @param value [Object] JSON serializable object
20
+ # @return [Object] The object you just stored.
21
+ def set(key, value)
22
+ @client.post("key/#{key}", { :value => value });
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,116 @@
1
+ require 'active_model'
2
+
3
+ module Hook
4
+ #
5
+ # Provides ActiveRecord/like methods for querying Collections
6
+ #
7
+ module Model
8
+ def self.included(base)
9
+ base.class_eval do
10
+ extend ClassMethods
11
+ extend ActiveModel::Naming
12
+ extend ActiveModel::Translation
13
+ extend ActiveModel::Callbacks
14
+ include ActiveModel::Validations
15
+ include ActiveModel::Conversion
16
+ include ActiveModel::Dirty
17
+ include ActiveModel::AttributeMethods
18
+ include ActiveModel::Serializers::JSON
19
+ include ActiveModel::Serializers::Xml
20
+
21
+ field :_id
22
+ end
23
+ end
24
+
25
+ module InstanceMethods
26
+ def initialize(attrs = {})
27
+ # is Hook::Client configured?
28
+ throw RuntimeError.new("Please use Hook::Client.configure.") unless Hook::Client.instance
29
+
30
+ @collection = Hook::Client.instance.collection(self.class.collection_name)
31
+ self.attributes = {}
32
+ attrs.each_pair do |name, value|
33
+ self.send(:"#{name}=", value) if self.respond_to?(:"#{name}")
34
+ end
35
+
36
+ # reset_changes
37
+ changes_applied if self._id
38
+ end
39
+
40
+ def save
41
+ return false if !self.changed? || !self.valid?
42
+
43
+ changes_applied
44
+ if self._id.nil?
45
+ self.attributes = @collection.update(self._id, attributes)
46
+ else
47
+ self.attributes = @collection.create(attributes)
48
+ end
49
+ self
50
+ end
51
+
52
+ def attributes=(attrs)
53
+ @attributes = attrs
54
+ end
55
+
56
+ def attributes
57
+ @attributes
58
+ end
59
+
60
+ def inspect
61
+ "#<#{self.class.name}: attributes=#{attributes.inspect}>"
62
+ end
63
+
64
+ # Delegator methods
65
+ def method_missing(m, *args, &block)
66
+ res = @collection.send(m, *args, &block)
67
+
68
+ # Check for success/error responses
69
+ if res.kind_of?(Hash) && res.length == 1
70
+ return res['success'] unless res['success'].nil?
71
+ throw RuntimeError.new(res['error']) unless res['error'].nil?
72
+ end
73
+
74
+ if (res.kind_of?(Hook::Collection))
75
+ self
76
+ else
77
+ (res.kind_of?(Array)) ? res.map {|r| self.class.new(r) } : self.class.new(res)
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ module ClassMethods
84
+ def collection_name(name = nil)
85
+ if name
86
+ @collection_name = name
87
+ else
88
+ @collection_name = ActiveModel::Naming.route_key(self)
89
+ end
90
+ end
91
+
92
+ def field name, options = {}
93
+ define_attribute_method name
94
+
95
+ # Define getter
96
+ define_method name do
97
+ # self.instance_variable_get("@#{name}")
98
+ attributes[name]
99
+ end
100
+
101
+ # Define setter
102
+ define_method "#{name}=" do |value|
103
+ self.send(:"#{name}_will_change!")
104
+ attributes[name] = value
105
+ end
106
+ end
107
+
108
+ def method_missing(m, *args, &block)
109
+ instance = self.new
110
+ instance.send(m, *args, &block)
111
+ end
112
+ end
113
+
114
+ include InstanceMethods
115
+ end
116
+ end
@@ -0,0 +1,3 @@
1
+ module Hook
2
+ VERSION = '0.2.0'
3
+ end
@@ -0,0 +1,11 @@
1
+ describe Hook::Client do
2
+
3
+ subject do
4
+ Hook::Client.instance
5
+ end
6
+
7
+ it "" do
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,68 @@
1
+ describe Hook::Collection do
2
+
3
+ subject do
4
+ Hook::Client.instance
5
+ end
6
+
7
+ it "should create new items to collection" do
8
+ user = subject.collection(:users).create(:name => "Endel", :newsletter => true)
9
+ expect(user['name']).to eq("Endel")
10
+ expect(user['newsletter']).to eq(true)
11
+ end
12
+
13
+ it "should query for items" do
14
+ rows = subject.collection(:users).where(:name => "Endel").all
15
+ expect(rows.length).to be > 0
16
+ end
17
+
18
+ it "should delete all items" do
19
+ # create a dummy item
20
+ subject.collection(:users).create(:name => "Endel", :newsletter => true)
21
+ # remove every items
22
+ subject.collection(:users).delete_all
23
+ rows = subject.collection(:users).where(:name => "Endel").all
24
+ expect(rows.length).to be == 0
25
+ end
26
+
27
+ it "general methods" do
28
+ subject.collection(:highscores).delete_all
29
+
30
+ one = created = subject.collection(:highscores).create(:player => "One", :score => 50)
31
+ expect(created['player']).to eq("One")
32
+ expect(created['score']).to eq(50)
33
+
34
+ two = subject.collection(:highscores).create(:player => "Two", :score => 100)
35
+ three = subject.collection(:highscores).create(:player => "Three", :score => 25)
36
+ four = subject.collection(:highscores).create(:player => "Four", :score => 10)
37
+ five = subject.collection(:highscores).create(:player => "Five", :score => 150)
38
+ six = subject.collection(:highscores).create(:player => "Six", :score => 125)
39
+
40
+ # find multiple by id
41
+ rows = subject.collection(:highscores).find([two['_id'], three['_id']])
42
+ expect(rows.length).to be == 2
43
+ expect(rows[0]['player']).to be == 'Two'
44
+ expect(rows[1]['player']).to be == 'Three'
45
+
46
+ # .all
47
+ all = subject.collection(:highscores).all
48
+ expect(all.length).to be >= 6
49
+
50
+ # .first
51
+ five = subject.collection(:highscores).where(:player => "Five").first
52
+ expect(five['player']).to eq("Five")
53
+
54
+ # .count
55
+ count = subject.collection(:highscores).where(:score.lt => 25).count
56
+ expect(count).to be == 1
57
+
58
+ count = subject.collection(:highscores).where(:score.lte => 25).count
59
+ expect(count).to be == 2
60
+
61
+ count = subject.collection(:highscores).where(:score.between => 11..149).count
62
+ expect(count).to be == 4
63
+
64
+ count = subject.collection(:highscores).where(:player.ne => "One").count
65
+ expect(count).to be == 5
66
+ end
67
+
68
+ end
@@ -0,0 +1,47 @@
1
+ describe Hook::Extensions do
2
+ it "should extend core Ruby classes" do
3
+ gt = :field_gt.gt
4
+ expect(gt.comparation).to eq('>')
5
+ expect(gt.field).to eq(:field_gt)
6
+
7
+ gte = :field_gte.gte
8
+ expect(gte.comparation).to eq('>=')
9
+ expect(gte.field).to eq(:field_gte)
10
+
11
+ lt = :field_lt.lt
12
+ expect(lt.comparation).to eq('<')
13
+ expect(lt.field).to eq(:field_lt)
14
+
15
+ lte = :field_lte.lte
16
+ expect(lte.comparation).to eq('<=')
17
+ expect(lte.field).to eq(:field_lte)
18
+
19
+ ne = :field_ne.ne
20
+ expect(ne.comparation).to eq('!=')
21
+ expect(ne.field).to eq(:field_ne)
22
+
23
+ _in = :field_in.in
24
+ expect(_in.comparation).to eq('in')
25
+ expect(_in.field).to eq(:field_in)
26
+
27
+ not_in = :field_not_in.not_in
28
+ expect(not_in.comparation).to eq('not_in')
29
+ expect(not_in.field).to eq(:field_not_in)
30
+
31
+ nin = :field_nin.nin
32
+ expect(nin.comparation).to eq('not_in')
33
+ expect(nin.field).to eq(:field_nin)
34
+
35
+ like = :field_like.like
36
+ expect(like.comparation).to eq('like')
37
+ expect(like.field).to eq(:field_like)
38
+
39
+ between = :field_between.between
40
+ expect(between.comparation).to eq('between')
41
+ expect(between.field).to eq(:field_between)
42
+
43
+ not_between = :field_not_between.not_between
44
+ expect(not_between.comparation).to eq('not_between')
45
+ expect(not_between.field).to eq(:field_not_between)
46
+ end
47
+ end
@@ -0,0 +1,20 @@
1
+ describe Hook::Keys do
2
+
3
+ subject do
4
+ Hook::Client.instance
5
+ end
6
+
7
+ it "should get and set keys" do
8
+ expect(subject.keys.get(:inexistent)).to be_nil
9
+
10
+ subject.keys.set :numeric, 12345
11
+ expect(subject.keys.get(:numeric)).to be == 12345
12
+
13
+ subject.keys.set :float, 19.99
14
+ expect(subject.keys.get(:float)).to be == 19.99
15
+
16
+ subject.keys.set :string, 'hello there!'
17
+ expect(subject.keys.get(:string)).to be == 'hello there!'
18
+ end
19
+ end
20
+
@@ -0,0 +1,55 @@
1
+ class MyCollection
2
+ include Hook::Model
3
+ field :name
4
+ field :score
5
+ validates_presence_of :score
6
+ end
7
+
8
+ class CustomHighscore
9
+ include Hook::Model
10
+ field :name
11
+ field :score
12
+ end
13
+
14
+ describe Hook::Model do
15
+ it "should respond to activemodel dirty methods" do
16
+ instance = MyCollection.new
17
+ expect(instance.name).to be_nil
18
+ expect(instance.name_changed?).to be == false
19
+
20
+ instance.name = 'Endel'
21
+ expect(instance.name).to be == 'Endel'
22
+ expect(instance.name_changed?).to be == true
23
+
24
+ expect(instance.save).to be == false, "shouldn't save when model has validation errors"
25
+ expect(instance.errors.messages.length).to be == 1
26
+ end
27
+
28
+ it "should set default attributes" do
29
+ instance = MyCollection.new(:name => "Endel", :score => 100)
30
+ expect(instance.name).to be == "Endel"
31
+ expect(instance.score).to be == 100
32
+ expect(instance.changed?).to be == true
33
+ instance.save
34
+ expect(instance.changed?).to be == false
35
+ end
36
+
37
+ it "general methods" do
38
+ instance = MyCollection.create(:name => "Endel", :score => 100)
39
+ expect(instance.name).to be == "Endel"
40
+ expect(instance.score).to be == 100
41
+ expect(instance.changed?).to be == false
42
+ expect(instance.save).to be == false
43
+ end
44
+
45
+ it "should create multiple" do
46
+ rows = CustomHighscore.create([
47
+ {:name => "Somebody", :score => 50},
48
+ {:name => "Anybody", :score => 10},
49
+ ])
50
+ expect(rows.length).to be == 2
51
+ expect(rows[0].name).to be == "Somebody"
52
+ expect(rows[1].name).to be == "Anybody"
53
+ expect(CustomHighscore.delete_all).to be == 2
54
+ end
55
+ end
@@ -0,0 +1,15 @@
1
+ $: << File.expand_path('../lib')
2
+ require 'hook-client'
3
+ require 'logger'
4
+ require 'json'
5
+
6
+ logger = Logger.new(STDOUT)
7
+ app = JSON.parse(File.open('spec/app.json').read)
8
+ app_key = app['keys'].select{|k| k['type'] == 'server' }.first
9
+
10
+ Hook::Client.configure(
11
+ :app_id => app_key['app_id'],
12
+ :key => app_key['key'],
13
+ :endpoint => 'http://hook.dev/public/index.php/'
14
+ # :logger => logger
15
+ )
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hook-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Endel Dreyer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: addressable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '2.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '2.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: http
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 0.6.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.6.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.0.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: 2.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: activemodel
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: 3.0.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: 3.0.0
83
+ description: Hook Client for Ruby
84
+ email: endel@doubleleft.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - lib/hook-client/auth.rb
90
+ - lib/hook-client/channel/sse.rb
91
+ - lib/hook-client/channel/websocket.rb
92
+ - lib/hook-client/channel.rb
93
+ - lib/hook-client/client.rb
94
+ - lib/hook-client/collection.rb
95
+ - lib/hook-client/extensions/symbol.rb
96
+ - lib/hook-client/extensions.rb
97
+ - lib/hook-client/keys.rb
98
+ - lib/hook-client/model.rb
99
+ - lib/hook-client/version.rb
100
+ - lib/hook-client.rb
101
+ - spec/client_spec.rb
102
+ - spec/collection_spec.rb
103
+ - spec/extensions_spec.rb
104
+ - spec/keys_spec.rb
105
+ - spec/model_spec.rb
106
+ - spec/spec_helper.rb
107
+ - README.md
108
+ - LICENSE
109
+ homepage: http://github.com/doubleleft/hook-ruby
110
+ licenses: []
111
+ metadata: {}
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 2.0.6
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: Hook Ruby Client
132
+ test_files: []
133
+ has_rdoc: