diff --git a/Makefile b/Makefile index a9bbb34..83cf629 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,10 @@ dep_wpool = git https://github.com/inaka/worker_pool.git 1.0 TEST_DEPS = mixer dep_mixer = git git://github.com/inaka/mixer.git 0.1.2 +DIALYZER_DIRS := ebin/ +DIALYZER_OPTS := --verbose --statistics -Werror_handling \ + -Wrace_conditions #-Wunmatched_returns + include erlang.mk ERLC_OPTS += +'{parse_transform, lager_transform}' diff --git a/elvis.config b/elvis.config index b908db3..cf981d6 100644 --- a/elvis.config +++ b/elvis.config @@ -5,46 +5,47 @@ {config, [#{dirs => ["src", "test"], filter => "*.erl", - rules => [{elvis_style, line_length, [80]}, - {elvis_style, no_tabs, []}, - {elvis_style, macro_names, []}, - {elvis_style, macro_module_names, []}, - {elvis_style, operator_spaces, [{right, ","}, - {right, "++"}, - {left, "++"}]}, - {elvis_style, nesting_level, [3]}, - {elvis_style, god_modules, [25]}, - {elvis_style, no_if_expression, []}, + rules => [{elvis_style, line_length, #{limit => 80}}, + {elvis_style, no_tabs}, + {elvis_style, macro_names}, + {elvis_style, macro_module_names}, + {elvis_style, operator_spaces, #{rules => [{right, ","}, + {right, "++"}, + {left, "++"}]}}, + {elvis_style, nesting_level, #{level => 3}}, + {elvis_style, god_modules, #{limit => 25}}, + {elvis_style, no_if_expression}, {elvis_style, invalid_dynamic_call, - [conditional_logic_SUITE, - sumo_find_SUITE, - sumo_basic_SUITE, - sumo, - sumo_internal] + #{ignore => + [conditional_logic_SUITE, + sumo_find_SUITE, + sumo_basic_SUITE, + sumo, + sumo_internal]} }, - {elvis_style, used_ignored_variable, []}, - {elvis_style, no_behavior_info, []}, + {elvis_style, used_ignored_variable}, + {elvis_style, no_behavior_info}, { elvis_style, module_naming_convention, - ["^([a-z][a-z0-9]*_?)*(_SUITE)?$", []] + #{regex => "^([a-z][a-z0-9]*_?)*(_SUITE)?$", ignore => []} }, - {elvis_style, state_record_and_type, []}, - {elvis_style, no_spec_with_records, []} + {elvis_style, state_record_and_type}, + {elvis_style, no_spec_with_records} ] }, #{dirs => ["."], filter => "Makefile", - rules => [{elvis_project, no_deps_master_erlang_mk, []}] + rules => [{elvis_project, no_deps_master_erlang_mk, #{ignore => []}}] }, #{dirs => ["."], filter => "rebar.config", - rules => [{elvis_project, no_deps_master_rebar, []}] + rules => [{elvis_project, no_deps_master_rebar, #{ignore => []}}] }, #{dirs => ["."], filter => "elvis.config", - rules => [{elvis_project, old_configuration_format, []}] + rules => [{elvis_project, old_configuration_format}] } ] } diff --git a/erlang.mk b/erlang.mk index 6db2cc0..d0d08ac 100644 --- a/erlang.mk +++ b/erlang.mk @@ -12,7 +12,7 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.PHONY: all deps app rel docs tests clean distclean help +.PHONY: all deps app rel docs tests clean distclean help erlang-mk ERLANG_MK_VERSION = 1 @@ -72,6 +72,18 @@ define core_http_get endef endif +# Automated update. + +ERLANG_MK_BUILD_CONFIG ?= build.config +ERLANG_MK_BUILD_DIR ?= .erlang.mk.build + +erlang-mk: + git clone https://github.com/ninenines/erlang.mk $(ERLANG_MK_BUILD_DIR) + if [ -f $(ERLANG_MK_BUILD_CONFIG) ]; then cp $(ERLANG_MK_BUILD_CONFIG) $(ERLANG_MK_BUILD_DIR); fi + cd $(ERLANG_MK_BUILD_DIR) && make + cp $(ERLANG_MK_BUILD_DIR)/erlang.mk ./erlang.mk + rm -rf $(ERLANG_MK_BUILD_DIR) + # Copyright (c) 2013-2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. @@ -108,7 +120,7 @@ deps:: $(ALL_DEPS_DIRS) if [ -f $$dep/GNUmakefile ] || [ -f $$dep/makefile ] || [ -f $$dep/Makefile ] ; then \ $(MAKE) -C $$dep ; \ else \ - echo "include $(CURDIR)/erlang.mk" | $(MAKE) -f - -C $$dep ; \ + echo "include $(CURDIR)/erlang.mk" | ERLC_OPTS=+debug_info $(MAKE) -f - -C $$dep ; \ fi ; \ done @@ -176,8 +188,10 @@ pkg-search: $(error Usage: make pkg-search q=STRING) endif +ifeq ($(PKG_FILE2),$(CURDIR)/.erlang.mk.packages.v2) distclean-pkg: $(gen_verbose) rm -f $(PKG_FILE2) +endif help:: @printf "%s\n" "" \ @@ -192,22 +206,32 @@ help:: # Configuration. -ERLC_OPTS ?= +debug_info +warn_export_all +warn_export_vars \ - +warn_shadow_vars +warn_obsolete_guard # +bin_opt_info +warn_missing_spec +ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars \ + +warn_obsolete_guard # +bin_opt_info +warn_export_all +warn_missing_spec COMPILE_FIRST ?= COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST))) +ERLC_EXCLUDE ?= +ERLC_EXCLUDE_PATHS = $(addprefix src/,$(addsuffix .erl,$(ERLC_EXCLUDE))) + +ERLC_MIB_OPTS ?= +COMPILE_MIB_FIRST ?= +COMPILE_MIB_FIRST_PATHS = $(addprefix mibs/,$(addsuffix .mib,$(COMPILE_MIB_FIRST))) # Verbosity. appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src; appsrc_verbose = $(appsrc_verbose_$(V)) -erlc_verbose_0 = @echo " ERLC " $(filter %.erl %.core,$(?F)); +erlc_verbose_0 = @echo " ERLC " $(filter-out $(patsubst %,%.erl,$(ERLC_EXCLUDE)),\ + $(filter %.erl %.core,$(?F))); erlc_verbose = $(erlc_verbose_$(V)) xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F)); xyrl_verbose = $(xyrl_verbose_$(V)) +mib_verbose_0 = @echo " MIB " $(filter %.bin %.mib,$(?F)); +mib_verbose = $(mib_verbose_$(V)) + # Core targets. app:: erlc-include ebin/$(PROJECT).app @@ -217,13 +241,16 @@ app:: erlc-include ebin/$(PROJECT).app echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \ exit 1; \ fi + $(eval GITDESCRIBE := $(shell git describe --dirty --abbrev=7 --tags --always --first-parent 2>/dev/null || true)) $(appsrc_verbose) cat src/$(PROJECT).app.src \ | sed "s/{modules,[[:space:]]*\[\]}/{modules, \[$(MODULES)\]}/" \ + | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(GITDESCRIBE)\"}/" \ > ebin/$(PROJECT).app define compile_erl $(erlc_verbose) erlc -v $(ERLC_OPTS) -o ebin/ \ - -pa ebin/ -I include/ $(COMPILE_FIRST_PATHS) $(1) + -pa ebin/ -I include/ $(filter-out $(ERLC_EXCLUDE_PATHS),\ + $(COMPILE_FIRST_PATHS) $(1)) endef define compile_xyrl @@ -232,10 +259,22 @@ define compile_xyrl @rm ebin/*.erl endef +define compile_mib + $(mib_verbose) erlc -v $(ERLC_MIB_OPTS) -o priv/mibs/ \ + -I priv/mibs/ $(COMPILE_MIB_FIRST_PATHS) $(1) + $(mib_verbose) erlc -o include/ -- priv/mibs/*.bin +endef + ifneq ($(wildcard src/),) ebin/$(PROJECT).app:: @mkdir -p ebin/ +ifneq ($(wildcard mibs/),) +ebin/$(PROJECT).app:: $(shell find mibs -type f -name \*.mib) + @mkdir -p priv/mibs/ include + $(if $(strip $?),$(call compile_mib,$?)) +endif + ebin/$(PROJECT).app:: $(shell find src -type f -name \*.erl) \ $(shell find src -type f -name \*.core) $(if $(strip $?),$(call compile_erl,$?)) @@ -255,7 +294,8 @@ erlc-include: fi clean-app: - $(gen_verbose) rm -rf ebin/ + $(gen_verbose) rm -rf ebin/ priv/mibs/ \ + $(addprefix include/,$(addsuffix .hrl,$(notdir $(basename $(wildcard mibs/*.mib))))) # Copyright (c) 2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. @@ -278,6 +318,7 @@ help:: bs_appsrc = "{application, $(PROJECT), [" \ " {description, \"\"}," \ " {vsn, \"0.1.0\"}," \ + " {id, \"git\"}," \ " {modules, []}," \ " {registered, []}," \ " {applications, [" \ @@ -290,6 +331,7 @@ bs_appsrc = "{application, $(PROJECT), [" \ bs_appsrc_lib = "{application, $(PROJECT), [" \ " {description, \"\"}," \ " {vsn, \"0.1.0\"}," \ + " {id, \"git\"}," \ " {modules, []}," \ " {registered, []}," \ " {applications, [" \ @@ -533,6 +575,112 @@ endif list-templates: @echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES)))) +# Copyright (c) 2014, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: clean-c_src distclean-c_src-env +# todo + +# Configuration. + +C_SRC_DIR = $(CURDIR)/c_src +C_SRC_ENV ?= $(C_SRC_DIR)/env.mk +C_SRC_OUTPUT ?= $(CURDIR)/priv/$(PROJECT).so + +# System type and C compiler/flags. + +UNAME_SYS := $(shell uname -s) +ifeq ($(UNAME_SYS), Darwin) + CC ?= cc + CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall + LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress +else ifeq ($(UNAME_SYS), FreeBSD) + CC ?= cc + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -finline-functions -Wall +else ifeq ($(UNAME_SYS), Linux) + CC ?= gcc + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -finline-functions -Wall +endif + +CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) +CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) + +LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei +LDFLAGS += -shared + +# Verbosity. + +c_verbose_0 = @echo " C " $(?F); +c_verbose = $(c_verbose_$(V)) + +cpp_verbose_0 = @echo " CPP " $(?F); +cpp_verbose = $(cpp_verbose_$(V)) + +link_verbose_0 = @echo " LD " $(@F); +link_verbose = $(link_verbose_$(V)) + +# Targets. + +ifeq ($(wildcard $(C_SRC_DIR)),) +else ifneq ($(wildcard $(C_SRC_DIR)/Makefile),) +app:: + $(MAKE) -C $(C_SRC_DIR) + +clean:: + $(MAKE) -C $(C_SRC_DIR) clean + +else +SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \)) +OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) + +COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c +COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c + +app:: $(C_SRC_ENV) $(C_SRC_OUTPUT) + +$(C_SRC_OUTPUT): $(OBJECTS) + @mkdir -p priv/ + $(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT) + +%.o: %.c + $(COMPILE_C) $(OUTPUT_OPTION) $< + +%.o: %.cc + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +%.o: %.C + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +%.o: %.cpp + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +$(C_SRC_ENV): + @erl -noshell -noinput -eval "file:write_file(\"$(C_SRC_ENV)\", \ + io_lib:format( \ + \"ERTS_INCLUDE_DIR ?= ~s/erts-~s/include/~n\" \ + \"ERL_INTERFACE_INCLUDE_DIR ?= ~s~n\" \ + \"ERL_INTERFACE_LIB_DIR ?= ~s~n\", \ + [code:root_dir(), erlang:system_info(version), \ + code:lib_dir(erl_interface, include), \ + code:lib_dir(erl_interface, lib)])), \ + erlang:halt()." + +clean:: clean-c_src + +clean-c_src: + $(gen_verbose) rm -f $(C_SRC_OUTPUT) $(OBJECTS) + +distclean:: distclean-c_src-env + +distclean-c_src-env: + $(gen_verbose) rm -f $(C_SRC_ENV) + +-include $(C_SRC_ENV) +endif + # Copyright (c) 2013-2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. @@ -548,7 +696,7 @@ else endif TEST_ERLC_OPTS ?= +debug_info +warn_export_vars +warn_shadow_vars +warn_obsolete_guard -TEST_ERLC_OPTS += -DTEST=1 -DEXTRA=1 +'{parse_transform, eunit_autoexport}' +TEST_ERLC_OPTS += -DTEST=1 -DEXTRA=1 # Core targets. @@ -622,6 +770,7 @@ DIALYZER_PLT ?= $(CURDIR)/.$(PROJECT).plt export DIALYZER_PLT PLT_APPS ?= +DIALYZER_DIRS ?= --src -r src DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions \ -Wunmatched_returns # -Wunderspecs @@ -650,7 +799,71 @@ dialyze: else dialyze: $(DIALYZER_PLT) endif - @dialyzer --no_native --src -r src $(DIALYZER_OPTS) + @dialyzer --no_native $(DIALYZER_DIRS) $(DIALYZER_OPTS) + +# Copyright (c) 2013-2014, Loïc Hoguin +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: distclean-edoc + +# Configuration. + +EDOC_OPTS ?= + +# Core targets. + +docs:: distclean-edoc + $(gen_verbose) erl -noshell \ + -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), init:stop().' + +distclean:: distclean-edoc + +# Plugin-specific targets. + +distclean-edoc: + $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info + +# Copyright (c) 2014, Juan Facorro +# This file is part of erlang.mk and subject to the terms of the ISC License. + +.PHONY: elvis distclean-elvis + +# Configuration. + +ELVIS_CONFIG ?= $(CURDIR)/elvis.config + +ELVIS ?= $(CURDIR)/elvis +export ELVIS + +ELVIS_URL ?= https://github.com/inaka/elvis/releases/download/0.2.3/elvis +ELVIS_CONFIG_URL ?= https://github.com/inaka/elvis/releases/download/0.2.3/elvis.config +ELVIS_OPTS ?= + +# Core targets. + +help:: + @printf "%s\n" "" \ + "Elvis targets:" \ + " elvis Run Elvis using the local elvis.config or download the default otherwise" + +ifneq ($(wildcard $(ELVIS_CONFIG)),) +rel:: distclean-elvis +endif + +distclean:: distclean-elvis + +# Plugin-specific targets. + +$(ELVIS): + @$(call core_http_get,$(ELVIS_CONFIG),$(ELVIS_CONFIG_URL)) + @$(call core_http_get,$(ELVIS),$(ELVIS_URL)) + @chmod +x $(ELVIS) + +elvis: $(ELVIS) + @$(ELVIS) rock -c $(ELVIS_CONFIG) $(ELVIS_OPTS) + +distclean-elvis: + $(gen_verbose) rm -rf $(ELVIS) # Copyright (c) 2013-2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. @@ -678,27 +891,68 @@ ebin/$(PROJECT).app:: $(shell find templates -type f -name \*.dtl 2>/dev/null) $(if $(strip $?),$(call compile_erlydtl,$?)) endif -# Copyright (c) 2013-2014, Loïc Hoguin +# Copyright (c) 2014 Dave Cottlehuber # This file is part of erlang.mk and subject to the terms of the ISC License. -.PHONY: distclean-edoc +.PHONY: distclean-escript escript # Configuration. -EDOC_OPTS ?= +ESCRIPT_NAME ?= $(PROJECT) +ESCRIPT_COMMENT ?= This is an -*- erlang -*- file + +ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*" +ESCRIPT_SYS_CONFIG ?= "rel/sys.config" +ESCRIPT_EMU_ARGS ?= -pa . \ + -noshell -noinput \ + -sasl errlog_type error \ + -escript main $(ESCRIPT_NAME) +ESCRIPT_SHEBANG ?= /usr/bin/env escript +ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**" # Core targets. -docs:: distclean-edoc - $(gen_verbose) erl -noshell \ - -eval 'edoc:application($(PROJECT), ".", [$(EDOC_OPTS)]), init:stop().' +distclean:: distclean-escript -distclean:: distclean-edoc +help:: + @printf "%s\n" "" \ + "Escript targets:" \ + " escript Build an executable escript archive" \ # Plugin-specific targets. -distclean-edoc: - $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info +# Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl +# Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center +# Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE : +# Software may only be used for the great good and the true happiness of all +# sentient beings. +define ESCRIPT_RAW +'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\ +'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\ +' [F || F <- A, not filelib:is_dir(F) ] end,'\ +'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\ +'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\ +'Ez = fun(Escript) ->'\ +' Static = Files([$(ESCRIPT_STATIC)]),'\ +' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\ +' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\ +' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\ +' {archive, Archive, [memory]},'\ +' {shebang, "$(ESCRIPT_SHEBANG)"},'\ +' {comment, "$(ESCRIPT_COMMENT)"},'\ +' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\ +' ]),'\ +' file:change_mode(Escript, 8#755)'\ +'end,'\ +'Ez("$(ESCRIPT_NAME)").' +endef +ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW)) + +escript:: distclean-escript deps app + $(gen_verbose) erl -noshell -eval $(ESCRIPT_COMMAND) -s init stop + +distclean-escript: + $(gen_verbose) rm -f $(ESCRIPT_NAME) # Copyright (c) 2013-2014, Loïc Hoguin # This file is part of erlang.mk and subject to the terms of the ISC License. @@ -756,7 +1010,7 @@ distclean-relx: # Configuration. -SHELL_PATH ?= -pa ../$(PROJECT)/ebin $(DEPS_DIR)/*/ebin +SHELL_PATH ?= -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin SHELL_OPTS ?= ALL_SHELL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(SHELL_DEPS)) diff --git a/src/sumo.erl b/src/sumo.erl index bbcf405..9c7051f 100644 --- a/src/sumo.erl +++ b/src/sumo.erl @@ -47,13 +47,15 @@ integer | string | binary | text | float | date | datetime. -type field_name() :: atom(). -type field_value() :: term(). --type doc() :: [{field_name(), field_value()}]. --type conditions() :: [{field_name(), field_value()}]. +-type doc() :: #{field_name() => field_value()}. +-type conditions() :: [ {field_name(), field_value()} + | {field_name(), atom(), field_value()}]. -type sort_order() :: asc | desc. -type sort() :: field_name() | [{field_name(), sort_order()}]. -export_type([schema_name/0, field_attr/0, field_attrs/0, field_type/0, - field_name/0, field_value/0, doc/0, conditions/0]). + field_name/0, field_value/0, doc/0, conditions/0, + sort/0, sort_order/0]). -type schema() :: sumo_internal:schema(). -type field() :: sumo_internal:field(). diff --git a/src/sumo_app.erl b/src/sumo_app.erl index 87530b2..859378e 100644 --- a/src/sumo_app.erl +++ b/src/sumo_app.erl @@ -32,7 +32,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Code starts here. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec start(term(), term()) -> ok. +-spec start(term(), term()) -> {error, term()} | {ok, pid()}. start(_StartType, _StartArgs) -> sumo_sup:start_link(). diff --git a/src/sumo_backend_elasticsearch.erl b/src/sumo_backend_elasticsearch.erl index b89cc3c..3ad65b8 100644 --- a/src/sumo_backend_elasticsearch.erl +++ b/src/sumo_backend_elasticsearch.erl @@ -61,7 +61,7 @@ start_link(Name, Options) -> gen_server:start_link({local, Name}, ?MODULE, Options, []). --spec get_index(atom() | pid()) -> atom(). +-spec get_index(atom() | pid()) -> atom() | string() | binary(). get_index(Name) -> gen_server:call(Name, get_index). diff --git a/src/sumo_backend_mongo.erl b/src/sumo_backend_mongo.erl index 7f3a90d..b0656f8 100644 --- a/src/sumo_backend_mongo.erl +++ b/src/sumo_backend_mongo.erl @@ -69,7 +69,7 @@ get_pool(Name) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% gen_server stuff. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec init([term()]) -> {ok, #state{}}. +-spec init([term()]) -> {ok, state()}. init(Options) -> PoolSize = proplists:get_value(poolsize, Options), Pool = erlang:ref_to_list(make_ref()), @@ -87,7 +87,7 @@ init(Options) -> ), {ok, #state{pool=Pool}}. --spec handle_call(term(), term(), state()) -> {reply, term(), #state{}}. +-spec handle_call(term(), term(), state()) -> {reply, term(), state()}. handle_call(get_pool, _From, State = #state{pool=Pool}) -> {reply, Pool, State}. @@ -95,14 +95,14 @@ handle_call(get_pool, _From, State = #state{pool=Pool}) -> %%% Unused Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec handle_cast(term(), #state{}) -> {noreply, #state{}}. +-spec handle_cast(term(), state()) -> {noreply, state()}. handle_cast(_Msg, State) -> {noreply, State}. --spec handle_info(term(), #state{}) -> {noreply, #state{}}. +-spec handle_info(term(), state()) -> {noreply, state()}. handle_info(_Msg, State) -> {noreply, State}. --spec terminate(term(), #state{}) -> ok. +-spec terminate(term(), state()) -> ok. terminate(_Reason, _State) -> ok. --spec code_change(term(), #state{}, term()) -> {ok, #state{}}. +-spec code_change(term(), state(), term()) -> {ok, state()}. code_change(_OldVsn, State, _Extra) -> {ok, State}. diff --git a/src/sumo_backend_mysql.erl b/src/sumo_backend_mysql.erl index 23f3fb0..24e66a2 100644 --- a/src/sumo_backend_mysql.erl +++ b/src/sumo_backend_mysql.erl @@ -72,7 +72,7 @@ get_pool(Name) -> %% gen_server stuff. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec init([term()]) -> {ok, #state{}}. +-spec init([term()]) -> {ok, state()}. init(Options) -> PoolSize = proplists:get_value(poolsize, Options), Pool = list_to_atom(erlang:ref_to_list(make_ref())), @@ -88,7 +88,7 @@ init(Options) -> ), {ok, #state{pool=Pool}}. --spec handle_call(term(), term(), state()) -> {reply, term(), #state{}}. +-spec handle_call(term(), term(), state()) -> {reply, term(), state()}. handle_call(get_pool, _From, State = #state{pool=Pool}) -> {reply, Pool, State}. @@ -96,14 +96,14 @@ handle_call(get_pool, _From, State = #state{pool=Pool}) -> %%% Unused Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec handle_cast(term(), #state{}) -> {noreply, #state{}}. +-spec handle_cast(term(), state()) -> {noreply, state()}. handle_cast(_Msg, State) -> {noreply, State}. --spec handle_info(term(), #state{}) -> {noreply, #state{}}. +-spec handle_info(term(), state()) -> {noreply, state()}. handle_info(_Msg, State) -> {noreply, State}. --spec terminate(term(), #state{}) -> ok. +-spec terminate(term(), state()) -> ok. terminate(_Reason, _State) -> ok. --spec code_change(term(), #state{}, term()) -> {ok, #state{}}. +-spec code_change(term(), state(), term()) -> {ok, state()}. code_change(_OldVsn, State, _Extra) -> {ok, State}. diff --git a/src/sumo_internal.erl b/src/sumo_internal.erl index a1ef0aa..038cfe4 100644 --- a/src/sumo_internal.erl +++ b/src/sumo_internal.erl @@ -1,4 +1,5 @@ -%%% @doc Main **internal** module for sumo. Use this one from your own applications. +%%% @doc Main **internal** module for sumo. +%%% Use this one from your own applications. %%% %%% Copyright 2012 Inaka <hello@inaka.net> %%% diff --git a/src/sumo_sql_builder.erl b/src/sumo_sql_builder.erl index d4750fe..b477d4b 100644 --- a/src/sumo_sql_builder.erl +++ b/src/sumo_sql_builder.erl @@ -51,9 +51,12 @@ %%% Public API. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% @doc Returns number of results, useful for pagination. --spec s_count(string(), [field()], condition(), string()) -> {iolist(), [term()]}. +-spec s_count( + string(), [field()], condition(), string() +) -> {iolist(), [term()]}. s_count(TableName, SelectFields, Conditions, ExtraWhere) -> - {_Select, Where, WValues} = form_select_query(SelectFields, Conditions, ExtraWhere), + {_Select, Where, WValues} = + form_select_query(SelectFields, Conditions, ExtraWhere), { ["SELECT COUNT(1) AS `count` FROM ", escape(TableName), " WHERE ", Where], WValues @@ -61,18 +64,24 @@ s_count(TableName, SelectFields, Conditions, ExtraWhere) -> %% @doc Generic select function. -spec s( - string(), [field()], condition(), string(), non_neg_integer(), non_neg_integer(), string() + string(), [field()], condition(), string(), non_neg_integer(), + non_neg_integer(), string() ) -> {iolist(), [term()]}. s(TableName, SelectFields, Conditions, ExtraWhere, Page, PageSize, OrderBy) -> - Paging = [" LIMIT ", integer_to_list((Page-1) * PageSize), ",", integer_to_list(PageSize)], - {Select, Where, WValues} = form_select_query(SelectFields, Conditions, ExtraWhere), + Paging = + [ " LIMIT ", integer_to_list((Page-1) * PageSize), ", ", + integer_to_list(PageSize)], + {Select, Where, WValues} = + form_select_query(SelectFields, Conditions, ExtraWhere), { - ["SELECT ", Select, " FROM ", escape(TableName), " WHERE ", Where, " ", OrderBy, " ", Paging], + ["SELECT ", Select, + " FROM ", escape(TableName), + " WHERE ", Where, " ", OrderBy, " ", Paging], WValues }. %% @doc INSERT. --spec i(string(), sumo:doc()) -> {iolist(), [term()]}. +-spec i(atom() | string(), proplists:proplist()) -> {iodata(), [term()]}. i(TableName, Proplist) -> {Fields, Values, Args} = lists:foldr( fun({K, V}, {Fs, Vs, Args}) -> @@ -83,16 +92,16 @@ i(TableName, Proplist) -> ), { [ - "INSERT INTO ", escape(TableName), " (", string:join(Fields, ","), ") ", - "VALUES (", string:join(Args, ","),")" + "INSERT INTO ", escape(TableName), " (", string:join(Fields, ", "), ") ", + "VALUES (", string:join(Args, ", "), ")" ], Values }. %% @doc UPDATE. -spec u( - string(), sumo:doc(), condition() -) -> {iolist(), [term()], [term()]}. + atom() | string(), proplists:proplist(), condition() +) -> {iodata(), [term()], [term()]}. u(TableName, UpdateFields, Conditions) -> {_Select, Where, WValues} = form_select_query([], Conditions, ""), {UFields, UValues} = lists:foldr( @@ -103,7 +112,10 @@ u(TableName, UpdateFields, Conditions) -> UpdateFields ), Update = string:join(UFields, ","), - {["UPDATE ", escape(TableName), " SET ", Update, " WHERE ", Where], UValues, WValues}. + {["UPDATE ", escape(TableName), " SET ", Update, " WHERE ", Where], + UValues, + WValues + }. %% @doc DELETE. -spec d(string(), condition()) -> {iolist(), [term()]}. @@ -125,14 +137,16 @@ escape(Field) when is_atom(Field) -> escape(Field) when is_list(Field) -> lists:flatten(["`", Field, "`"]). --spec form_select_query([field()], condition(), string()) -> {string(), string(), [string()]}. +-spec form_select_query([field()], condition(), string()) -> + {string(), string(), [string()]}. form_select_query(SelectFields, Conditions, ExtraWhere) -> {Values, CleanConditions} = values_conditions(Conditions), WhereTmp = where_clause(CleanConditions), SFields = [escape(F) || F <- SelectFields], Where = case ExtraWhere of [] -> WhereTmp; - ExtraWhere -> [WhereTmp, case WhereTmp of [] -> " "; _ -> " AND " end, ExtraWhere] + ExtraWhere -> + [WhereTmp, case WhereTmp of [] -> " "; _ -> " AND " end, ExtraWhere] end, Select = string:join(SFields, ","), % SelectedFields, Where clause, and Where values @@ -150,8 +164,11 @@ values_conditions({LogicalOp, Exprs}, {Values, CleanExprs, Count}) when (LogicalOp == 'and') or (LogicalOp == 'or') or (LogicalOp == 'not') -> - {NewValues, NewCleanExprs, NewCount} = values_conditions(Exprs, {Values, [], Count}), - {NewValues, [{LogicalOp, lists:reverse(NewCleanExprs)} | CleanExprs], NewCount}; + {NewValues, NewCleanExprs, NewCount} = + values_conditions(Exprs, {Values, [], Count}), + {NewValues, + [{LogicalOp, lists:reverse(NewCleanExprs)} | CleanExprs], + NewCount}; values_conditions({Name, Op, Value}, {Values, CleanExprs, Count}) when not is_atom(Value) -> sumo_internal:check_operator(Op), @@ -187,13 +204,15 @@ where_clause(Exprs, EscapeFun) -> where_clause(Exprs, EscapeFun, fun slot_question/1). -spec where_clause(sumo_internal:expression(), fun(), fun()) -> iodata(). +where_clause([], _EscapeFun, _SlotFun) -> + []; where_clause(Exprs, EscapeFun, SlotFun) when is_list(Exprs) -> - Clauses = lists:map(fun(Expr) -> where_clause(Expr, EscapeFun, SlotFun) end, Exprs), + Clauses = [where_clause(Expr, EscapeFun, SlotFun) || Expr <- Exprs], ["(", interpose(" AND ", Clauses), ")"]; where_clause({'and', Exprs}, EscapeFun, SlotFun) -> where_clause(Exprs, EscapeFun, SlotFun); where_clause({'or', Exprs}, EscapeFun, SlotFun) -> - Clauses = lists:map(fun(Expr) -> where_clause(Expr, EscapeFun, SlotFun) end, Exprs), + Clauses = [where_clause(Expr, EscapeFun, SlotFun) || Expr <- Exprs], ["(", interpose(" OR ", Clauses), ")"]; where_clause({'not', Expr}, EscapeFun, SlotFun) -> [" NOT ", "(", where_clause(Expr, EscapeFun, SlotFun), ")"]; @@ -211,7 +230,7 @@ where_clause({Name, 'not_null'}, EscapeFun, _SlotFun) -> -spec slot_question({'?', integer()}) -> string(). slot_question(_) -> " ? ". --spec slot_numbered({'?', integer()}) -> string(). +-spec slot_numbered({'?', integer()}) -> iodata(). slot_numbered({_, N}) -> [" $", integer_to_list(N), " "]. -spec interpose(term(), list()) -> list(). diff --git a/src/sumo_store.erl b/src/sumo_store.erl index b03d029..df5fc92 100644 --- a/src/sumo_store.erl +++ b/src/sumo_store.erl @@ -146,7 +146,7 @@ find_all(Name, DocName) -> %% @doc Returns Limit docs starting at Offset from the given store name, %% ordered by OrderField. OrderField may be 'undefined'. -spec find_all( - atom(), sumo:schema_name(), sumo:field_name(), + atom(), sumo:schema_name(), sumo:sort(), non_neg_integer(), non_neg_integer() ) -> {ok, [sumo_internal:doc()]} | {error, term()}. find_all(Name, DocName, SortFields, Limit, Offset) -> @@ -207,42 +207,42 @@ init([Module, Options]) -> -spec handle_call(term(), _, state()) -> {reply, tuple(), state()}. handle_call( {persist, Doc}, _From, - #state{handler=Handler, handler_state=HState}=State + #state{handler = Handler, handler_state = HState} = State ) -> {OkOrError, Reply, NewState} = Handler:persist(Doc, HState), {reply, {OkOrError, Reply}, State#state{handler_state=NewState}}; handle_call( {delete, DocName, Id}, _From, - #state{handler=Handler,handler_state=HState}=State + #state{handler = Handler, handler_state = HState} = State ) -> {OkOrError, Reply, NewState} = Handler:delete(DocName, Id, HState), {reply, {OkOrError, Reply}, State#state{handler_state=NewState}}; handle_call( {delete_by, DocName, Conditions}, _From, - #state{handler=Handler,handler_state=HState}=State + #state{handler = Handler, handler_state = HState} = State ) -> {OkOrError, Reply, NewState} = Handler:delete_by(DocName, Conditions, HState), {reply, {OkOrError, Reply}, State#state{handler_state=NewState}}; handle_call( {delete_all, DocName}, _From, - #state{handler=Handler,handler_state=HState}=State + #state{handler = Handler, handler_state = HState} = State ) -> {OkOrError, Reply, NewState} = Handler:delete_all(DocName, HState), {reply, {OkOrError, Reply}, State#state{handler_state=NewState}}; handle_call( {find_all, DocName}, _From, - #state{handler=Handler, handler_state=HState}=State + #state{handler = Handler, handler_state = HState} = State ) -> {OkOrError, Reply, NewState} = Handler:find_all(DocName, HState), {reply, {OkOrError, Reply}, State#state{handler_state = NewState}}; handle_call( {find_all, DocName, SortFields, Limit, Offset}, _From, - #state{handler=Handler, handler_state=HState}=State + #state{handler = Handler, handler_state = HState} = State ) -> {OkOrError, Reply, NewState} = Handler:find_all( DocName, SortFields, Limit, Offset, HState @@ -251,14 +251,14 @@ handle_call( handle_call( {find_by, DocName, Conditions}, _From, - #state{handler=Handler,handler_state=HState}=State + #state{handler = Handler, handler_state = HState} = State ) -> {OkOrError, Reply, NewState} = Handler:find_by(DocName, Conditions, HState), {reply, {OkOrError, Reply}, State#state{handler_state=NewState}}; handle_call( {find_by, DocName, Conditions, Limit, Offset}, _From, - #state{handler=Handler,handler_state=HState}=State + #state{handler = Handler, handler_state = HState} = State ) -> {OkOrError, Reply, NewState} = Handler:find_by( DocName, Conditions, Limit, Offset, HState @@ -284,7 +284,7 @@ handle_call( handle_call( {create_schema, Schema}, _From, - #state{handler=Handler,handler_state=HState}=State + #state{handler = Handler, handler_state = HState} = State ) -> {Result, NewState} = case Handler:create_schema(Schema, HState) of {ok, NewState_} -> {ok, NewState_}; diff --git a/src/sumo_store_elasticsearch.erl b/src/sumo_store_elasticsearch.erl index bf361bc..ce9574e 100644 --- a/src/sumo_store_elasticsearch.erl +++ b/src/sumo_store_elasticsearch.erl @@ -66,7 +66,8 @@ init(Options) -> {ok, #{index => Index, pool_name => PoolName}}. --spec persist(sumo:doc(), state()) -> sumo:user_doc(). +-spec persist(sumo_internal:doc(), state()) -> + sumo_store:result(sumo_internal:doc(), state()). persist(Doc, #{index := Index, pool_name := PoolName} = State) -> DocName = sumo_internal:doc_name(Doc), Type = atom_to_binary(DocName, utf8), diff --git a/src/sumo_store_mongo.erl b/src/sumo_store_mongo.erl index 025f3ca..84d5314 100644 --- a/src/sumo_store_mongo.erl +++ b/src/sumo_store_mongo.erl @@ -199,27 +199,29 @@ create_schema(Schema, #state{pool=Pool} = State) -> Fields = sumo_internal:schema_fields(Schema), lists:foreach( fun(Field) -> + create_field(Field), Name = sumo_internal:field_name(Field), - Attrs = sumo_internal:field_attrs(Field), - lists:foldl( - fun(Attr, Acc) -> - case create_index(Attr) of - none -> Acc; - IndexProp -> [IndexProp|Acc] - end - end, - [], - Attrs - ), lager:debug("creating index: ~p for ~p", [Name, SchemaName]), ok = emongo:ensure_index( - Pool, atom_to_list(SchemaName), [{atom_to_list(Name), 1}] - ) + Pool, atom_to_list(SchemaName), [{atom_to_list(Name), 1}]) end, Fields ), {ok, State}. +create_field(Field) -> + Attrs = sumo_internal:field_attrs(Field), + lists:foldl( + fun(Attr, Acc) -> + case create_index(Attr) of + none -> Acc; + IndexProp -> [IndexProp|Acc] + end + end, + [], + Attrs + ). + create_index(index) -> none; diff --git a/src/sumo_store_mysql.erl b/src/sumo_store_mysql.erl index 27b44e2..d6e0ad0 100644 --- a/src/sumo_store_mysql.erl +++ b/src/sumo_store_mysql.erl @@ -88,7 +88,7 @@ persist(Doc, State) -> [ColumnDqls, ColumnSqls] = lists:foldl( fun(Name, [Dqls, Sqls]) -> - Dql = [escape(atom_to_list(Name))], + Dql = [escape(Name)], Sql = "?", [[Dql | Dqls], [Sql | Sqls]] end, @@ -99,14 +99,14 @@ persist(Doc, State) -> NPColumnDqls = lists:foldl( fun(Name, Dqls) -> - Dql = [escape(atom_to_list(Name))], + Dql = [escape(Name)], [Dql | Dqls] end, [], NPFieldNames ), - TableName = escape(atom_to_list(DocName)), + TableName = escape(DocName), ColumnsText = string:join(ColumnDqls, ","), InsertValueSlots = string:join(ColumnSqls, ","), OnDuplicateColumns = [[ColumnName, "=?"] || ColumnName <- NPColumnDqls], @@ -149,8 +149,8 @@ persist(Doc, State) -> sumo_store:result(sumo_store:affected_rows(), state()). delete(DocName, Id, State) -> StatementName = prepare(DocName, delete, fun() -> [ - "DELETE FROM ", escape(atom_to_list(DocName)), - " WHERE ", escape(atom_to_list(sumo_internal:id_field_name(DocName))), + "DELETE FROM ", escape(DocName), + " WHERE ", escape(sumo_internal:id_field_name(DocName)), "=? LIMIT 1" ] end), case execute(StatementName, [Id], State) of @@ -169,7 +169,7 @@ delete_by(DocName, Conditions, State) -> StatementFun = fun() -> [ "DELETE FROM ", - escape(atom_to_list(DocName)), + escape(DocName), " WHERE ", lists:flatten(Clauses) ] @@ -185,7 +185,7 @@ delete_by(DocName, Conditions, State) -> sumo_store:result(sumo_store:affected_rows(), state()). delete_all(DocName, State) -> StatementName = prepare(DocName, delete_all, fun() -> - ["DELETE FROM ", escape(atom_to_list(DocName))] + ["DELETE FROM ", escape(DocName)] end), case execute(StatementName, State) of #ok_packet{affected_rows = NumRows} -> {ok, NumRows, State}; @@ -263,7 +263,7 @@ find_by(DocName, Conditions, SortFields, Limit, Offset, State) -> Fun = fun() -> % Select * is not good.. Sql1 = [ "SELECT * FROM ", - escape(atom_to_list(DocName)), + escape(DocName), WhereClause, OrderByClause ], @@ -306,8 +306,8 @@ create_schema(Schema, State) -> lists:map(fun create_index/1, Fields) ), Dql = [ - "CREATE TABLE IF NOT EXISTS ", escape(atom_to_list(Name)), " (", - string:join(FieldsDql, ","), ",", string:join(Indexes, ","), + "CREATE TABLE IF NOT EXISTS ", escape(Name), " (", + string:join(FieldsDql, ", "), ", ", string:join(Indexes, ", "), ") ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8" ], case execute(Dql, State) of @@ -322,25 +322,25 @@ create_column(Field) -> sumo_internal:field_attrs(Field)). create_column(Name, integer, Attrs) -> - [escape(atom_to_list(Name)), " INT(11) ", create_column_options(Attrs)]; + [escape(Name), " INT(11) ", create_column_options(Attrs)]; create_column(Name, float, Attrs) -> - [escape(atom_to_list(Name)), " FLOAT ", create_column_options(Attrs)]; + [escape(Name), " FLOAT ", create_column_options(Attrs)]; create_column(Name, text, Attrs) -> - [escape(atom_to_list(Name)), " TEXT ", create_column_options(Attrs)]; + [escape(Name), " TEXT ", create_column_options(Attrs)]; create_column(Name, binary, Attrs) -> - [escape(atom_to_list(Name)), " BLOB ", create_column_options(Attrs)]; + [escape(Name), " BLOB ", create_column_options(Attrs)]; create_column(Name, string, Attrs) -> - [escape(atom_to_list(Name)), " VARCHAR ", create_column_options(Attrs)]; + [escape(Name), " VARCHAR ", create_column_options(Attrs)]; create_column(Name, date, Attrs) -> - [escape(atom_to_list(Name)), " DATE ", create_column_options(Attrs)]; + [escape(Name), " DATE ", create_column_options(Attrs)]; create_column(Name, datetime, Attrs) -> - [escape(atom_to_list(Name)), " DATETIME ", create_column_options(Attrs)]. + [escape(Name), " DATETIME ", create_column_options(Attrs)]. create_column_options(Attrs) -> lists:filter(fun(T) -> is_list(T) end, lists:map( @@ -373,7 +373,7 @@ create_index(Field) -> )). create_index(Name, id) -> - ["PRIMARY KEY(", escape(atom_to_list(Name)), ")"]; + ["PRIMARY KEY(", escape(Name), ")"]; create_index(Name, unique) -> List = atom_to_list(Name), diff --git a/src/sumo_store_pgsql.erl b/src/sumo_store_pgsql.erl index 13c39fc..dfbaab6 100644 --- a/src/sumo_store_pgsql.erl +++ b/src/sumo_store_pgsql.erl @@ -117,7 +117,7 @@ persist(Doc, #{conn := Conn} = State) -> end, ProcessedValues = lists:map(ToNullFun, Values), - case pgsql:equery(Conn, Sql, ProcessedValues) of + case pgsql:equery(Conn, stringify(Sql), ProcessedValues) of {ok, _Count, _Columns, Rows} -> {LastId} = hd(Rows), NewDoc = sumo_internal:set_field(IdField, LastId, Doc), @@ -135,7 +135,7 @@ delete(DocName, Id, #{conn := Conn} = State) -> " WHERE ", escape(atom_to_list(sumo_internal:id_field_name(DocName))), "= $1 LIMIT 1"] , - case pgsql:equery(Conn, Sql, [Id]) of + case pgsql:equery(Conn, stringify(Sql), [Id]) of {ok, Count} -> {ok, Count > 0, State}; {error, Error} -> @@ -157,7 +157,7 @@ delete_by(DocName, Conditions, #{conn := Conn} = State) -> lists:flatten(Clauses) ], - case pgsql:equery(Conn, Sql, Values) of + case pgsql:equery(Conn, stringify(Sql), Values) of {ok, Count} -> {ok, Count, State}; {error, Error} -> @@ -169,7 +169,7 @@ delete_by(DocName, Conditions, #{conn := Conn} = State) -> delete_all(DocName, #{conn := Conn} = State) -> Sql = ["DELETE FROM ", escape(DocName)], - case pgsql:equery(Conn, Sql, []) of + case pgsql:equery(Conn, stringify(Sql), []) of {ok, Count} -> {ok, Count, State}; {error, Error} -> @@ -213,7 +213,8 @@ find_by(DocName, Conditions, Limit, Offset, State) -> non_neg_integer(), state()) -> sumo_store:result([sumo_internal:doc()], state()). -find_by(DocName, Conditions, SortFields, Limit, Offset, #{conn := Conn} = State) -> +find_by(DocName, Conditions, SortFields, Limit, Offset, State) -> + #{conn := Conn} = State, {Values, CleanConditions} = sumo_sql_builder:values_conditions(Conditions), Clauses = sumo_sql_builder:where_clause(CleanConditions, fun escape/1, @@ -256,7 +257,7 @@ find_by(DocName, Conditions, SortFields, Limit, Offset, #{conn := Conn} = State) Limit -> Values ++ [Limit, Offset] end, - case pgsql:equery(Conn, Sql2, AllValues) of + case pgsql:equery(Conn, stringify(Sql2), AllValues) of {ok, Columns, Rows} -> ColFun = fun(Col) -> binary_to_atom(Col#column.name, utf8) end, ColumnNames = lists:map(ColFun, Columns), @@ -295,7 +296,7 @@ create_schema(Schema, #{conn := Conn} = State) -> ), Dql = [ "CREATE TABLE IF NOT EXISTS ", escape(atom_to_list(Name)), " (", - string:join(FieldsDql, ","), ",", string:join(Indexes, ","), + string:join(FieldsDql, ", "), ", ", string:join(Indexes, ", "), ") " ], BinDql = iolist_to_binary(Dql), @@ -380,3 +381,7 @@ create_index(Name, index) -> ["KEY ", escape(List), " (", escape(List), ")"]; create_index(_, _) -> none. + +%% @todo remove this once pgsql specs are fixed to support iodata and make +%% dialyzer happy +stringify(Sql) -> binary_to_list(iolist_to_binary(Sql)). diff --git a/src/sumo_sup.erl b/src/sumo_sup.erl index 01f8536..c947745 100644 --- a/src/sumo_sup.erl +++ b/src/sumo_sup.erl @@ -29,8 +29,7 @@ {{supervisor:strategy(), non_neg_integer(), non_neg_integer()}, [supervisor:child_spec()] } - } - | ignore. + }. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Exports. @@ -41,23 +40,20 @@ %% Supervisor callbacks -export([init/1]). -%% Helper macro for declaring children of supervisor --define(CLD(I), {I, {I, start_link, []}, permanent, 5000, worker, [I]}). --define(SUP(I), {I, {I, start_link, []}, permanent, infinity, supervisor, [I]}). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Code starts here. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec start_link() -> supervisor:startlink_ret(). -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). +-spec start_link() -> {ok, pid()} | {error, term()}. +start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). --spec init(term()) -> init_result(). +-spec init([]) -> init_result(). init([]) -> {ok, { {one_for_one, 5, 10}, - [ ?SUP(sumo_backend_sup) - , ?SUP(sumo_store_sup) + [ sup(sumo_backend_sup) + , sup(sumo_store_sup) ] }}. + +sup(I) -> {I, {I, start_link, []}, permanent, infinity, supervisor, [I]}.