smooth 2.0.1
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.
- 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
|