restpack_activity_service 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +14 -0
  7. data/Rakefile +2 -0
  8. data/config/database.yml +8 -0
  9. data/db/migrate/20130630145408_create_activity_table.rb +32 -0
  10. data/lib/restpack_activity_service.rb +13 -0
  11. data/lib/restpack_activity_service/api/activity_api.rb +40 -0
  12. data/lib/restpack_activity_service/configuration.rb +24 -0
  13. data/lib/restpack_activity_service/models/activity.rb +64 -0
  14. data/lib/restpack_activity_service/models/base.rb +9 -0
  15. data/lib/restpack_activity_service/serializers/activity_serializer.rb +11 -0
  16. data/lib/restpack_activity_service/services.rb +9 -0
  17. data/lib/restpack_activity_service/services/activity/create.rb +40 -0
  18. data/lib/restpack_activity_service/services/activity/destroy.rb +22 -0
  19. data/lib/restpack_activity_service/services/activity/get.rb +21 -0
  20. data/lib/restpack_activity_service/services/activity/list.rb +28 -0
  21. data/lib/restpack_activity_service/services/activity/update.rb +32 -0
  22. data/lib/restpack_activity_service/tasks.rb +7 -0
  23. data/lib/restpack_activity_service/tasks/db.rake +39 -0
  24. data/lib/restpack_activity_service/version.rb +7 -0
  25. data/restpack_activity_service.gemspec +32 -0
  26. data/spec/factories/activity_factory.rb +19 -0
  27. data/spec/services/create_spec.rb +108 -0
  28. data/spec/services/destroy_spec.rb +38 -0
  29. data/spec/services/get_spec.rb +37 -0
  30. data/spec/services/list_spec.rb +146 -0
  31. data/spec/spec_helper.rb +28 -0
  32. data/spec/support/mutations_matchers.rb +32 -0
  33. metadata +222 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 48922b0729f2c02601c4a9927369e13bb0705368
4
+ data.tar.gz: b671dc3857b4c16d8cc10176c2a3566fb1bcfbb4
5
+ SHA512:
6
+ metadata.gz: ba265ce0e17626dafad173d6827130c1932aa50c35c8023ee09d2b754b8b34bac061756fcdd28cdadbd7b01682a4df47ef90d2314c27ecc2c9eccab19575fd50
7
+ data.tar.gz: a7d3bb6e3b86dd198085d3ea5d6165466ddcc39051f83e7cf437d9dc0359634ee14d504f8cb4e48e1aa0f3ae8db9939c29e3d8ff353d3c3d051c1b78aa24f0a1
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .DS_Store
19
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ env:
5
+ - DATABASE_URL=$(curl http://api.postgression.com)
6
+ script: bundle exec rake test
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'coveralls', require: false
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Gavin Joyce
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.
data/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # Restpack Activity Service
2
+
3
+ [![Build Status](https://travis-ci.org/RestPack/restpack_activity_service.png?branch=master)](https://travis-ci.org/RestPack/restpack_activity_service) [![Code Climate](https://codeclimate.com/github/RestPack/restpack_activity_service.png)](https://codeclimate.com/github/RestPack/restpack_activity_service) [![Dependency Status](https://gemnasium.com/RestPack/restpack_activity_service.png)](https://gemnasium.com/RestPack/restpack_activity_service) [![Gem Version](https://badge.fury.io/rb/restpack_activity_service.png)](http://badge.fury.io/rb/restpack_activity_service) [![Coverage Status](https://coveralls.io/repos/RestPack/restpack_activity_service/badge.png?branch=master)](https://coveralls.io/r/RestPack/restpack_activity_service?branch=master)
4
+
5
+
6
+ **Work In Progress**
7
+
8
+ This gem provides a service for managing lists of activities. It supports full text search, tagging, access control and paging.
9
+
10
+ ## Development Setup
11
+
12
+ 1. install postgres
13
+ 2. create a `restpack_activity_service_test` database
14
+ 3. rake
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "restpack_gem"
2
+ RestPack::Gem::Tasks.load_tasks
@@ -0,0 +1,8 @@
1
+ test:
2
+ adapter: postgresql
3
+ host: localhost
4
+ encoding: utf8
5
+ database: restpack_activity_service_test
6
+ pool: 5
7
+ username:
8
+ password:
@@ -0,0 +1,32 @@
1
+ class CreateActivityTable < ActiveRecord::Migration
2
+ def table_name
3
+ RestPack::Activity::Service.configuration.prefix_table_name(:activities)
4
+ end
5
+
6
+ def up
7
+ create_table table_name do |t|
8
+ t.integer :application_id
9
+ t.integer :user_id
10
+ t.string :title
11
+ t.text :content
12
+ t.text :tag_list, :array => true, :default => []
13
+ t.decimal :latitude, precision: 10, scale: 6, null: true
14
+ t.decimal :longitude, precision: 10, scale: 6, null: true
15
+ t.column :search_vector, 'tsvector'
16
+ t.json :data
17
+ t.timestamps
18
+ end
19
+
20
+ execute "CREATE INDEX #{table_name}_tag_list_index ON #{table_name} USING gin(tag_list)"
21
+ execute "CREATE INDEX #{table_name}_search_index ON #{table_name} USING gin(search_vector)"
22
+ execute "CREATE TRIGGER #{table_name}_vector_update BEFORE INSERT OR UPDATE
23
+ ON #{table_name} FOR EACH ROW EXECUTE PROCEDURE
24
+ tsvector_update_trigger(search_vector, 'pg_catalog.english', title, content);"
25
+ end
26
+
27
+ def down
28
+ execute "DROP INDEX #{table_name}_tag_list_index"
29
+ execute "DROP INDEX #{table_name}_search_index"
30
+ drop_table table_name
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ require "active_record"
2
+ require "restpack_service"
3
+ require "restpack_serializer"
4
+
5
+ require "restpack_activity_service/version"
6
+ require "restpack_activity_service/configuration"
7
+ require "restpack_activity_service/models/activity"
8
+
9
+ require "restpack_activity_service/serializers/activity_serializer"
10
+ require "restpack_activity_service/services"
11
+ require "restpack_activity_service/tasks"
12
+
13
+ require "restpack_activity_service/api/activity_api"
@@ -0,0 +1,40 @@
1
+ require 'sinatra'
2
+
3
+ module RestPack
4
+ class ActivityApi < Sinatra::Base
5
+ get "/.json" do
6
+ render RestPack::Services::Activity::List.run(params, application_params)
7
+ end
8
+
9
+ get "/:id.json" do
10
+ render RestPack::Services::Activity::Get.run(params, application_params)
11
+ end
12
+
13
+ post "/.json" do
14
+ render RestPack::Services::Activity::Create.run(params, application_params)
15
+ end
16
+
17
+ put "/:id.json" do
18
+ render RestPack::Services::Activity::Update.run(params, application_params)
19
+ end
20
+
21
+ delete "/:id.json" do
22
+ render RestPack::Services::Activity::Destroy.run(params, application_params)
23
+ end
24
+
25
+ private
26
+
27
+ def application_params
28
+ #TODO: application_id may come from a domain mapping
29
+ {
30
+ application_id: 1
31
+ }
32
+ end
33
+
34
+ def render(response)
35
+ status response.code
36
+ response.result[:errors] = response.errors
37
+ response.result.to_json
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ module RestPack::Activity::Service
2
+ class Configuration
3
+ attr_accessor :database_table_prefix, :application_id
4
+
5
+ def initialize
6
+ @database_table_prefix = "restpack_"
7
+ end
8
+
9
+ def prefix_table_name(name)
10
+ "#{@database_table_prefix}#{name}".to_sym
11
+ end
12
+ end
13
+
14
+ class << self
15
+ attr_accessor :configuration
16
+ end
17
+
18
+ def self.configure
19
+ self.configuration ||= Configuration.new
20
+ yield configuration if block_given?
21
+ end
22
+
23
+ self.configure
24
+ end
@@ -0,0 +1,64 @@
1
+ require_relative 'base'
2
+
3
+ module RestPack::Models
4
+ class Activity < Base
5
+ self.restpack_table_name :activities
6
+
7
+ def self.search(query)
8
+ conditions = <<-EOS
9
+ search_vector @@ plainto_tsquery('english', ?)
10
+ EOS
11
+
12
+ where(conditions, query)
13
+ end
14
+
15
+ scope :any_tags, -> (tags) { where('tag_list && ARRAY[?]', tags) }
16
+ scope :all_tags, -> (tags) { where('tag_list @> ARRAY[?]', tags) }
17
+
18
+ scope :any_tags_csv, -> (tags_csv, namespace = '') do
19
+ any_tags(parse_tags_csv(tags_csv, namespace))
20
+ end
21
+ scope :all_tags_csv, -> (tags_csv, namespace = '') do
22
+ all_tags(parse_tags_csv(tags_csv, namespace))
23
+ end
24
+
25
+ def tags=(tags_csv)
26
+ set_tags(tags_csv)
27
+ end
28
+
29
+ def tags
30
+ get_tags
31
+ end
32
+
33
+ def access=(access_csv)
34
+ set_tags(access_csv, :access)
35
+ end
36
+
37
+ def access
38
+ get_tags(:access)
39
+ end
40
+
41
+ private
42
+
43
+ def set_tags(tags_csv, namespace = '')
44
+ tags_csv = tags_csv.delete('|')
45
+ clear_tags(namespace)
46
+
47
+ self.tag_list += self.class.parse_tags_csv(tags_csv, namespace)
48
+ self.tag_list.uniq!
49
+ end
50
+
51
+ def clear_tags(namespace = '')
52
+ self.tag_list = self.tag_list.reject {|tag| tag.start_with?("#{namespace}|")}
53
+ end
54
+
55
+ def get_tags(namespace = '')
56
+ tags = self.tag_list.select {|tag| tag.start_with?("#{namespace}|")}
57
+ tags.map {|tag| tag.gsub("#{namespace}|", '') }
58
+ end
59
+
60
+ def self.parse_tags_csv(tags_csv, namespace='')
61
+ tags_csv.split(',').map {|tag| "#{namespace}|#{tag.strip}"}
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,9 @@
1
+ module RestPack::Models
2
+ class Base < ActiveRecord::Base
3
+ self.abstract_class = true
4
+
5
+ def self.restpack_table_name(name)
6
+ self.table_name = RestPack::Activity::Service.configuration.prefix_table_name(name)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module RestPack::Serializers
2
+ class ActivitySerializer
3
+ include RestPack::Serializer
4
+
5
+ self.model_class = RestPack::Models::Activity
6
+ self.key = :activities
7
+
8
+ attributes :id, :application_id, :user_id, :title, :content, :latitude, :longitude,
9
+ :tags, :access, :data, :href, :updated_at, :created_at
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module RestPack::Services
2
+
3
+ end
4
+
5
+ require "restpack_activity_service/services/activity/list"
6
+ require "restpack_activity_service/services/activity/create"
7
+ require "restpack_activity_service/services/activity/get"
8
+ require "restpack_activity_service/services/activity/update"
9
+ require "restpack_activity_service/services/activity/destroy"
@@ -0,0 +1,40 @@
1
+ module RestPack::Services::Activity
2
+ class Create < RestPack::Service::Command
3
+ required do
4
+ integer :application_id
5
+ integer :user_id
6
+ string :content
7
+ end
8
+
9
+ optional do
10
+ string :title, empty: true
11
+ string :tags, empty: true
12
+ string :access, empty: true
13
+ float :latitude
14
+ float :longitude
15
+ end
16
+
17
+ def init
18
+ inputs[:data] = raw_inputs[:data] if raw_inputs[:data]
19
+
20
+ if latitude.present? || longitude.present?
21
+ if latitude.present? != longitude.present?
22
+ service_error "Both Latitude and Longitude are required"
23
+ end
24
+ end
25
+
26
+ if title == "error"
27
+ service_error "This is a service error"
28
+ end
29
+
30
+ if title == "custom"
31
+ field_error :title, "Title should not be 'custom'"
32
+ end
33
+ end
34
+
35
+ def execute
36
+ activity = RestPack::Models::Activity.create(inputs)
37
+ RestPack::Serializers::ActivitySerializer.as_json(activity)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ module RestPack::Services::Activity
2
+ class Destroy < RestPack::Service::Command
3
+ required do
4
+ integer :id
5
+ integer :application_id
6
+ end
7
+
8
+ def execute
9
+ activity = RestPack::Models::Activity.find_by_id_and_application_id(
10
+ inputs[:id],
11
+ inputs[:application_id]
12
+ )
13
+
14
+ if activity
15
+ activity.destroy
16
+ nil
17
+ else
18
+ status :not_found
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ module RestPack::Services::Activity
2
+ class Get < RestPack::Service::Command
3
+ required do
4
+ integer :id
5
+ integer :application_id
6
+ end
7
+
8
+ def execute
9
+ activity = RestPack::Models::Activity.find_by_id_and_application_id(
10
+ inputs[:id],
11
+ inputs[:application_id]
12
+ )
13
+
14
+ if activity
15
+ RestPack::Serializers::ActivitySerializer.as_json(activity)
16
+ else
17
+ status :not_found
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ module RestPack::Services::Activity
2
+ class List < RestPack::Service::Command
3
+ required do
4
+ integer :application_id
5
+ end
6
+
7
+ optional do
8
+ integer :user_id
9
+ integer :page
10
+ integer :page_size
11
+ string :tags
12
+ string :access
13
+ string :query
14
+ end
15
+
16
+ def execute
17
+ scope = RestPack::Models::Activity.all
18
+ scope = scope.where(application_id: application_id)
19
+
20
+ scope = scope.where(user_id: user_id) if user_id
21
+ scope = scope.all_tags_csv(tags) if tags
22
+ scope = scope.any_tags_csv(access, :access) if access
23
+ scope = scope.search(query) if query
24
+
25
+ RestPack::Serializers::ActivitySerializer.resource(inputs, scope)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ module RestPack::Services::Activity
2
+ class Update < RestPack::Service::Command
3
+ required do
4
+ integer :id
5
+ integer :application_id
6
+ end
7
+
8
+ optional do
9
+ string :title, empty: true
10
+ string :content
11
+ string :tags, empty: true
12
+ string :access, empty: true
13
+ float :latitude
14
+ float :longitude
15
+ end
16
+
17
+ def init
18
+ inputs[:data] = raw_inputs[:data] if raw_inputs[:data]
19
+ end
20
+
21
+ def execute
22
+ activity = RestPack::Models::Activity.find_by_id_and_application_id(inputs[:id], inputs[:application_id])
23
+
24
+ if activity
25
+ activity.update_attributes(inputs)
26
+ RestPack::Serializers::ActivitySerializer.as_json(activity)
27
+ else
28
+ status :not_found
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,7 @@
1
+ module RestPack::Activity::Service
2
+ class Tasks
3
+ def self.load_tasks
4
+ load "restpack_activity_service/tasks/db.rake"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,39 @@
1
+ namespace :restpack do
2
+ desc "Run any outstanding RestPack migrations"
3
+ task :migrate do
4
+ Rake::Task["restpack:activity:migrate"].invoke
5
+ end
6
+
7
+ desc "List RestPack configuration"
8
+ task :configuration do
9
+ Rake::Task["restpack:activity:configuration"].invoke
10
+ end
11
+
12
+ namespace :activity do
13
+ desc "Run any outstanding RestPack::Activity migrations"
14
+ task :migrate => ["connection"] do
15
+ source_migrations_path = File.dirname(__FILE__) + "/../../../db/migrate"
16
+ target_migrations_path = "db/migrate"
17
+
18
+ ActiveRecord::Migration.verbose = true
19
+ ActiveRecord::Migrator.migrate(source_migrations_path)
20
+
21
+ if File.directory?(target_migrations_path)
22
+ FileUtils.cp_r(Dir["#{source_migrations_path}/*"], target_migrations_path)
23
+ end
24
+ end
25
+
26
+ task :connection do
27
+ config = YAML.load(IO.read('config/database.yml'))
28
+ environment = ENV['RAILS_ENV'] || ENV['DB'] || 'development'
29
+ ActiveRecord::Base.establish_connection config[environment]
30
+ end
31
+
32
+ desc "List RestPack::Activity::Service configuration"
33
+ task :configuration do
34
+ p "RestPack::Activity::Service Configuration"
35
+ p "--------------------------------"
36
+ p "database_table_prefix: #{RestPack::Activity::Service.configuration.database_table_prefix}"
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ module RestPack
2
+ module Activity
3
+ module Service
4
+ VERSION = "0.0.3"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'restpack_activity_service/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "restpack_activity_service"
8
+ spec.version = RestPack::Activity::Service::VERSION
9
+ spec.authors = ["Gavin Joyce"]
10
+ spec.email = ["gavinjoyce@gmail.com"]
11
+ spec.description = %q{RestPack Activity Services}
12
+ spec.summary = %q{Simple Activity Streams}
13
+ spec.homepage = "https://github.com/RestPack"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "restpack_service", "~> 0.0.25"
22
+ spec.add_dependency "restpack_serializer", "~> 0.4.1"
23
+ spec.add_dependency "restpack_gem", "~> 0.0.9"
24
+ spec.add_dependency "sinatra", "~> 1.4.3"
25
+ spec.add_dependency "pg", "~> 0.15.1"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "database_cleaner", "~> 1.0.1"
29
+ spec.add_development_dependency "rake"
30
+ spec.add_development_dependency "rspec"
31
+ spec.add_development_dependency "bump"
32
+ end
@@ -0,0 +1,19 @@
1
+ class ActivityFactory
2
+ def self.create(params = {})
3
+ params.reverse_merge!({
4
+ application_id: 456,
5
+ user_id: 123,
6
+ content: "This is the content ##{sequence}"
7
+ })
8
+
9
+ RestPack::Services::Activity::Create.run!(params)
10
+ end
11
+
12
+ private
13
+
14
+ def self.sequence
15
+ @@sequence ||= 0
16
+ @@sequence += 1
17
+ @@sequence
18
+ end
19
+ end
@@ -0,0 +1,108 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe RestPack::Services::Activity::Create do
4
+ is_required :application_id, :user_id, :content
5
+ is_optional :title, :tags, :access, :latitude, :longitude
6
+
7
+ context 'creating an activity' do
8
+ let(:response) { subject.class.run(params) }
9
+
10
+ context 'with valid params' do
11
+ let(:params) { {
12
+ application_id: 123,
13
+ user_id: 234,
14
+ content: "this is the activity content",
15
+ title: "this is the title",
16
+ latitude: 53.180999,
17
+ longitude: -6.301528
18
+ } }
19
+
20
+ it 'returns the newly created activity' do
21
+ response.success?.should == true
22
+
23
+ response.result[:application_id].should == params[:application_id]
24
+ response.result[:user_id].should == params[:user_id]
25
+ response.result[:content].should == params[:content]
26
+ response.result[:title].should == params[:title]
27
+ response.result[:latitude].should == 53.180999
28
+ response.result[:longitude].should == -6.301528
29
+
30
+ response.result[:tags].should == []
31
+ response.result[:access].should == []
32
+ response.result[:data].should == nil
33
+ end
34
+
35
+ service_should_map :tags, {
36
+ nil => [],
37
+ '' => [],
38
+ ' ' => [],
39
+ 'tag1' => ['tag1'],
40
+ 'Tag1' => ['Tag1'],
41
+ ' tag1 ' => ['tag1'],
42
+ 'tag1,tag2' => ['tag1', 'tag2'],
43
+ 'tag1,tag1' => ['tag1'],
44
+ 'foo bar' => ['foo bar'],
45
+ 'a|b||c|||' => ['abc'],
46
+ }
47
+
48
+ service_should_map :access, {
49
+ nil => [],
50
+ '' => [],
51
+ ' ' => [],
52
+ ' public ' => ['public'],
53
+ 'group:123' => ['group:123'],
54
+ 'gavin,sarah' => ['gavin', 'sarah']
55
+ }
56
+
57
+ service_should_map :data, {
58
+ nil => nil,
59
+ { } => { },
60
+ { 'a' => 1 } => { 'a' => 1 }
61
+ }
62
+ end
63
+
64
+ context 'latitude / longitude' do
65
+ let(:params) { {
66
+ application_id: 123,
67
+ user_id: 234,
68
+ content: "this is the activity content",
69
+ title: "this is the title",
70
+ latitude: latititude,
71
+ longitude: longitude
72
+ } }
73
+
74
+ context 'when both are nil' do
75
+ let(:latititude) { nil }
76
+ let(:longitude) { nil }
77
+
78
+ it 'latitude and longitude should both be nil' do
79
+ response.success?.should == true
80
+
81
+ response.result[:latitude].should == nil
82
+ response.result[:longitude].should == nil
83
+ end
84
+ end
85
+
86
+ context 'when latitude is present, but longitude is not present' do
87
+ let(:latititude) { 123.45 }
88
+ let(:longitude) { nil }
89
+
90
+ it 'should return a validation error' do
91
+ response.success?.should == false
92
+ response.errors["base"].should == ["Both Latitude and Longitude are required"]
93
+ end
94
+ end
95
+
96
+ context 'when latitude is not present, but longitude is present' do
97
+ let(:latititude) { nil }
98
+ let(:longitude) { 90.1 }
99
+
100
+ it 'should return a validation error' do
101
+ response.success?.should == false
102
+ response.errors["base"].should == ["Both Latitude and Longitude are required"]
103
+ end
104
+ end
105
+
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,38 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe RestPack::Services::Activity::Destroy do
4
+ is_required :id, :application_id
5
+
6
+ let(:response) { subject.class.run(params) }
7
+ let(:params) { {} }
8
+
9
+ before do
10
+ @activity = ActivityFactory.create({ application_id: 100, title: 'test activity' })
11
+ end
12
+
13
+ context 'with valid params' do
14
+ let(:params) { {
15
+ id: @activity[:id],
16
+ application_id: @activity[:application_id]
17
+ } }
18
+
19
+ it 'deletes the activity' do
20
+ response.success?.should == true
21
+ response.result.should == {}
22
+ RestPack::Services::Activity::Get.run(params).status.should == :not_found
23
+ end
24
+ end
25
+
26
+ context 'with invalid :id' do
27
+ let(:params) { {
28
+ id: 142857,
29
+ application_id: @activity[:application_id]
30
+ }}
31
+
32
+ it 'is unsuccessful' do
33
+ response.success?.should == false
34
+ response.result.should == {}
35
+ response.status.should == :not_found
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe RestPack::Services::Activity::Get do
4
+ is_required :id, :application_id
5
+
6
+ let(:response) { subject.class.run(params) }
7
+ let(:params) { {} }
8
+
9
+ before do
10
+ @activity = ActivityFactory.create({ application_id: 100, title: 'test activity' })
11
+ end
12
+
13
+ context 'with valid params' do
14
+ let(:params) { {
15
+ id: @activity[:id],
16
+ application_id: @activity[:application_id]
17
+ } }
18
+
19
+ it 'returns the correct activity' do
20
+ response.success?.should == true
21
+ response.result.should == @activity
22
+ end
23
+ end
24
+
25
+ context 'with invalid :id' do
26
+ let(:params) { {
27
+ id: 142857,
28
+ application_id: @activity[:application_id]
29
+ }}
30
+
31
+ it 'is unsuccessful' do
32
+ response.success?.should == false
33
+ response.result.should == {}
34
+ response.status.should == :not_found
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,146 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe RestPack::Services::Activity::List do
4
+ is_required :application_id
5
+ is_optional :user_id, :page, :page_size, :tags, :access, :query
6
+
7
+ before do
8
+ ActivityFactory.create({
9
+ application_id: 100, user_id: 200, title: 'Achievement Unlocked 1',
10
+ content: 'Three in a row! You have unlocked the "three-in-a-row" achievement.',
11
+ tags: 'achievement,three in a row',
12
+ access: 'user:100,group:300'
13
+ })
14
+
15
+ ActivityFactory.create({
16
+ application_id: 100, user_id: 200, title: 'Achievement Unlocked 2',
17
+ content: 'Five in a row! You have unlocked the "five-in-a-row" achievement.',
18
+ tags: 'achievement,five in a row',
19
+ access: 'user:100,group:300'
20
+ })
21
+
22
+ ActivityFactory.create({
23
+ application_id: 100, user_id: 200, title: '3 Photos Uploaded',
24
+ content: '3 photos have been uploaded',
25
+ tags: 'photos',
26
+ access: 'user:100,group:142857',
27
+ data: {
28
+ photos: ['1.png', '2.png', '3.png']
29
+ }
30
+ })
31
+
32
+ ActivityFactory.create({
33
+ application_id: 100, user_id: 200, title: 'Check In: Eatery120',
34
+ content: 'You checked in to Eatery 130, Ranelagh, Dublin 6',
35
+ tags: 'checkin,eatery120,ranelagh,dublin',
36
+ access: 'user:100,public',
37
+ latitude: 53.324577,
38
+ longitude: -6.254193
39
+ })
40
+ end
41
+
42
+ context 'listing activities' do
43
+ let(:response) { subject.class.run(params) }
44
+
45
+ context ':application_id' do
46
+ context 'valid' do
47
+ let(:params) { { application_id: 100 } }
48
+ it 'retuns activities' do
49
+ response.result[:meta][:activities][:count].should == RestPack::Models::Activity.count
50
+ end
51
+ end
52
+
53
+ context 'invalid' do
54
+ let(:params) { { application_id: 999 } }
55
+ it 'retuns no activities' do
56
+ response.result[:meta][:activities][:count].should == 0
57
+ end
58
+ end
59
+ end
60
+
61
+ context ':user_id' do
62
+ context 'valid' do
63
+ let(:params) { { application_id: 100, user_id: 200 }}
64
+
65
+ it 'returns activities' do
66
+ response.result[:meta][:activities][:count].should == RestPack::Models::Activity.count
67
+ end
68
+ end
69
+
70
+ context 'invalid' do
71
+ let(:params) { { application_id: 100, user_id: 999 }}
72
+
73
+ it 'returns no activities' do
74
+ response.result[:meta][:activities][:count].should == 0
75
+ end
76
+ end
77
+ end
78
+
79
+ context ':tags' do
80
+ context '"achievement" tag' do
81
+ let(:params) { { application_id:100, tags: 'achievement' } }
82
+
83
+ it 'returns two "achievement" activities' do
84
+ response.result[:meta][:activities][:count].should == 2
85
+ response.result[:activities][0][:title].should == 'Achievement Unlocked 1'
86
+ response.result[:activities][1][:title].should == 'Achievement Unlocked 2'
87
+ end
88
+ end
89
+
90
+ context 'two tags' do
91
+ let(:params) { { application_id:100, tags: 'achievement,five in a row' } }
92
+
93
+ it 'returns single "achievement" activity' do
94
+ response.result[:meta][:activities][:count].should == 1
95
+ response.result[:activities][0][:title].should == 'Achievement Unlocked 2'
96
+ end
97
+ end
98
+ end
99
+
100
+ context ':access' do
101
+ context 'invalid group' do
102
+ let(:params) { { application_id:100, access: 'group:999' } }
103
+
104
+ it 'returns no activities' do
105
+ response.result[:meta][:activities][:count].should == 0
106
+ end
107
+ end
108
+
109
+ context 'valid group' do
110
+ let(:params) { { application_id:100, access: 'group:300' } }
111
+
112
+ it 'returns relevant activities' do
113
+ response.result[:meta][:activities][:count].should == 2
114
+ end
115
+ end
116
+
117
+ context 'two access tags' do
118
+ let(:params) { { application_id:100, access: 'public,group:142857' } }
119
+
120
+ it 'returns activities with either access tag' do
121
+ response.result[:meta][:activities][:count].should == 2
122
+ end
123
+ end
124
+ end
125
+
126
+ context ':query' do
127
+ context 'with match in title' do
128
+ let(:params) { { application_id:100, query: "check in" } }
129
+
130
+ it 'returns a match' do
131
+ response.result[:meta][:activities][:count].should == 1
132
+ response.result[:activities][0][:title].should == 'Check In: Eatery120'
133
+ end
134
+ end
135
+
136
+ context 'with match in content' do
137
+ let(:params) { { application_id:100, query: 'Ranelagh' } }
138
+
139
+ it 'returns a match' do
140
+ response.result[:meta][:activities][:count].should == 1
141
+ response.result[:activities][0][:title].should == 'Check In: Eatery120'
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'factories/activity_factory'
2
+ require_relative 'support/mutations_matchers'
3
+
4
+ require 'rspec'
5
+ require 'database_cleaner'
6
+ require 'yaml'
7
+ require 'restpack_activity_service'
8
+ require 'coveralls'
9
+ Coveralls.wear!
10
+
11
+ config = YAML.load_file('./config/database.yml')
12
+ ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'] || config['test'])
13
+
14
+ migrations_path = File.dirname(__FILE__) + "/../db/migrate"
15
+ migrator = ActiveRecord::Migrator.new(:up, migrations_path)
16
+ migrator.migrate
17
+
18
+ DatabaseCleaner.strategy = :transaction
19
+
20
+ RSpec.configure do |config|
21
+ config.before(:each) do
22
+ DatabaseCleaner.start
23
+ end
24
+
25
+ config.after(:each) do
26
+ DatabaseCleaner.clean
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ def is_required(*params)
2
+ params.each do |param|
3
+ it ":#{param} parameter must be required" do
4
+ expect(subject.class.input_filters.required_inputs).to include(param)
5
+ end
6
+ end
7
+ end
8
+
9
+ def is_optional(*params)
10
+ params.each do |param|
11
+ it ":#{param} parameter must be required" do
12
+ expect(subject.class.input_filters.optional_inputs).to include(param)
13
+ end
14
+ end
15
+ end
16
+
17
+ def service_request_with(param, value, &block)
18
+ context "when :#{param} = #{value.inspect}" do
19
+ let(:params) do
20
+ super().merge({ param => value })
21
+ end
22
+ it { yield(response.result) }
23
+ end
24
+ end
25
+
26
+ def service_should_map(param, map)
27
+ context param do
28
+ map.each do |value, expected|
29
+ service_request_with(param, value) { |r| r[param].should == expected }
30
+ end
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,222 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: restpack_activity_service
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Gavin Joyce
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: restpack_service
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.0.25
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.25
27
+ - !ruby/object:Gem::Dependency
28
+ name: restpack_serializer
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 0.4.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: restpack_gem
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.9
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.9
55
+ - !ruby/object:Gem::Dependency
56
+ name: sinatra
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.4.3
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 1.4.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: pg
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.15.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 0.15.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: database_cleaner
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 1.0.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 1.0.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: bump
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: RestPack Activity Services
154
+ email:
155
+ - gavinjoyce@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - .gitignore
161
+ - .travis.yml
162
+ - Gemfile
163
+ - LICENSE.txt
164
+ - README.md
165
+ - Rakefile
166
+ - config/database.yml
167
+ - db/migrate/20130630145408_create_activity_table.rb
168
+ - lib/restpack_activity_service.rb
169
+ - lib/restpack_activity_service/api/activity_api.rb
170
+ - lib/restpack_activity_service/configuration.rb
171
+ - lib/restpack_activity_service/models/activity.rb
172
+ - lib/restpack_activity_service/models/base.rb
173
+ - lib/restpack_activity_service/serializers/activity_serializer.rb
174
+ - lib/restpack_activity_service/services.rb
175
+ - lib/restpack_activity_service/services/activity/create.rb
176
+ - lib/restpack_activity_service/services/activity/destroy.rb
177
+ - lib/restpack_activity_service/services/activity/get.rb
178
+ - lib/restpack_activity_service/services/activity/list.rb
179
+ - lib/restpack_activity_service/services/activity/update.rb
180
+ - lib/restpack_activity_service/tasks.rb
181
+ - lib/restpack_activity_service/tasks/db.rake
182
+ - lib/restpack_activity_service/version.rb
183
+ - restpack_activity_service.gemspec
184
+ - spec/factories/activity_factory.rb
185
+ - spec/services/create_spec.rb
186
+ - spec/services/destroy_spec.rb
187
+ - spec/services/get_spec.rb
188
+ - spec/services/list_spec.rb
189
+ - spec/spec_helper.rb
190
+ - spec/support/mutations_matchers.rb
191
+ homepage: https://github.com/RestPack
192
+ licenses:
193
+ - MIT
194
+ metadata: {}
195
+ post_install_message:
196
+ rdoc_options: []
197
+ require_paths:
198
+ - lib
199
+ required_ruby_version: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - '>='
202
+ - !ruby/object:Gem::Version
203
+ version: '0'
204
+ required_rubygems_version: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - '>='
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ requirements: []
210
+ rubyforge_project:
211
+ rubygems_version: 2.0.3
212
+ signing_key:
213
+ specification_version: 4
214
+ summary: Simple Activity Streams
215
+ test_files:
216
+ - spec/factories/activity_factory.rb
217
+ - spec/services/create_spec.rb
218
+ - spec/services/destroy_spec.rb
219
+ - spec/services/get_spec.rb
220
+ - spec/services/list_spec.rb
221
+ - spec/spec_helper.rb
222
+ - spec/support/mutations_matchers.rb