smooth 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +24 -0
- data/app/assets/images/smooth/.keep +0 -0
- data/app/assets/javascripts/smooth/.keep +0 -0
- data/app/controllers/.keep +0 -0
- data/app/helpers/.keep +0 -0
- data/app/mailers/.keep +0 -0
- data/app/models/.keep +0 -0
- data/app/views/.keep +0 -0
- data/config/routes.rb +2 -0
- data/lib/smooth.rb +64 -0
- data/lib/smooth/api.rb +72 -0
- data/lib/smooth/api/policy.rb +18 -0
- data/lib/smooth/api/tracking.rb +31 -0
- data/lib/smooth/cache.rb +19 -0
- data/lib/smooth/command.rb +55 -0
- data/lib/smooth/command/instrumented.rb +51 -0
- data/lib/smooth/configuration.rb +25 -0
- data/lib/smooth/documentation.rb +31 -0
- data/lib/smooth/dsl.rb +36 -0
- data/lib/smooth/event.rb +27 -0
- data/lib/smooth/example.rb +6 -0
- data/lib/smooth/ext/core.rb +5 -0
- data/lib/smooth/ext/mutations.rb +1 -0
- data/lib/smooth/query.rb +62 -0
- data/lib/smooth/resource.rb +173 -0
- data/lib/smooth/resource/tracking.rb +17 -0
- data/lib/smooth/serializer.rb +94 -0
- data/lib/smooth/version.rb +3 -0
- data/smooth.gemspec +36 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/apis/application_api.rb +34 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/author.rb +2 -0
- data/spec/dummy/app/models/book.rb +2 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/resources/books.rb +103 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +30 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +82 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20140822065900_create_books.rb +11 -0
- data/spec/dummy/db/migrate/20140822065916_create_authors.rb +9 -0
- data/spec/dummy/db/schema.rb +30 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/lib/smooth/api/policy_spec.rb +5 -0
- data/spec/lib/smooth/api_spec.rb +23 -0
- data/spec/lib/smooth/cache_spec.rb +8 -0
- data/spec/lib/smooth/command_spec.rb +36 -0
- data/spec/lib/smooth/configuration_spec.rb +16 -0
- data/spec/lib/smooth/event_spec.rb +20 -0
- data/spec/lib/smooth/example_spec.rb +5 -0
- data/spec/lib/smooth/query_spec.rb +5 -0
- data/spec/lib/smooth/resource_spec.rb +38 -0
- data/spec/lib/smooth/serializer_spec.rb +26 -0
- data/spec/spec_helper.rb +18 -0
- metadata +394 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Smooth
|
4
|
+
class Configuration
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
cattr_accessor :query_class,
|
8
|
+
:command_class,
|
9
|
+
:serializer_class,
|
10
|
+
:enable_events
|
11
|
+
|
12
|
+
@@query_class = Smooth::Query
|
13
|
+
@@command_class = Smooth::Command
|
14
|
+
@@serializer_class = defined?(ApplicationSerializer) ? ApplicationSerializer : Smooth::Serializer
|
15
|
+
@@enable_events = true
|
16
|
+
|
17
|
+
def enable_event_tracking?
|
18
|
+
!!@@enable_events
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.method_missing meth, *args, &block
|
22
|
+
instance.send(meth, *args, &block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Smooth
|
2
|
+
module Documentation
|
3
|
+
|
4
|
+
def self.included base
|
5
|
+
binding.pry
|
6
|
+
|
7
|
+
base.class_eval do
|
8
|
+
attr_accessor :_inline_description
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :_inline_description
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
base.extend Smooth::Documentation
|
16
|
+
end
|
17
|
+
|
18
|
+
def desc description, *args
|
19
|
+
self._inline_description = {
|
20
|
+
description: description,
|
21
|
+
args: args
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def inline_description
|
26
|
+
val = self._inline_description && self._inline_description.dup
|
27
|
+
self._inline_description = nil
|
28
|
+
val
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/smooth/dsl.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Smooth
|
2
|
+
module Dsl
|
3
|
+
# Creates or opens an API definition
|
4
|
+
def api name, *args, &block
|
5
|
+
Smooth.current_api_name = name
|
6
|
+
|
7
|
+
instance = Smooth.fetch_api(name) do |key|
|
8
|
+
options = args.dup.extract_options!
|
9
|
+
|
10
|
+
Smooth::Api.new(name, options).tap do |obj|
|
11
|
+
obj.instance_eval(&block) if block_given?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
instance
|
16
|
+
end
|
17
|
+
|
18
|
+
# Creates or opens a resource definition
|
19
|
+
def resource name, *args, &block
|
20
|
+
options = args.extract_options!
|
21
|
+
|
22
|
+
api = case
|
23
|
+
when options[:api].is_a?(Symbol) || options[:api].is_a?(String)
|
24
|
+
Smooth.fetch_api(options[:api])
|
25
|
+
when options[:api].is_a?(Smooth::Api)
|
26
|
+
options[:api]
|
27
|
+
else
|
28
|
+
Smooth.current_api
|
29
|
+
end
|
30
|
+
|
31
|
+
api.resource(name, options, &block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
extend(Smooth::Dsl)
|
data/lib/smooth/event.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Smooth
|
2
|
+
class Event < ActiveSupport::Notifications::Event
|
3
|
+
|
4
|
+
def self.provider
|
5
|
+
ActiveSupport::Notifications
|
6
|
+
end
|
7
|
+
|
8
|
+
def payload
|
9
|
+
hash = super
|
10
|
+
hash && hash.to_mash
|
11
|
+
end
|
12
|
+
|
13
|
+
module Adapter
|
14
|
+
def track_event *args, &block
|
15
|
+
Smooth::Event.provider.send(:instrument, *args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def subscribe_to event_name, &block
|
19
|
+
Smooth::Event.provider.subscribe(event_name) do |*args|
|
20
|
+
block.call(Smooth::Event.new(*args), event_name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
extend Adapter
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Mutations.cache_constants = false
|
data/lib/smooth/query.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Smooth
|
2
|
+
class Query
|
3
|
+
include Smooth::Documentation
|
4
|
+
|
5
|
+
class_attribute :query_config
|
6
|
+
self.query_config = Hashie::Mash.new(base:{})
|
7
|
+
|
8
|
+
def self.configure options, resource=nil
|
9
|
+
resource ||= Smooth.current_resource
|
10
|
+
klass = define_or_open(options, resource)
|
11
|
+
|
12
|
+
Array(options.blocks).each do |blk|
|
13
|
+
klass.class_eval(&blk)
|
14
|
+
end
|
15
|
+
|
16
|
+
klass
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.define_or_open(options, resource)
|
20
|
+
resource_name = resource.name
|
21
|
+
base = Smooth.query
|
22
|
+
|
23
|
+
name = options.name
|
24
|
+
name = nil if name == "Default"
|
25
|
+
|
26
|
+
klass = "#{ resource_name }#{ name }".singularize + "Query"
|
27
|
+
|
28
|
+
if query_klass = Object.const_get(klass) rescue nil
|
29
|
+
return query_klass
|
30
|
+
end
|
31
|
+
|
32
|
+
Object.const_set(klass, Class.new(base))
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.start_from *args, &block
|
36
|
+
options = args.extract_options!
|
37
|
+
config.start_from = options
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.params *args, &block
|
41
|
+
options = args.extract_options!
|
42
|
+
config.params = options
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.role name, &block
|
46
|
+
@current_config = name
|
47
|
+
instance_eval(&block) if block_given?
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.config
|
51
|
+
val = query_config.send(@current_config || :base)
|
52
|
+
|
53
|
+
if val.nil?
|
54
|
+
val = query_config[@current_config] = {}
|
55
|
+
return config
|
56
|
+
end
|
57
|
+
|
58
|
+
val
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
module Smooth
|
2
|
+
class Resource
|
3
|
+
include Smooth::Documentation
|
4
|
+
|
5
|
+
attr_accessor :resource_name,
|
6
|
+
:api_name
|
7
|
+
|
8
|
+
# These store the configuration values for the various
|
9
|
+
# objects belonging to the resource.
|
10
|
+
attr_reader :_queries,
|
11
|
+
:_commands,
|
12
|
+
:_serializers,
|
13
|
+
:_routes,
|
14
|
+
:_examples
|
15
|
+
|
16
|
+
def initialize(resource_name, options={}, &block)
|
17
|
+
@resource_name = resource_name
|
18
|
+
@options = options
|
19
|
+
|
20
|
+
@_serializers = {}.to_mash
|
21
|
+
@_commands = {}.to_mash
|
22
|
+
@_queries = {}.to_mash
|
23
|
+
@_routes = {}.to_mash
|
24
|
+
@_examples = {}.to_mash
|
25
|
+
|
26
|
+
@loaded = false
|
27
|
+
|
28
|
+
instance_eval(&block) if block_given?
|
29
|
+
|
30
|
+
load!
|
31
|
+
end
|
32
|
+
|
33
|
+
def name
|
34
|
+
resource_name
|
35
|
+
end
|
36
|
+
|
37
|
+
def fetch_config object_type, object_key
|
38
|
+
source = send("_#{ object_type }s") rescue nil
|
39
|
+
source && source.fetch(object_key.to_sym)
|
40
|
+
end
|
41
|
+
|
42
|
+
def fetch object_type, object_key
|
43
|
+
source = instance_variable_get("@#{ object_type }s") rescue nil
|
44
|
+
source && source.fetch(object_key.to_sym)
|
45
|
+
end
|
46
|
+
|
47
|
+
def loaded?
|
48
|
+
!!@loaded
|
49
|
+
end
|
50
|
+
|
51
|
+
def load!
|
52
|
+
configure_commands
|
53
|
+
configure_serializers
|
54
|
+
configure_queries
|
55
|
+
configure_routes
|
56
|
+
configure_examples
|
57
|
+
end
|
58
|
+
|
59
|
+
def api
|
60
|
+
Smooth.fetch_api(api_name || :default)
|
61
|
+
end
|
62
|
+
|
63
|
+
def apply_options *opts
|
64
|
+
@options.send(:merge!, *opts)
|
65
|
+
end
|
66
|
+
|
67
|
+
def serializer serializer_name="Default", *args, &block
|
68
|
+
if args.empty? && !block_given? && exists = fetch(:serializer, serializer_name)
|
69
|
+
return exists
|
70
|
+
end
|
71
|
+
|
72
|
+
options = args.extract_options!
|
73
|
+
|
74
|
+
description = options.fetch(:description) do
|
75
|
+
args.first || inline_description
|
76
|
+
end
|
77
|
+
|
78
|
+
config = _serializers[serializer_name.to_sym] ||= Hashie::Mash.new(options: {}, name: serializer_name, blocks: [block].compact)
|
79
|
+
config.description = description unless description.nil?
|
80
|
+
|
81
|
+
config
|
82
|
+
end
|
83
|
+
|
84
|
+
def command command_name, *args, &block
|
85
|
+
if args.empty? && !block_given? && exists = fetch(:command, command_name)
|
86
|
+
return exists
|
87
|
+
end
|
88
|
+
|
89
|
+
options = args.extract_options!
|
90
|
+
|
91
|
+
description = options.fetch(:description) do
|
92
|
+
args.first || inline_description
|
93
|
+
end
|
94
|
+
|
95
|
+
config = _commands[command_name.to_sym] ||= Hashie::Mash.new(options: {}, name: command_name, blocks: [block].compact)
|
96
|
+
|
97
|
+
config.options.merge!(options)
|
98
|
+
config.description = description unless description.nil?
|
99
|
+
|
100
|
+
config
|
101
|
+
end
|
102
|
+
|
103
|
+
def query query_name="Default", *args, &block
|
104
|
+
if args.empty? && !block_given? && exists = fetch(:query, query_name)
|
105
|
+
return exists
|
106
|
+
end
|
107
|
+
|
108
|
+
options = args.extract_options!
|
109
|
+
|
110
|
+
description = options.fetch(:description) do
|
111
|
+
args.first || inline_description
|
112
|
+
end
|
113
|
+
|
114
|
+
config = _queries[query_name.to_sym] ||= Hashie::Mash.new(options: {}, name: query_name, blocks: [block].compact)
|
115
|
+
config.options.merge!(options)
|
116
|
+
config.description = description unless description.nil?
|
117
|
+
|
118
|
+
config
|
119
|
+
end
|
120
|
+
|
121
|
+
def routes &block
|
122
|
+
return @routes if !block_given?
|
123
|
+
end
|
124
|
+
|
125
|
+
def examples options={}, &block
|
126
|
+
if options.empty? && !block_given?
|
127
|
+
return @examples
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
protected
|
132
|
+
|
133
|
+
def configure_commands
|
134
|
+
resource = self
|
135
|
+
|
136
|
+
@commands = _commands.inject({}.to_mash) do |memo, p|
|
137
|
+
ref, cfg = p
|
138
|
+
memo[cfg.name] = Smooth::Command.configure(cfg, resource)
|
139
|
+
memo
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def configure_serializers
|
144
|
+
resource = self
|
145
|
+
|
146
|
+
@serializers = _serializers.inject({}.to_mash) do |memo, p|
|
147
|
+
ref, cfg = p
|
148
|
+
memo[cfg.name] = Smooth::Serializer.configure(cfg, resource)
|
149
|
+
memo
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def configure_queries
|
154
|
+
resource = self
|
155
|
+
|
156
|
+
@queries = _queries.inject({}.to_mash) do |memo, p|
|
157
|
+
ref, cfg = p
|
158
|
+
memo[cfg.name] = Smooth::Query.configure(cfg, resource)
|
159
|
+
memo
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def configure_routes
|
164
|
+
@routes = {}
|
165
|
+
end
|
166
|
+
|
167
|
+
def configure_examples
|
168
|
+
@examples = {}
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
require 'smooth/resource/tracking'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Smooth
|
2
|
+
class Resource
|
3
|
+
module Tracking
|
4
|
+
def resources
|
5
|
+
@@resources ||= {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def current_resource= resource_object
|
9
|
+
@current_resource = resource_object.identifier
|
10
|
+
end
|
11
|
+
|
12
|
+
def current_resource
|
13
|
+
resources[@current_resource]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Smooth
|
2
|
+
class Serializer < ActiveModel::Serializer
|
3
|
+
include Smooth::Documentation
|
4
|
+
|
5
|
+
class_attribute :attribute_descriptions,
|
6
|
+
:relationship_descriptions
|
7
|
+
|
8
|
+
self.attribute_descriptions = {}.to_mash
|
9
|
+
self.relationship_descriptions = {}.to_mash
|
10
|
+
|
11
|
+
def self.method_added method_name
|
12
|
+
if documented = inline_description
|
13
|
+
attribute_descriptions[method_name.to_sym] = documented
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.schema_attributes
|
18
|
+
schema[:attributes]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.schema_associations
|
22
|
+
schema[:associations]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.configure options, resource=nil
|
26
|
+
resource ||= Smooth.current_resource
|
27
|
+
klass = define_or_open(options, resource)
|
28
|
+
|
29
|
+
Array(options.blocks).each do |blk|
|
30
|
+
klass.class_eval(&blk)
|
31
|
+
end
|
32
|
+
|
33
|
+
klass
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.define_or_open(options, resource)
|
37
|
+
resource_name = resource.name
|
38
|
+
base = Smooth.serializer
|
39
|
+
|
40
|
+
name = options.name
|
41
|
+
name = nil if name == "Default"
|
42
|
+
|
43
|
+
klass = "#{ resource_name }#{ name }".singularize + "Serializer"
|
44
|
+
|
45
|
+
if serializer_klass = Object.const_get(klass) rescue nil
|
46
|
+
return serializer_klass
|
47
|
+
end
|
48
|
+
|
49
|
+
Object.const_set(klass, Class.new(base))
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.documentation_for_attribute attribute
|
53
|
+
attribute_descriptions[attribute.to_sym]
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.documentation_for_association association
|
57
|
+
relationship_descriptions[association.to_sym]
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.documentation
|
61
|
+
attribute_descriptions.merge(relationship_descriptions).to_mash
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.attribute attr, options={}
|
65
|
+
documented = inline_description
|
66
|
+
|
67
|
+
if documented
|
68
|
+
attribute_descriptions[attr.to_sym] = documented
|
69
|
+
end
|
70
|
+
|
71
|
+
super
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.has_one attr, options={}
|
75
|
+
documented = inline_description
|
76
|
+
|
77
|
+
if documented
|
78
|
+
relationship_descriptions[attr.to_sym] = documented
|
79
|
+
end
|
80
|
+
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.has_many attr, options={}
|
85
|
+
documented = inline_description
|
86
|
+
|
87
|
+
if documented
|
88
|
+
relationship_descriptions[attr.to_sym] = documented
|
89
|
+
end
|
90
|
+
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|